當前聚焦:GD32F4單片機實現接收超時中斷+DMA實現串口的不定長接收和DMA發送

2023-06-09 15:20:59 來源:FunIO


(相關資料圖)

1、通常的實現方式介紹

環形緩沖區+定時器超時中斷的方式設備任務比較繁重時,使用中斷接收可能會丟失數據。尤其是在長時間關閉中斷或者串口中斷優先級不高時頻繁進出中斷。在使用 RTOS的系統中,每收到一個數據就會進行一次任務到中斷的切換和中斷到任務的切換環形緩沖區可以接收多幀數據數據幀超時間隔可以設置優點缺點使用串口接收空閑中斷+DMA的方式空閑中斷的時間對于同一個波特率來說是固定的,但某些時候 1 個字節的接收時間太短,不能作為數據幀接收完成的標志不會頻繁在任務和中斷之間切換,效率會更高一般不會丟失數據優點缺點

2、接收超時中斷的相關內容

GD32F4 系列的單片機串口除了空閑中斷外,還有可配置時間的接收超時中斷(STM32F4 系列沒有此中斷、STM32L4 系列有),使能配置在USART_CTL3寄存器RTIE,如下圖

USART_CTL3寄存器

接收超時標志在USART_STAT1寄存器的 RTF,如下圖

USART_STAT1寄存器

超時時間在USART_RT寄存器中**RT[23:0]**配置,如下圖

USART_RT寄存器

其中RT24位,單位是波特率的位時間,即 bps。舉個例子,如果串口的參數配置位 8-N-1(一個開始位、8 個數據位、沒有奇偶校驗位、一個停止位),即一個字節的傳輸需要 10 個波特率的比特位,RT 設置為 100,則表示 10(100/10)個字節的傳輸超時時間。

3、接收超時中斷+DMA 實現

示例中用到了 串口 2DMA0通道 1(串口 2 的 DMA 接收)和 通道 3(串口 2 的 DMA 發送),串口 2 的TxPB10RxPB11

串口接收數據緩沖區
#define BLE_UARTUSART2      ///< 串口2#define RX_SERIAL_BUF_SIZE 256    ///< 串口2的接收緩沖區大小static char recv_buf[RX_SERIAL_BUF_SIZE]; ///< receive bufferstatic uint8_t uart2_rx_state = 0;   ///< 串口接收完成標志。1表示接收完成static uint8_t uart2_tx_state = 0;   ///< 串口DMA發送完成標志。1表示發送完成static uint16_t uart2_rx_len = 0;   ///< 串口實際接收的數據長度
串口中斷處理函數
/**  * @brief uart2的中斷處理函數  *  只關心接收超時中斷  *  * @retval void  *  * @note*/void USART2_IRQHandler(void){ /* UART接收超時中斷 */ if ((usart_interrupt_flag_get(BLE_UART, USART_INT_FLAG_RT) != RESET) &&         (usart_flag_get(BLE_UART, USART_FLAG_RT) != RESET)) {  /* disable DMA and reconfigure */  dma_channel_disable(DMA0, DMA_CH1); //關閉DMA,在沒有讀取該接收幀數據之前禁止DMA再接收數據  dma_flag_clear(DMA0, DMA_CH1, DMA_FLAG_FTF);  // 清除DMA傳輸完成標志位  /* Clear receiver timeout flag *///  usart_flag_clear(BLE_UART, USART_FLAG_RT);  usart_interrupt_flag_clear(BLE_UART,USART_INT_FLAG_RT);  usart_data_receive(BLE_UART); /* 清除接收完成標志位 */  // 設置接收的數據長度  uart2_rx_len = get_uart2_dma_recv_data_size();  /* 接收超時后,說明一幀數據接收完畢,置接收完成標志 */  uart2_rx_state = 1; }}
DMA0_Channel1 傳輸完成中斷(用于串口的接收完成),正常情況下,此中斷不會發生。
/**  * @brief DMA0_Channel1傳輸完成中斷  *  用于串口DMA接收  *  * @retval void  *  * @note 因用到了串口的接收超時中斷方式,正常情況下,串口的DMA接收完成不會發生  */void DMA0_Channel1_IRQHandler(void){ if(dma_interrupt_flag_get(DMA0, DMA_CH1, DMA_INT_FLAG_FTF)) {     dma_interrupt_flag_clear(DMA0, DMA_CH1, DMA_INT_FLAG_FTF); // uart2_rx_state = 1;  dma_channel_disable(DMA0, DMA_CH1);  // 關閉DMA接收傳輸 }}
DMA0_Channel3 傳輸完成中斷(用于串口的發送完成)
/**  * @brief DMA0_Channel3傳輸完成中斷  *  用于BLE模塊的串口DMA發送  *  * @retval void  *  * @note  */void DMA0_Channel3_IRQHandler(void){ if(dma_interrupt_flag_get(DMA0, DMA_CH3, DMA_INT_FLAG_FTF)) {     dma_interrupt_flag_clear(DMA0, DMA_CH3, DMA_INT_FLAG_FTF);  uart2_tx_state = 1;  dma_channel_disable(DMA0, DMA_CH3);  // 關閉DMA發送傳輸    }}
獲取 uart2 串口 DMA 接收的數據長度
/**  * @brief 獲取uart2串口DMA接收的數據長度.  *  * @retval void  *  * @note  */static unsigned int get_uart2_dma_recv_data_size(void){    /*    dma_transfer_number_get(DMA_CH2);是獲取當前指針計數值,    用內存緩沖區大小 - 此計數值 = 接收到的數據長度(這里單位為字節)。    需要說明下在讀取數據長度的時候需要先把接收DMA關閉,讀取完了或者是數據處理完了在打開接收DMA,防止在處理的過程中有數據到來而出錯。    */    return (RT_SERIAL_RB_BUFSZ - (dma_transfer_number_get(DMA0, DMA_CH1)));}
uart2 串口初始化
/**  * @brief uart2串口初始化.  *   串口接收通過DMA+接收超時中斷實現,設置的超時時間為100個bps  *  * @parambaudrate 串口波特率  *  * @retval void  *  * @note  */static void uart2_init(uint32_t baudrate){ /*uart dma rx and tx set*/ dma_single_data_parameter_struct dma_init_uart; /***************************** 配置uart2的gpio *****************************/ /* enable GPIO clock */ rcu_periph_clock_enable(RCU_GPIOB); /* connect port to USARTx_Tx */ gpio_af_set(GPIOB, GPIO_AF_7, GPIO_PIN_10); /* connect port to USARTx_Rx */ gpio_af_set(GPIOB, GPIO_AF_7, GPIO_PIN_11); /* configure USART Tx as alternate function push-pull */ gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_10); gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_10); /* configure USART Rx as alternate function push-pull */ gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_11); gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_11); /***************************** 配置uart2的參數 *****************************/ /* enable USART clock */ rcu_periph_clock_enable(RCU_USART2); /* USART configure */ usart_deinit(BLE_UART); usart_oversample_config(BLE_UART, USART_OVSMOD_8); usart_baudrate_set(BLE_UART, baudrate); // 波特率 usart_parity_config(BLE_UART, USART_PM_NONE); // 校驗位:NONE usart_word_length_set(BLE_UART, USART_WL_8BIT); // 數據位:8 usart_stop_bit_set(BLE_UART, USART_STB_1BIT); // 停止位:1 usart_receive_config(BLE_UART, USART_RECEIVE_ENABLE); // 打開串口接收功能 usart_transmit_config(BLE_UART, USART_TRANSMIT_ENABLE); // 打開串口發送功能 // 接收超時設置,100個波特率的比特位 usart_receiver_timeout_threshold_config(BLE_UART, 100); usart_interrupt_enable(BLE_UART, USART_INT_RT); usart_receiver_timeout_enable(BLE_UART); /* USART interrupt configuration */ nvic_irq_enable(USART2_IRQn, 0, 1); usart_enable(BLE_UART); usart_dma_receive_config(BLE_UART, USART_DENR_ENABLE); // 使能DMA接收功能 usart_dma_transmit_config(BLE_UART, USART_DENT_ENABLE); // 使能DMA發送功能 /***************************** 配置uart2的DMA接收 ****************************/ /* enable DMA0 */ rcu_periph_clock_enable(RCU_DMA0); /* deinitialize DMA channel */ dma_deinit(DMA0, DMA_CH1); dma_init_uart.direction = DMA_PERIPH_TO_MEMORY; dma_init_uart.memory0_addr = (uint32_t)(recv_buf); // 存儲器地址 dma_init_uart.memory_inc = DMA_MEMORY_INCREASE_ENABLE; dma_init_uart.periph_memory_width = DMA_PERIPH_WIDTH_8BIT; dma_init_uart.number = sizeof(recv_buf); dma_init_uart.periph_addr = (uint32_t)&USART_DATA(BLE_UART); dma_init_uart.periph_inc = DMA_PERIPH_INCREASE_DISABLE; dma_init_uart.priority = DMA_PRIORITY_ULTRA_HIGH; dma_init_uart.circular_mode = DMA_CIRCULAR_MODE_DISABLE; dma_single_data_mode_init(DMA0, DMA_CH1, &dma_init_uart); dma_channel_subperipheral_select(DMA0, DMA_CH1, DMA_SUBPERI4);    uart2_rx_state = 0;    uart2_rx_len = 0; //使能通道 dma_channel_enable(DMA0, DMA_CH1); /***************************** 配置uart2的DMA發送 ***************************/ /* deinitialize DMA channel */ dma_deinit(DMA0, DMA_CH3); dma_init_uart.direction = DMA_MEMORY_TO_PERIPH; dma_init_uart.memory0_addr = RT_NULL;  // 內存基地址 dma_init_uart.number = 0;  // len個數據 dma_single_data_mode_init(DMA0, DMA_CH3, &dma_init_uart); dma_channel_subperipheral_select(DMA0, DMA_CH3, DMA_SUBPERI4);// nvic_irq_enable(DMA0_Channel3_IRQn, 0, 2); uart2_tx_state = 0; return;}
重新配置 uart2 串口的 DMA 接收通道
/**  * @brief 重新配置uart2串口的DMA接收通道  *  * @retval void  *  * @note  */static void uart2_dma_rx_refcg(void){ /* disable DMA and reconfigure */ dma_channel_disable(DMA0, DMA_CH1); //關閉DMA,在沒有讀取該接收幀數據之前禁止DMA再接收數據// DMA_INTC0(DMA0) |= DMA_FLAG_ADD(DMA_CHINTF_RESET_VALUE, DMA_CH1); dma_memory_address_config(DMA0, DMA_CH1, DMA_MEMORY_0, (uint32_t)(recv_buf)); // 存儲器地址 dma_transfer_number_config(DMA0, DMA_CH1, sizeof(recv_buf););    uart2_rx_state = 0; uart2_rx_len = 0; // 使能通道 dma_channel_enable(DMA0, DMA_CH1);}
DMA 串口發送串口發送使用 DMA 方式時,直接調用 uart2_sendData_DMA 函數即可。等待發送完成時,可以通過等待(uart2_tx_state == 1)實現,或者等待 DMA0 通道 3 的 DMA_FLAG_FTF 置位實現,或者使用 RTOS 的信號量實現
/**  * @brief BLE模塊的DMA串口發送.  *  *  * @param data 發送數據緩沖區地址  * @param len 發送數據長度  *  * @retval void  *  * @note  */void uart2_sendData_DMA(uint8_t *data, uint32_t len){ /* disable DMA and reconfigure */ dma_channel_disable(DMA0, DMA_CH3); dma_flag_clear(DMA0, DMA_CH3, DMA_FLAG_FTF);  // 清除DMA傳輸完成標志位 dma_memory_address_config(DMA0, DMA_CH3, DMA_MEMORY_0, (uint32_t)(data)); // 存儲器地址 dma_transfer_number_config(DMA0, DMA_CH3, len);// /* enable DMA0 channel3 transfer complete interrupt */// dma_interrupt_enable(DMA0, DMA_CH3, DMA_CHXCTL_FTFIE);// uart2_tx_state = 0; dma_channel_enable(DMA0, DMA_CH3);  // 使能DMA傳輸 // 等待傳輸完成 while(dma_flag_get(DMA0, DMA_CH3, DMA_FLAG_FTF) == RESET) {}// // 等待傳輸完成// while(uart2_tx_state == 0);// printf("uart2_sendData_DMA complete");}
接收處理任務
/**  * @brief  main函數  * @param  argc  * @param  argv  * @note   等待接收數據完成,然后做相應的處理  * @retval None  */int main(char argc, char *argv[]){ uart2_init(115200);    while (1)    {        // 接收完成一幀數據        if(uart2_rx_state == 1)        {            // 數據處理            ... ...            // 處理完成后,重新啟動串口的DMA接收            uart2_dma_rx_refcg();        }        // 其他處理        ... ...    }}

標簽:

上一篇:ARM Cortex-M4內核架構概述
下一篇:最后一頁