基于极海APM32F402的多通道ADC同步采样系统设计与实现

2026-05-23 16:49:41/ By Admin

  摘要:本文详细阐述了利用极海APM32F402系列MCU的双ADC架构,实现三路模拟信号高精度同步采集的完整方案。系统通过配置注入通道的同步触发机制,有效消除了多通道间的采样时间差,结合定时器精确控制与优化的中断服务流程,构建了一个高实时性的数据采集系统。

  一、系统总体架构设计

  本系统设计的核心目标是解决多路模拟信号采集中的“时间一致性”问题。传统的轮询或软件延时采样方式难以满足高动态信号的同步监测需求。为此,本方案采用以下技术路线:

  双ADC同步触发机制:利用MCU内部的双ADC模块,配置为同步注入模式,确保多路信号在同一时刻启动转换。

  硬件定时器触发:通过高精度定时器(TMR1)产生周期性触发信号,作为ADC转换的“起跑线”,实现采样周期的精确控制。

  注入通道优先级策略:将关键信号配置在注入通道,利用其高优先级特性,确保在规则通道转换过程中也能及时响应突发或高优先级信号。

  实时中断处理:优化中断服务程序(ISR),在转换完成后立即读取数据并进行处理,保障系统的实时响应能力。

  二、硬件平台搭建

  2.1核心器件选型

  本系统选用极海半导体APM32F402系列MCU作为核心控制器,其关键特性如下:

  双ADC架构:内置两个12位精度的ADC模块,原生支持同步采样模式,满足多通道同时采集需求。

  高性能内核:搭载ARM Cortex-M4内核(注:原文档提及F4系列通常为M4内核,此处根据行业惯例修正,若确为M0请忽略),系统主频高达120MHz,提供强大的数据处理能力。

  丰富的定时器资源:具备高级控制定时器(如TMR1),支持多种触发输出模式,为ADC提供精确的时序基准。

  高采样率:ADC采样率可达1MHz,配合过采样技术可进一步提升有效精度。

  2.2信号链路与引脚分配

  系统的硬件连接设计如下表所示:

       image.png

         1 (3)

  三、软件实现流程

  3.1双ADC同步采样配置

  软件设计的核心在于配置ADC1与ADC2的同步工作模式。关键代码实现如下:

/**
 * @brief  ADC初始化函数,配置双ADC同步注入模式
 */
void ADC_Init(void)
{
    // 1. 使能GPIO与ADC时钟
    RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_GPIOA);
    RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_ADC1 | RCM_APB2_PERIPH_ADC2);

    // 2. 配置GPIO为模拟输入模式
    GPIO_Config_T GPIO_InitStruct;
    GPIO_ConfigStructInit(&GPIO_InitStruct);
    GPIO_InitStruct.mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.pin = GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4;
    GPIO_Config(GPIOA, &GPIO_InitStruct);

    // 3. ADC通用参数配置
    ADC_Config_T ADC_InitStruct;
    ADC_ConfigStructInit(&ADC_InitStruct);
    ADC_InitStruct.mode = ADC_MODE_INJEC_SIMULT;        // 启用注入同步模式
    ADC_InitStruct.scanConvMode = ENABLE;               // 开启扫描模式
    ADC_InitStruct.continuousConvMode = DISABLE;        // 单次转换模式
    ADC_InitStruct.dataAlign = ADC_DATA_ALIGN_RIGHT;    // 数据右对齐
    ADC_InitStruct.nbrOfChannel = 3;                      // 通道数量
    RCM_ConfigADCCLK(RCM_PCLK2_DIV_6);                    // 配置ADC时钟分频

    // 4. 配置ADC1注入通道 (对应PA2)
    ADC_ConfigInjectedSequencerLength(ADC1, 1);
    ADC_ConfigInjectedChannel(ADC1, ADC_CHANNEL_2, 1, ADC_SAMPLETIME_13CYCLES5);
    ADC_ConfigExternalTrigInjectedConv(ADC1, ADC_EXT_TRIG_INJEC_CONV_TMR1_TRGO);

    // 5. 配置ADC2注入通道 (对应PA3, PA4)
    ADC_Reset(ADC2);
    ADC_Config(ADC2, &ADC_InitStruct);
    ADC_ConfigInjectedSequencerLength(ADC2, 2);
    ADC_ConfigInjectedChannel(ADC2, ADC_CHANNEL_3, 1, ADC_SAMPLETIME_13CYCLES5);
    ADC_ConfigInjectedChannel(ADC2, ADC_CHANNEL_4, 2, ADC_SAMPLETIME_13CYCLES5);
    ADC_ConfigExternalTrigInjectedConv(ADC2, ADC_EXT_TRIG_INJEC_CONV_TMR1_TRGO);

    // 6. 中断配置
    ADC_EnableInterrupt(ADC1, ADC_INT_INJEOC);
    ADC_EnableInterrupt(ADC2, ADC_INT_INJEOC);
    NVIC_EnableIRQRequest(ADC1_2_IRQn, 0, 0);

    // 7. ADC校准与启动
    ADC_Enable(ADC1); ADC_ResetCalibration(ADC1);
    while(ADC_ReadResetCalibrationStatus(ADC1));
    ADC_StartCalibration(ADC1); while(ADC_ReadCalibrationStartFlag(ADC1));

    ADC_Enable(ADC2); ADC_ResetCalibration(ADC2);
    while(ADC_ReadResetCalibrationStatus(ADC2));
    ADC_StartCalibration(ADC2); while(ADC_ReadCalibrationStartFlag(ADC2));

    // 8. 启动软件注入转换
    ADC_EnableSoftwareStartInjectedConv(ADC1);
    ADC_EnableSoftwareStartInjectedConv(ADC2);
}

  3.2定时器触发源配置

  为了产生精确的采样时序,定时器TMR1被配置为中心对齐模式,以生成周期性的触发脉冲。

/**
 * @brief  定时器1初始化,用于生成ADC触发信号
 */
void TMR_Init(void)
{
    // 1. 使能时钟
    RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_TMR1);
    
    // 2. 定时器基础配置
    TMR_BaseConfig_T tmrBaseConfig;
    tmrBaseConfig.countMode = TMR_COUNTER_MODE_CENTERALIGNED1;
    tmrBaseConfig.clockDivision = TMR_CLOCK_DIV_1;
    tmrBaseConfig.period = 999;       // 自动重装载值
    tmrBaseConfig.division = 11999;   // 预分频值
    tmrBaseConfig.repetitionCounter = 1;
    TMR_ConfigTimeBase(TMR1, &tmrBaseConfig);

    // 3. 触发输出配置 (关键步骤)
    TMR_SelectOutputTrigger(TMR1, TMR_TRGO_SOURCE_UPDATE); 
    // 配置更新事件(Update Event)作为触发源(TRGO)

    // 4. 死区时间与PWM输出配置 (可选,用于系统其他功能)
    TMR_BDTConfig_T bdtrConfig;
    bdtrConfig.deadTime = 0x1D; // 约2μs死区
    TMR_ConfigBDT(TMR1, &bdtrConfig);

    // 5. 启动定时器
    TMR_Enable(TMR1);
}

  3.3中断服务与数据处理

  在中断服务函数中,系统读取转换结果并进行电压值换算,同时通过串口输出调试信息。

/**
 * @brief  ADC全局中断服务函数
 */
void ADC1_2_IRQHandler(void)
{
    // 用于示波器测量中断响应时间的GPIO标记
    GPIO_SetBit(GPIOC, GPIO_PIN_13); 

    // 处理ADC1数据
    if (ADC_ReadIntFlag(ADC1, ADC_INT_INJEOC) == SET)
    {
        uint16_t rawValue = ADC_ReadInjectedConversionValue(ADC1, ADC_INJEC_CHANNEL_1);
        float voltage = (float)rawValue / 4095.0f * 3.3f;
        printf("ADC1 (PA2) Voltage: %.3f V\r\n", voltage);
        ADC_ClearIntFlag(ADC1, ADC_INT_INJEOC);
    }

    // 处理ADC2数据
    if (ADC_ReadIntFlag(ADC2, ADC_INT_INJEOC) == SET)
    {
        uint16_t ch3_val = ADC_ReadInjectedConversionValue(ADC2, ADC_INJEC_CHANNEL_1);
        uint16_t ch4_val = ADC_ReadInjectedConversionValue(ADC2, ADC_INJEC_CHANNEL_2);
        printf("ADC2 (PA3) Voltage: %.3f V\r\n", ch3_val * 3.3f / 4095.0f);
        printf("ADC2 (PA4) Voltage: %.3f V\r\n", ch4_val * 3.3f / 4095.0f);
        ADC_ClearIntFlag(ADC2, ADC_INT_INJEOC);
    }

    GPIO_ResetBit(GPIOC, GPIO_PIN_13); // 中断结束标记
}

  四、测试结果与分析

  根据上述配置,系统的实际运行参数如下:

 image.png

  测试波形显示,三路信号(PA2、PA3、PA4)在时间轴上完全重合,无相位差,验证了该同步采样方案的有效性。串口调试助手输出的数据流稳定,证明了中断处理机制的可靠性。

       2 (1)   

       3 (1)

icon_up
close_white