新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > 基于 STM32F4 的串口通信驅動實現詳解(環形緩沖區版)

        基于 STM32F4 的串口通信驅動實現詳解(環形緩沖區版)

        作者:嵌入式芯視野 時間:2025-07-09 來源:今日頭條 收藏

        在嵌入式系統開發中,串口通信(UART)是最常用的基礎通信方式之一。為了解決串口數據讀寫的不連續性問題,通常會配合環形緩沖區使用,以實現高效、穩定的數據收發緩存管理。

        本文介紹一個基于 STM32F4 系列 MCU 編寫的,采用中斷方式配合發送/接收緩沖區,并封裝為控制臺接口,便于在系統中調用。

        一、串口緩沖區定義與初始化

        串口收發數據通常不直接讀寫寄存器,而是通過緩沖機制管理數據流。本例中使用了兩個環形緩沖區 rxbuf 和 txbuf,分別用于接收和發送:

        static unsigned char rxbuf[TTY_RXBUF_SIZE];  // 接收緩沖區static unsigned char txbuf[TTY_TXBUF_SIZE];  // 發送緩沖區static ring_buf_t rbsend, rbrecv;            // 緩沖區控制結構體

        并在串口初始化時調用 ring_buf_init() 對緩沖區進行初始化。

        二、串口初始化函數 uart_init

        static void uart_init(int baudrate)

        該函數用于初始化 USART1,并完成如下步驟:

        1. 初始化收發緩沖區控制結構體。

        2. 打開 GPIOA 和 USART1 時鐘。

        3. 配置 USART1 的引腳復用功能(PA9 → TX,PA10 → RX)。

        4. 配置 GPIO 模式(復用模式,無上下拉)。

        5. 配置串口參數(波特率等)。

        6. 配置 NVIC 中斷優先級并使能 USART1 中斷。

        通過將底層串口配置封裝在 uart_init() 中,使得后續調用更加簡潔。


        三、串口寫函數 uart_write

        static unsigned int uart_write(const void *buf, unsigned int len)

        此函數負責將要發送的數據寫入發送緩沖區,并開啟發送中斷:

        • 通過 ring_buf_put() 將數據寫入 rbsend。

        • 開啟 USART_IT_TXE 發送中斷。

        • 返回實際寫入字節數。

        由于發送在中斷中進行,所以只需觸發中斷即可自動依次發送緩沖區數據。


        四、串口讀函數 uart_read

        static unsigned int uart_read(void *buf, unsigned int len)

        用于從接收緩沖區讀取數據:

        • 通過 ring_buf_get() 從 rbrecv 中取出數據放入 buf。

        • 返回實際讀取長度。

        適用于非阻塞方式的讀取調用。


        五、緩沖區狀態查詢函數

        代碼中定義了以下緩沖區狀態查詢接口:

        static bool tx_isfull(void);   // 判斷發送緩沖區是否滿bool tx_isempty(void);         // 判斷發送緩沖區是否為空bool rx_isempty(void);         // 判斷接收緩沖區是否為空

        這些函數用于上層邏輯判斷是否可以繼續發送、是否有接收數據等,提升串口使用靈活性。


        六、TTY 接口結構體封裝

        串口驅動最終以一個結構體 tty_t 形式暴露接口:

        const tty_t tty = {
            uart_init,
            uart_write,
            uart_read,
            tx_isfull,
            tx_isempty,
            rx_isempty
        };

        這種方式便于統一管理串口控制接口,適合在大型項目中引入“控制臺抽象層”統一管理多個串口。


        七、串口中斷處理函數 USART1_IRQHandler

        這是串口驅動的核心部分,負責響應 USART1 的收發中斷:

        void USART1_IRQHandler(void)

        主要包含以下處理邏輯:

        1. 接收中斷 RXNE:

        2. 從接收寄存器讀取數據。

        3. 存入接收緩沖區 rbrecv。

        4. 發送中斷 TXE:

        5. 從發送緩沖區 rbsend 中取出下一個字節發送。

        6. 若無數據可發,關閉發送中斷。

        7. 溢出錯誤中斷 ORE_RX:

        8. 讀取一次數據清除溢出標志位。

        通過中斷方式處理串口收發,不僅避免了阻塞操作,還能在高速數據傳輸中保持系統響應性。


        八、總結

        本驅動模塊實現了一個完整的串口通信功能,具備如下特點:

        • 支持發送與接收雙緩沖。

        • 使用中斷驅動方式收發數據。

        • 提供狀態判斷接口,便于上層調用。

        • 封裝為 tty_t 控制臺結構,支持模塊化應用。

        其設計適用于嵌入式系統中多個串口同時工作的場景,也適合作為 CLI 控制臺、調試口或上位機通信口的底層驅動支撐。結合環形緩沖區,可有效避免數據丟失或阻塞,是一種常用、穩定的串口通信實現方式。

        開源代碼:

        #include "stm32f4xx.h"#include "ringbuffer.h"#include "tty.h"#include "public.h"   #include <string.h>#if (TTY_RXBUF_SIZE & (TTY_RXBUF_SIZE - 1)) != 0 
            #error "TTY_RXBUF_SIZE must be power of 2!"#endif#if (TTY_TXBUF_SIZE & (TTY_TXBUF_SIZE - 1)) != 0 
            #error "TTY_RXBUF_SIZE must be power of 2!"#endifstatic unsigned char rxbuf[TTY_RXBUF_SIZE];         /*接收緩沖區 */static unsigned char txbuf[TTY_TXBUF_SIZE];         /*發送緩沖區 */static ring_buf_t rbsend, rbrecv;                   /*收發緩沖區管理*//*
         * @brief     串口初始化
         * @param[in]   baudrate - 波特率
         * @return      none
         */static void uart_init(int baudrate){
            ring_buf_init(&rbsend, txbuf, sizeof(txbuf));/*初始化環形緩沖區 */
            ring_buf_init(&rbrecv, rxbuf, sizeof(rxbuf)); 
            
            RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA , ENABLE);
            
        GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
        GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);  
            
            gpio_conf(GPIOA, GPIO_Mode_AF, GPIO_PuPd_NOPULL, 
                      GPIO_Pin_9 | GPIO_Pin_10);
            
            RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
            uart_conf(USART1, baudrate);                    /*串口配置*/
            
            nvic_conf(USART1_IRQn, 1, 1);
            
        }/*
         * @brief     向串口發送緩沖區內寫入數據并啟動發送
         * @param[in]   buf       -  數據緩存
         * @param[in]   len       -  數據長度
         * @return      實際寫入長度(如果此時緩沖區滿,則返回len)
         */static unsigned int uart_write(const void *buf, unsigned int len){   
            unsigned int ret;
            ret = ring_buf_put(&rbsend, (unsigned char *)buf, len);  
            USART_ITConfig(USART1, USART_IT_TXE, ENABLE);    return ret; 
        }/*
         * @brief     讀取串口接收緩沖區的數據
         * @param[in]   buf       -  數據緩存
         * @param[in]   len       -  數據長度
         * @return      (實際讀取長度)如果接收緩沖區的有效數據大于len則返回len否則返回緩沖
         *              區有效數據的長度
         */static unsigned int uart_read(void *buf, unsigned int len){    return ring_buf_get(&rbrecv, (unsigned char *)buf, len);
        }/*發送緩沖區滿*/static bool tx_isfull(void){    return ring_buf_len(&rbsend) == TTY_TXBUF_SIZE;
        }/*發送緩沖區空*/bool tx_isempty(void){    return ring_buf_len(&rbsend) == 0;
        }/*接收緩沖區空*/bool rx_isempty(void){    return ring_buf_len(&rbrecv) == 0;
        }/*控制臺接口定義 -------------------------------------------------------------*/const tty_t tty = {
            uart_init,
            uart_write,
            uart_read,
            tx_isfull,
            tx_isempty,
            rx_isempty
        };/*
         * @brief     串口1收發中斷
         * @param[in]   none
         * @return      none
         */void USART1_IRQHandler(void){     
            unsigned char data;    if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
                data = USART_ReceiveData(USART1);
                ring_buf_put(&rbrecv, &data, 1);           /*將數據放入接收緩沖區*/             
            }    if (USART_GetITStatus(USART1, USART_IT_TXE) != RESET) {        if (ring_buf_get(&rbsend, &data, 1))      /*從緩沖區中取出數據---*/
                    USART_SendData(USART1, data);            
                else{
                    USART_ITConfig(USART1, USART_IT_TXE, DISABLE);    
                }
            }    if (USART_GetITStatus(USART1, USART_IT_ORE_RX) != RESET) {
                data = USART_ReceiveData(USART1);        
            }
        }



        關鍵詞: 串口通信驅動

        評論


        技術專區

        關閉
        主站蜘蛛池模板: 绥德县| 紫云| 江陵县| 诸暨市| 张家口市| 北辰区| 中江县| 仙游县| 山西省| 周宁县| 博白县| 林口县| 缙云县| 正安县| 高雄市| 天祝| 肇州县| 南陵县| 缙云县| 台安县| 荣成市| 道孚县| 奇台县| 维西| 宽甸| 江达县| 习水县| 老河口市| 博白县| 五大连池市| 缙云县| 阜城县| 谢通门县| 花垣县| 尖扎县| 镇江市| 永善县| 新晃| 河津市| 绥中县| 普兰县|