cubemx stm32 串口终端的实现与使用 (可以由用户自定义各种终端指令任意) 驱动代码

【嵌入式项目应用】__UART自定义通信协议代码实现方法 通信协议不难理解,就是两个(或多个)设备之间进行通信,必须要遵循的一种协议。百度百科的解释:通信协议是指双方实体完成通信或服务所必须遵循的规则和约定。通过通信信道和设备互连起来的多个不同地理位置的数据通信系统,要使其能协同工作实现信息交换和资源共享,它们之间必须具有共同的语言。交流什么、怎样交流及何时交流,都必须遵循某种互相都能接受的规则。这个规则就是通信协议。相应该有很多读者都买过一些基于串口通信的模块,市面上很多基于串口通信的模块都是自定义通信协议,有的比较简单,有的相对复杂一点。 阅读详情

简言

我之前学linux的时候,觉得linux的命令很酷,最近又有写代码的热情,于是手撸了一个串口终端。

初次使用

介绍特性

  1. 像终端一样使用

    • 输入niubi后,键入回车,输出自己写的回调函数中的内容。

    image-20221026143340286

  2. 支持tab键补全

    • 按下n后按下tab键后,到库中匹配关键词,匹配到niubi,显示到下一行。

    image-20221026143340286

    • 当有多个关键词可以匹配时,提示匹配关键词,并在新的一行显示刚刚输入的内容。

      在这里插入图片描述

  3. 支持Backspace键

    • 当输入有误时,可以按Backspace键,回退到上一格。

宏定义的定义与配置

在 yxy_open_cmd.h 中修改对应的宏:

  • 主要修改第一个串口句柄的宏,以及最后一个换行的宏,其他的根据需求来修改。

在这里插入图片描述

初始化

全局区(添加自定义终端指令变量和对应的回调函数)

// 全局区
User_USART_RX uClear;

/* 关键词clear回调函数 */
static void clear_callback(void)
{
	yxy_DEBUG("\x1b[2J\x1b[H");
}

main函数

// main
uart_RX_init();

rx_Pack_add(&uClear, (uint8_t *)"clear", str_len((uint8_t *)"clear"), (uint8_t *)"清屏", clear_callback);

使用

循环


uart_rx_scan();
HAL_Delay(1);

效果

在这里插入图片描述

主要代码

yxy_open_cmd.c

/* 头文件 */
#include "yxy_open_cmd.h"
#include "stdlib.h"
#include "stdarg.h"
#include "string.h"
#include "usart.h"
#include "yxy_debug.h"



/* 全局变量 */

// 接收数据结构体
rx_Struct rx_InitStr;

// 包链表数据头节点
struct pack_struct * packhandNode = NULL;

// 轮询调用函数指针,以处理对应的包函数
pack_Callback scanHandle = NULL;

// 添加 过 的特殊包的总数量
uint16_t packIdNum = 0;

// 屏幕上的数据数量
uint32_t cmd_buff = 0;


/*********************
 *   内部函数区域
 *********************/

static HAL_StatusTypeDef string_contrast(uint8_t * s1, uint16_t s1Len, uint8_t * s2);
static uint16_t string_compare_hand(uint8_t * s1, uint8_t * s2);
static void rx_struct_Clear(void);
static void pack_classify(void);




/**
  * @brief  字符串对比函数
  * @param  字符串1
  * @param  字符串2
  * @retval 返回HAL_OK,匹配成功;返回HAL_ERROR,匹配失败。
  */
static HAL_StatusTypeDef string_contrast(uint8_t * s1, uint16_t s1Len, uint8_t * s2)
{
	uint32_t len = s1Len;
	uint8_t *pS1 = s1, *pS2 = s2;
	
	while(len>0)
	{
//		yxy_DEBUG("s1: %c, S2: %c\r\n", *pS1, *pS2);
		
		if(*pS1 != *pS2)
		{
//			yxy_DEBUG("%s != %s\r\n", s1, s2);
			return HAL_ERROR;
		}
		pS1++;
		pS2++;
		len--;
	}
	
	return HAL_OK;
}


/**
  * @brief  子串比较函数
  * @param  子串1
  * @param  子串2
  * @retval 从头比较的相同字符的子串长度
  */
static uint16_t string_compare_hand(uint8_t * s1, uint8_t * s2)
{
	uint16_t sonLen = 0;
	uint16_t l1=0, l2=0;
	uint8_t *pS1=s1, *pS2=s2;
	
	l1 = str_len(s1);
	l2 = str_len(s2);
	
//	yxy_DEBUG("comp : l1=%d, l2=%d\r\n", l1, l2);
	
	// 比较 关键词串 和 接收串,一旦 接收串>=关键词串,则不用执行tab。
	if(l1 <= l2)
	{
		return  0;
	}
	
	while(*pS1 == *pS2)
	{
		pS1++;
		pS2++;
		sonLen++;
	}
	
	return sonLen;
}

/* 接收结构体清除 */
static void rx_struct_Clear(void)
{
	int i;
	for(i=0; i<RX_BUF_SIZE; i++)
	{
		rx_InitStr.rx_buf[i] = 0;
	}
	
	rx_InitStr.a_rx_buf = 0;
	rx_InitStr.rx_cnt = 0;
}

/**
  * @brief  tab处理函数,用于查找并适配库中的文本
  * @retval 无
  */
static void tab_handler(void)
{
	uint16_t maxLen = 0;	// 记录最大长度
	uint16_t middleLen = 0; // 记录一次比较的长度
	uint16_t sameCount = 0;	// 记录相同长度的计数
	uint16_t wrapCnt = 0;	// 相同关键词的换行计数,一行超过5个,则换行
	
	uint8_t *pRxBuf = rx_InitStr.rx_buf;
	struct pack_struct * pTemp[5] = {NULL};	// 用来存储子串相同的
	struct pack_struct * pPN = packhandNode->packNext;
	
	while(pPN != NULL)
	{
		middleLen = string_compare_hand(pPN->special, pRxBuf);
		
		if((maxLen == middleLen) && (maxLen != 0))
		{
			pTemp[sameCount++] = pPN;
		}

		if(maxLen < middleLen)
		{
			maxLen = middleLen;
			sameCount = 0;
			
			pTemp[sameCount++] = pPN;
		}
		
		pPN = pPN->packNext;
	}
	
	// 如果只有1个元素
	if(sameCount == 1)
	{
		while(rx_InitStr.rx_cnt <= pTemp[0]->specialLen)
		{
			rx_InitStr.rx_buf[rx_InitStr.rx_cnt] = pTemp[0]->special[rx_InitStr.rx_cnt];
			rx_InitStr.rx_cnt++;
		}
		rx_InitStr.rx_cnt -= 1;
		
		yxy_DEBUG("\r\n%s", pTemp[0]->special);
		return;
	}
	
	// 如果有一堆元素
	yxy_DEBUG("\r\n");
	while(sameCount--)
	{
		// 一行过多字符换行
		wrapCnt++;
		if(wrapCnt >= 3)
		{
			yxy_DEBUG("\r\n");
			wrapCnt=0;
		}
		
		// 显示有可能的字符
		yxy_DEBUG("%s  ", pTemp[sameCount]->special);
	}
	yxy_DEBUG("\r\n");
	
	// 显示原本输入的
//	yxy_DEBUG("\r\nux cnt %d\r\n", rx_InitStr.rx_cnt);
	yxy_DEBUG("%s", rx_InitStr.rx_buf);
}


/**
  * @brief  包处理分类函数
  * @retval 
  */
static void pack_classify(void)
{
	uint16_t iCnt = 0;
	uint8_t *pRxBuf = rx_InitStr.rx_buf;
	struct pack_struct * pPN = packhandNode->packNext; // 节点特殊字节点
	
	while(pPN)
	{
		// 如果输入的词句与关键词匹配成功
		if(string_contrast(pPN->special, pPN->specialLen, pRxBuf) == HAL_OK)
		{
			// 算出关键词后的,数据的长度
			pPN->userRX->dataLen = str_len(pRxBuf) - pPN->specialLen;
			
			// 赋值关键词的数据
			while(iCnt < pPN->userRX->dataLen)
			{
				pPN->userRX->dataVal[iCnt] = pRxBuf[pPN->specialLen+iCnt];
				iCnt++;
			}
			pPN->userRX->dataVal[iCnt] = '\0';
			
//			yxy_DEBUG("keyword data      : %s \r\n", pPN->userRX->dataVal);
//			yxy_DEBUG("keyword data size : %d \r\n", pPN->userRX->dataLen);
//			yxy_DEBUG("keyword           : %s be ready.\r\n\r\n", pPN->special);
			
			// 则置位在 scanHandle 变量中,等待下次在扫描函数中去调用
			scanHandle = pPN->callbackFun;
			return;
		}
		
		pPN = pPN->packNext;
	}
}



/*********************
 *   外部函数区域
 *********************/


/* 手写printf,避免使用微库造成不必要的错误 */
void U_Printf(const char *fmt, ...)
{
    char Uart_buf[TX_BUF_SIZE];

    va_list args;
    va_start(args, fmt);
    int length = vsnprintf(Uart_buf, sizeof(Uart_buf) - 1, fmt, args);
    va_end(args);
	
	cmd_buff += length;

    HAL_UART_Transmit(&uuart, (uint8_t *)Uart_buf, length, 0xfff);
}

/* 串口接收初始化 */
HAL_StatusTypeDef uart_RX_init(void)
{
	// 接收结构体清除
	rx_struct_Clear();
	
	// 串口接收中断初始化
	HAL_UART_Receive_IT(&uuart, &rx_InitStr.a_rx_buf, 1);
	
	packhandNode = (struct pack_struct *)malloc(sizeof(struct pack_struct));
	if(packhandNode == NULL)
	{
		yxy_DEBUG("open usart initialization failed... \r\n");
		return HAL_ERROR;
	}
	packhandNode->packNext = NULL;
	
	packhandNode->userRX = NULL;
	packhandNode->packId = 0xffff;
	
	strcpy((char *)packhandNode->special, "x");
	strcpy((char *)packhandNode->packText, "USER");
	
//	yxy_DEBUG("special : %s, size = %d\r\n", packhandNode->special, sizeof("xxxxxxxxx"));
//	yxy_DEBUG("packText : %s, size = %d\r\n", packhandNode->packText, sizeof("UART link hand node."));
	
	return HAL_OK;
}

/**
  * @brief  串口接收添加可识别的包,包头为特殊字符串
			当接收到这个特殊字符串时,既包开始接收,
			数据不定长,需要用户在自己的回调中处理,
			当接收到回车换行时,处理包的内容,
			并在下次main循环中调用对应的回调函数。
  * @param  userRX 用户定义的初始化结构体
  * @param  special 需要添加的特殊字符串
  * @param  specialLen 特殊字符串的长度
  * @param  packText 包的解释语言
  * @param  callbackFun 解析包的回调函数
  * @retval 返回hal库错误码
  */
HAL_StatusTypeDef rx_Pack_add(User_USART_RX *userRX, uint8_t * special, uint8_t specialLen,
								uint8_t * packText, pack_Callback callbackFun)
{
	uint16_t iCnt = 0;
	struct pack_struct * pPN = packhandNode; // 节点特殊字节点
	
	// 找到空的节点
	while(pPN->packNext != NULL)
	{
		pPN = pPN->packNext;
	}
	
	pPN->packNext = (struct pack_struct *)malloc(sizeof(struct pack_struct));
	
	pPN = pPN->packNext;
	if(pPN == NULL)
	{
		yxy_DEBUG("usart add failed... \r\n");
		return HAL_ERROR;
	}
	pPN->packNext = NULL;
	
	pPN->specialLen = specialLen;
	pPN->userRX = userRX;
	pPN->packId = packIdNum;
	
	strcpy((char *)pPN->special, (const char *)special);
	strcpy((char *)pPN->packText, (const char *)packText);
	
	yxy_DEBUG("add special : %s, size = %d\r\n", pPN->special, str_len(special));
//	yxy_DEBUG("add packText : %s, size = %d\r\n", pPN->packText, str_len(packText));
	
	pPN->callbackFun = callbackFun;
	
	
	// 用户句柄赋值
	userRX->special = pPN->special;
	userRX->packText = pPN->packText;
	userRX->specialLen = pPN->specialLen;
	userRX->packId = packIdNum;
	while(iCnt++ < SPECIAL_DATA_SIZE)
	{
		userRX->dataVal[iCnt] = 0;
	}
	userRX->dataLen = 0;
	
	packIdNum++;
	
	return HAL_OK;
}

/**
  * @brief  串口接收可识的包的删除
  * @param  
  * @retval 返回hal库错误码
  */
HAL_StatusTypeDef rx_Pack_clear(User_USART_RX *userRX)
{
	struct pack_struct * pPN = packhandNode; // 节点特殊字节点
	struct pack_struct * pTextN = NULL; 
	
	// 找到空的节点
	while(pPN->packNext != NULL)
	{
		// 如果id匹配的则删除对应id的特殊包节点
		if(pPN->packNext->packId == userRX->packId)
		{
			pTextN = pPN->packNext;
			pPN->packNext = pPN->packNext->packNext;
			
			free(pTextN);
			pTextN = NULL;
			
			userRX->packId = 0;
			
			yxy_DEBUG("clear rx ok\r\n");
			return HAL_OK;
		}
		
		pPN = pPN->packNext;
	}
	
	return HAL_ERROR;
}

/**
  * @brief  接收扫描函数,一旦接收到关键词,在这里处理 
  * @retval 
  */
void uart_rx_scan(void)
{
	if(scanHandle != NULL)
	{
		scanHandle();
		scanHandle = NULL;
	}
}



/* 串口中断函数 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(rx_InitStr.rx_cnt >= RX_BUF_SIZE)//溢出判断
	{
		yxy_DEBUG("ESP data overflow\r\n");
//			yxy_DEBUG("%s\r\n", rx_InitStr.rx_buf);
		rx_struct_Clear();
	}
	else //数据存入
	{
		HAL_UART_Transmit(&uuart, (uint8_t *)&(rx_InitStr.a_rx_buf), 1, 0xff);
//			yxy_DEBUG("%c: 0x%02x \r\n", rx_InitStr.a_rx_buf, rx_InitStr.a_rx_buf);

//			yxy_DEBUG("%d : %c\r\n", rx_InitStr.rx_cnt, rx_InitStr.a_rx_buf);
		
		rx_InitStr.rx_buf[rx_InitStr.rx_cnt] = rx_InitStr.a_rx_buf;
		rx_InitStr.rx_cnt++;
		
		// 一旦等于删除建,就退回到之前的
		if((rx_InitStr.a_rx_buf == 0x08) && (rx_InitStr.rx_cnt >= 2))
		{
			rx_InitStr.rx_cnt -= 2;
		}
		
		// 等于水平制表符键位
		if(rx_InitStr.a_rx_buf == 0x09)
		{
			rx_InitStr.rx_cnt--;
			rx_InitStr.rx_buf[rx_InitStr.rx_cnt] = '\0';
			if(rx_InitStr.rx_cnt != 0)
			{
				tab_handler();
			}
		}
		
		
#if (CR_LF_IS_WHITCH == 0)
		// 当上一个字符为\r这一次字符为\n,说明一行的结束
		if(rx_InitStr.rx_buf[rx_InitStr.rx_cnt-2] == '\r' && 
			rx_InitStr.rx_buf[rx_InitStr.rx_cnt-1] == '\n')
		{
			rx_InitStr.rx_buf[rx_InitStr.rx_cnt-2] = '\0';
#elif (CR_LF_IS_WHITCH == 1)
		// 当这一次字符为\n,说明一行的结束
		if(rx_InitStr.rx_buf[rx_InitStr.rx_cnt-1] == '\n')
		{
		
#elif (CR_LF_IS_WHITCH == 2)
		if(rx_InitStr.rx_buf[rx_InitStr.rx_cnt-1] == '\r')
		{
#endif
//			yxy_DEBUG("\r\ninput = %s\r\n", rx_InitStr.rx_buf);
			yxy_DEBUG("\r\n");

			rx_InitStr.rx_buf[rx_InitStr.rx_cnt-1] = '\0';
			pack_classify();
			rx_struct_Clear();
		}
	}
	
	HAL_UART_Receive_IT(&uuart, (uint8_t *)&(rx_InitStr.a_rx_buf), 1);   //再开启接收中断
}





yxy_open_cmd.h

#ifndef __YXY_OPEN_CMD_H_
#define __YXY_OPEN_CMD_H_


#ifdef __cplusplus
extern "C" {
#endif

/*********************
 *      INCLUDES
 *********************/

#include "stdio.h"
#include "usart.h"

/*********************
 *      DEFINES
 *********************/

/* 串口的hal库的句柄 */
#define uuart huart1

/* 串口发送缓冲区 */
#define TX_BUF_SIZE 64

/* 串口接收buff大小 */
#define RX_BUF_SIZE 64

/* 包关键词支持的大小 */
#define SPECIAL_CHAR_SIZE 10

/* 包关键词的说明文档支持的大小 */
#define SPECIAL_TEXT_SIZE 50

/* 包关键词的数据支持的大小 */
#define SPECIAL_DATA_SIZE 15

/* 回车换行是\r\n 则设置为0是;为\n(0x0a),则设置为 1 ;为\r(0x0d),则设置成 2*/
#define CR_LF_IS_WHITCH 2


#if (CR_LF_IS_WHITCH == 0)
#	define USER_CRLF \r\n
#elif (CR_LF_IS_WHITCH == 1)
#	define USER_CRLF \n
#elif (CR_LF_IS_WHITCH == 2)
#	define USER_CRLF \r
#else
#	define USER_CRLF
#endif

/**********************
 *      TYPEDEFS
 **********************/
 
 /* 包回调函数格式 */ 
typedef void(*pack_Callback)(void);
 
/* 接收结构体 */
typedef struct rx_struct
{
	uint8_t  rx_buf[RX_BUF_SIZE];	// 接收缓冲存取区
	uint8_t  a_rx_buf;				// 缓冲区元素暂存位置
	uint16_t rx_cnt;				//当前接收计数
}rx_Struct;

/* 用户存储信息指针结构体 */
typedef struct 
{
	uint8_t *special;	// 关键词
	uint8_t *packText;	// 关键词解释
	uint8_t specialLen;	// 关键词长度
	uint8_t dataVal[SPECIAL_DATA_SIZE];	// 数据值
	uint8_t dataLen;	// 数据长度
	uint16_t packId;	// 特殊字id
}User_USART_RX;

/* 接收包的链表结构体 */
struct pack_struct
{
	uint8_t special[SPECIAL_CHAR_SIZE];	// 需要判定的特殊字符串
	uint8_t packText[SPECIAL_TEXT_SIZE];// 特殊字的说明	
	
	uint8_t specialLen;					// 特殊字长度
	
	uint16_t packId;					// 特殊字id
	
	User_USART_RX * userRX;				// 访问用户的指针
	
	pack_Callback callbackFun;			// 包回调函数
	
	struct pack_struct * packNext;		// 下一个节点
};




/**********************
* GLOBAL PROTOTYPES
**********************/
/**
  * @brief  获取字符串长度
  * @param  字符串指针
  * @retval 返回字符串长度
  */
inline static uint16_t str_len(uint8_t * str)
{
	uint16_t len=0;
	
	while(*str != '\0')
	{
		len++;
		str++;
	}
	
	return len;
}


/* 测试 */

//HAL_StatusTypeDef string_cont(uint8_t * s1, uint8_t * s2);

/**********************
* GLOBAL PROTOTYPES
**********************/

extern uint32_t cmd_buff;


void U_Printf(const char *fmt, ...);


/*	//用法

	U_Printf("niubi%d\n", 12);

*/

HAL_StatusTypeDef uart_RX_init(void);
HAL_StatusTypeDef rx_Pack_add(User_USART_RX *userRX, uint8_t * special, uint8_t specialLen,
								uint8_t * packText, pack_Callback callbackFun);
HAL_StatusTypeDef rx_Pack_clear(User_USART_RX *userRX);

void uart_rx_scan(void);


#ifdef __cplusplus
} /*extern "C"*/
#endif

#endif /*__YXY_OPEN_CMD_H_*/

yxy_debug.c

#include "yxy_debug.h"
#include "stdio.h"


#ifdef YXY_DEBUG
#	if (YXY_DEBUG_SELE == 0)
		//重定向c库函数printf到串口DEBUG_USART,重定向后可使用printf函数
		int fputc(int ch, FILE *f)
		{
			/* 发送一个字节数据到串口DEBUG_USART */
			HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1000);	
			
			return (ch);
		}
#	endif
#endif


void error_printf(const char * file, int line, const char * func, const char * format, ...)
{
	USER_PASS;
}

yxy_debug.h

#ifndef __YXY_DEBUG_H_
#define __YXY_DEBUG_H_


#ifdef __cplusplus
extern "C" {
#endif

/*********************
 *      INCLUDES
 *********************/

#include "stdint.h"


/*********************
 *      DEFINES
 *********************/
 
// 相当于python的 pass 语句
#define USER_PASS ((void)0U)

// 打印转换后的宏
#define _VNAME(name) (#name)
#define VNAME(name)	(_VNAME(name))


//// 弱定义
//#define __WEAK                                 __attribute__((weak))

/* 按键串口调试支持 0 不支持 , 1 支持*/
#define YXY_DEBUG 1
#ifdef YXY_DEBUG

	/* debug 选择,
	 * 为 1 选择外部的 用户自定义的打印函数,
	 * 为0则调用stdio库中的printf
	*/
#	define YXY_DEBUG_SELE 1

	/* 开启debug,这里的外部的debug文件可以重写 */
	/* 调试的外部函数,可以是printf或者是其他手写的多参数打印函数
	 * 多参数打印函数的格式是 :
	 * 		void function(const char *, ...);
	 */
#	if (YXY_DEBUG_SELE == 1)
#		include "yxy_open_cmd.h" 
#		define __uprintf__ U_Printf
#	else
#		include "stdio.h"
#		define __uprintf__ printf
#	endif

#endif

/* 调试函数实现 */
#if (YXY_DEBUG == 1)
#	define yxy_DEBUG(...) __uprintf__(__VA_ARGS__)
#else
#	define yxy_DEBUG(...) do{}while(0)
#endif
/**********************
 *      TYPEDEFS
 **********************/
 


/**********************
*  GLOBAL PROTOTYPES
**********************/


#ifdef __cplusplus
} /*extern "C"*/
#endif

#endif /*__YXY_DEBUG_H_*/

完整代码工程

开源终端完整工程代码点这里

STM32串口自定义数据接收协议 最近使用STM32做串口数据收发,遇到了一些问题。折腾了一番,在此记录一下。 阅读详情

相关推荐

STM32学习笔记 二、基于STM32F103C8T6和STM32CubeMX实现UART串口通信数据收发

系列文章目录 一、基于STM32F103C8T6最小系统板和STM32CubeMX实现LED灯循环闪烁 文章目录系列文章目录前言一、配置CubeMX二、逻辑代码部分 前言 这次我用的板子是一个用STM32F103C8T6作为主控芯片的一个数据采集卡,两个LED灯连接的引脚是PB3与PB4,TX与RX引脚分别是PA9和PA10。 一、配置CubeMX 1、新建工程; 2、配置时钟源,在RCC里面的HSE配置的是晶振时钟; 3、配置程序烧录引脚SYS为SWD模式; 4、配置GPIO口,配置一个LE

遗忘丶的博客 1万+

STM32F1的命令行解释器shell,单片机同系列移植便捷版

在我们调试STM32的时候有时候会不知道怎么调试,或者调试起来麻烦,但是拥有了控制台就不一样了,调试效率会大大提升,这篇文章就是基于别人的shell修改而来,加强了stm32f1系列单片机的移植,实现傻瓜式移植 ,在此感谢“古么宁”大大的开源项目。

-小龙--的博客 2628

STM32CubeMX在FreeRTOS下使用串口进行数据收发(不定长度)

STM32在使用操作系统时频繁进出临界区会导致操作系统状态混乱从而造成不知道会发生什么的后果,写程序是不喜欢这些东西的。所以通过定时器、串口中断为操作系统开发串口功能,以免在使用操作系统时对使用AT指令的串口外设造成影响!

ASWaterbenben的博客 1万+

STM32 自定义串口协议

STM32 自定义串口协议串行通信原理与优缺点分类按通信方向按通信方式异步串行引脚连接串口外设之间ARM与PC之间字符帧格式串口通信过程串口框图并行通信异步串行实例(自定义通信协议)项目需求项目框架上位机下传帧格式代码设计 串行通信 在设计实现某种功能的系统时,常需要不同设备之间的通信。通信主要有串行和并行两种方式,应用比较广泛的UART(Universial Asynchonous Receiver Transmitter)通用异步收发器,SPI(Serial Peripheral Interface)串

尽欢的博客 1万+

STM32 HAL库多串口中断通讯【任意长度 不丢帧】

基于HAL库的多串口通信入门,多串口中断通信,可以发任意字节数据,用的STM32F302CBTx,STM32F103也一样通用。 本例使用三组串口中断互相通信。 1、背景了解和工具 现在开发stm32一般有几种方式,如下 HAL库开发: STM32CubeMX中可以下载各个stm系列的HAL库,集成stm常见的外设,便于一键生成项目代码。STM32CubeMX是ST公司现在主推的一个配置...

liusuxx的专栏 8449

【Qt上位机与STM32进行串口通信】-1-Qt Creator、keil以及STM32CubeMX的安装

【Qt学习】Qt Creator、keil以及STM32CubeMX的安装

Xie_01的博客 6709

STM32CubeMX 真的不要太好用

STM32CubeMX 真的不要太好用 由于工作内容的变动,我已经很久没有正经的玩过单片机了,近期又要用它做个小玩意了,还是选 stm32 吧,外设库开发不要太方便,哈哈哈 先去 stm32 社区逛了逛,发现了一个新字眼 STM32CubeMX,简单看了下,大概明白是个 ST 公司新推出的一个配置工具,直接由图形界面简单配置下,生成初始化代码,并对外设做了进一步的抽象,让开发人员更只专注应用的开发...

jxgz_leo的博客 6万+

stm32cubeMX 串口usart + DMA + FREERTOS配置 DMA接收任意长度数据

STM32cubeMX版本5.2.1 最近由于工作需要用到LWIP,看到网上有人说用cubeMX可以生成LWIP库,同时还能生成FREERTOS系统。就索性把整个工程的驱动库都换成了cubeMX生成的HAL库,在之前写工程基础上更改驱动配置,踩了一些坑,在这里把串口的配置及部分软件代码分享出来,希望能对大家有所帮助。 1、时钟配置 2、串口配置 3、...

weixin_40729877的博客 8951

【STM32】基于STM32F407实现串口通信

目录一、通用同步异步收发器 (USART)1.USART 简介2.USART 功能说明3.USART框图二、基于HAL库实现串口通信1.工程创建2. HAL库UART函数库介绍3.USART接收与发送三、总结四、参考 一、通用同步异步收发器 (USART) 1.USART 简介   通用同步异步收发器 (USART)能够灵活地与外部设备进行全双工数据交换,满足外部设备对工业标准 NRZ异步串行数据格式的要求。USART通过小数波特率发生器提供了多种波特率。   它支持同步单向通信和半双工单线通信,还支持LI

YangMax1的博客 1万+

一篇文章入门Stm32CubeMX在freertos系统下进行uart串口通讯

一篇文章学会一个开发软件和一个操作系统的使用

qq_42289734的博客 7966

第二章:STM32MxCube配置串口

2134567890-

xuquanlin_1680的博客 2万+

【STM32】STM32CubeMX安装教程详解

STM32CubeMX是STM32Cube工具家族中的一员,从MCU/MPU选型,引脚配置,系统时钟以及外设时钟设置,到外设参数配置,中间件参数配置,它给STM32开发者们提供了一种简单,方便,并且直观的方式来完成这些工作。所有的配置完成后,它还可以根据所选的IDE生成对应的工程和初始化C代码。除此以外,STM32CubeMX还提供了功耗计算工具,可作为产品设计中功耗评估的参考。

C/C++软件工程师、嵌入式软件工程师、物联网研发工程师、C/C++讲师、物联网讲师、嵌入式讲师、鸿蒙讲师---欢迎大家一起交流(私信添加博主微信) 1万+

适用于stm32的shell代码和例程

内置 "cmd-list" 命令获取所有命令列表 支持 tab 键补全命令 支持 backspace 回退,这个在 putty 上有 bug ,还没修复。在secureCRT正常。 支持上下箭头回溯历史,这个功能没有做的很好; 支持左右箭头编辑当前命令行输入; 提供 shell_cmdparam()函数转换命令后所跟的数字参数(字符串转整型),详见 demo 提供 shell_option_suport() 函数,使命令行支持 getopt()函数,详见 demo 系统共有9个文件,全部与硬件无关,编译语言要在 C99 以上(keil 在 project -> Options .. -> c/c++ -> C99 勾上) kernel.h // 一些必要的宏定义 shell.c,shell.h //具体的命令行解释的实现 ustdio.c,ustdio.h //非标准输出文件,重新链接 printf ,并提供一个小巧型的 printk 函数 avltree.c,avltree.h //平衡二叉树支持,shell 默认用链表建立查询机制,有必要可在shell.h 中开启二叉树 getopt.c,getopt.h //网上找的 getopt() 源码 除了 getopt.c,getopt.h 两个文件是我从网上找的源码,主要实现命令行的 getopt()解析,其他的都是笔者所写。 使用: 1,首先把 shell.c ustdio.c 加入工程,这两个文件是必须的。 如果不用 getopt()函数可以不添加getopt.c文件。 shell默认使用链表来构建查询系统,但也提供了平衡二叉树的方式,在shell.h中有开启的开关,如不需要可以不添加avltree.c。 include "shell.h" 2,先对硬件进行基本的初始化。 撰写串口发送函数,形如 void usart_puts(char * str , uint16_t len) ; 调用函数 shell_init("shell >",usart_puts); //初始化shell的输入标志和默认输出指向 串口接收以包为单位。 3,新建全局变量 struct shell_input serial_shell ; 并初始化 SHELL_INPUT_INIT(&serial;_shell ,usart_puts);初始化输入缓存和输出交互; 4,串口接收到一整包函数后,调用 shell_input(&serial;_shell , packet , pktlen) ;

使用stm32cubemx的usb-host-cdc库驱动EC20模块

使用stm32cubemx的usb-host-cdc库驱动EC20模块开发环境:实现功能:开始1、使用STM32CubeMX配置工程,生成基础代码2、修改cubemx生成的代码3、 编译下载运行,成功发送at并接收到at指令回复。总结 开发环境: 开发板:正点原子F407探索者 代码生成工具:STM32CubeMX v5.4.0 IDE: eclipse + ac6工具链 实现功能: 单片...

gyzw_mx的博客 1万+

【深度学习】肺结节分割项目实战一:处理数据集

一、LIDC-IDRI数据集简介 官方网站 肺图像数据库协会的图像收集(LIDC-IDRI)包括诊断和带有病变注释标记的肺癌筛查胸部CT。这是一个网络公开的国际资源,用于肺癌检测和诊断的计算机辅助诊断(CAD)方法的开发、培训和评估。 数据集包含1018个病例,每个病例包括来自临床胸部CT扫描的图像和一个相关的XML文件,该文件记录了由四名经验丰富的胸部放射科医生进行的两阶段图像注释过程的结果。在最初的盲读阶段,每个放射科医生独立审查每个CT扫描,并标记出属于三种类型之一的病变(“结节>= 3mm”,

weixin_42182906的博客 1万+

论文解析-sciPENN

用这2个CITE-seq共有的protein作为训练集,分别将他们特有的protein作为测试集,评估sciPENN对CITE-seq数据集中缺失的protein丰度预测准确度。来自Seurat 4 的PBMC数据集,样本被注射水疱性口炎病毒 (VSV) 载体 HIV 疫苗,数据集包含每个样本注射疫苗0天、3天、7天的CITE-seq数据。PBMCs作为训练集,MALT作为测试集,利用scRNA-seq测试17个蛋白质表达水平。Monocyte:随机取4个样本作为训练集,剩余4个样本作为测试集。

weixin_46065198的博客 1022

STM32CubeMX | 基于STM32使用HAL库驱动RTC时钟及闹钟功能

STM32使用HAL库驱动RTC时钟及闹钟功能 目录STM32使用HAL库驱动RTC时钟及闹钟功能(一)工程配置(二)代码编写(三)运行效果 本章实现效果: RTC时钟获取 周期进行闹钟提醒(这个功能常用于周期性低功耗的需求,例如让RTC闹钟提醒事件的中断唤醒处于低功耗模式的单片机,让单片机采集完数据之后再次进入低功耗,这样周期执行以达到省电的目的。) (一)工程配置 我使用STM32F103RB这一款MCU,首先使用STM32CUBEMX创建工程,配置主时钟和RTC时钟,我板子上是使用的外部的32

^_^ 1万+

【算法】图解A* 搜索算法

A* 算法是启发式搜索算法,是根据Dijkstra算法改进而来。 问题引入 如下图所示,S为起始(start)节点,G为目标(goal)节点。 节点之间连线是两点的路径长度,如A到E的路径长度c(A,E) = 9。 节点旁的h值时当前节点到达目标节点(G)的预估值,如h(A)=15, 表示从当前点A到达目标点G的估计路径长度为15,此处h(x)即为启发函数,启发函数的设计有很多方法,具体可......

zhuhua造轮子的博客 4万+

STM32CubeMX-配置SPI驱动MAX31865读取铂电阻温度

STM32CubeMX-配置SPI驱动MAX31865读取铂电阻温度

嵌入式学习记录站点 1万+

基于51单片机语音识别智能台灯设计

智能台灯;语音识别;STC89C52RC单片机

Benso_H的博客 5842

uniapp - 实现免费OCR文字识别方案,用户上传图片后进行扫描识别获取图片上的文字,uniApp纯前端做图片OCR识别——无需服务器进行OCR识别插件(不需要依赖任何SDK或第三方API服务)

uniapp实现orc识别免费的插件识别不花钱,无需第三方OCR文字识别API,uniApp拍照或者选择图片免费识别,纯前端不需要服务器,Uniapp提取图片上的文字,本地ocr识别技术,uni-app图片OCR识别文本,uniapp有没有免费的ocr识别组件,uniapp前端实现文字识别,身份证识别,营业执照识别 (兼容APP、H5、小程序 不需要任何SDK)跨端无SDK方案:Uniapp实现OCR文字,离线OCR识别,uniapp tesseract.js实现文字识别,uniapp文字识别无需依赖sdk

🏆 前端领域优质创作者 4250

STM32 HAL库 CubeMX教程(五)串口通信基础

STM32 HAL库 CubeMX教程(五)串口通信基础串口通信简介CubeMX配置初始化程序分析程序编写 UART: 通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),通常称作 UART。它将要传输的资料在串行通信与并行通信之间加以转换。作为把并行输入信号转成串行输出信号的芯片, UART 通常被集成于其他通讯接口的连结上。 USART...

带火星的小木条的博客 2万+

Python re库【正则表达式操作库】全面讲解及代码示例

以下是Pythonre库的全面讲解,包含主要功能、常用方法及代码示例。完整代码和注释附后。正则表达式(Regular Expression)是用于匹配、查找、替换字符串的工具,通过特定语法定义模式。?{n}[...][a-z]A|B\d[0-9]\D\s\S\w\Wre从字符串匹配模式,返回Match对象或None。在字符串中第一个匹配项,返回Match对象或None。返回所有匹配项的。返回所有匹配项的Match对象)。替换匹配项,返回新字符串。根据模式分割字符串,返回列表。通过。

记录点滴 1161

STM32CubeMX串口接收数据(中断方式,HAL库)

系统:linux mint 18.3 xfce 64bit 软件: STM32CubeMX 4.24 SW4STM32 2.4 链接:https://pan.baidu.com/s/1nxgh1VF 密码:rvzu 开发板芯片:STM32F103RCT6,STM32F407VET6 实现效果:打开两个串口助手窗口,一个是USART1的,一个是USART2的,任意一个串口(串口1

ouening的博客 8万+

yolov7目标追踪:基于自定义数据集完成检测

目标追踪包含两个阶段:(1)基于自定义数据集,训练yolo目标检测模型;(2)基于检测模型,将实时检测结果传递到StrongSORT追踪算法中,该算法在OSNet基础上结合运动和外观信息,可以跟踪yolo模型检测到的任何物体。

📷 图像处理践行者 | 传统算法 · 深度学习 · 多模态 | 分享实战经验与技术洞见,探索从像素到智能的无限可能。 2306

从0到1打造一台机器人走起来

1)本体机械结构设计,依据人体生物学和生物力学知识,结合机器人运动时各关节扭矩需求,进行合理选用电机,比如胯部需要大扭矩,腿部惯量小等2)硬件拓扑上,分层架构的信号层和功率协同工作,高带宽,低延时,硬实时3)步态规划设计与验证环节实机回溯和部署过程:Real2Sim,Sim2Sim,Sim2Real。

weixin_43163941的博客 244
上一篇: cubemx stm32 按键key 支持单、双、三、四击以及长按 链表实现 驱动代码
下一篇: C语言 自带换行的 printf 函数
嵌入一下?
博客等级 码龄6年 324粉丝 66原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入一下?

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值