在嵌入式串口通信中,不定长、大数据量的稳定接收一直是高频刚需。极海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占用极低,可直接用于量产项目。

五、移植要点
确认芯片型号(极海APM32F030/F072),中断通道与重映射保持一致
缓存uart1RecvData大小根据实际业务调整
空闲中断必须配合清DR寄存器操作,否则无法清除标志
每次帧结束后重置DMA计数器,保证连续接收
Copyrights© Shenzhen Linkchip Co.,LTD All Rights Reserved.