极海APM32F0系列串口DMA+空闲中断大数据接收移植指南

2026-04-24 14:36:41/ By Admin

  在嵌入式串口通信中,不定长、大数据量的稳定接收一直是高频刚需。极海APM32F0系列单片机凭借高性价比与完善SDK,广泛用于工控、传感器、打印、通信等场景。本文基于APM32F0xx_SDK_v1.7,完整讲解串口DMA配合空闲中断实现高效大数据接收的原理、配置与可直接移植代码。

  一、串口DMA+空闲中断的核心价值

  高效处理不定长数据DMA自动搬运串口数据,空闲中断精准判断一帧结束,不用提前指定数据长度,适配传感器流、自定义协议、打印数据等场景。

  大幅降低CPU占用不用每接收1字节就进一次中断,CPU只在整帧接收完成后响应一次,空闲时可专注处理逻辑、算法或其他外设任务。

  提升系统实时性DMA硬件独立搬运,不占用CPU,数据传输与业务处理并行执行,中断更少、延迟更低,适合高频采集与高速通信。

  二、实现原理:DMA搬运+空闲中断判帧

  1.DMA“无感搬运”

  DMA(直接存储器访问)是独立硬件控制器,可把串口接收寄存器的数据自动搬到内存缓冲区,全程无需CPU干预。配置为合适模式后,可连续接收不溢出。

  2.空闲中断“帧结束检测”

  串口收到一段数据后,总线出现大于1字节传输时间的空闲时,硬件自动触发IDLE空闲中断,标志当前帧接收完成。

  3.协同工作流程

  数据接收:DMA后台持续把串口数据写入缓存

  帧结束:空闲中断触发,CPU读取DMA计数器,算出实际长度

  数据处理:处理完当前帧,重置DMA,等待下一帧

  一句话总结:DMA负责搬数据,空闲中断负责喊“收完了”。

  三、完整移植代码(APM32F0xx_SDK_v1.7)

  1.串口初始化(USART1)

void USART1_Init(void)
{
    GPIO_Config_T gpioConfig;
    USART_Config_T usartConfigStruct;

    // 开时钟
    RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_GPIOA);
    RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_USART1);

    // 复用引脚映射
    GPIO_ConfigPinAF(GPIOA, GPIO_PIN_SOURCE_9,  GPIO_AF_PIN1);  // TX
    GPIO_ConfigPinAF(GPIOA, GPIO_PIN_SOURCE_10, GPIO_AF_PIN1);  // RX

    // TX配置:复用推挽
    gpioConfig.mode    = GPIO_MODE_AF;
    gpioConfig.pin     = GPIO_PIN_9;
    gpioConfig.speed   = GPIO_SPEED_50MHz;
    gpioConfig.outtype = GPIO_OUT_TYPE_PP;
    gpioConfig.pupd    = GPIO_PUPD_PU;
    GPIO_Config(GPIOA, &gpioConfig);

    // RX配置
    gpioConfig.pin = GPIO_PIN_10;
    GPIO_Config(GPIOA, &gpioConfig);

    // 串口参数:115200 8N1
    usartConfigStruct.baudRate        = 115200;
    usartConfigStruct.mode            = USART_MODE_TX_RX;
    usartConfigStruct.hardwareFlowCtrl= USART_FLOW_CTRL_NONE;
    usartConfigStruct.parity          = USART_PARITY_NONE;
    usartConfigStruct.stopBits        = USART_STOP_BIT_1;
    usartConfigStruct.wordLength      = USART_WORD_LEN_8B;
    USART_Config(USART1, &usartConfigStruct);

    // 使能空闲中断 + NVIC
    USART_EnableInterrupt(USART1, USART_INT_IDLEIE);
    NVIC_EnableIRQRequest(USART1_IRQn, 2);

    // 使能串口
    USART_Enable(USART1);
}

  2.DMA初始化(USART1_RX)

void DMA_Init(void)
{
    RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_DMA1);
    RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_SYSCFG);

    // 重映射USART1_RX到DMA通道5
    SYSCFG_EnableDMAChannelRemap(SYSCFG_DAM_REMAP_USART1RX);

    DMA_Config_T dmaConfig;

    dmaConfig.bufferSize        = sizeof(uart1RecvData);
    dmaConfig.memoryDataSize    = DMA_MEMORY_DATASIZE_BYTE;
    dmaConfig.peripheralDataSize= DMA_PERIPHERAL_DATASIZE_BYTE;
    dmaConfig.memoryInc         = DMA_MEMORY_INC_ENABLE;
    dmaConfig.peripheralInc     = DMA_PERIPHERAL_INC_DISABLE;
    dmaConfig.circular          = DMA_CIRCULAR_DISABLE;
    dmaConfig.memoryTomemory    = DMA_M2M_DISABLE;
    dmaConfig.priority          = DMA_PRIORITY_LEVEL_HIGHT;
    dmaConfig.direction         = DMA_DIR_PERIPHERAL;
    dmaConfig.memoryAddress     = (uint32_t)uart1RecvData;
    dmaConfig.peripheralAddress = (uint32_t)&USART1->RXDATA;

    DMA_Config(DMA1_CHANNEL_5, &dmaConfig);
    DMA_ClearIntFlag(DMA1_INT_FLAG_TF5);

    USART_ClearStatusFlag(USART1, USART_FLAG_TXC);

    // 使能DMA
    DMA_Enable(DMA1_CHANNEL_5);
}

  3.空闲中断服务函数

void USART1_IRQHandler(void)
{
    uint8_t data;

    if(USART_ReadIntFlag(USART1, USART_INT_FLAG_IDLE) == SET &&
       USART_ReadStatusFlag(USART1, USART_FLAG_IDLEF) == SET)
    {
        // 清空闲中断
        data = USART1->RXDATA;
        USART_ClearIntFlag(USART1, USART_INT_FLAG_IDLE);

        // 计算实际接收长度
        uart1RecvLen = sizeof(uart1RecvData) - DMA_ReadDataNumber(DMA1_CHANNEL_5);

        // 重置DMA,准备下一帧
        DMA_ClearIntFlag(DMA1_INT_FLAG_TF5);
        DMA_Disable(DMA1_CHANNEL_5);
        DMA_SetDataNumber(DMA1_CHANNEL_5, sizeof(uart1RecvData));
        DMA_Enable(DMA1_CHANNEL_5);
    }
}

  4.主函数示例

int main(void)
{
    USART1_Init();
    DMA_Init();
    USART_EnableDMA(USART1, USART_DMA_REQUEST_RX);

    printf("RUN\r\n");

    while(1)
    {
        if(uart1RecvLen > 0)
        {
            printf("uart1RecvData(%d):", uart1RecvLen);
            for(uint32_t i=0; i<uart1RecvLen; i++)
            {
                printf(" X", uart1RecvData[i]);
            }
            printf("\r\n");
            uart1RecvLen = 0;
        }
    }
}

  四、测试效果

  使用串口助手以115200波特率发送大数据帧,系统可稳定接收并正确打印长度与十六进制数据,无丢包、无乱码、CPU占用极低,可直接用于量产项目。

1 (1)

  五、移植要点

  确认芯片型号(极海APM32F030/F072),中断通道与重映射保持一致

  缓存uart1RecvData大小根据实际业务调整

  空闲中断必须配合清DR寄存器操作,否则无法清除标志

  每次帧结束后重置DMA计数器,保证连续接收

icon_up
close_white