[GD32450I-EVAL] USART and DMA variable length reception
[Copy link]
This post was last edited by tinnu on 2020-10-25 23:18
(1) Enable DMA
The recommended DMA sending USART mode in the routine:
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);
}
(2) Question 1
However, according to actual tests, this mode will cause overflow if the DMA is not restarted in time after sending 10 data in the buffer.
Regarding overflow, there is this passage on page 507 of the reference manual:
When a frame of data is received and the RBNE bit has not been cleared, subsequent data frames will not be stored in the data receive buffer.
The overflow error flag bit ORERR in the USART_STAT0 register will be set. If DMA is enabled and the ERRIE bit in the USART_CTL2 register is set or RBNEIE is set, an interrupt will be generated.
In this way, you still have to deal with the ORERR error flag, which is very cumbersome.
(3) Idle interrupts
can be processed by idle interrupts. By default, we restart DMA after each idle interrupt is triggered:
Enable idle interrupts:
usart_interrupt_enable(USART0, USART_INT_IDLE);
nvic_irq_enable(USART0_IRQn, 2U, 0U);
Processing function:
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();
}
}
(4) Problem 2
The above method still has problems:
Every time the idle interrupt is completed, it will be triggered multiple times. This may be the IP core strategy of GigaDevice. To solve this problem, you can query the current DMA that has not been sent. How many bits are left to resolve? If not a single bit is sent, it is considered that no restart is required.
Because the idle interrupt is not interrupted for every bit, but after a string of bits is completed, the IDLE flag will be set for a period of time when no new bit appears. I guess this is why the IDLE flag will continue to trigger. Reasons why it cannot be cleared by software.
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();
}
}
That's it. In fact, you can also query the data volume and data content in the cache to restart if it is not full.
|