1、通常的實(shí)現(xiàn)方式介紹
- 環(huán)形緩沖區(qū)+定時器超時中斷的方式
- 設(shè)備任務(wù)比較繁重時,使用中斷接收可能會丟失數(shù)據(jù)。尤其是在長時間關(guān)閉中斷或者串口中斷優(yōu)先級不高時
- 頻繁進(jìn)出中斷。在使用 RTOS 的系統(tǒng)中,每收到一個數(shù)據(jù)就會進(jìn)行一次任務(wù)到中斷的切換和中斷到任務(wù)的切換
- 環(huán)形緩沖區(qū)可以接收多幀數(shù)據(jù)
- 數(shù)據(jù)幀超時間隔可以設(shè)置
- 優(yōu)點(diǎn)
- 缺點(diǎn)
- 使用串口接收空閑中斷+DMA 的方式
- 空閑中斷的時間對于同一個波特率來說是固定的,但某些時候 1 個字節(jié)的接收時間太短,不能作為數(shù)據(jù)幀接收完成的標(biāo)志
- 不會頻繁在任務(wù)和中斷之間切換,效率會更高
- 一般不會丟失數(shù)據(jù)
- 優(yōu)點(diǎn)
- 缺點(diǎn)
2、接收超時中斷的相關(guān)內(nèi)容
GD32F4 系列的單片機(jī)串口除了空閑中斷外,還有可配置時間的接收超時中斷(STM32F4 系列沒有此中斷、STM32L4 系列有),使能配置在USART_CTL3寄存器的 RTIE ,如下圖
USART_CTL3寄存器
接收超時標(biāo)志在USART_STAT1寄存器的 RTF ,如下圖
USART_STAT1寄存器
超時時間在USART_RT寄存器中**RT[23:0]**配置,如下圖
USART_RT寄存器
其中RT為24位,單位是波特率的位時間,即 bps。舉個例子,如果串口的參數(shù)配置位 8-N-1(一個開始位、8 個數(shù)據(jù)位、沒有奇偶校驗(yàn)位、一個停止位),即一個字節(jié)的傳輸需要 10 個波特率的比特位,RT 設(shè)置為 100,則表示 10(100/10)個字節(jié)的傳輸超時時間。
3、接收超時中斷+DMA 實(shí)現(xiàn)
示例中用到了 串口 2 、DMA0的 通道 1 (串口 2 的 DMA 接收)和 通道 3 (串口 2 的 DMA 發(fā)送),串口 2 的Tx為 PB10 、Rx為 PB11 。
- 串口接收數(shù)據(jù)緩沖區(qū)
#define BLE_UART USART2 ///< 串口2 #define RX_SERIAL_BUF_SIZE 256 ///< 串口2的接收緩沖區(qū)大小 static char recv_buf[RX_SERIAL_BUF_SIZE]; ///< receive buffer static uint8_t uart2_rx_state = 0; ///< 串口接收完成標(biāo)志。1表示接收完成 static uint8_t uart2_tx_state = 0; ///< 串口DMA發(fā)送完成標(biāo)志。1表示發(fā)送完成 static uint16_t uart2_rx_len = 0; ///< 串口實(shí)際接收的數(shù)據(jù)長度
- 串口中斷處理函數(shù)
/** * @brief uart2的中斷處理函數(shù) * 只關(guān)心接收超時中斷 * * @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); //關(guān)閉DMA,在沒有讀取該接收幀數(shù)據(jù)之前禁止DMA再接收數(shù)據(jù) dma_flag_clear(DMA0, DMA_CH1, DMA_FLAG_FTF); // 清除DMA傳輸完成標(biāo)志位 /* 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); /* 清除接收完成標(biāo)志位 */ // 設(shè)置接收的數(shù)據(jù)長度 uart2_rx_len = get_uart2_dma_recv_data_size(); /* 接收超時后,說明一幀數(shù)據(jù)接收完畢,置接收完成標(biāo)志 */ uart2_rx_state = 1; } }
- DMA0_Channel1 傳輸完成中斷(用于串口的接收完成),正常情況下,此中斷不會發(fā)生。
/** * @brief DMA0_Channel1傳輸完成中斷 * 用于串口DMA接收 * * @retval void * * @note 因用到了串口的接收超時中斷方式,正常情況下,串口的DMA接收完成不會發(fā)生 */ 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); // 關(guān)閉DMA接收傳輸 } }
- DMA0_Channel3 傳輸完成中斷(用于串口的發(fā)送完成)
/** * @brief DMA0_Channel3傳輸完成中斷 * 用于BLE模塊的串口DMA發(fā)送 * * @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); // 關(guān)閉DMA發(fā)送傳輸 } }
- 獲取 uart2 串口 DMA 接收的數(shù)據(jù)長度
/** * @brief 獲取uart2串口DMA接收的數(shù)據(jù)長度. * * @retval void * * @note */ static unsigned int get_uart2_dma_recv_data_size(void) { /* dma_transfer_number_get(DMA_CH2);是獲取當(dāng)前指針計數(shù)值, 用內(nèi)存緩沖區(qū)大小 - 此計數(shù)值 = 接收到的數(shù)據(jù)長度(這里單位為字節(jié))。 需要說明下在讀取數(shù)據(jù)長度的時候需要先把接收DMA關(guān)閉,讀取完了或者是數(shù)據(jù)處理完了在打開接收DMA,防止在處理的過程中有數(shù)據(jù)到來而出錯。 */ return (RT_SERIAL_RB_BUFSZ - (dma_transfer_number_get(DMA0, DMA_CH1))); }
- uart2 串口初始化
/** * @brief uart2串口初始化. * 串口接收通過DMA+接收超時中斷實(shí)現(xiàn),設(shè)置的超時時間為100個bps * * @param baudrate 串口波特率 * * @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的參數(shù) *****************************/ /* 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); // 校驗(yàn)位:NONE usart_word_length_set(BLE_UART, USART_WL_8BIT); // 數(shù)據(jù)位: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); // 打開串口發(fā)送功能 // 接收超時設(shè)置,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發(fā)送功能 /***************************** 配置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發(fā)送 ***************************/ /* deinitialize DMA channel */ dma_deinit(DMA0, DMA_CH3); dma_init_uart.direction = DMA_MEMORY_TO_PERIPH; dma_init_uart.memory0_addr = RT_NULL; // 內(nèi)存基地址 dma_init_uart.number = 0; // len個數(shù)據(jù) 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; }