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就够了。

 

posted @ 2024-04-10 16:04  xjxcxjx  阅读(862)  评论(0编辑  收藏  举报