應(yīng)用場景
比賽需要
我準(zhǔn)備電賽的時候參加了學(xué)校為了準(zhǔn)備電賽而舉辦的的積分賽,隊友通過樹莓派用給stm32發(fā)送執(zhí)行指令,而我在隊里作為寫單片機(jī)的就需要分析數(shù)據(jù)包,每一個數(shù)據(jù)包都比較大也比較復(fù)雜,而且不定長,用傳統(tǒng)的一個字節(jié)一個字節(jié)接收數(shù)據(jù)的方式收串口在代碼層面上就顯得和很復(fù)雜,因此我需要一個能定長接收數(shù)據(jù)并分析的方法。
ESP-01s
在我之前用AT指令玩ESP-01s模塊的時候,服務(wù)器下發(fā)的數(shù)據(jù)往往是不定長的,因此我也需要一個用單片機(jī)接收不定長數(shù)據(jù)的方式。
因此我在網(wǎng)上找了一些方法,整合了一些我認(rèn)為比較優(yōu)雅的基于stm32的解決方案。
廢話少說,開始實(shí)現(xiàn)。
原理
因?yàn)槲沂稚蠐碛凶疃嗟膯纹瑱C(jī)是stm32f401ccu6,因此這里以stm32f401為例演示
根據(jù)stm32f4的數(shù)據(jù)手冊
從這張表中可以看出stm32有RXNE(RX寄存器非空,代表有數(shù)據(jù)輸入)中斷,和IDLE(串口空閑,代表發(fā)送端發(fā)送結(jié)束)中斷,那么我們只需要從有數(shù)據(jù)輸入,一直接受直到串口出現(xiàn)空閑即可接收下整包不定長的數(shù)據(jù)。
但是在此之前,如果配置RXNE中斷,發(fā)送端每發(fā)送一個字節(jié),CPU就要進(jìn)一次中斷去處理該字節(jié),那這樣就和一個個字節(jié)接收無異了,那這時候就需要請出CPU的小弟:DMA出場,讓每個字節(jié)進(jìn)入單片機(jī)時都產(chǎn)生一個接收請求,然后讓DMA一個個字節(jié)搬入內(nèi)存,CPU在IDLE中斷中再對所有數(shù)據(jù)進(jìn)行處理,并重置DMA,則可大大減小CPU的占用,也方便了代碼的編寫。
STM32CUBEMX配置
配置Debug接口配置HSE和LSE(LSE其實(shí)可以不配)
配置時鐘樹
配置USART1為異步模式
打開USART1全局中斷
打開USART1 DMA接收請求,配置默認(rèn)就好,主要的是Increment Address勾選Memery,Data Width外設(shè)和內(nèi)存都選擇BYTE(這兩個必須相同)
然后接下來的就是工程配置,那個就按照自己的情況來,該怎么樣就怎么樣
代碼編寫
在USER CODE BEGIN Include之后加上兩個頭文件
#include "stdio.h"
#include "string.h"
點(diǎn)開魔術(shù)棒勾選Use MicroLIB
在工程的任意地方添加重定向代碼(我這里添加在了/* USER CODE BEGIN 4 */的后面)
int fputc(int ch, FILE *f)
{
// HAL_UART_Transmit (&huart2 ,(uint8_t *)&ch,1,HAL_MAX_DELAY);
while((USART1->SR&0x40)==0);
USART1->DR = (uint8_t)ch;
//采用輪詢方式發(fā)送一個字節(jié)的數(shù)據(jù),沒有發(fā)送成功就一直等待
return ch;
}
int fgetc(FILE *f)
//int fgetc(int ch, FILE *F)
{
uint8_t ch;
HAL_UART_Receive(&huart1 ,(uint8_t *)&ch,1,HAL_MAX_DELAY );
return ch;
}
在工程的任意位置添加串口接收事件服務(wù)函數(shù)(我這里添加在了/* USER CODE BEGIN 4 */的后面)
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
if(huart == &huart1)
{
//獲取幀長
usart1_data_len = 1024 - huart->hdmarx->Instance->NDTR;
sscanf(usart1_rx_buf,"N%dM%d",&test1,&test2);
HAL_UART_Transmit(&huart1,usart1_rx_buf,usart1_data_len,100);
printf("%d,%d\r\n",test1,test2);
//使dma從頭存數(shù)據(jù)
huart->hdmarx->Instance -> NDTR = 1024;
//重新打開dma接收,ILDE中斷
HAL_UARTEx_ReceiveToIdle_DMA(&huart1,usart1_rx_buf,1024);
}
}
在主函數(shù)的while(1)之前加上一句話
HAL_UARTEx_ReceiveToIdle_DMA(&huart1,usart1_rx_buf,1024);
這句話是串口接收數(shù)據(jù)直至IDLE事件,記得定義usart1_rx_buf作為串口接收數(shù)據(jù)緩沖區(qū),第三個參數(shù)是你定義的接收緩沖區(qū)的大小,建議用宏定義,我這里為了演示就懶得用宏定義了XD.
這樣就程序就編寫完成啦,目前的程序作用是:串口使用DMA接收數(shù)據(jù)至IDLE事件,當(dāng)IDLE事件發(fā)生時進(jìn)入中斷服務(wù)函數(shù)對數(shù)據(jù)進(jìn)行處理。
細(xì)心的小伙伴應(yīng)該已經(jīng)發(fā)現(xiàn)中斷服務(wù)函數(shù)中不僅有一個
HAL_UART_Transmit(&huart1,usart1_rx_buf,usart1_data_len,100);
將接收的數(shù)據(jù)從新發(fā)送,還有一個
sscanf(usart1_rx_buf,“N%dM%d”,&test1,&test2);
用于按格式提取數(shù)據(jù),當(dāng)發(fā)送端是使用print來發(fā)送數(shù)據(jù)的,那么它每個字節(jié)都是用ASCII編碼的,例如對方printf(“%d”,123)的時候,發(fā)送的數(shù)據(jù)為0X31 0X32 0X33,這就對我們接收端分析數(shù)據(jù)包時產(chǎn)生了障礙。
因此我們可以使用stdio.h中的sscanf函數(shù)對數(shù)據(jù)包按照格式提取數(shù)據(jù)
例如我這里格式為:“N%dM%d”
則發(fā)送端按照這個格式發(fā)送數(shù)據(jù)的話,我就可以把這兩個%d位置的整數(shù)提取出來
所以以上代碼的實(shí)際效果be like:
總結(jié)&擴(kuò)展
總的來說,大致思路就是,打開USART1的中斷,打開USART1的DMA接收請求,讓DMA一個個字節(jié)搬運(yùn)數(shù)據(jù)到內(nèi)存中,然后在檢測串口空閑,當(dāng)串口空閑時,代表發(fā)送端發(fā)送完畢,則進(jìn)入中斷服務(wù)函數(shù)中處理數(shù)據(jù)包
看起來是不是很優(yōu)雅?
但是這還是有部分不是很優(yōu)雅的地方,例如:
當(dāng)發(fā)送端因某些原因發(fā)送數(shù)據(jù)的速度較慢,使得每發(fā)送一個字節(jié)之間時間間隔較大,而在字節(jié)之間出現(xiàn)空閑,則目前這個方法就不適用,會出bug,大家可以自己想想為什么會出bug,這里就不過多贅述。
而有了這個方法,愛動腦子的小伙伴應(yīng)該發(fā)現(xiàn)了這個方法的一個新用途。
既然可以按照格式發(fā)送接收數(shù)據(jù),那么在兩個單片機(jī)之間,用串口收發(fā)浮點(diǎn)數(shù)將不再是難事。
例如可以將中斷服務(wù)函數(shù)改為:
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
if(huart == &huart1)
{
//獲取幀長
usart1_data_len = 1024 - huart->hdmarx->Instance->NDTR;
sscanf(usart1_rx_buf,"N%fM%f",&test1,&test2);
HAL_UART_Transmit(&huart1,usart1_rx_buf,usart1_data_len,100);
printf("%.2f,%.2f\r\n",test1,test2);
//使dma從頭存數(shù)據(jù)
huart->hdmarx->Instance -> NDTR = 1024;
//重新打開dma接收,ILDE中斷
HAL_UARTEx_ReceiveToIdle_DMA(&huart1,usart1_rx_buf,1024);
}
}
則在發(fā)送端和接收端之間則可以直接使用printf函數(shù)來實(shí)現(xiàn)浮點(diǎn)數(shù)的收發(fā),而再也不需要將浮點(diǎn)數(shù)拆成一個個字節(jié)發(fā)送,然后再在接收端將數(shù)據(jù)拼起來了
效果be like:文章來源:http://www.zghlxwxcb.cn/news/detail-757460.html
好啦,此貼到這里就結(jié)束啦,希望各位能開發(fā)出更優(yōu)雅的串口收發(fā)的方式,該貼為博主第一次寫文章,若是有任何問題歡迎討論 ??文章來源地址http://www.zghlxwxcb.cn/news/detail-757460.html
到了這里,關(guān)于極度優(yōu)雅的用stm32串口接收并分析不定長數(shù)據(jù)的方法(可用于發(fā)送和接收浮點(diǎn)數(shù))的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!