娇小w搡bbbb搡bbb,《第一次の人妻》,中国成熟妇女毛茸茸,边啃奶头边躁狠狠躁视频免费观看

STM32串口接收不定長數(shù)據(jù)(接收中斷+超時判斷)

發(fā)布者:RadiantDreams最新更新時間:2024-12-17 來源: jianshu關(guān)鍵字:STM32  串口接收  不定長數(shù)據(jù)  接收中斷  超時判斷 手機看文章 掃描二維碼
隨時隨地手機看文章

玩轉(zhuǎn) STM32 單片機,肯定離不開串口。串口使用一個稱為串行通信協(xié)議的協(xié)議來管理數(shù)據(jù)傳輸,該協(xié)議在數(shù)據(jù)傳輸期間控制數(shù)據(jù)流,包括數(shù)據(jù)位數(shù)、波特率、校驗位和停止位等。由于串口簡單易用,在各種產(chǎn)品交互中都有廣泛應(yīng)用。

但在使用串口通訊的時候,我們并不知道對方會發(fā)送多少個數(shù)據(jù),也不知道數(shù)據(jù)什么時候發(fā)送完,簡單來講就是:如何確保收到一幀完整的數(shù)據(jù)?

串口發(fā)送的數(shù)據(jù)有長有短,如果沒有接收完整,肯定會影響后續(xù)業(yè)務(wù)的處理。為了接收不定長數(shù)據(jù),常見的處理方法有:

1. 固定格式

比如雙方約定,一幀的數(shù)據(jù)以 AA BB 開頭,以 BB AA 結(jié)尾,這樣在從機接收數(shù)據(jù)的時候,一旦收到 AA BB 字符,就知道對方要發(fā)來一個數(shù)據(jù)包了,然后就把后面發(fā)來的數(shù)據(jù)保存起來,直到接收到 BB AA 為止。

這種方法簡單高效,但缺點就是需要每個字符都進行判斷,浪費 CPU 資源,增加功耗。

2. 接收中斷+超時判斷

串口接收到一個數(shù)據(jù)時,就會觸發(fā)接收中斷。但如何判斷數(shù)據(jù)已經(jīng)發(fā)送完了呢?

通常來講,兩幀數(shù)據(jù)之間,會有個時間間隔。因此,我們可以使用一個計時器,如果在一個固定的時間點里沒接收到新的字符,則認為一幀數(shù)據(jù)接收完成了。

3. 空閑中斷

串口在空閑時,也就是說串口在一段時間里沒有接收到新數(shù)據(jù),則會觸發(fā)空閑中斷。細心的同學(xué)應(yīng)該發(fā)現(xiàn)了,空閑中斷實際上跟上面的超時判斷是一樣樣的,只不過空閑中斷是硬件自帶,但超時判斷需要我們自己實現(xiàn)。

所以,一旦接收到空閑中斷,可以認為接收到一幀完整的數(shù)據(jù)。

但是,空閑中斷并不是所有的 MCU 都具備,一般高端一點的 MCU 才有,低端一些的 MCU 并沒有空閑中斷。

1. 源碼下載及前置閱讀

本文首發(fā) 良許嵌入式網(wǎng) :https://www.lxlinux.net/e/ ,歡迎關(guān)注!

本文所涉及的源碼及安裝包如下(由于平臺限制,請點擊以下鏈接閱讀原文下載):

https://www.lxlinux.net/e/stm32/stm32-usart-receive-data-using-rxne-time-out.html

如果你是個零基礎(chǔ)的小白,連 STM32 都沒見過,我也給你準備了一個保姆級教程,手把手教你搭建好 STM32 開發(fā)環(huán)境,并教你如何下載程序,簡直業(yè)界良心!

https://www.lxlinux.net/e/stm32/stm32-quick-start-for-beginner.html

如果你連代碼都不知道怎么燒錄到 STM32 的,可以參考下文,提供了 5 種代碼燒錄方式:

https://www.lxlinux.net/e/stm32/five-ways-to-flash-program-to-stm32.html

如果你想自己搭一個屬于自己的工程模板,可以參考下面這篇文章:

https://www.lxlinux.net/e/stm32/create-stm32-hal-project-template.html

在本文中,我們詳細來介紹如何使用接收中斷+超時判斷完成不定長數(shù)據(jù)的接收,對于空閑中斷的接收,請查看下文:

https://www.lxlinux.net/e/stm32/stm32-usart-receive-data-using-idle-dma.html

2. 什么是接收中斷?

前文已經(jīng)提到,當接收到一字節(jié)數(shù)據(jù)時,會觸發(fā)接收中斷,對應(yīng)串口狀態(tài)寄存器第 5 位被置 1 ,如下圖示。

當我們將 DR 寄存器的值讀取之后,該位又被自動清零。

3. 硬件準備

  • STM32 核心板

本文使用正點原子 M48Z 核心板,小巧好用,某寶 20 元出頭。

  • USB 轉(zhuǎn) TTL

這種設(shè)備主要作用是用來調(diào)試或下載程序。價格也很便宜,普遍 5~8 元。

  • ST-Link

ST-Link 是一種用于 STM32 微控制器的調(diào)試和編程工具,它可以通過 SWD 或 JTAG 接口與開發(fā)板進行通信。一般也很便宜,七八元左右。

4. 編程實戰(zhàn)

在本實驗中,我們將串口 1 作為 log 輸出端口,串口 2 作為本次實驗的接收端口。

因此我們需要提前創(chuàng)建 uart2 模塊,包含 uart2.c 及 uart2.h 兩個文件,并加載進工程模板。

4.1 串口初始化

串口的初始化大家應(yīng)該不陌生,主要步驟為:

  1. 定義串口句柄 uart2_handle ,并調(diào)用 HAL_UART_Init 進行初始化;

  2. 初始化串口底層函數(shù),調(diào)用 HAL_UART_MspInit 函數(shù)。

第一步在 uart2.c 文件里進行:

UART_HandleTypeDef uart2_handle;


void uart2_init(uint32_t baudrate)

{

    uart2_handle.Instance          = UART2_INTERFACE;              /* UART2 */

    uart2_handle.Init.BaudRate     = baudrate;                     /* 波特率 */

    uart2_handle.Init.WordLength   = UART_WORDLENGTH_8B;           /* 數(shù)據(jù)位 */

    uart2_handle.Init.StopBits     = UART_STOPBITS_1;              /* 停止位 */

    uart2_handle.Init.Parity       = UART_PARITY_NONE;             /* 校驗位 */

    uart2_handle.Init.Mode         = UART_MODE_TX_RX;              /* 收發(fā)模式 */

    uart2_handle.Init.HwFlowCtl    = UART_HWCONTROL_NONE;          /* 無硬件流控 */

    uart2_handle.Init.OverSampling = UART_OVERSAMPLING_16;         /* 過采樣 */

    HAL_UART_Init(&uart2_handle);                                  /* 使能UART2 */

}

第二步在 usart.c 文件里進行,其實也可以在 uart2.c 文件里做,但我懶~

在最下面一行代碼,我們使用 __HAL_UART_ENABLE_IT() 使能接收中斷。

void HAL_UART_MspInit(UART_HandleTypeDef *huart)

{

    GPIO_InitTypeDef gpio_init_struct;


    if (huart->Instance == USART_UX)                            /* 如果是串口1,進行串口1 MSP初始化 */

    {

        ....

        // 節(jié)略串口1相關(guān)代碼

        ....

    }

    else if (huart->Instance == UART2_INTERFACE)                /* 如果是UART2 */

    {

        UART2_TX_GPIO_CLK_ENABLE();                             /* 使能UART2 TX引腳時鐘 */

        UART2_RX_GPIO_CLK_ENABLE();                             /* 使能UART2 RX引腳時鐘 */

        UART2_CLK_ENABLE();                                     /* 使能UART2時鐘 */


        gpio_init_struct.Pin    = UART2_TX_GPIO_PIN;            /* UART2 TX引腳 */

        gpio_init_struct.Mode   = GPIO_MODE_AF_PP;              /* 復(fù)用推挽輸出 */

        gpio_init_struct.Pull   = GPIO_NOPULL;                  /* 無上下拉 */

        gpio_init_struct.Speed  = GPIO_SPEED_FREQ_HIGH;         /* 高速 */

        HAL_GPIO_Init(UART2_TX_GPIO_PORT, &gpio_init_struct);   /* 初始化UART2 TX引腳 */


        gpio_init_struct.Pin    = UART2_RX_GPIO_PIN;            /* UART2 RX引腳 */

        gpio_init_struct.Mode   = GPIO_MODE_INPUT;              /* 輸入 */

        gpio_init_struct.Pull   = GPIO_NOPULL;                  /* 無上下拉 */

        gpio_init_struct.Speed  = GPIO_SPEED_FREQ_HIGH;         /* 高速 */

        HAL_GPIO_Init(UART2_RX_GPIO_PORT, &gpio_init_struct);   /* 初始化UART2 RX引腳 */


        HAL_NVIC_SetPriority(UART2_IRQn, 0, 0);                 /* 搶占優(yōu)先級0,子優(yōu)先級0 */

        HAL_NVIC_EnableIRQ(UART2_IRQn);                         /* 使能UART2中斷通道 */


        __HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);              /* 使能UART2接收中斷 */

    }

}


4.2 判斷接收中斷

在串口 2 接收中斷里,我們先使用 __HAL_UART_GET_FLAG() 函數(shù)判斷 RXNE 這一位有沒有被置 1 ,如果被置 1 ,則代表接收到字符,調(diào)用 HAL_UART_Receive() 函數(shù)接收字符,并保存于臨時變量 receive_data 中。

之后,再調(diào)用 HAL_UART_Transmit() 函數(shù)將接收到的字符打印出來。

void UART2_IRQHandler(void)

{

  uint8_t receive_data = 0;   

  if(__HAL_UART_GET_FLAG(&uart2_handle,UART_FLAG_RXNE) != RESET)

  {

    HAL_UART_Receive(&uart2_handle, &receive_data, 1, 1000);        //串口2接收1位數(shù)據(jù)

    HAL_UART_Transmit(&uart2_handle, &receive_data, 1, 1000);       //將接收的數(shù)據(jù)打印出來

  }

}


現(xiàn)在我們通過接收中斷就可以實現(xiàn)了自發(fā)自收,編譯后燒進板子,效果如下:

但現(xiàn)在我們只實現(xiàn)了字符的接收,并不知道一幀的數(shù)據(jù)什么時候接收完。

在下面的操作里,我們就通過超時的方法,進一步判斷數(shù)據(jù)是否完成傳輸。

4.3 數(shù)據(jù)接收完成判斷

如何判斷一幀的數(shù)據(jù)接收完成了?

在本文中,我們使用超時的方法進行判斷,這種方法雖然會耗費 CPU 資源,但因為比較簡單,所以使用也很廣泛。在下一篇文章里,我們將使用空閑中斷+DMA 的方法,更高效進行幀數(shù)據(jù)接收完成判斷。

超時判斷的思路如下:

  1. 將接收到的字符保存在接收緩沖區(qū)里,并定義一個變量 uart2_cnt 計算總共收到了多少個字符;

  2. 假如一幀的數(shù)據(jù)接收完成了,那么 uart2_cnt 變量的值應(yīng)該維持不變。

第一個步驟比較好實現(xiàn),還是在串口 2 接收中斷里,做一些小小的改動:

uint16_t uart2_cnt = 0, uart2_cntPre = 0;


void UART2_IRQHandler(void)

{

    uint8_t receive_data = 0;   

    if(__HAL_UART_GET_FLAG(&uart2_handle, UART_FLAG_RXNE) != RESET){    //獲取接收RXNE標志位是否被置位

        if(uart2_cnt >= sizeof(uart2_rx_buf))                           //如果接收的字符數(shù)大于接收緩沖區(qū)大小,

            uart2_cnt = 0;                                              //則將接收計數(shù)器清零

        HAL_UART_Receive(&uart2_handle, &receive_data, 1, 1000);        //接收一個字符

        uart2_rx_buf[uart2_cnt++] = receive_data;                       //將接收到的字符保存在接收緩沖區(qū)

    }

}


關(guān)鍵是第二步,我們?nèi)绾闻袛?uart2_cnt 什么時候維持不變(也就是一幀的數(shù)據(jù)接收完成了)?也很簡單,我們就定時去查看一下這個變量的值,看看是否跟上一次一樣,如果一樣的話就說明數(shù)據(jù)接收完成了。

因此我們需要再借助一個新的變量 uart2_cntPre ,記錄上一次接收到的數(shù)據(jù)的長度(上面的代碼已經(jīng)定義好了)。

uint8_t uart2_wait_receive(void)

{

    if(uart2_cnt == 0)                                      //如果接收計數(shù)為0,則說明沒有處于接收數(shù)據(jù)中,所以直接跳出,結(jié)束函數(shù)

        return UART_ERROR;


    if(uart2_cnt == uart2_cntPre) {                         //如果上一次的值和這次相同,則說明接收完畢

        uart2_cnt = 0;                                      //清0接收計數(shù)

        return UART_EOK;                                    //返回接收完成標志

    }


    uart2_cntPre = uart2_cnt;                               //置為相同

    return UART_ERROR;                                      //返回接收未完成標志

}


然后我們在 main 函數(shù)里的 while 死循環(huán)定期(例如10ms)調(diào)用 uart2_wait_receive 函數(shù),如果返回值為 UART_EOK 則代表幀數(shù)據(jù)接收完成,我們就可以將數(shù)據(jù)打印出來。

while(1)

{

    if(uart2_wait_receive() == UART_EOK) {      //判斷串口2是否數(shù)據(jù)接收完成

        printf('recv: %srn', uart2_rx_buf);   //打印收到的數(shù)據(jù)

        uart2_rx_clear();                       //清空接收緩沖區(qū)

    }


    delay_ms(10);                               //每隔10毫秒判斷一次

}


當然,接收到的數(shù)據(jù)使用完成之后,我們就應(yīng)該清空接收緩沖區(qū),并將計數(shù)器置 0 ,方便下一次接收,所以我們調(diào)用了 uart2_rx_clear() 函數(shù),其代碼實現(xiàn)為:

[1] [2]
關(guān)鍵字:STM32  串口接收  不定長數(shù)據(jù)  接收中斷  超時判斷 引用地址:STM32串口接收不定長數(shù)據(jù)(接收中斷+超時判斷)

上一篇:《嵌入式-STM32開發(fā)指南》第二部分 基礎(chǔ)篇 - 第3章 按鍵(HAL庫)
下一篇:《嵌入式-STM32開發(fā)指南》第二部分 基礎(chǔ)篇 - 第7章DMA(HAL庫)

推薦閱讀最新更新時間:2025-06-16 02:50

STM32 | 串口空閑中斷接收定長數(shù)據(jù)(DMA方式)
在使用STM32的串口接收數(shù)據(jù)的時候,我們常常會使用接收中斷的方式來接收數(shù)據(jù),常用的是RXNE。這里分享另一種接收數(shù)據(jù)的方式——IDLE中斷(PS:本文的例子運行在STM32F103ZET6上)。 一、IDLE中斷什么時候發(fā)生? IDLE就是串口收到一幀數(shù)據(jù)后,發(fā)生的中斷。什么是一幀數(shù)據(jù)呢?比如說給單片機一次發(fā)來1個字節(jié),或者一次發(fā)來8個字節(jié),這些一次發(fā)來的數(shù)據(jù),就稱為一幀數(shù)據(jù),也可以叫做一包數(shù)據(jù)。 二、RXNE中斷和IDLE中斷的區(qū)別? 當接收到1個字節(jié),就會產(chǎn)生RXNE中斷,當接收到一幀數(shù)據(jù),就會產(chǎn)生IDLE中斷。比如給單片機一次性發(fā)送了8個字節(jié),就會產(chǎn)生8次RXNE中斷,1次IDLE中斷。 三、IDLE中斷如何配
[單片機]
<font color='red'>STM32</font> | <font color='red'>串口</font>空閑<font color='red'>中斷</font><font color='red'>接收</font>不<font color='red'>定長</font><font color='red'>數(shù)據(jù)</font>(DMA方式)
C51:串口通信接收與發(fā)送
假設(shè)要發(fā)送一組數(shù)據(jù) Send 一般采用查詢發(fā)送(循環(huán)發(fā)送)方式: unsigned char Send ; //發(fā)送量 unsigned char i; //循環(huán)量 for(i=0;i 10;i++) { SBUF= Send ; //發(fā)送 while(TI==0); //等待發(fā)送完成 TI=0; //清標志 } 采用中斷發(fā)送方式: unsigned char Send ; //發(fā)送量 unsigned char num; //發(fā)送數(shù)據(jù)
[單片機]
51單片機串口程序,字符串/16進制發(fā)送與接收
這篇文章將說明51串口通信的發(fā)送與接收。分為:單個字符接收,字符串接收;十進制發(fā)送與接收,十六進制發(fā)送與接收。 字符串發(fā)送與十六進制發(fā)送,參考:http://blog.csdn.net/yibu_refresh/article/details/22695063 程序皆由PC串口工具發(fā)送,由單片機接收,并返回接收值給PC機。 一:單個字符的發(fā)送與接收 #include reg52.h #define uint unsigned int #define uchar unsigned char //定義接收 字符 uchar Buffer; //串口初始化函數(shù) void URATinit( ) { TMOD=0x20; S
[單片機]
51單片機<font color='red'>串口</font>程序,字符串/16進制發(fā)送與<font color='red'>接收</font>
基于stm32的多功能時鐘10——數(shù)據(jù)采集與藍牙控制
嘿,我的小可愛們! 在《藍牙串口通信》這一章中,小編帶著大家編寫了藍牙串口通信程序,測試了藍牙通信正常。由于我后來又找到了《藍牙調(diào)試器》軟件,功能強大,可自定義控件,所以下面將編寫通信協(xié)議,通過這款軟件,來實現(xiàn)數(shù)據(jù)采集和藍牙控制。而我們在上一章中,完成了藍牙監(jiān)控界面的設(shè)計,所以這一章中,我們開始講解程序的編寫。 首先,我們要將數(shù)據(jù)包設(shè)置成結(jié)構(gòu)體的形式,便于后面的操作和管理,同時定義接收數(shù)據(jù)堆棧和發(fā)送數(shù)據(jù)堆棧。之所以這樣做,是因為通信協(xié)議規(guī)定,數(shù)據(jù)包必須包括起始字節(jié)、數(shù)據(jù)字節(jié)、校驗字節(jié)和結(jié)束字節(jié),這樣做的目的就是確保數(shù)據(jù)傳輸?shù)恼_性和穩(wěn)定性。關(guān)于通信方面的知識,我以后用到的話,還會介紹的,例如ESP8266等
[單片機]
使用MCU GD32替代STM32的體會
GD32作為國產(chǎn)MCU里的佼佼者,產(chǎn)品線也比較豐富,是替代STM32的一個很好的選擇。前段時間有個項目用到GD32的單片機,今天來說說使用的一些體會。 1.硬件我用的單片機型號為GD32F405RGT6,對應(yīng)STM32F405RGT6。首先,硬件上基本兼容,有一點不同的是GD32的31和47腳為NC,STM32的為VCAP。STM32這兩個引腳需要分別連接一個電容到GND,而GD32則不需要。當然,有這兩個電容也無所謂,所以,硬件上GD32可以直接替換STM32。 仿真器可以使用Jlink,也可以使用STLink,但是下載程序時會彈框提示,非ST芯片。 2.軟件軟件上,前期
[單片機]
使用MCU GD32替代<font color='red'>STM32</font>的體會
STM32 DMA
DMA,全稱為:Direct Memory Access,即直接存儲器訪問,DMA 傳輸將數(shù)據(jù)從一個地址空間復(fù)制到另外一個地址空間。當 CPU 初始化這個傳輸動作,傳輸動作本身是由DMA 控制器 來實行和完成。典型的例子就是移動一個外部內(nèi)存的區(qū)塊到芯片內(nèi)部更快的內(nèi)存區(qū)。像是這樣的操作并沒有讓處理器工作拖延,反而可以被重新排程去處理其他的工作。 DMA 傳輸對于高效能嵌入式系統(tǒng)算法和網(wǎng)絡(luò)是很重要的。DMA 傳輸方式無需 CPU 直接控制傳輸,也沒有中斷處理方式那樣保留現(xiàn)場和恢復(fù)現(xiàn)場的過程,通過硬件為 RAM 與 I/O 設(shè)備開辟一條直接傳送數(shù)據(jù)的通路,能使 CPU 的效率大為提高。DMA 是個非常好的功能,它不但能減輕 CPU 負擔(dān)
[單片機]
STM32的SRAM調(diào)試
據(jù)說Flash的擦寫次數(shù)是有限的,所以在調(diào)試的時候擦來擦去不好,看到boot0、boot1可以配置從SRam啟動,就查了相關(guān)資料,試了一下,ok了。記錄一下,免得以后又忘了。跟flash調(diào)試部分相同的就不再描述了,重點在于SRam調(diào)試的設(shè)置部分,大部分以圖片形式。 Dbg_RAM.ini(D:KeilARMBoardsKeilMCBSTM32Blinky下面有一個,其實MCBSTM32目錄下的都一樣的,只要有)的內(nèi)容: /*---------------------------------------------------------------------------- * Name: Dbg_RAM.ini *
[單片機]
要用STM32實現(xiàn)什么?為什么使用STM32而不是8051?
單片機用處這么廣,尤其是STM32生態(tài)這么火!如何快速上手學(xué)習(xí)呢? 你要考慮的是,要用STM32實現(xiàn)什么 為什么使用STM32而不是8051? 是因為51的頻率太低,無法滿足計算需求?是51的管腳太少,無法滿足眾多外設(shè)的IO? 是51的功耗太大,電池挺不住?是51的功能太弱,而你要使用SPI、I2C、ADC、DMA? 是51的內(nèi)存太小而你要存儲的東西太多? 當你需要使用STM32某些功能,而51實現(xiàn)不了的時候,那STM32自然不需要學(xué)習(xí),你會直接去尋找STM32某方面的使用方法。比如要用spi協(xié)議的網(wǎng)卡、要使用串口通信、要使用rtos等等。寄存器vs庫函數(shù) 我的觀點是:當你debug的時候寄存器很重要,當你需要理解芯片工作細節(jié)
[單片機]
小廣播
設(shè)計資源 培訓(xùn) 開發(fā)板 精華推薦

最新單片機文章

 
EEWorld訂閱號

 
EEWorld服務(wù)號

 
汽車開發(fā)圈

 
機器人開發(fā)圈

電子工程世界版權(quán)所有 京ICP證060456號 京ICP備10001474號-1 電信業(yè)務(wù)審批[2006]字第258號函 京公網(wǎng)安備 11010802033920號 Copyright ? 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
主站蜘蛛池模板: 南汇区| 米脂县| 丽水市| 罗平县| 石屏县| 雷山县| 云霄县| 得荣县| 阿克苏市| 托里县| 获嘉县| 张家界市| 竹溪县| 连平县| 临高县| 文山县| 章丘市| 威海市| 曲靖市| 太康县| 武清区| 曲周县| 灵石县| 肥乡县| 水城县| 西昌市| 泰州市| 双鸭山市| 勃利县| 兴山县| 呼伦贝尔市| 惠来县| 舞阳县| 会昌县| 义乌市| 嘉黎县| 罗定市| 汽车| 西安市| 凭祥市| 长泰县|