GD32F103和GD32E230在串口接收模式(dma + idle)的区别

本文对比了GD32E230与GD32F103两种型号MCU的串口DMA配置及中断处理差异。GD32E230仅有一个DMA,而GD32F103有多个DMA资源;F103的IDLE中断处理需额外读取数据寄存器。文章还提到了MDKV6编译器中断标志位的注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先上GD32E230的串口DMA代码。

static void uart_dma_init(void)
{
    /* enable DMA clock */
    rcu_periph_clock_enable(RCU_DMA);
    /*configure DMA interrupt*/
    // nvic_irq_enable(DMA_Channel1_2_IRQn, 1);

    /* initialize DMA channel2 */
    dma_deinit(DMA_CH2);
    dma_init_struct.direction = DMA_PERIPHERAL_TO_MEMORY;
    dma_init_struct.memory_addr = (uint32_t)uart0.rx_array;
    dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
    dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
    dma_init_struct.number = 0x100;
    dma_init_struct.periph_addr = USART0_RDATA_ADDRESS;
    dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
    dma_init_struct.memory_width = DMA_PERIPHERAL_WIDTH_8BIT;
    dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
    dma_init(DMA_CH2, &dma_init_struct);

    /* configure DMA mode */
    dma_circulation_disable(DMA_CH2);
    dma_memory_to_memory_disable(DMA_CH2);
    
    /* USART DMA enable for reception */
    usart_dma_receive_config(USART0, USART_DENR_ENABLE);
    /* enable DMA channel2 transfer complete interrupt */
    // dma_interrupt_enable(DMA_CH2, DMA_INT_FTF);
    /* enable DMA channel2 */
    dma_channel_enable(DMA_CH2);
}
void USART0_IRQHandler(void)
{
    uint16_t count = 0;
    if (RESET != usart_interrupt_flag_get(USART0, USART_INT_FLAG_IDLE)){
        dma_channel_disable(DMA_CH2);                               // 先关闭dma通道,不然后面没法复位dma cnt寄存器的值
        count = dma_transfer_number_get(DMA_CH2);                   // count是接收数组的剩余长度
        if (!uart0.is_busy && count != UART_RX_SIZE) {uart0.is_busy = SET;}
        dma_transfer_number_config(DMA_CH2, UART_RX_SIZE);                 // 重置cnt寄存器的值
        dma_channel_enable(DMA_CH2);                                // 再次打开dma通道
        usart_interrupt_flag_clear(USART0, USART_INT_FLAG_IDLE);    // 清中断退出
    }
}

再看GD32F103的DMA部分代码

static void init_uart_dma_receive(void)
{
    /* enable DMA0 clock */
    rcu_periph_clock_enable(RCU_DMA0);
    /*configure DMA0 interrupt*/
    // nvic_irq_enable(DMA0_Channel4_IRQn, 0, 1);
    /* deinitialize DMA channel4 (USART0 rx) */
    dma_deinit(DMA0, DMA_CH4);
    dma_struct_para_init(&dma_init_struct);

    dma_init_struct.direction = DMA_PERIPHERAL_TO_MEMORY;
    dma_init_struct.memory_addr = (uint32_t)uart0.rx_array;
    dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
    dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
    dma_init_struct.number = 0x100;
    dma_init_struct.periph_addr = USART0_DATA_ADDRESS;
    dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
    dma_init_struct.memory_width = DMA_PERIPHERAL_WIDTH_8BIT;
    dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;

    dma_init(DMA0, DMA_CH4, &dma_init_struct);
    dma_circulation_disable(DMA0, DMA_CH4);
    dma_memory_to_memory_disable(DMA0, DMA_CH4);
    /* USART DMA0 enable for reception */
    usart_dma_receive_config(USART0, USART_DENR_ENABLE);
    /* enable DMA0 channel4 transfer complete interrupt */
    // dma_interrupt_enable(DMA0, DMA_CH4, DMA_INT_FTF);
    /* enable DMA0 channel4 */
    dma_channel_enable(DMA0, DMA_CH4);
}
void USART0_IRQHandler(void)
{
    if (RESET != usart_interrupt_flag_get(USART0, USART_INT_FLAG_IDLE)){
        dma_channel_disable(DMA0, DMA_CH4);
        uart0.rx_cnt = dma_transfer_number_get(DMA0, DMA_CH4);
        if (!uart0.is_busy) {uart0.is_busy = SET;}
        dma_transfer_number_config(DMA0, DMA_CH4, UART_RX_SIZE);
        dma_channel_enable(DMA0, DMA_CH4);
        usart_data_receive(USART0);     // 非读不可,不读清不掉IDLE中断
    }
}

第一、可以看到GD32E230的DMA只有一个,所以关于它的函数不需要指定是哪个DMA,指定DMA通道就好了。但是GD32F103有多个DMA,需要指定是哪个DMA的哪个通道。
GD32F103
可以看到F103的IDLEF位需要先读USART_STAT,再读USART_DATA,才能清除IDLE的中断标识。所以在f103的标准库的usart_interrupt_flag_clear函数没有IDLE的实参。
GD32E230
但是GD32E230可以通过写IDLEC清除中断。usart_interrupt_flag_clear有IDLE的实参。
因此在GD32F103的串口中断中,需要usart_data_receive读一下数据寄存器才行。
另外,如果使用MDK V6的编译器,那么在中断中判断的标志位需要声明为volatile的,不然容易出bug。

### GD32E230 串口 DMA 使用方法 对于GD32E230系列微控制器,在实现串口DMA传输时,主要涉及配置UART接口以及DMA控制器来完成高效的数据传输。具体来说: - **初始化USART模块**:需要设定波特率、字长、停止位等参数,并开启相应的中断或DMA请求功能。 - **配置DMA通道**:由于GD32E230仅有一个DMA控制器,因此只需指定具体的DMA通道用于发送或接收操作即可。 #### 发送数据流程说明 当通过DMA方式发送数据时,程序应当遵循如下逻辑处理过程[^1]: - 失能当前使用的DMA流; - 设置待传送缓冲区首地址及长度至相应寄存器; - 启动DMA传输并等待直到传输完成标志置位; 下面给出一段基于上述描述的C语言代码片段作为参考实例,展示了如何利用DMA机制向RS485总线发送一组字符序列: ```c void RS_485_SEND(uint8_t *psrc_data, int num) { // 控制DE引脚状态切换到驱动端 GPIO_BOP(GPIOA) = (uint32_t)GPIO_PIN_8; // 调用自定义函数执行实际的DMA发送动作 MYDMA_Send(psrc_data, num); // 循环检测直至本次DMA事务结束 while(RESET == usart_flag_get(USART0, USART_FLAG_TC)); // 切换回监听模式(关闭DE) GPIO_BC(GPIOA) = (uint32_t)GPIO_PIN_8; } ``` 这段代码中`MYDMA_Send()`负责配置DMA的具体细节,比如加载源地址、目标地址、传输数量等信息给DMA硬件单元。而外部循环则用来阻塞主线程直到所有字节都被成功发出为止。 #### 接收数据注意事项 针对GD32E230GD32F103之间的差异,在采用DMA配合空闲线(IDLE line)检测的方式进行异步接收时需要注意几点特殊之处[^2]: - 对于GD32E230而言,可以直接通过对IDLEC位置一来进行IDLE中断标记清除工作; - 当使用Keil MDK V6工具链开发时,建议将参与条件判断的状态变量声明为`volatile`类型以防止编译优化带来的潜在问题。
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值