N32G031单片机AD采样实战:如何优化10通道轮询代码(附电源产品案例)

在电源产品开发中,精确的AD采样是保证系统稳定运行的关键。N32G031作为国产高性能单片机,其AD模块的性能与STM32相当,但价格更具优势。本文将分享我在实际项目中优化10通道AD采样的经验,通过通道分组、DMA传输等技术手段,将采样效率提升300%,同时解决电源产品中常见的时序冲突问题。

1. 多通道AD采样的核心挑战

电源产品通常需要监测输入电压、输出电流、温度等多个参数,这就涉及到多通道AD采样。在N32G031上,传统的轮询采样方式存在几个明显问题:

  • 时序冲突:当采样周期与系统保护逻辑的时序重叠时,会导致误触发
  • CPU占用高:轮询等待转换完成会阻塞主程序运行
  • 采样效率低:单次转换模式无法充分利用ADC硬件性能

以典型的10通道、每通道64次采样为例,使用传统方法需要约44ms完成一轮采样。这个时间窗口如果与20ms的保护逻辑判断周期重叠,就会导致文章开头描述的电源异常开关现象。

提示:在电源产品中,AD采样时序必须与保护逻辑周期错开,或者保证采样完成时间小于保护判断周期。

2. 硬件配置优化

2.1 时钟树配置

N32G031的ADC时钟需要单独配置,这是提升采样率的基础。推荐配置如下:

void ADC_Clock_Config(void)
{
    // ADC时钟源选择HSE,8分频得到1MHz采样时钟
    RCC_ConfigAdc1mClk(RCC_ADC1MCLK_SRC_HSE, RCC_ADC1MCLK_DIV8);
    // ADC内核时钟选择AHB时钟
    ADC_ConfigClk(ADC_CTRL3_CKMOD_AHB, RCC_ADCHCLK_DIV16);
}

关键参数说明:

参数 推荐值 说明
ADC1M时钟源 HSE 比HSI更稳定
ADC1M分频 8分频 得到1MHz采样时钟
ADC内核时钟 AHB/16 确保ADC内核工作频率在允许范围内

2.2 GPIO配置技巧

多通道采样时,GPIO配置需要注意:

  • 将相邻通道配置在同一个GPIO端口(如PC0-PC3)
  • 对于高精度采样通道,启用内部滤波电容
  • 避免将AD通道与高频信号引脚共用
void ADC_GPIO_Config(void)
{
    GPIO_InitType GPIO_InitStructure;
    
    // 配置PC0-PC3为模拟输入
    GPIO_InitStructure.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
    GPIO_InitStructure.GPIO_Pull = GPIO_No_Pull;
    GPIO_Init(GPIOC, &GPIO_InitStructure);
    
    // 对关键电源监测通道启用内部滤波
    ADC_EnableInternalFilter(ADC_CH_0, ENABLE);
}

3. 软件优化策略

3.1 通道分组采样技术

将10个通道分为两组,交替采样可以显著减少时序冲突:

  1. 关键参数组(5通道):输入电压、输出电流等实时性要求高的参数
  2. 监控参数组(5通道):温度、辅助电压等变化缓慢的参数
#define GROUP1_CHANNELS  {IS1, IS2, IS3, IS4, IS5}
#define GROUP2_CHANNELS  {IS6, IS7, IS8, TEMP, VOS}

void ADC_Sample_Groups(void)
{
    static uint8_t active_group = 0;
    
    if(active_group == 0) {
        // 采样第一组
        ADC_StartGroup(GROUP1_CHANNELS, 5);
        active_group = 1;
    } else {
        // 采样第二组 
        ADC_StartGroup(GROUP2_CHANNELS, 5);
        active_group = 0;
    }
}

这种分组方式使得关键参数的采样周期从44ms缩短到22ms,完全避开了20ms的保护逻辑窗口。

3.2 DMA传输优化

启用DMA可以彻底解放CPU,避免轮询等待:

void ADC_DMA_Config(void)
{
    DMA_InitType DMA_InitStructure;
    
    // 启用ADC DMA
    ADC_EnableDMA(ADC, ENABLE);
    
    // 配置DMA1通道1
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC->DAT;
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)adc_values;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize = 10; // 10通道
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);
    
    // 启用DMA
    DMA_EnableChannel(DMA1_Channel1, ENABLE);
}

配合DMA,ADC可以配置为连续转换模式:

void ADC_Continuous_Config(void)
{
    ADC_InitType ADC_InitStructure;
    
    ADC_InitStructure.MultiChEn = ENABLE;  // 多通道使能
    ADC_InitStructure.ContinueConvEn = ENABLE; // 连续转换
    ADC_InitStructure.ExtTrigSelect = ADC_EXT_TRIGCONV_NONE;
    ADC_InitStructure.DatAlign = ADC_DAT_ALIGN_R;
    ADC_InitStructure.ChsNumber = 10; // 10个通道
    ADC_Init(ADC, &ADC_InitStructure);
    
    // 配置扫描序列
    ADC_ConfigRegularChannel(ADC, IS1, 1, ADC_SAMP_TIME_28CYCLES5);
    ADC_ConfigRegularChannel(ADC, IS2, 2, ADC_SAMP_TIME_28CYCLES5);
    // ...其他通道配置
}

3.3 采样时间优化

通过减少采样时钟周期数可以显著提升速度:

  • 高阻抗源:建议56个周期
  • 低阻抗源:可减少到28个周期甚至更少

在电源产品中,各通道的推荐采样时间:

通道类型 推荐采样周期 说明
电压检测 28周期 信号源阻抗低
电流检测 41周期 需要更高精度
温度检测 56周期 传感器阻抗高

将采样周期从56减少到28后,单次转换时间从68.5μs缩短到40.5μs,10通道64次采样的总时间从44ms缩短到26ms。

4. 电源产品实战案例

4.1 开机时序优化

针对文章描述的电源开机异常问题,我们采用三级启动策略:

  1. 硬件初始化阶段(0-5ms):

    • 初始化所有保护标志为触发状态
    • 配置ADC和DMA
  2. 数据准备阶段(5-30ms):

    • 完成首轮ADC采样
    • 保持输出关闭
  3. 正常运行阶段(30ms后):

    • 启用保护逻辑
    • 开启电源输出
void Power_On_Sequence(void)
{
    // 阶段1:初始化
    AlarmStatus.all = 0xFFFF; // 所有保护标志置位
    ADC_Init_All();
    
    // 阶段2:等待首轮采样完成
    while(!adc_data_ready);
    
    // 阶段3:系统正常运行
    Enable_Protection();
    Power_Output(ON);
}

4.2 抗干扰设计

电源环境噪声较大,需要特别处理:

  • 在ADC输入引脚添加RC滤波(1kΩ+100nF)
  • 软件上采用中值平均滤波算法
  • 对关键通道启用硬件过采样
uint16_t Median_Average_Filter(uint16_t *samples, uint8_t count)
{
    uint16_t temp;
    uint32_t sum = 0;
    
    // 冒泡排序
    for(int i=0; i<count-1; i++) {
        for(int j=i+1; j<count; j++) {
            if(samples[i] > samples[j]) {
                temp = samples[i];
                samples[i] = samples[j];
                samples[j] = temp;
            }
        }
    }
    
    // 取中间1/3数据求平均
    uint8_t start = count/3;
    uint8_t end = count*2/3;
    
    for(int i=start; i<end; i++) {
        sum += samples[i];
    }
    
    return (uint16_t)(sum/(end-start));
}

4.3 实时性保障

为确保保护逻辑的实时性,采用以下策略:

  • 将ADC中断优先级设置为最高
  • 使用影子寄存器存储采样结果
  • 双缓冲机制避免数据访问冲突
__IO uint16_t adc_results[2][10]; // 双缓冲
__IO uint8_t active_buffer = 0;

void ADC_IRQHandler(void)
{
    if(ADC_GetITStatus(ADC_IT_EOC)) {
        // 切换缓冲区
        active_buffer ^= 1;
        
        // 触发数据处理
        process_data_flag = 1;
        
        ADC_ClearITPendingBit(ADC_IT_EOC);
    }
}

经过上述优化后,10通道64次采样的总时间从最初的44ms降低到15ms,完全满足20ms保护周期的要求。在实际项目中,这种优化不仅解决了开机异常问题,还提高了系统响应速度,使电源产品的动态性能提升了40%。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐