1、问题描述
在使用gd32f450时,由于之前没有使用过,所以不太熟悉。板子拿到首先要调试下串口收发,串口接收采用dma。
但是遇到这样的问题,第一次接收正常,但是后面就接收不到了。经过调试,串口空闲中断是可以进入。但是得到的数据长度全部是0
void USART1_IRQHandler( void )
{
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
uint16_t usart1_rx_len = 0;
uint8_t data = 0;
if (RESET != usart_interrupt_flag_get(USART1, USART_INT_FLAG_IDLE)) //空闲中断
{
usart_interrupt_flag_clear(USART1, USART_INT_FLAG_IDLE); // 清除空闲中断标志位
data = (uint8_t)usart_data_receive(USART1);
dma_channel_disable(DMA0, DMA_CH5); // 关闭DMA传输
usart1_rx_len = sizeof(uart1_rx_buf) - dma_transfer_number_get(DMA0, DMA_CH5);
// dma_interrupt_flag_clear(DMA0,DMA_CH5,DMA_INT_FLAG_FTF);
// dma_channel_subperipheral_select(DMA0, DMA_CH5, DMA_SUBPERI4);
// dma_memory_address_config(DMA0, DMA_CH5,DMA_MEMORY_0, (uint32_t)uart1_rx_buf);
// dma_transfer_number_config(DMA0, DMA_CH5, sizeof(uart1_rx_buf));
// usart_dma_receive_config(USART1, USART_DENR_ENABLE);
dma_channel_enable(DMA0, DMA_CH5); // 开启DMA传输
}
}
代码如上,进入串口中断后,先关闭dma,等数据处理完毕后,再打开dma。就出现了上述描述的现象。
2、问题排查
初步考虑到可能是重新打开dma后要重新初始化部分寄存器于是加入了如下的代码:
dma_channel_subperipheral_select(DMA0, DMA_CH5, DMA_SUBPERI4);
dma_memory_address_config(DMA0, DMA_CH5,DMA_MEMORY_0, (uint32_t)uart1_rx_buf);
dma_transfer_number_config(DMA0, DMA_CH5, sizeof(uart1_rx_buf));
usart_dma_receive_config(USART1, USART_DENR_ENABLE);
发现问题依旧,用串口助手发送0xd1
只有第一次能收到0xd1数据,后面每次就会出现读取到的接收数据长度为0的情况。
既然问题可能是由于重新开启了dma造成的,直接在中断中将
dma_channel_disable(DMA0, DMA_CH5); // 关闭DMA传输
dma_channel_enable(DMA0, DMA_CH5); // 开启DMA传输
全部注释掉。再继续调试。发现依然是第一次收到的数据是正常的,后面每次一次都收不到。
接下来直接看寄存器值的变化,可能是第一次接收后,寄存器的值发生了变化。
查找gd32f4的说明书,发现dma0的地址为0x40046000。dma寄存器最大拍偏移地址为0x114,所有重点要观察0x40046000~0x40046114之间寄存器的值。
打开硬件调试,初始化串口后,dma所有寄存器的值如下
第一次发送0xd1,此时进入串口中断后,寄存器的值如下图
发现只有一个值发生了变化,就是从0x80变成了0x7f,对应的十进制分别是128和127。这几本就确定是接收计数器,默认值是128刚好是设置的dmabuffer长度。
代码再次全速运行,发现偏移地址为5的寄存器数值从0变成0A。
也就是说从,接收到0xd1这个数据的前后,只有偏移地址为5的寄存器数值从0变成0A。所以接下来需要查这个偏移地址的作用是啥。
经过查找这个寄存器实际上是中断寄存器,0x0a表示FTFIF5 =1;TAEIF5=1
到这里基本上就有结论了。
中断中没有给dma中断标志位清0
3、问题解决
找出了问题原因那就好办了。直接在在中断中加入清零的函数。
dma_interrupt_flag_clear(DMA0,DMA_CH5,DMA_INT_FLAG_FTF);
经过测试只需要加入清除FTFIF5的函数即可正常运行。
void USART1_IRQHandler( void )
{
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
uint16_t usart1_rx_len = 0;
uint8_t data = 0;
if (RESET != usart_interrupt_flag_get(USART1, USART_INT_FLAG_IDLE)) //空闲中断
{
usart_interrupt_flag_clear(USART1, USART_INT_FLAG_IDLE); // 清除空闲中断标志位
data = (uint8_t)usart_data_receive(USART1);
dma_channel_disable(DMA0, DMA_CH5); // 关闭DMA传输
usart1_rx_len = sizeof(uart1_rx_buf) - dma_transfer_number_get(DMA0, DMA_CH5);
for(int i = 0; i<usart1_rx_len;i++)
{
xQueueSendFromISR(xRxedChars, &uart1_rx_buf[i], &xHigherPriorityTaskWoken);
portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
}
dma_interrupt_flag_clear(DMA0,DMA_CH5,DMA_INT_FLAG_FTF);
dma_channel_enable(DMA0, DMA_CH5);
}
进入中断后,正常关闭dma,待数据处理完成后,清零dma中断及标志位,然后再打开dma。