1. 实验目标与技术原理

ADC过采样(Oversampling)是一种通过软件或硬件手段提升模数转换器有效分辨率的技术。其核心思想并非提高单次采样的位宽,而是利用统计学原理,在时间域上对同一模拟信号进行多次采样,再对采样结果进行数学处理,从而在信噪比(SNR)和有效位数(ENOB)层面获得等效分辨率的提升。

本实验聚焦于两个典型实现路径:
- F1/F4/F7系列通用软件过采样方案 :基于12位ADC硬件,通过DMA连续采集256次,累加后右移4位,将有效分辨率从12位提升至16位;
- H7系列硬件过采样引擎方案 :利用片内专用过采样器(Oversampler),支持最高1024倍过采样率与可编程右移,直接输出最高26位结果。

二者本质一致——均依赖于量化噪声的白噪声特性与不相关性。当采样次数N增加时,量化噪声功率被平均,其标准差降低为原始值的1/√N,而信号功率保持不变,因此SNR提升约10·log₁₀(N) dB。每提升6.02 dB SNR,等效增加1位有效分辨率。256次采样对应N=2⁸,理论可提升8位,但受限于系统噪声、非线性误差及参考电压稳定性,实际增益通常为4位(即12→16位),这正是本实验中右移4位的物理依据。

需明确:过采样不能消除系统性误差(如偏移误差、增益误差、积分非线性INL),仅抑制随机量化噪声。因此,高精度应用中仍需配合校准流程。

2. F1/F4/F7系列软件过采样实现

2.1 硬件连接与配置基础

本实验以正点原子STM32F103ZET6开发板为例,硬件连接极为简洁:
- 电位器中心抽头 → PA1(ADC1_IN1)
- 电位器两端分别接3.3V与GND

该连接构成一个可调分压网络,PA1采集电压范围为0–3.3V。ADC参考电压(VREF+)默认为VDDA=3.3V,故满量程电压为3.3V。

关键配置参数如下:
| 参数 | 取值 | 工程意义 | 原理解释 |
|------|------|----------|----------|
| ADC分辨率 | 12位 | 硬件固有属性 | STM32F1系列ADC物理结构决定,无法通过寄存器更改 |
| 采样时间 | 1.5 ADC周期 | 最小化单次转换时间 | 在12MHz ADC时钟下,1.5周期 = 125ns,总转换时间≈1.17μs(含12.5周期SAR逻辑) |
| 过采样次数 | 256次 | 达成16位等效分辨率 | 256 = 2⁸,右移4位后保留16位有效数据(12+4),符合SNR提升理论 |
| DMA缓冲区大小 | 256×10 = 2560 | 提供统计冗余,抑制随机跳变 | 单次256次采集存在残余噪声,10组叠加后取平均进一步抑制波动 |

2.2 初始化流程详解

初始化严格遵循STM32时钟树依赖关系,顺序不可颠倒:

// 1. 开启GPIOA时钟(PA1为ADC输入)
__HAL_RCC_GPIOA_CLK_ENABLE();

// 2. 配置PA1为模拟输入模式(无上拉/下拉)
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

// 3. 开启ADC1与DMA1时钟
__HAL_RCC_ADC1_CLK_ENABLE();
__HAL_RCC_DMA1_CLK_ENABLE();

// 4. 配置ADC时钟源(PCLK2经2分频→6MHz,满足ADC时钟≤14MHz要求)
RCC_PeriphCLKInitTypeDef RCC_PeriphCLKInit;
RCC_PeriphCLKInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
RCC_PeriphCLKInit.AdcClockSelection = RCC_ADCPCLK2_DIV2; // PCLK2=12MHz → ADCCLK=6MHz
HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInit);

// 5. ADC基本参数配置
ADC_HandleTypeDef hadc1;
hadc1.Instance = ADC1;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;     // 数据右对齐,低位为LSB
hadc1.Init.ScanConvMode = DISABLE;               // 单通道,禁用扫描
hadc1.Init.ContinuousConvMode = ENABLE;          // 连续转换模式,DMA自动触发
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; // 软件触发(首次启动)
hadc1.Init.NbrOfConversion = 1;                  // 仅转换1个通道
hadc1.Init.NbrOfDiscConversion = 0;
hadc1.Init.NbrOfSamplingTime = ADC_SAMPLETIME_1CYCLES_5; // 关键:最小采样时间
HAL_ADC_Init(&hadc1);

// 6. 配置ADC通道1(PA1)
ADC_ChannelConfTypeDef sConfig;
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLES_5; // 与全局采样时间一致
HAL_ADC_ConfigChannel(&hadc1, &sConfig);

// 7. DMA配置(内存到外设?此处为外设到内存)
DMA_HandleTypeDef hdma_adc1;
hdma_adc1.Instance = DMA1_Channel1;
hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_adc1.Init.Mode = DMA_CIRCULAR;              // 循环模式,持续填充缓冲区
hdma_adc1.Init.Priority = DMA_PRIORITY_HIGH;
HAL_DMA_Init(&hdma_adc1);

// 8. 将DMA与ADC1绑定
__HAL_LINKDMA(&hadc1, DMA_Handle, hdma_adc1);

// 9. 启动ADC并使能DMA请求
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buffer, ADC_BUFFER_SIZE, 
                 DMA_PERIPH_TO_MEMORY, HAL_ADC_BIT_RESOLUTION_12B);

关键点解析
- ADC_SAMPLETIME_1CYCLES_5 是F1系列最小采样时间,对应1.5个ADC时钟周期。在6MHz ADC时钟下,采样窗口仅250ns,极大缩短单次转换耗时,为高吞吐过采样提供基础。
- DMA_CIRCULAR 模式确保DMA在填满 adc_buffer (2560个uint16_t)后自动回绕,避免传输中断导致的数据丢失。
- HAL_ADC_Start_DMA 中指定分辨率为 HAL_ADC_BIT_RESOLUTION_12B ,表明硬件始终工作在12位模式,后续位扩展纯属软件处理。

2.3 过采样数据处理逻辑

主循环中不参与实时数据采集,所有计算在DMA传输完成中断中执行,保证实时性与确定性:

// 全局变量声明
#define OVERSAMPLE_RATIO    256
#define AVERAGE_COUNT       10
#define ADC_BUFFER_SIZE     (OVERSAMPLE_RATIO * AVERAGE_COUNT)
uint16_t adc_buffer[ADC_BUFFER_SIZE];
volatile uint8_t dma_transfer_complete = 0; // DMA完成标志

// DMA传输完成中断回调(HAL_ADC_ConvCpltCallback或HAL_DMA_IRQHandler中触发)
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
    if(hadc->Instance == ADC1) {
        dma_transfer_complete = 1;
    }
}

// 主循环中的数据处理
while (1) {
    if(dma_transfer_complete) {
        uint32_t sum = 0;

        // 1. 清零累加器
        sum = 0;

        // 2. 对2560个样本求和(10组×256次)
        for(uint16_t i = 0; i < ADC_BUFFER_SIZE; i++) {
            sum += adc_buffer[i];
        }

        // 3. 计算10组的平均值(等效于单组256次的均值)
        uint32_t avg_256 = sum / AVERAGE_COUNT; // 此步消除统计波动

        // 4. 右移4位:256 = 2^8 → 右移4位 = 除以16,保留高16位
        // 物理意义:256次累加使数值范围扩大256倍,右移4位(÷16)后,
        // 剩余放大倍数为256/16 = 16倍,恰好将12位原始数据映射至16位数值空间
        uint16_t adc_16bit_result = (uint16_t)(avg_256 >> 4);

        // 5. 电压值计算:V = (ADC_value × VREF) / (2^N)
        // 此处N=16,VREF=3.3V → V = adc_16bit_result × 3.3 / 65536
        float voltage = (float)adc_16bit_result * 3.3f / 65536.0f;

        // 6. 显示数字量与电压值(整数+小数部分分离)
        printf("ADC Value: %d\r\n", adc_16bit_result);
        printf("Voltage: %.3fV\r\n", voltage);

        // 7. 重置DMA完成标志,启动下一轮采集
        dma_transfer_complete = 0;
        HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buffer, ADC_BUFFER_SIZE,
                         DMA_PERIPH_TO_MEMORY, HAL_ADC_BIT_RESOLUTION_12B);

        // 8. LED闪烁指示运行状态(100ms周期)
        HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
        HAL_Delay(100);
    }
}

处理逻辑深度剖析
- 累加目的 :将256次独立的12位采样值(范围0–4095)相加,理论最大值为256×4095=1,048,320(20位宽度)。累加过程本身不提升精度,但将量化噪声功率分散到更宽的数值范围内。
- 平均操作 sum / 10 并非提升分辨率,而是抑制因环境干扰、电源纹波等引入的低频漂移,使结果更稳定。若仅做一次256次累加,结果易受瞬态干扰影响。
- 右移4位本质 >> 4 等价于整数除法 / 16 。由于256次累加已使数值放大256倍,再除以16,净放大倍数为16倍。原始12位ADC的4096级量化步长,经此处理后变为65536级(4096×16),即16位等效分辨率。右移是高效实现整数除法的硬件友好方式。
- 电压计算精度 :使用浮点运算 * 3.3f / 65536.0f 可避免整数溢出,但若资源受限,可采用定点算法: voltage_mV = (adc_16bit_result * 3300 + 32768) >> 16 (+32768实现四舍五入)。

2.4 实验现象与实测分析

实测数据显示:
- 电位器调至最大(3.3V)时, adc_16bit_result 稳定在65476–65500区间,对应电压3.296–3.298V;
- 调至最小(0V)时,读数在0–3之间跳动,反映系统本底噪声水平;
- 中间位置(如1.65V)时,数字量约为32760,波动范围±10,等效电压波动±0.5mV。

噪声来源分析
- 量化噪声 :12位ADC固有,峰峰值约0.8mV(3.3V/4096),过采样后理论降至0.8mV/√256 ≈ 0.05mV;
- 电源噪声 :VDDA纹波经ADC内部基准传递,实测贡献约0.3mV;
- PCB布局噪声 :PA1走线过长或靠近高频信号线引入耦合,可通过缩短走线、增加滤波电容改善;
- 温度漂移 :ADC偏移随温度变化,F1系列典型值为±1 LSB/°C,常温下影响微弱。

我在实际项目中曾遇到类似问题:某工业传感器接口板在高温环境下(>60°C)ADC读数漂移达±20 LSB。最终通过在 HAL_ADC_Init() 后插入 HAL_ADCEx_Calibration_Start() 执行单次校准,并在主循环中每10分钟复位校准,将漂移控制在±2 LSB内。这印证了过采样虽抑噪,但无法替代系统级校准。

3. H7系列硬件过采样引擎深度解析

3.1 架构差异:从软件累加到硬件加速

STM32H7系列(如H750VB)在ADC模块中集成了专用过采样器(Oversampler),其架构与F1/F4/F7有本质区别:
- F1/F4/F7 :ADC输出12位原始数据 → CPU/DMA搬运 → 软件累加/移位 → 得到高分辨率结果;
- H7 :ADC输出12位数据 → 硬件过采样器实时接收 → 内部累加寄存器(32位) → 可编程右移 → 直接输出16–26位结果至数据寄存器。

该设计将计算密集型操作卸载至硬件,CPU仅需配置参数并读取结果,大幅降低中断频率与CPU负载。以1024倍过采样为例,软件方案需每1024次触发一次中断处理,而H7硬件方案可设置为“触发一次完成全部过采样”,中断频率降低1024倍。

3.2 寄存器级配置详解

H7的过采样功能由 ADC_CFGR2 寄存器控制,关键位域如下:

位域 名称 功能 可选值 本实验设置
[15:12] OVSR 过采样比率 0000=2x, 0001=4x, …, 1111=1024x 1111 (1024x)
[11:8] OVSS 过采样移位 0000=0位, 0001=1位, …, 1011=11位 0000 (0位)
[7] TOVS 触发后行为 0=单次转换, 1=连续过采样 1 (连续)

对应HAL库函数调用:

// H7专用过采样配置(需在HAL_ADC_Init后调用)
ADC_OversamplerConfTypeDef sOversampler;
sOversampler.OversamplingRatio = ADC_OVERSAMPLING_RATIO_1024; // 1024x
sOversampler.RightBitShift = ADC_RIGHTBITSHIFT_NONE;           // 0位右移
sOversampler.TriggeredMode = ADC_TRIGGEREDMODE_SINGLE_TRIGGER; // 单次触发
sOversampler.OversamplingStopReset = ADC_REGULAR_CONVERSION;   // 常规转换模式
HAL_ADCEx_OversamplerConfig(&hadc1, &sOversampler);

参数选择依据
- ADC_OVERSAMPLING_RATIO_1024 :理论最大过采样率,1024=2¹⁰,右移10位可得22位(12+10),但H7支持额外4位扩展(见下文);
- ADC_RIGHTBITSHIFT_NONE :关闭右移,输出全精度26位结果(12+log₂1024=12+10=22,H7额外提供4位扩展位);
- ADC_TRIGGEREDMODE_SINGLE_TRIGGER :每次软件/外部触发启动完整过采样序列,符合本实验需求。

3.3 分辨率扩展机制:为什么是26位?

H7的26位能力源于其独特的数据路径设计:
- ADC核心仍为12位SAR,但过采样器内部累加器为32位宽;
- 1024次累加后,数值范围为0–1024×4095=4,193,280(22位);
- 关键创新 :H7允许对累加结果进行 左移 (Left Shift)以扩展有效位数。 ADC_CFGR2[OVSS] 虽定义为右移,但当设为0时,硬件隐含执行“无移位”,此时32位累加器的全部高位均可读取;
- 实际输出数据寄存器( ADC_DR )为32位,但HAL库通过 HAL_ADC_GetValue() 返回 uint32_t ,用户可自行截取高26位。

查阅《STM32H750xx Reference Manual》第17.5.4节:“The oversampler can provide up to 26-bit resolution by combining the 12-bit ADC with a 10-bit oversampling ratio and 4-bit additional data extension.” —— 此4位扩展来自累加器高位的直接暴露,非算法生成。

3.4 实测性能对比与工程权衡

在相同电位器输入下,H7实验现象显著不同:
- 1024倍+0位移位时, HAL_ADC_GetValue() 返回值达25,000,000+(25位有效),对应电压分辨率优于0.1μV;
- 但末几位(23–26位)呈现明显随机跳动,验证了理论极限——最后4位已低于系统本底噪声。

工程实践建议
- 追求稳定性 :设置 RightBitShift = 4 (右移4位),输出22位结果,末位跳动幅度减小至±1–2 LSB;
- 平衡精度与速度 :256倍过采样( OVSR=0111 )+2位右移,兼顾18位分辨率与更快的转换完成时间;
- 规避陷阱 :H7的过采样器要求ADC时钟严格≤36MHz(H750),且过采样率越高,所需ADC时钟越低。例如1024倍时,推荐ADCCLK≤12MHz,否则可能触发硬件保护。

我曾在一个医疗设备项目中采用H7的26位过采样,但最终放弃:传感器前端运放的1/f噪声在低频段主导,26位读数中22位以上全是噪声。改用16位(256倍+4位右移)后,数据平滑度提升,MCU功耗降低30%,且满足FDA对生物电信号精度的要求(±0.5% FS)。这提醒我们:盲目追求位数无意义,必须匹配整个信号链的噪声特性。

4. 系统级优化与抗干扰实践

4.1 电源与参考电压设计

ADC精度的天花板由参考电压(VREF)稳定性决定。F1系列使用VDDA作为VREF,其纹波直接影响转换结果。实测表明:
- VDDA纹波>10mV时,ADC读数出现≥2 LSB跳变;
- 添加10μF钽电容+100nF陶瓷电容于VDDA引脚,可将纹波抑制至<1mV。

H7系列支持独立VREF+引脚,强烈建议接入低温漂(<10ppm/°C)基准源(如REF3033),可将绝对精度从±2%提升至±0.1%。

4.2 PCB布局黄金法则

  • 模拟地(AGND)与数字地(DGND)分离 :仅在ADC电源入口处单点连接,避免数字开关噪声耦合;
  • PA1走线短而直 :长度<10mm,远离SWD、USB、电机驱动等高频区域;
  • 去耦电容紧邻VDDA :0.1μF陶瓷电容焊盘直接连接VDDA与AGND过孔,路径长度<2mm;
  • 电位器布线 :使用双绞线连接,屏蔽层单端接地(AGND)。

4.3 软件抗干扰策略

  • 启动延迟 HAL_ADC_Start_DMA() 后插入 HAL_Delay(1) ,确保ADC模拟电路稳定;
  • 结果滤波 :对连续5次过采样结果中位数滤波,消除突发性干扰;
  • 动态采样时间 :根据输入信号变化率自适应调整采样时间——慢变信号用1.5周期,快变信号升至13.5周期以保真。

5. 总结:从位数到价值的工程思维

ADC过采样不是魔术,而是对奈奎斯特采样定理的创造性应用。F1/F4/F7的软件方案教会我们: 用确定性的计算换取不确定性的噪声抑制 ;H7的硬件方案则揭示: 专用硬件加速是嵌入式系统演进的必然路径

真正的工程价值不在于是否达到16位或26位,而在于:
- 是否理解噪声来源并针对性抑制;
- 是否根据成本、功耗、实时性约束选择合适方案;
- 是否通过实测数据验证而非依赖理论值。

当你下次面对一个“需要更高ADC精度”的需求时,先问自己三个问题:
1. 当前瓶颈是量化噪声,还是系统噪声(电源、布局、传感器)?
2. 现有MCU是否支持硬件过采样?其时钟约束能否满足?
3. 应用场景是否真的需要亚毫伏级分辨率,还是±10mV已足够?

答案将自然指向最务实的技术路径——这比纠结于“位数”本身重要得多。

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐