GD32F470II的UART+DMA方式的使用筆記
GD32官方給的DEMO真的是屎一樣的存在,僅展示最基本簡單的應用案例,拿到實際工程中參考性非常低,也就基本的配置過程具有有限的參考性。
在這種環境下,使用UART+DMA的方式完全是瞎用,感覺能用的函數都給用上。
UART & DMA配置如下:
1 /*! 2 \brief configure USART DMA 3 \param[in] none 4 \param[out] none 5 \retval none 6 */ 7 void usart_dma_config(void) 8 { 9 dma_single_data_parameter_struct dma_init_struct; 10 /* enable DMA1 */ 11 rcu_periph_clock_enable(RCU_DMA1); 12 /* deinitialize DMA channel7(USART0 TX) */ 13 dma_deinit(DMA1, DMA_CH7); 14 dma_init_struct.direction = DMA_MEMORY_TO_PERIPH; 15 dma_init_struct.memory0_addr = (uint32_t)Uart0_tx_buffer; 16 dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE; 17 dma_init_struct.number = HITPAD_DATA_SEND_NUM;//9/42 18 dma_init_struct.periph_addr = USART0_DATA_ADDRESS; 19 dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE; 20 dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT; 21 dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH; 22 dma_single_data_mode_init(DMA1, DMA_CH7, &dma_init_struct); 23 dma_channel_subperipheral_select(DMA1, DMA_CH7, DMA_SUBPERI4); 24 /* configure DMA mode */ 25 dma_circulation_disable(DMA1, DMA_CH7); 26 27 dma_interrupt_enable(DMA1, DMA_CH7, DMA_CHXCTL_FTFIE); 28 29 /* deinitialize DMA channel2(USART0 RX) */ 30 dma_deinit(DMA1, DMA_CH5); 31 dma_init_struct.direction = DMA_PERIPH_TO_MEMORY; 32 dma_init_struct.memory0_addr = (uint32_t)Uart0_rx_buffer; 33 dma_single_data_mode_init(DMA1, DMA_CH5, &dma_init_struct); 34 dma_channel_subperipheral_select(DMA1, DMA_CH5, DMA_SUBPERI4); 35 /* configure DMA mode */ 36 dma_circulation_disable(DMA1, DMA_CH5); 37 } 38 39 void Uart0Tx_init_DMA(UINT32 * Addr, UINT32 number) 40 { 41 dma_single_data_parameter_struct dma_init_struct; 42 43 dma_init_struct.direction = DMA_MEMORY_TO_PERIPH; 44 dma_init_struct.memory0_addr = (uint32_t)Addr; 45 dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE; 46 dma_init_struct.number = (uint32_t)number; 47 dma_init_struct.periph_addr = USART0_DATA_ADDRESS; 48 dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE; 49 dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT; 50 dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH; 51 dma_single_data_mode_init(DMA1, DMA_CH7, &dma_init_struct); 52 dma_channel_subperipheral_select(DMA1, DMA_CH7, DMA_SUBPERI4); 53 /* configure DMA mode */ 54 dma_circulation_disable(DMA1, DMA_CH7); 55 } 56 57 void Usart_init() // 58 {//c12-tx/d2-rx 59 60 uint32_t com = USART0; 61 62 rcu_periph_clock_enable(RCU_GPIOA); 63 rcu_periph_clock_enable(RCU_USART0); 64 65 UINT32 gpio = GPIO_PIN_9 | GPIO_PIN_10; 66 67 gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, gpio); 68 gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, gpio); 69 gpio_af_set(GPIOA, GPIO_AF_7, gpio); 70 71 /* USART interrupt configuration */ 72 nvic_irq_enable(USART0_IRQn, USART0_IRQ_LEVEL, 0); 73 74 usart_deinit(com); 75 usart_baudrate_set(com,UART0_BAUDRATE); 76 usart_receive_config(com, USART_RECEIVE_ENABLE); 77 usart_transmit_config(com, USART_TRANSMIT_ENABLE); 78 usart_enable(com); 79 80 /*enable transmission complete interrupt*/ 81 usart_interrupt_enable(com, USART_INT_TC); 82 usart_flag_clear(com,USART_FLAG_TC); 83 84 usart_dma_config(); 85 86 /* enable USART0 DMA channel transmission and reception */ 87 /*TX*/ 88 dma_channel_enable(DMA1, DMA_CH7); 89 /*RX*/ 90 dma_channel_enable(DMA1, DMA_CH5); 91 92 /* USART DMA enable for transmission and reception */ 93 usart_dma_transmit_config(com, USART_DENT_DISABLE); 94 usart_dma_receive_config(com, USART_DENR_DISABLE); 95 96 HAL_UART0_Config(); 97 98 }
這里僅用到了串口發送,接收沒用,因此發送函數如下:
1 void Usart0DataSend(UINT8* data, UINT32 len) 2 { 3 if(Uart0_Tx_Flag) 4 { 5 Uart0_Tx_Flag = FALSE; 6 7 dma_channel_enable(DMA1, DMA_CH7); 8 9 usart_dma_transmit_config(USART0, USART_DENT_ENABLE); 10 } 11 12 }
上層應用:
1 void Uart0TransmitFunc(void) 2 { 3 if(Uart0_Tx_Flag) 4 { 5 while(GCommon_BufNotEmptyCheck((WR_RD_BUF *)g_HAL_UART_TxBuf)) 6 { 7 for(UINT32 i=0; i<HITPAD_DATA_SEND_NUM; i++) 8 { 9 Uart0_tx_buffer[i] = GCommon_RdDataFromBuf((WR_RD_BUF *)g_HAL_UART_TxBuf); 10 } 11 12 Usart0DataSend(Uart0_tx_buffer, HITPAD_DATA_SEND_NUM); 13 14 break; 15 } 16 } 17 }
串口中斷:
1 void USART0_IRQHandler(void) 2 { 3 4 if(usart_flag_get(USART0,USART_FLAG_TC)!= RESET) 5 { 6 usart_flag_clear(USART0,USART_FLAG_TC);//中斷標記清除 7 8 Uart0_Tx_Flag = TRUE; 9 10 // /*disable TX*/ 11 usart_dma_transmit_config(USART0, USART_DENT_DISABLE); 12 dma_channel_disable(DMA1, DMA_CH7); 13 dma_flag_clear(DMA1, DMA_CH7,DMA_INTF_FTFIF); 14 } 15 16 }
原理也很簡單。
DMA配置成DMA_MEMORY_TO_PERIPH,串口發送的時候開啟下相應的通道開關,DMA會自動搬運配置好的數據量到串口的發送緩沖,串口全部發送完成后觸發中斷。中斷里再做下相應的FLAG清除工作即可。
坑就坑在上面在開啟發送和中斷里清理工作上有不需要干的事情。
- 在重新給專用于顯示的GD32F470II的其中一個串口增加DMA功能的時候,發現串口運行一段時間后就停止工作了,簡單查看DMA + UART寄存器也看不出來哪里有問題。
- 通過測試發現停止傳輸的時候執行一下這段話就又正常了
dma_flag_clear(DMA1, DMA_CH7,DMA_INTF_FTFIF);

不知道為什么,然后查看手冊

這就是原因了。在一次開啟DMA傳輸后,此次DMA傳輸發生了FIFO錯誤/總線錯誤/寄存器訪問錯誤,導致傳輸停止,串口未進中斷清理DMA的FTFIFx標志,DMA就無法繼續使用了
正確的(應該說更精煉)使用配置如下:
1 1、串口初始化里串口DMA開關要直接打開 2 usart_dma_transmit_config(com, USART_DENT_ENABLE);
3 2、發送接口里只需要使能DMA通道即可 4 void Usart0DataSend(UINT8* data, UINT32 len) 5 { 6 if(Uart0_Tx_Flag) 7 { 8 Uart0_Tx_Flag = FALSE; 9 dma_flag_clear(DMA1, DMA_CH7,DMA_INTF_FTFIF); 10 dma_channel_enable(DMA1, DMA_CH7); 11 } 13 } 14 3、串口中斷里僅需清理下DMA通道的發送完成標志 15 void USART0_IRQHandler(void) 16 { 17 if(usart_flag_get(USART0,USART_FLAG_TC)!= RESET) 18 { 19 usart_flag_clear(USART0,USART_FLAG_TC);//中斷標記清除 20 21 Uart0_Tx_Flag = TRUE; 24 } 27 }
這里解釋下為什么這樣改可以
用戶手冊里DMA 的DMA_CHxCTL寄存器0bit CHEN這樣介紹


所以,不需要手動禁止DMA通道,在其傳輸完成的時候會自動清0,實際仿真查看也的確如此。
串口的DMA發送開關也沒必要來回開關,僅控制DMA就夠了。
本文來自博客園,作者:xjxcxjx,轉載請注明原文鏈接:http://www.rzrgm.cn/xjxcxjx/p/18126244,謝絕CSDN轉載!
浙公網安備 33010602011771號