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,转载请注明原文链接:https://www.cnblogs.com/xjxcxjx/p/18126244,谢绝CSDN转载!