當(dāng)前位置:首頁 > 公眾號精選 > 嵌入式云IOT技術(shù)圈
[導(dǎo)讀]最近更文頻率少了,但依然會保持一周一篇原創(chuàng)文章,主要是在做這幾個活: 1 編寫韋東山老師的嵌入式書籍的輸入系統(tǒng)章節(jié) 2 應(yīng)本公眾號粉絲要求,準(zhǔn)備造一臺智能小車并開源分享 3 自己工作上項(xiàng)目的學(xué)習(xí):機(jī)器視覺,圖像處理,TKM32F499芯片入門等 4 PID溫控套件測

最近更文頻率少了,但依然會保持一周一篇原創(chuàng)文章,主要是在做這幾個活:

  • 1 編寫韋東山老師的嵌入式書籍的輸入系統(tǒng)章節(jié)

  • 2 應(yīng)本公眾號粉絲要求,準(zhǔn)備造一臺智能小車并開源分享

  • 3 自己工作上項(xiàng)目的學(xué)習(xí):機(jī)器視覺,圖像處理,TKM32F499芯片入門等


  • 4 PID溫控套件測試(后面會分享PID算法的實(shí)戰(zhàn)使用)

所以最近會比較忙一些,也就不會更新太頻啦,但是我還是會用心分享我的所見所聞及所經(jīng)歷的東西,希望各位諒解!

在日常工作中,我們經(jīng)常會跟各種協(xié)議打交道,最常見的就是串口協(xié)議了,接下來我們將通過幾個案例來實(shí)現(xiàn)串口解析命令,以下案例基于STM32L431RCT6小熊派開發(fā)板。

案例一

實(shí)現(xiàn)需求:

協(xié)議制定:

指令(字符串) 含義
led_on 打開燈
led_off 關(guān)閉燈
motor_on 打開電機(jī)
motor_off 關(guān)閉電機(jī)

1、硬件配置


1、STM32CubeMX軟件配置

1.1、RCC配置

1.2、串口配置

1.3、LED和電機(jī)配置

1.4、工程生成設(shè)置

2、軟件核心功能實(shí)現(xiàn)


main.c

typedef void (*cmd_func)(void);
typedef struct __CMD_PARSE
{
const char *cmd_type;
cmd_func fun_ptr;
} CMD_PARSE;

/*命令表定義===>表驅(qū)動法*/
CMD_PARSE CMD_TABLE[CMD_SIZE] =
{
{"led_on", led_on_process},
{"led_off", led_off_process},
{"motor_on", motor_on_process},
{"motor_off", motor_off_process},
};

/*命令匹配*/
int Command_Matching(char *cmd_type)
{
uint8_t cmd_at = 0 ;
uint8_t cmd_match_flag = 0 ;
uint8_t type_num = sizeof(CMD_TABLE) / sizeof(CMD_PARSE);

for(cmd_at = 0 ; cmd_at < type_num ; cmd_at++)
{
if(0 == strcmp(CMD_TABLE[cmd_at].cmd_type, cmd_type))
{
cmd_match_flag = 1 ;
break ;
}
}

if(1 == cmd_match_flag)
{
cmd_match_flag = 0 ;
CMD_TABLE[cmd_at].fun_ptr();
}
else
return -1 ;
return 0 ;
}

int main(void)
{
/* USER CODE BEGIN 1 */
uint8_t cmd_at = 0 ;
int find_cmd_Index = 0;
/* USER CODE END 1 */

/* MCU Configuration--------------------------------------------------------*/

/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();

/* USER CODE BEGIN Init */

/* USER CODE END Init */

/* Configure the system clock */
SystemClock_Config();

/* USER CODE BEGIN SysInit */

/* USER CODE END SysInit */

/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
printf("命令解析器\n");
/*使能串口接收*/
HAL_UART_Receive_IT(&huart1, (uint8_t *)&cmd_parse_typedef.Res, 1);
/* USER CODE END 2 */

/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */
/*如果串口數(shù)據(jù)接收完成了,則進(jìn)行處理*/
if(1 == cmd_parse_typedef.BufferReady)
{
for(cmd_at = 0 ; cmd_at < NR(CMD_TABLE) ; cmd_at++)
{
if(strcmp((char *)cmd_parse_typedef.cmd_buffer, CMD_TABLE[cmd_at].cmd_type) == 0)
{
find_cmd_Index = cmd_at ;
break ;
}
else
find_cmd_Index = -1 ;
}

if(-1 == find_cmd_Index)
printf("當(dāng)前指令列表無該指令\n");
else
printf("接收到指令%d:%s\n", find_cmd_Index, cmd_parse_typedef.cmd_buffer);

Command_Matching((char *)cmd_parse_typedef.cmd_buffer);
memset(&cmd_parse_typedef, 0, sizeof(cmd_parse_typedef));
}
}
/* USER CODE END 3 */
}

stm32l4xx_it.h

#include <stdint.h>
#define CMD_STR_SIZE 30
/*串口接收結(jié)構(gòu)體*/
typedef struct __CMD
{
/*接收計(jì)數(shù)*/
int rx_count ;
/*接收單個字符*/
uint8_t Res ;
/*是否已經(jīng)接收完成?*/
uint8_t BufferReady : 1 ;
/*數(shù)據(jù)緩存區(qū)*/
uint8_t cmd_buffer[CMD_STR_SIZE] ;
/*數(shù)據(jù)備份緩存區(qū)*/
uint8_t cmd_buffer_temp[CMD_STR_SIZE] ;
}CMDSTR_PARSE ;
extern CMDSTR_PARSE cmd_parse_typedef ;

stm32l4xx_it.c

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
{
if(UartHandle->Instance == USART1)
{
HAL_UART_Receive_IT(&huart1, (uint8_t *)&cmd_parse_typedef.Res, 1);

if('\n' != cmd_parse_typedef.Res)
{
if(cmd_parse_typedef.rx_count < CMD_STR_SIZE - 1)
cmd_parse_typedef.cmd_buffer_temp[cmd_parse_typedef.rx_count++] = cmd_parse_typedef.Res ;
else
cmd_parse_typedef.rx_count = 0 ;
}
else
{
//如果接收的是\n,則上一個接收的數(shù)據(jù)為'\r'結(jié)束
if('\r' == cmd_parse_typedef.cmd_buffer_temp[cmd_parse_typedef.rx_count - 1])
{
//添加結(jié)束符
cmd_parse_typedef.cmd_buffer_temp[cmd_parse_typedef.rx_count - 1] = 0x00 ;
memcpy(cmd_parse_typedef.cmd_buffer, cmd_parse_typedef.cmd_buffer_temp, CMD_STR_SIZE);
cmd_parse_typedef.BufferReady = 1;
}
}
}
}

void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
/*如果發(fā)生了串口溢出,則清溢出標(biāo)志后再次開啟終端接收*/
if(__HAL_UART_GET_FLAG(huart, UART_FLAG_ORE) != RESET)
{
memset(&cmd_parse_typedef, 0, sizeof(cmd_parse_typedef));
__HAL_UART_CLEAR_OREFLAG(huart);
HAL_UART_Receive_IT(&huart1, (uint8_t *)&cmd_parse_typedef.Res, 1);
}
}

案例一中,軟件邏輯實(shí)現(xiàn)較為簡單,即通過串口中斷接收協(xié)議數(shù)據(jù),然后通過遍歷調(diào)用對應(yīng)的函數(shù)執(zhí)行相應(yīng)的邏輯,但是如果想把項(xiàng)目做得更具可復(fù)用性的話,我們還需要改改代碼,讓它更低耦合。

執(zhí)行效果:

案例二

基于案例一,我們對代碼做一些升華,修改main.c部分如下:

main.c

typedef void (*cmd_func)(void);
typedef struct __CMD_PARSE
{
const char *cmd_type;
const char *cmd_help;
cmd_func fun_ptr;
} CMD_PARSE;
CMD_PARSE CMD_TABLE[CMD_SIZE];
#define NR(array) sizeof(array) / sizeof(array[0])

//初始化命令,這里注冊了一個默認(rèn)的list_cmd命令,用于查詢當(dāng)前結(jié)構(gòu)體中所有注冊的指令
void Cmd_init(void)
{
Register_Cmd("list_cmd", "list all cmd info", list_cmd_callback);
}
//注冊命令
/*
cmd:指令
cmd_help:指令功能描述
ptr:該指令對應(yīng)的執(zhí)行函數(shù)
*/
int Register_Cmd(char *cmd, char *cmd_help, cmd_func ptr)
{
static uint8_t cmd_index = 0 ;

if(cmd_index > CMD_SIZE - 1)
{
printf("注冊命令失敗,表越界\n");
return -1 ;
}

CMD_TABLE[cmd_index].cmd_type = cmd ;
CMD_TABLE[cmd_index].cmd_help = cmd_help ;
CMD_TABLE[cmd_index].fun_ptr = ptr ;
cmd_index++ ;
return cmd_index ;
}

/*命令匹配*/
int Command_Matching(char *cmd_type)
{
uint8_t cmd_at = 0 ;
uint8_t cmd_match_flag = 0 ;
uint8_t type_num = NR(CMD_TABLE);

for(cmd_at = 0 ; cmd_at < type_num ; cmd_at++)
{
if(0 == strcmp(CMD_TABLE[cmd_at].cmd_type, cmd_type))
{
cmd_match_flag = 1 ;
break ;
}
}

if(1 == cmd_match_flag)
{
cmd_match_flag = 0 ;
CMD_TABLE[cmd_at].fun_ptr();
}
else
return -1 ;

return 0 ;
}

int main(void)
{
/* USER CODE BEGIN 1 */
uint8_t cmd_at = 0 ;
int find_cmd_Index = 0;
/* USER CODE END 1 */

/* MCU Configuration--------------------------------------------------------*/

/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();

/* USER CODE BEGIN Init */

/* USER CODE END Init */

/* Configure the system clock */
SystemClock_Config();

/* USER CODE BEGIN SysInit */

/* USER CODE END SysInit */

/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
//開啟串口接收中斷
HAL_UART_Receive_IT(&huart1, (uint8_t *)&cmd_parse_typedef.Res, 1);
//初始化list_cmd
Cmd_init();
//注冊指令
Register_Cmd("led_on", "Open BearPi IA1 LED", led_on_process);
Register_Cmd("led_off", "Close BearPi IA1 LED", led_off_process);
Register_Cmd("motor_on", "Open BearPi IA1 MOTOR", motor_on_process);
Register_Cmd("motor_off", "Close BearPi IA1 MOTOR", motor_off_process);
//開機(jī)上電即執(zhí)行l(wèi)ist_cmd,打印當(dāng)前已經(jīng)注冊的所有指令
list_cmd_callback();
/* USER CODE END 2 */

/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */
if(1 == cmd_parse_typedef.BufferReady)
{
for(cmd_at = 0 ; cmd_at < NR(CMD_TABLE) ; cmd_at++)
{
if(strcmp((char *)cmd_parse_typedef.cmd_buffer, CMD_TABLE[cmd_at].cmd_type) == 0)
{
find_cmd_Index = cmd_at ;
break ;
}
else
find_cmd_Index = -1 ;
}

if(-1 == find_cmd_Index)
printf("當(dāng)前指令列表無該指令\n");
else
printf("當(dāng)前輸入:%s指令\n", cmd_parse_typedef.cmd_buffer);

Command_Matching((char *)cmd_parse_typedef.cmd_buffer);
memset(&cmd_parse_typedef, 0, sizeof(cmd_parse_typedef));
}
}
/* USER CODE END 3 */
}

針對案例一的修改以后,不用再直接修改命令表CMD_TABLE里的數(shù)據(jù),而是通過一個函數(shù),調(diào)用一次則自動往后增加一個指令,這樣用起來顯然會比較舒服一些,我自己項(xiàng)目也經(jīng)常會這么用,在不考慮代碼執(zhí)行性能的條件下相當(dāng)靈活。

運(yùn)行結(jié)果:

案例三

一個超牛逼的命令解析器:cmd-parser由物聯(lián)網(wǎng)大佬杰杰所造,他也是我們開源以及嵌入式社區(qū)的朋友,不得不說這個解析器做得真香!

以下是他的Github,有興趣的朋友也可以關(guān)注一下,杰杰在開源軟件方面在同齡人里做得東西都相當(dāng)出色,大家要多多向他學(xué)習(xí)!

Github倉庫地址

https://github.com/jiejieTop/cmd-parser

解析器功能

簡單來說,我希望我的開發(fā)板,可以通過命令執(zhí)行一些處理,比如說我用串口發(fā)一個命令A(yù),開發(fā)板就執(zhí)行A的一些處理,或者,在調(diào)試某些AT模組的時候,當(dāng)我收到模組返回的一些指令后,自動執(zhí)行一些處理。當(dāng)然,還有其他的地方可以用得上的,兄弟們自行挖掘?。?/p>

解析器特色

  • 用戶無需關(guān)心命令的存儲區(qū)域與大小,由編譯器靜態(tài)分配。
  • 加入哈希算法超快速匹配命令,時間復(fù)雜度從O(n*m)變?yōu)镺(n)。
  • 命令支持忽略大小寫。
  • 非常易用與非常簡潔的代碼(不足150行)。

使用方法

1、注冊命令 在工程中的任意位置均可調(diào)用(在函數(shù)外)

REGISTER_CMD(test1, test1_cmd);

2、cmd初始化

cmd_init();

3、解析命令

cmd_parsing("test1");

目前本代碼只支持MDK與IAR的編譯器,對于GCC還沒有移植,不過我想要移植也不困難!歡迎大家一起提交pr!

1、在小熊派上使用cmd-parser

1.1 添加頭文件及路徑到Keil MDK

1.2、編寫源代碼

這里還是一樣,借用案例一的工程,對main.c做下改造。

main.c

#include "cmd.h"

void led_on_process(void);
void led_off_process(void);
void motor_on_process(void);
void motor_off_process(void);

/*注冊命令*/
REGISTER_CMD(led_on, led_on_process);
REGISTER_CMD(led_off, led_off_process);
REGISTER_CMD(motor_on, motor_on_process);
REGISTER_CMD(motor_off, motor_off_process);


int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */

/* MCU Configuration--------------------------------------------------------*/

/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();

/* USER CODE BEGIN Init */

/* USER CODE END Init */

/* Configure the system clock */
SystemClock_Config();

/* USER CODE BEGIN SysInit */

/* USER CODE END SysInit */

/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
/*命令初始化*/
cmd_init();
HAL_UART_Receive_IT(&huart1, (uint8_t *)&cmd_parse_typedef.Res, 1);
/* USER CODE END 2 */

/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */
if(1 == cmd_parse_typedef.BufferReady)
{
printf("接收到指令:%s\n", cmd_parse_typedef.cmd_buffer);
cmd_parsing((char *)cmd_parse_typedef.cmd_buffer);
memset(&cmd_parse_typedef, 0, sizeof(cmd_parse_typedef));
}
}

/* USER CODE END 3 */
}

有木有很輕量?看起來簡直舒服爆啦!

執(zhí)行結(jié)果:

當(dāng)然,除了杰杰開源的cmd-parser,還有很多優(yōu)秀的指令解析器,比如RT-Thread的finsh,還有比如世偉兄之前發(fā)的一期項(xiàng)目源碼分析的letter-shell,原理都差不多:

第2期 | letter-shell,一個功能強(qiáng)大的嵌入式shell

這些都是非常優(yōu)秀的作品,大家都可以學(xué)習(xí)使用下,有那么好用的輪子為啥不用?所以咱們在工作中要避免重復(fù)造輪子,這樣才能提高工作效率,做出漂亮的產(chǎn)品!

項(xiàng)目下載

公眾號后臺回復(fù):命令 即可獲取這幾個案例的下載鏈接。

往期精彩

華為LiteOS智慧路燈項(xiàng)目案例學(xué)習(xí)筆記(一)

最近收集的開源項(xiàng)目專欄(持續(xù)更新,收好車輪,方便造車)

推薦三個我工作中經(jīng)常使用的驅(qū)動大全wiki(建議收藏并轉(zhuǎn)發(fā)讓更多人知道!)

覺得本次分享的文章對您有幫助,隨手點(diǎn)[在看]并轉(zhuǎn)發(fā)分享,也是對我的支持。


免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個人觀點(diǎn),不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動 BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險,如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運(yùn)營商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術(shù)學(xué)會聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(shù)(集團(tuán))股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉