1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > cubemx stm32 串口终端的实现与使用 (可以由用户自定义各种终端指令任意) 驱动代码

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

时间:2023-06-27 13:38:43

相关推荐

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

简言

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

初次使用

介绍特性

像终端一样使用

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

支持tab键补全

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

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

支持Backspace键

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

宏定义的定义与配置

yxy_open_cmd.h中修改对应的宏:

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

初始化

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

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

main函数

// mainuart_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 __cplusplusextern "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;// 特殊字idUser_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#endifvoid 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 __cplusplusextern "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_*/

完整代码工程

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

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。