【GD32450I-EVAL】USART与DMA不定长接收
[复制链接]
本帖最后由 tinnu 于 2020-10-25 23:18 编辑
(一)使能DMA
例程中推荐的DMA发送USART的模式:
void USART_DMA_Init(void)
{
dma_single_data_parameter_struct dma_init_struct;
rcu_periph_clock_enable(RCU_DMA1);
dma_deinit(DMA1, DMA_CH2);
dma_init_struct.direction = DMA_PERIPH_TO_MEMORY;
dma_init_struct.memory0_addr = (uint32_t)rxbuffer;
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_struct.number = 10;
dma_init_struct.periph_addr = (uint32_t)&USART_DATA(USART0);
dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT;
dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
dma_single_data_mode_init(DMA1, DMA_CH2, &dma_init_struct);
dma_channel_subperipheral_select(DMA1, DMA_CH2, DMA_SUBPERI4);
/* enable DMA channel2 */
dma_channel_enable(DMA1, DMA_CH2);
usart_dma_receive_config(USART0, USART_DENR_ENABLE);
}
(二)问题1
但根据实际测试,这个模式在发送完缓冲区10个数据之后,如果不及时重启DMA的话会导致溢出。
针对溢出,在参考手册507页有这样的一段话:
当接收到一帧数据,而RBNE位还没有被清零,随后的数据帧将不会存储在数据接收缓冲区中。
USART_STAT0 寄存器中的溢出错误标志位 ORERR 将置位。如果使能 DMA 并置位 USART_CTL2寄存器中ERRIE位或者置位RBNEIE,将产生中断。
这样还得去处理 ORERR 这个错误标志位,非常繁琐。
(三)空闲中断
可以考虑用空闲中断来处理,我们默认每次空闲中断触发后都重启DMA :
使能空闲中断:
usart_interrupt_enable(USART0, USART_INT_IDLE);
nvic_irq_enable(USART0_IRQn, 2U, 0U);
处理函数:
void USART0_IRQHandler()
{
static uint32_t g_counter1 = 0;
if (RESET != usart_flag_get(USART0, USART_FLAG_IDLE))
{
usart_flag_clear(USART0, USART_FLAG_IDLE);
g_counter2++;
USART_DMA_Init();
}
}
(四)问题2
上述方法依然存在问题:
每次完成空闲中断都会触发好多次,这可能是兆易方面IP核的策略,针对这个问题可以通过查询当前DMA未发送的位还有多少来解决,如果一个位都没有发送,那就认为不需要重启。
因为空闲中断不是每个bit都会中断的,而是一串bit完成之后,多少时间内没有新的bit出现才会置位一段时间的 IDLE 标志位,我猜这也是为什么IDLE标志位会持续触发而无法被软件清除的原因。
void USART0_IRQHandler()
{
static uint32_t g_counter1 = 0;
if (RESET != usart_flag_get(USART0, USART_FLAG_IDLE))
{
usart_flag_clear(USART0, USART_FLAG_IDLE);
if (dma_transfer_number_get(DMA1, DMA_CH2) == 10)
return;
g_counter2++;
USART_DMA_Init();
}
}
这样就解决了。事实上也可以通过查询缓存中的数据量,数据内容,在没有满的条件下重启。
|