日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當(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)然,還有其他的地方可以用得上的,兄弟們自行挖掘??!

解析器特色

  • 用戶無需關(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)系本站刪除。
換一批
延伸閱讀

LED驅(qū)動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: 驅(qū)動電源

在工業(yè)自動化蓬勃發(fā)展的當(dāng)下,工業(yè)電機(jī)作為核心動力設(shè)備,其驅(qū)動電源的性能直接關(guān)系到整個系統(tǒng)的穩(wěn)定性和可靠性。其中,反電動勢抑制與過流保護(hù)是驅(qū)動電源設(shè)計(jì)中至關(guān)重要的兩個環(huán)節(jié),集成化方案的設(shè)計(jì)成為提升電機(jī)驅(qū)動性能的關(guān)鍵。

關(guān)鍵字: 工業(yè)電機(jī) 驅(qū)動電源

LED 驅(qū)動電源作為 LED 照明系統(tǒng)的 “心臟”,其穩(wěn)定性直接決定了整個照明設(shè)備的使用壽命。然而,在實(shí)際應(yīng)用中,LED 驅(qū)動電源易損壞的問題卻十分常見,不僅增加了維護(hù)成本,還影響了用戶體驗(yàn)。要解決這一問題,需從設(shè)計(jì)、生...

關(guān)鍵字: 驅(qū)動電源 照明系統(tǒng) 散熱

根據(jù)LED驅(qū)動電源的公式,電感內(nèi)電流波動大小和電感值成反比,輸出紋波和輸出電容值成反比。所以加大電感值和輸出電容值可以減小紋波。

關(guān)鍵字: LED 設(shè)計(jì) 驅(qū)動電源

電動汽車(EV)作為新能源汽車的重要代表,正逐漸成為全球汽車產(chǎn)業(yè)的重要發(fā)展方向。電動汽車的核心技術(shù)之一是電機(jī)驅(qū)動控制系統(tǒng),而絕緣柵雙極型晶體管(IGBT)作為電機(jī)驅(qū)動系統(tǒng)中的關(guān)鍵元件,其性能直接影響到電動汽車的動力性能和...

關(guān)鍵字: 電動汽車 新能源 驅(qū)動電源

在現(xiàn)代城市建設(shè)中,街道及停車場照明作為基礎(chǔ)設(shè)施的重要組成部分,其質(zhì)量和效率直接關(guān)系到城市的公共安全、居民生活質(zhì)量和能源利用效率。隨著科技的進(jìn)步,高亮度白光發(fā)光二極管(LED)因其獨(dú)特的優(yōu)勢逐漸取代傳統(tǒng)光源,成為大功率區(qū)域...

關(guān)鍵字: 發(fā)光二極管 驅(qū)動電源 LED

LED通用照明設(shè)計(jì)工程師會遇到許多挑戰(zhàn),如功率密度、功率因數(shù)校正(PFC)、空間受限和可靠性等。

關(guān)鍵字: LED 驅(qū)動電源 功率因數(shù)校正

在LED照明技術(shù)日益普及的今天,LED驅(qū)動電源的電磁干擾(EMI)問題成為了一個不可忽視的挑戰(zhàn)。電磁干擾不僅會影響LED燈具的正常工作,還可能對周圍電子設(shè)備造成不利影響,甚至引發(fā)系統(tǒng)故障。因此,采取有效的硬件措施來解決L...

關(guān)鍵字: LED照明技術(shù) 電磁干擾 驅(qū)動電源

開關(guān)電源具有效率高的特性,而且開關(guān)電源的變壓器體積比串聯(lián)穩(wěn)壓型電源的要小得多,電源電路比較整潔,整機(jī)重量也有所下降,所以,現(xiàn)在的LED驅(qū)動電源

關(guān)鍵字: LED 驅(qū)動電源 開關(guān)電源

LED驅(qū)動電源是把電源供應(yīng)轉(zhuǎn)換為特定的電壓電流以驅(qū)動LED發(fā)光的電壓轉(zhuǎn)換器,通常情況下:LED驅(qū)動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: LED 隧道燈 驅(qū)動電源
關(guān)閉