0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

HAL庫UART在cubemx中的配置

jf_L18yujSQ ? 來源:小飛哥玩嵌入式 ? 2023-07-05 09:47 ? 次閱讀

串口原理圖

串口1咱們已經(jīng)用作rtt的print使用了,所以使用另外一組串口來進行串口的教程,這里一定要注意下,alios的這個板子原理圖是有點問題的,標(biāo)注的是串口3PA2和PA3,實際上小飛哥調(diào)了好久,最后萬用表量引腳才發(fā)現(xiàn)是原理圖標(biāo)注錯誤,實際上是UART4,PA0和PA1

86dfda86-1a6f-11ee-962d-dac502259ad0.png

cubemx中引腳選擇預(yù)配置

選擇PA0、PA1,配置為串口模式,波特率什么的見圖示:

8712d2c4-1a6f-11ee-962d-dac502259ad0.png

開啟中斷,優(yōu)先級可以根據(jù)自己的需求配置,本次不使用DMA,所以DMA就先不進行配置了

8755e60e-1a6f-11ee-962d-dac502259ad0.png

配置是非常簡單的,就不多啰嗦了,配置完直接生成代碼就OK了

HAL庫串口代碼詳解

cubemx里面配置了一大堆,生成的應(yīng)用代碼主要在初始化中:

876ece94-1a6f-11ee-962d-dac502259ad0.png87a026a6-1a6f-11ee-962d-dac502259ad0.png

關(guān)于串口的接口是很多的,本次主要使用3個接口,發(fā)送、接收和接收回調(diào)

87c89cbc-1a6f-11ee-962d-dac502259ad0.png

HAL庫數(shù)據(jù)接收的設(shè)計思想是底層配置完成后,暴露給用戶的是一組回調(diào)函數(shù),用戶不用關(guān)心底層實現(xiàn),只需要關(guān)注應(yīng)用層邏輯即可,回調(diào)函數(shù)是定義為_weak屬性的接口,用戶可以在應(yīng)用層實現(xiàn)

/**
*@briefRxTransfercompletedcallback.
*@paramhuartUARThandle.
*@retvalNone
*/
__weakvoidHAL_UART_RxCpltCallback(UART_HandleTypeDef*huart)
{
/*Preventunusedargument(s)compilationwarning*/
UNUSED(huart);

/*NOTE:Thisfunctionshouldnotbemodified,whenthecallbackisneeded,
theHAL_UART_RxCpltCallbackcanbeimplementedintheuserfile.
*/
}

發(fā)送也有對應(yīng)的callback,我們只需要在callback處理我們的邏輯即可。

串口收發(fā)設(shè)計

教程不玩虛的,本章節(jié)小飛哥從實際應(yīng)用出發(fā),通過解析協(xié)議數(shù)據(jù),順便講解uart的收發(fā)設(shè)計。

1、串口接收:

先來看看HAL庫串口接收的接口函數(shù),這就是使用庫函數(shù)的好處,底層實現(xiàn)不用關(guān)心,只要會用接口就行了

/**
*@briefReceiveanamountofdataininterruptmode.
*@noteWhenUARTparityisnotenabled(PCE=0),andWordLengthisconfiguredto9bits(M1-M0=01),
*thereceiveddataishandledasasetofu16.Inthiscase,Sizemustindicatethenumber
*ofu16availablethroughpData.
*@paramhuartUARThandle.
*@parampDataPointertodatabuffer(u8oru16dataelements).
*@paramSizeAmountofdataelements(u8oru16)tobereceived.
*@retvalHALstatus
*/
HAL_StatusTypeDefHAL_UART_Receive_IT(UART_HandleTypeDef*huart,uint8_t*pData,uint16_tSize);

如何使用這個接口接收數(shù)據(jù)呢?

從接口描述可以看到,第1個參數(shù)是我們的串口號,第2個參數(shù)數(shù)我們用于接收數(shù)據(jù)的buffer,第3個參數(shù)是數(shù)據(jù)長度,即要接受的數(shù)據(jù)量,這里我們每次僅接收一個數(shù)據(jù)即進入邏輯處理

每次取一個數(shù)據(jù),放到rxdata的變量中

HAL_UART_Receive_IT(&huart4,&rxdata,1);

HAL庫所有的串口是共享一個回調(diào)函數(shù)的,那么如何區(qū)分數(shù)據(jù)是來自哪一個串口的?這個邏輯可以在應(yīng)用實現(xiàn),區(qū)分不同的串口號,根據(jù)對應(yīng)的串口號實現(xiàn)對應(yīng)的邏輯即可

voidHAL_UART_RxCpltCallback(UART_HandleTypeDef*huart)
{

if(huart->Instance==UART4)
{
//rt_sem_release(sem_uart_rec);
embedded_set_uart_rec_flag(RT_TRUE);
embedded_set_uart_timeout_cnt(0);
HAL_UART_Receive_IT(&huart4,&rxdata,1);
mb_process_frame(rxdata,CHANNEL_MODBUS);
}
}

2、數(shù)據(jù)幀接收完成判斷

通訊基本上都是不定長數(shù)據(jù)的接收,一般對于一個完整的通訊幀來說,是有長度字段的,分以下幾種接收完成判斷方式

特殊數(shù)據(jù)格式,比如結(jié)束符,像正點原子串口教程的“回車、換行(0x0D,0x0A)”

數(shù)據(jù)長度,適用已知數(shù)據(jù)長度的數(shù)據(jù)幀,根據(jù)接收到的數(shù)據(jù)長度跟數(shù)據(jù)幀里面的長度是否一致,判斷接受是否完成

超時判斷,定時器設(shè)計一個超時機制,一定時間內(nèi)沒有數(shù)據(jù)進來即認為數(shù)據(jù)傳輸結(jié)束

空閑中斷,串口是有個空閑中斷的,這個實現(xiàn)類似于超時機制

也可以從軟件設(shè)計實現(xiàn),比如設(shè)計一個隊列,取數(shù)據(jù)即可,隊列中沒數(shù)據(jù)即認為數(shù)據(jù)接受完成

方式有很多,本章節(jié)主要使用數(shù)據(jù)長度和定時器超時兩種方式來講解

3、串口發(fā)送

串口發(fā)送比較簡單,先來看看發(fā)送接口函數(shù),類似接收函數(shù),只需要把我們的數(shù)據(jù)放進發(fā)送buffer,啟動發(fā)送即可

/**
*@briefSendanamountofdatainblockingmode.
*@noteWhenUARTparityisnotenabled(PCE=0),andWordLengthisconfiguredto9bits(M1-M0=01),
*thesentdataishandledasasetofu16.Inthiscase,Sizemustindicatethenumber
*ofu16providedthroughpData.
*@noteWhenFIFOmodeisenabled,writingadataintheTDRregisteraddsone
*datatotheTXFIFO.WriteoperationstotheTDRregisterareperformed
*whenTXFNFflagisset.Fromhardwareperspective,TXFNFflagand
*TXEaremappedonthesamebit-field.
*@paramhuartUARThandle.
*@parampDataPointertodatabuffer(u8oru16dataelements).
*@paramSizeAmountofdataelements(u8oru16)tobesent.
*@paramTimeoutTimeoutduration.
*@retvalHALstatus
*/
HAL_StatusTypeDefHAL_UART_Transmit(UART_HandleTypeDef*huart,constuint8_t*pData,uint16_tSize,uint32_tTimeout);

數(shù)據(jù)接收及協(xié)議幀解析設(shè)計

數(shù)據(jù)接收:

基于數(shù)據(jù)長度和超時時間完成數(shù)據(jù)幀發(fā)送完成的判斷:

定時器中斷回調(diào)設(shè)計,實現(xiàn)邏輯為,當(dāng)收到串口數(shù)據(jù)時,開始計時,超過100ms無數(shù)據(jù)進來,認為數(shù)據(jù)幀結(jié)束,同時釋放數(shù)據(jù)接收完成的信號量,接收到接受完成的信號量之后,重置一些數(shù)據(jù),為下一次接收做好準(zhǔn)備

voidHAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef*htim)
{
if(htim->Instance==TIM15)
{
//if(RT_EOK==rt_sem_take(sem_uart_rec,RT_WAITING_NO))
//{
if(embedded_get_uart_rec_flag())
{
/*100ms超時無數(shù)據(jù)接收*/
if(embedded_get_uart_timeout_cnt()>9)
{
embedded_set_uart_rec_flag(RT_FALSE);

rt_sem_release(sem_uart_timeout);
}
}

//}
}
}

串口回調(diào)設(shè)計:

串口回調(diào)要實現(xiàn)的邏輯比較簡單,主要是數(shù)據(jù)接收、解析:

voidHAL_UART_RxCpltCallback(UART_HandleTypeDef*huart)
{
if(huart->Instance==UART4)
{
//rt_sem_release(sem_uart_rec);
embedded_set_uart_rec_flag(RT_TRUE);
embedded_set_uart_timeout_cnt(0);
HAL_UART_Receive_IT(&huart4,&rxdata,1);
process_frame(rxdata,CHANNEL_UART4);
}
}

/協(xié)議架構(gòu)/

/數(shù)據(jù)頭(2字節(jié))+數(shù)據(jù)長度(2字節(jié),不包含數(shù)據(jù)頭)+功能碼+數(shù)據(jù)+校驗碼(CRC16-MODBUS)/

我們采用這個協(xié)議框架來解析數(shù)據(jù),數(shù)據(jù)解析可以設(shè)計成一個簡單的狀態(tài)機,根據(jù)每一步?jīng)Q定下一步做什么

比如針對上面的協(xié)議,我們就可以分幾步設(shè)計:

1、解析數(shù)據(jù)頭1;

2、解析數(shù)據(jù)頭2;

3、解析數(shù)據(jù)長度;

4、接收數(shù)據(jù);

5、校驗數(shù)據(jù)CRC;

6、調(diào)用命令回調(diào)函數(shù);

把握好這個步驟,設(shè)計其實非常簡單

先來定義一個簡單的枚舉,表示每一個狀態(tài):

typedefenum
{
STATUS_HEAD1=0,
STATUS_HEAD2,
STATUS_LEN,
STATUS_HANDLE_PROCESS
}frame_status_e;

然后封裝數(shù)據(jù)解析函數(shù):

/*協(xié)議架構(gòu)*/

/**數(shù)據(jù)頭(1字節(jié))+數(shù)據(jù)長度(2字節(jié),不包含數(shù)據(jù)頭)+功能碼+數(shù)據(jù)+校驗碼(CRC16-MODBUS)**/

#definePROTOCOL_HEAD10x5A
#definePROTOCOL_HEAD20xA5

intprocess_frame(constuint8_tdata,constuint8_tchannel)
{
uint16_tcrc=0;
uint16_tlen=0;

staticframe_status_eframe_status;
staticuint16_tindex=0;

/*timeoutresetthereceivestatus*/
if(RT_EOK==rt_sem_take(sem_uart_timeout,RT_WAITING_NO))
{
index=0;
frame_status=STATUS_HEAD1;
}
switch(frame_status)
{
caseSTATUS_HEAD1:
if(data==PROTOCOL_HEAD1)
{
frame_status=STATUS_HEAD2;
buffer[index++]=data;
}
else
{
frame_status=STATUS_HEAD1;
index=0;
}
break;
caseSTATUS_HEAD2:
if(data==PROTOCOL_HEAD2)
{
frame_status=STATUS_LEN;
buffer[index++]=data;
}
else
{
frame_status=STATUS_HEAD1;
index=0;
}
break;
caseSTATUS_LEN:
if(data>=0&&data<=?MAX_DATA_LEN)
????????{
????????????frame_status?=?STATUS_HANDLE_PROCESS;
????????????buffer[index++]?=?data;
????????}
????????else
????????{
????????????frame_status?=?STATUS_HEAD1;
????????????index?=?0;
????????}
????????break;
????case?STATUS_HANDLE_PROCESS:
????????buffer[index++]?=?data;
????????len?=?buffer[LEN_POS];
????????if?(index?-?3?==?len)
????????{
????????????crc?=?embedded_mbcrc16(buffer,?index?-?2);
????????????if?(crc?==?(buffer[index?-?1]?|?buffer[index?-?2]?<

對用的功能函數(shù):

我們采用 attribute at機制的方式,將我們的回調(diào)函數(shù)注冊進去:

typedefvoid(*uart_dispatcher_func_t)(constuint32_t,constuint8_t*,constuint32_t);
typedefstructuart_dispatcher_item
{
union
{
struct
{
uint8_tchannel;
uint8_tcmd_id;
};

uint32_tmagic_number;
};

uart_dispatcher_func_tfunction;

}uart_dispatcher_item_t;

#defineUART_DISPATCHER_CALLBACK_REGISTER(ch,id,fn)staticconstuart_dispatcher_item_tuart_dis_table_##ch##_##id
__attribute__((section("uart_dispatcher_table"),__used__,aligned(sizeof(void*))))=
{.channel=ch,.cmd_id=id,.function=fn}
intcall_reg_cb(uint8_t*frame,uint8_tdata_len,intchannel,uint8_tcmd_id);

回調(diào)函數(shù):

這樣設(shè)計可以把驅(qū)動層,協(xié)議解析層和應(yīng)用層完全分開,用戶只需要注冊相關(guān)的命令,實現(xiàn)回調(diào)即可,完全不用關(guān)心底層實現(xiàn)

voiddispatcher_on_02_callback(constuint32_tchannel,constuint8_t*data,constuint32_tdata_len)
{
constchar*str="func02isrunning
";
uart_write((uint8_t*)str,rt_strlen(str),100);
rt_kprintf("func02isrunning
");
}
UART_DISPATCHER_CALLBACK_REGISTER(1,0x02,dispatcher_on_02_callback);

voiddispatcher_on_03_callback(constuint32_tchannel,constuint8_t*data,constuint32_tdata_len)
{
constchar*str="func03isrunning
";
uart_write((uint8_t*)str,rt_strlen(str),100);
rt_kprintf("func03isrunning
");
}
UART_DISPATCHER_CALLBACK_REGISTER(1,0x03,dispatcher_on_03_callback);

voiddispatcher_on_04_callback(constuint32_tchannel,constuint8_t*data,constuint32_tdata_len)
{
constchar*str="func04isrunning
";
uart_write((uint8_t*)str,rt_strlen(str),100);
rt_kprintf("func04isrunning
");
}
UART_DISPATCHER_CALLBACK_REGISTER(1,0x04,dispatcher_on_04_callback);

voiddispatcher_on_05_callback(constuint32_tchannel,constuint8_t*data,constuint32_tdata_len)
{
rt_kprintf("func05isrunning
");
}
UART_DISPATCHER_CALLBACK_REGISTER(1,0x05,dispatcher_on_05_callback);

voiddispatcher_on_06_callback(constuint32_tchannel,constuint8_t*data,constuint32_tdata_len)
{
rt_kprintf("func06isrunning
");
}
UART_DISPATCHER_CALLBACK_REGISTER(1,0x06,dispatcher_on_06_callback);

測試效果

通過上面的回調(diào)函數(shù)注冊,我們來測試下是不是達到預(yù)期情況:

87fd2ba8-1a6f-11ee-962d-dac502259ad0.png





審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 定時器
    +關(guān)注

    關(guān)注

    23

    文章

    3218

    瀏覽量

    113695
  • RTT
    RTT
    +關(guān)注

    關(guān)注

    0

    文章

    64

    瀏覽量

    16991
  • UART接口
    +關(guān)注

    關(guān)注

    0

    文章

    124

    瀏覽量

    15199
  • HAL庫
    +關(guān)注

    關(guān)注

    1

    文章

    114

    瀏覽量

    6034

原文標(biāo)題:04-HAL庫UART配置及協(xié)議解析設(shè)計

文章出處:【微信號:小飛哥玩嵌入式,微信公眾號:小飛哥玩嵌入式】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    stm32 HALUART中斷 使用說明

    大家參考。發(fā)帖真的不習(xí)慣,word粘貼也不好。所以詳見附件吧。也有例程一個。首先,下載一個STM32CubeMX,圖形化配置外設(shè),十分方便。至此,已非常明確HAL
    發(fā)表于 06-30 22:32

    STM32 HAL CUBEMX配置 ADC采集 精選資料分享

    提示:文章寫完后,目錄可以自動生成,如何生成可參考右邊的幫助文檔文章目錄STM32 HAL CUBEMX配置 ADC采集軟硬件型號1.單通道不定時任意時刻采集信號
    發(fā)表于 08-11 07:46

    HAL配合CUBEMX配置

    本次教程主要介紹 :HAL配合CUBEMX配置一些常用外設(shè)的初始化,直觀感受STM32編程,用最短時間入門STM32。
    發(fā)表于 08-11 07:07

    【STM32的HAL開發(fā)】CubeMX配置HAL,不進串口中斷的問題 精選資料分享

    【STM32的HAL開發(fā)】串口中斷開發(fā)環(huán)境main.c添加代碼(1/2)stm32f4xx_it.c添加代碼(2/2)注意附代碼開發(fā)環(huán)境cub
    發(fā)表于 08-16 07:00

    使用CUBEMX配置hal輸入捕獲

    之前有使用標(biāo)準(zhǔn)配置的,具體可以看我之前的博客這個項目是使用CUBEMX配置,hal來寫,相
    發(fā)表于 01-06 07:23

    STM32CubeMX 配置STM32F407 實現(xiàn)HAL延時微妙方案

    STM32CubeMX 配置STM32F407 實現(xiàn)HAL延時微妙方案
    發(fā)表于 11-24 20:51 ?20次下載
    STM32<b class='flag-5'>CubeMX</b> <b class='flag-5'>配置</b>STM32F407 實現(xiàn)<b class='flag-5'>HAL</b><b class='flag-5'>庫</b>延時微妙方案

    串口通信小試牛刀~使用STM32CubeMX+ HAL點亮流水燈

    STM32CubeMX與Keil MDK配合HAL完成流水燈的點亮。目錄一、STM32串口通信基礎(chǔ)二、STM32CubeMX與keil基于HAL
    發(fā)表于 12-07 10:06 ?14次下載
    串口通信小試牛刀~使用STM32<b class='flag-5'>CubeMX</b>+ <b class='flag-5'>HAL</b><b class='flag-5'>庫</b>點亮流水燈

    STM32 CubeMX+HAL基本操作

    STM32 CubeMX+HAL基本操作
    發(fā)表于 12-07 11:21 ?50次下載
    STM32 <b class='flag-5'>CubeMX+HAL</b><b class='flag-5'>庫</b>基本操作

    STM32實戰(zhàn) 2 | STM32CubeMXHAL點亮LED

    STM32實戰(zhàn) 2 | STM32CubeMXHAL點亮LED
    發(fā)表于 12-08 12:36 ?34次下載
    STM32實戰(zhàn) 2 | STM32<b class='flag-5'>CubeMX</b>及<b class='flag-5'>HAL</b><b class='flag-5'>庫</b>點亮LED

    【STM32 HALUART串口通訊

    HALUART的三種收發(fā)方式(一)阻塞收發(fā)特點:簡單粗暴,占滿單片機資源進行收發(fā)簡介:發(fā)送:發(fā)送指定長度的數(shù)據(jù)。如果超時沒發(fā)送完成,則不再發(fā)送,返回超時標(biāo)志接收:接收指定長度的數(shù)據(jù)
    發(fā)表于 12-24 18:44 ?5次下載
    【STM32 <b class='flag-5'>HAL</b>】<b class='flag-5'>UART</b>串口通訊

    STM32 HAL CubeMX教程(五)串口通信基礎(chǔ)

    STM32 HAL CubeMX教程(五)串口通信基礎(chǔ)串口通信簡介CubeMX配置初始化程序分析程序編寫
    發(fā)表于 12-24 18:49 ?12次下載
    STM32 <b class='flag-5'>HAL</b><b class='flag-5'>庫</b> <b class='flag-5'>CubeMX</b>教程(五)串口通信基礎(chǔ)

    第六節(jié):STM32基于HAL的IIC通信

    STM32 IIC通信; CubeMX配置, HAL, IAR或者Keil編程CubeMX系列使用經(jīng)驗分享
    發(fā)表于 12-27 18:45 ?26次下載
    第六節(jié):STM32基于<b class='flag-5'>HAL</b><b class='flag-5'>庫</b>的IIC通信

    STM32 HAL CUBEMX配置 ADC采集

    提示:文章寫完后,目錄可以自動生成,如何生成可參考右邊的幫助文檔文章目錄STM32 HAL CUBEMX配置 ADC采集軟硬件型號1.單通道不定時任意時刻采集信號
    發(fā)表于 12-27 18:57 ?25次下載
    STM32 <b class='flag-5'>HAL</b><b class='flag-5'>庫</b> <b class='flag-5'>CUBEMX</b><b class='flag-5'>配置</b> ADC采集

    STM32串口通信HAL配置 UART_IT_xx與UART_FLAG_xx 的區(qū)別

    STM32串口通信HAL配置 UART_IT_xx與UART_FLAG_xx 的區(qū)別:(最后
    發(fā)表于 12-28 19:05 ?2次下載
    STM32串口通信<b class='flag-5'>HAL</b><b class='flag-5'>庫</b><b class='flag-5'>配置</b><b class='flag-5'>中</b> <b class='flag-5'>UART</b>_IT_xx與<b class='flag-5'>UART</b>_FLAG_xx 的區(qū)別

    HAL無法實現(xiàn)UART的DMA傳輸真是這樣嗎?

    使用STM32CubeMx進行圖形化配置,并生成基于HAL的初始代碼,要實現(xiàn)UART收發(fā)功能的DMA傳輸?shù)脑挘税才藕玫氖瞻l(fā)緩沖內(nèi)存外,
    的頭像 發(fā)表于 01-08 11:16 ?2378次閱讀