跳到正文

ADC 与传感器驱动

本文性质声明:本机无开发板,全部内容未实测,为手册级框架。站内对照:dbg-data-layout(数据布局与缓存的本机实测)、emb-bus(DMA 环形缓冲)、emb-gpio-timer(定时器与 DMA 联动)。

一、ADC 的三个约束:采样率、分辨率、参考电压

ADC 的本质是”用 Vref 量输入”,三个参数互相锁死:

  1. 分辨率(8/10/12/16 位):数字量的满量程 = Vref。“12 位 ADC”的输出 4095 只意味着”输入 ≈ Vref”,不是”4.095V”——Vref 用 3.3V 还是内部 1.2V 基准,同一物理量读出的数差一倍。内部基准省事但精度差(温漂),精密测量用外部基准。
  2. 采样率:每秒多少次转换。约束来自源阻抗和采样保持电容(源阻抗大,电容充电慢,读数偏低——手册会给”最大源阻抗”)和每次转换的时钟周期。
  3. 噪声与有效分辨率:标称 12 位不等于 12 位可用——电源纹波、参考漂移、数字噪声让最低几位乱跳。判断标准是”多次采样同一稳定输入的抖动幅度”,不是数据手册的位数。

工程换算(最常用的一条):物理值 = 读数 × Vref / 2^N,再减去零点误差、乘增益系数——这两个系数来自校准(短接时的读数、标准源时的读数),出厂校一次写进 Flash。不校准的 ADC 通道之间能差 5%。

二、DMA 采集与滑动平均

软件轮询采样在高采样率下把 CPU 拖死;中断 + DMA 是标准解:

uint16_t adc_buf[256];
HAL_ADC_Start_DMA(&hadc, (uint32_t*)adc_buf, 256);   // DMA 循环填满 256 个半字
// 半传输/全传输中断里置标志,主循环按标志取"新的一半"处理

双缓冲(半中断)模式:DMA 填前一半时主循环处理后一半,反之亦然——零丢失 + 处理时间与采集重叠。这是 emb-bus 的 DMA 环形在 ADC 上的应用。

滤波四件套按复杂度排序:

  1. 算术平均:N 次求和除 N——简单,但毛刺被平均进去而不是剔除。
  2. 滑动平均(FIR):保留最近 N 个值滚动平均——延迟 = N/2 个采样周期,实现是环形缓冲 + 累加减。
  3. 中值滤波:取窗口内中位数——剔除脉冲毛刺的首选(ADC 偶发尖刺、传感器偶发野值),代价是排序开销。
  4. 一阶低通(IIR):y = y + α(x - y),α = 采样周期/(时间常数+周期)——一个乘法一个加法, smoothest 的信噪比/复杂度比,嵌入式滤波的默认选择。

别在中断里做重滤波:滤波放主循环/任务,中断只负责”把数据 DMA 走”——本站 dbg-log 量过”级别短路放在格式化前后差 900 倍”,同族思想:中断里越轻越好。

三、传感器驱动:上电-初始化-量测状态机

数字传感器(I2C/SPI 接口)的驱动不是”读个数”,是三段状态机:

上电 → [等稳定期 t_startup] → 软复位/自检 → 读 WHO_AM_I/ID 校验
     → 写配置(量程/速率/滤波器)→ 回读校验 → [等首次转换 t_conv]
     → 稳态量测循环(读-换算-滤波-发布)

四段各自的坑:

  1. 上电稳定期:手册给的是最小值,实际板子上电和传感器上电有时序差——没等够就通信,表现是 ID 偶发读错。
  2. ID 校验:读芯片标识寄存器比对——不校验的驱动在传感器型号变更/通信故障时静默返回垃圾。校验失败要能上报(状态机进 fault,不是继续读数)。
  3. 配置回读:写进去的配置寄存器读回来比对——SPI/I2C 的位错、拉伸期的时钟毛刺在这一步现形。
  4. 转换等待:连续转换模式读结果寄存器的节奏要和 t_conv 匹配——读太快要嘛拿到旧值要嘛触发时钟拉伸(I2C)。

量测循环的节拍用定时器驱动(emb-gpio-timer 的时基),任务式结构:定时器置”该采样了”标志 → 任务里读 + 滤波 + 发布。发布 = 更新一个带时间戳的结构体 + 置版本号,消费者无锁读快照(conc-patterns 的 COW 快照思想)。

四、驱动分层:HAL 与寄存器的取舍

层做法优点代价
寄存器直写GPIOA->ODR = ...无隐藏、最快、最好调换芯片 = 重写
厂商 HAL(STM32 HAL 等)HAL_ADC_Start_DMA(...)换芯片改动小、外设初始化省事黑盒、开销、版本坑(HAL 的 bug 和 API 变迁是日常)
自建薄 HAL自己包一层 adc_read_channel() 底下 HAL 或寄存器业务代码不碰硬件要维护

实践的混合:启动/时钟/中断向量这些”一锤子”代码用厂商库省事;数据通路(ADC 批量读、SPI 高频传输)用寄存器+DMA 自己写——热点路径的每一微秒都值钱,冷路径的可移植性值钱。业务代码只依赖自己那层:换 MCU 时重写的是薄 HAL 的底部,不是全部。

五、排障索引

症状入口
ADC 读数偏低且随源阻抗变采样时间不够(第一节);加大采样周期或加运放缓冲
最低几位乱跳有效分辨率问题(第一节);参考源和地线的处理
DMA 采集丢数据半中断处理太慢;缓冲区太小;DMA 与 CPU 访存冲突
传感器 ID 偶发读错上电稳定期不够;I2C 拉伸没处理(emb-bus)
读数有规律毛刺和 PWM/继电器同步 → 采样时机避开开关动作;中值滤波
换芯片移植痛苦业务代码漏到了 HAL 层;检查依赖方向(engineering-arch 的依赖方向)

六、未实测清单

  • 本文所有 ADC/传感器行为——无开发板,未测。
  • 有效分辨率的实测方法——未测(原理给出,数值以你的板子为准)。
  • 各 HAL 库的版本差异——未对比。
  • 用户 Linux 开发机上的项目传感器——该机不可达,未涉及。