
简介STM32F373 SDADC开发资料包面向以STM32F373为主控的嵌入式开发者针对该芯片独有的16位逐次逼近型SDADC外设提供从驱动初始化到多通道同步采集的完整参考工程。压缩包共159个文件约3.16MB以c源码、h头文件及axf/hex编译输出为主并含uvproj工程配置、map映射表与lst列表文件可直接在MDK环境中打开查看运行结果。资源详细覆盖启动转换、采样保持、逐次逼近比较及结果读取等关键流程支持独立模式、双模式与多通道模式并附自动校准与噪声抑制配置思路便于快速移植到传感器读取、工业数据采集等项目中。已有1866人学习下载适合正在调试SDADC精度或需要双通道同步采样方案的工程师参考。1. 直接切入STM32F373 SDADC 解决的是高精度小信号测量的最后一公里STM32F373 的 SDADC 是内置的 16 位 Sigma-Delta 模数转换器和普通 MCU 上的 12 位 SAR ADC 完全是两个物种。它面向的是毫伏级甚至微伏级的小信号称重传感器、电桥、电流采样电阻两端压降、热电偶冷端补偿这类场景。很多人一听到高精度就跑到外部挂一颗专用 ADC 芯片其实 F373 内部这三路 SDADC 足以把信号采到 16 位有效分辨率前提是配置对了。这篇文章不兜圈子直接讲清楚它是怎么工作的、代码怎么写、数据怎么算、哪些地方容易踩坑。适合做传感器变送器、仪表和电池检测的开发者也适合想从外部 ADC 省回成本和面积的老手。2. SDADC 的原理与选型同样是 ADC为什么这颗芯片能省掉外部调理芯片2.1 Sigma-Delta 的过采样与噪声整形看着慢其实赢在精度传统 SAR ADC 的精度由内部比较器和分压电阻阵列决定分辨率做到 16 位以上成本、面积和功耗都会很难看。Sigma-Delta 是另一条路子先用很低的精度比如 1 位对模拟量做高频采样再把大量原始采样点做数字滤波从而换回高分辨率。这个过程有两个关键词过采样和噪声整形。过采样把所有量化噪声均匀摊到很宽的频谱里噪声整形再把其中的大部分推到高频段最后经过数字低通滤波器留在信号频带内的噪声就非常小了。这个原理带来一个直接结果SDADC 的有效位数高但输出速率慢。F373 内部 SDADC 的 16 位分辨率并不是每个转换周期都能读出一个满 16 位有效数字而是靠持续累加的过采样结果换来的。所以把它当成“高速采集器”用基本会失望但当成“精密称重传感器采集器”则刚好合适。这一点在做选型时必须先有这个预期。和普通 ADC 相比SDADC 还天然适合差分输入。差分结构抑制共模干扰的能力极强现场有电机、继电器、开关电源的环境里信号线稍微长一点单端测量很容易被共模噪声拉偏差分测量则稳得多。F373 的 SDADC 每个通道都可以配置成单端或差分这比很多外置高精度 ADC 需要单独搭前端运放和差分电路灵活不少。从噪声角度看Sigma-Delta 的调制器对时钟抖动比较敏感所以 SDADC 的时钟一定要干净。这里说的干净不是格外的低抖动晶振而是避免让数字总线的翻转边沿直接耦合到 SDADC 时钟线。很多开发者把 SDADC 当作一个大号普通 ADC 用忽略了时钟域的隔离结果有效位数比数据手册标称低不少还不容易定位。调试时可以用示波器看 SDADC 时钟波形观察上升沿是否有明显毛刺。Sigma-Delta 还有个容易被忽略的点它存在“建立时间”的概念。因为输出是数字滤波器对一串采样的累积结果信号突变后要经过若干次采样读数才会稳定到新值上。应用层不能像读普通 ADC 那样读一次就认为实时特别是做连续采集并用来做阈值判断时要留出足够的收敛时间。这个特性也让 SDADC 更适合慢变信号而非阶跃响应要求苛刻的控制回路。2.2 F373 的 SDADC 单元结构与通道三个独立 ADC 能怎么用F373 一共有 3 个独立的 SDADC 单元编号 SDADC1、SDADC2、SDADC3。每个单元有 8 个外部输入引脚和 1 个内部通道内部通道可以把基准电压引出来做自测这个功能在产线自检和精度验证时非常好用。三个单元之间是真正独立的可以并行采集三路通道也可以配成互不干扰的三组传感器测量。通道本身支持单端和差分两种连接差分时两个引脚一对儿。信号进入调制器之前会经过可编程增益级常见的配置是 1、8、16、32 倍这几个档。这一点非常关键外部信号如果是 5V 电桥缩下来的几 mV 电压直接用增益 1 去采分辨率会浪费大半用 32 倍把信号放大到接近参考电压的摆幅才能把 16 位分辨率吃满。工程师选择用 F373 的 SDADC最常见理由就是省掉外部仪表放大器。因为 PGA 和 ADC 在同一个芯片里前端只需要一个抗混叠 RC 滤波外加必要的低输出阻抗驱动信号链路就成立了。相比外挂 24 位 Sigma-Delta ADC 芯片还要配 SPI 解码、隔离和调理电路F373 的方案在 BOM 成本和板子面积上都有明显优势。代价是它的绝对精度和低温漂参数不如专业计量级 ADC但大多数工业传感器场景16 位有效分辨率已经足够。三个 SDADC 是各自独立的时钟和采样配置不是三个共享一个时序。这个独立性意味着你可以同时运行三种不同的增益、采样速率和滤波配置比如一路采电桥低速信号另一路采振动传感器相对高速信号这在应用层非常灵活。要注意的是三个单元虽然独立但参考电压和供电是共用的。如果某一路的前端有大信号干扰会在电源和基准上影响到其他路所以模拟电源平面和基准走线仍然要按整体信号链来设计。内部通道是另一个值得优先了解的资源。它不需要外部引脚直接把内部基准电压连接到调制器输入端可以用于运行时自检和精度评估。比如开机时测量内部基准判断值是否落在理论区间如果偏差大就说明芯片或电源异常。这个通道的增益配置和外部通道一致用起来没有额外门槛稍后文章里我会演示如何用它验证整块板子的有效位数。2.3 选型对比什么时候用 F373 SDADC什么时候外挂 24 位 ADC有时候不是 F373 不好而是普通 ADC 更合适。做一个简单的选型对比维度F373 SDADC普通 12 位 SAR ADC外挂 24 位 Sigma-Delta ADC分辨率16 位12 位24 位集成度内置 PGA 差分输入低通常需要外部运放部分自带 PGA但需额外芯片和通信接口输出速率慢适合低频快适合高速采样慢类似 F373 SDADC低温漂和长稳中等中等更优成本较低较低高多了芯片和布板成本典型场景传感器变送器、电桥、电池检测电机电流、高速波形计量级仪器、称重仪表从这张表看F373 SDADC 的优势区间不在“绝对精度最高”而在“够用精度 高集成度”。如果产品目标是工业仪表精度要求 16 位足够那么用 F373 能省掉一整颗外部 ADC 和相关外围同时减少 SPI 通信的数据解析工作。如果目标是贸易计量秤要求低温漂和长时间稳定度就应该上外部 24 位方案这时 F373 的 SDADC 反而属于浪费功夫。选型时还有一个容易忽视的点供电和参考电压。SDADC 的性能与参考电压的干净程度强相关。如果板子上只有一个通用的 3.3V 电源没有单独的模拟基准那么即便 SDADC 硬件再好最终精度也不会高。不要把 ADC 芯片的“16 位”和系统端“能稳定采集到 16 位”直接画等号电源噪声和参考源漂移都会吃掉有效位数。选型完成后下一步就会进入工程配置。这部分看似简单但 SDADC 的外设初始化顺序和普通 ADC 差异很大下面的篇幅我会把它从引脚到读值的完整链路拆开讲。3. 用 SDADC 跑通一路高精度采集初始化、校准与读取的最小工程3.1 引脚与时钟配置模拟通道不是随便连的首先要把 GPIO 配置成模拟模式。很多人习惯把所有引脚初始化成复用推挽或者开漏到了 SDADC 就会出现采样值乱跳或者通道串扰。模拟模式意味着输入缓冲和施密特触发器都被禁用引脚呈现高阻模拟输入这样才能让外部模拟信号完整地进入采样网络。时钟方面SDADC 和普通 ADC 共用一个总线时钟还是各自独立需要看图工具里 SDADC 时钟树的默认连接。我一般会让 SDADC 时钟工作在厂商建议的频率范围内这个范围在数据手册的“SDADC characteristics”表里写得非常清楚。时钟高了噪声变大低了输出速率变慢把模块时钟配到上限附近再结合过采样率来控制最终数据率是常用的思路。引脚布局上还要注意差分输入的通道对是固定搭配的不能用任意两根引脚拼一对。想换引脚就得换通道号而不是靠代码硬配。这点容易在画板时忽略导致最后只能飞线。画 PCB 前就把 SDADC 通道和引脚绑定关系放在原理图标注里能省掉后续很多麻烦。模拟通道之间也存在串扰风险。如果两个相邻通道一个接的是微弱信号另一个接的是快速翻转的脉冲布局上没能拉开距离微弱信号那一通道的读数就会周期性跳动。这种问题在代码层几乎无解只能回到板卡布局上处理比如把干扰通道放到另一个 SDADC 单元去。3.2 代码实现校准之后再启动转换顺序别搞反拿到一个干净的工程后第一步不是读数据而是校准。F373 的 SDADC 内置线性校准和偏移校准两个步骤校准顺序不能反过来一般流程是先做线性校准再做偏移校准都完成后才开始转换。很多刚上手的开发者跳过校准直接读读出来的数据确实有“东西”但零点偏差可能达到几百个 LSB看起来像芯片坏了。下面这段是常见做法基于厂商 HAL 库函数名以你所用的库包为准/* SDADC1 配置示例单端输入增益8通道0 */ SDADC_HandleTypeDef hsdadc1; SDADC_ChannelConfTypeDef sConfig; hsdadc1.Instance SDADC1; /* 公共模式必须和需求对应这里用单端模式 */ hsdadc1.Init.CommonMode SDADC_COMMON_MODE_SINGLE_END; /* 增益8适合几十mV量级信号 */ hsdadc1.Init.Gain SDADC_GAIN_8; HAL_SDADC_Init(hsdadc1); sConfig.Channel SDADC_CHANNEL_0; sConfig.Differential SDADC_DIFFERENTIAL_DISABLE; HAL_SDADC_ChannelConfig(hsdadc1, sConfig, 0); /* 第一步线性校准 */ HAL_SDADC_CalibrationStart(hsdadc1); HAL_SDADC_PollForCalibration(hsdadc1, 1000); /* 第二步偏移校准 */ HAL_SDADC_CalibrationStart(hsdadc1); HAL_SDADC_PollForCalibration(hsdadc1, 1000); /* 校准完成后启动单次转换并读取 */ uint16_t raw; HAL_SDADC_Start(hsdadc1); HAL_SDADC_PollForConversion(hsdadc1, 1000); raw HAL_SDADC_GetValue(hsdadc1);这段代码的逻辑是先初始化外设结构体把 SDADC1 设置成单端输入和 8 倍增益然后配置通道。通道配置函数里那个最后的参数在部分 HAL 版本里代表超时或优先级具体要看库的注释。校准调用两次代表线性校准和偏移校准每次等到校准完成的标志位置位后再进行下一步。参数说明上单端模式和差分模式直接影响满量程电压范围单端时输入相对引脚之间是正值差分时允许双极性负值。增益 8 意味着输入信号被放大 8 倍后再进入调制器等效满量程变为参考电压除以 8因此小信号也能占用较大数字码范围。如果你手里是 100mV 量级信号增益 8 是合理起步值如果是 1V 级信号用增益 1 就好。读出来的 raw 值是原始码还不是物理电压。单端模式下把它乘上参考电压再除以满量程码才能换算成实际电压。数据格式上SDADC 的输出默认是二进制补码负输入会读出负数数值转换为有符号数时要用 int16 类型而不是 uint16。这一点在下一章细说。HAL_SDADC_GetValue 返回值宽度取决于具体库的设计有的版本返回 24 位右对齐有的版本返回 16 位有效数据。拿到数据后先打印原始值改变输入电压看数值是否跟着单调变化确认方向正确再做换算这是我在调试时必做的第一件事。3.3 从参考电压和增益推导满量程数值计算别靠猜很多人在代码跑通后卡在“读回的数不知道对应多少伏”。其实这个关系很简单单端输入的满量程电压等于参考电压除以增益。如果你用的参考电压是固定的那么增益越大满量程越小同样一个 100mV 信号在增益 8 时占的码值范围会大很多。举个例子参考电压 Vref 为 2.5V增益 1 时满量程是 2.5V一个 50mV 信号只占满量程的 2%对应 16 位码值里只有大约 1300 个码同一信号在增益 32 时等效满量程约 78mV50mV 信号占满量程 64%码值一下增加到 42000 左右精度表现完全不同。所以当外部信号摆幅非常小优先提高增益而不是怀疑 ADC 坏了。推导满量程时还要注意差分模式。差分输入的正负摆幅是围绕某个公共电压展开的最大正输入和最小负输入之间的总范围是满量程。很多人在做电桥测量时选了差分却用单端的公式去算结果换算出来的电压偏了一半。建议把通道模式和增益写进配置结构体在换算代码里同时带上这两个参数避免算法和配置脱节。4. 把精度榨出来增益、采样率、数据换算与滤波4.1 增益档位与量程设计小信号和大信号用同一套代码会翻车很多照着配置能跑通的人最后发现测量精度远低于标称值问题十有八九出在增益选错。增益 1 时满量程可能覆盖 0 到好几伏你把一个最大只有 20mV 的信号直接送进去ADC 只会用一个很小的码区表示它有效位数被白白丢掉了。反过来增益 32 时信号放大器饱和大电压输入会把前端削顶读数变成一块平整的直线。选增益的正确思路是让被放大后的信号峰峰值占到满量程的 70% 到 90%。以 16 位分辨率为例如果满量程是 4096mV信号是 20mV用增益 1 时信号只占约 0.5% 的码空间等效有效位数只有 8 位左右用增益 32信号放大到 640mV占满量程 15% 以上精度明显改善。但增益越大前端噪声也被放大所以不是倍增档而是要结合传感器输出阻抗和噪声水平做权衡。不同档位的偏移误差也不同。校准是在选定增益后进行的一旦运行中动态切换增益原先的校准值可能不适用了。我见过有个开发者在一次测试里来回切增益读数越来越飘最后发现是没在切换后重新校准。如果你确实需要多档增益建议要么做成手动切换并每次重启校准要么为每个增益档位保存校准值需要时重新写入。采样率设置也要和增益匹配。Sigma-Delta 的过采样率越高截止频率越低对前端噪声的抑制越好但输出数据率越低。低速高增益场景比如称重传感器通常用高过采样率换来的有效分辨率会高很多。如果追求速度有效位数会下降这是数据手册里列出的对应关系设计前应先确定需要的信号带宽再反推采样率和增益组合。4.2 数据换算与格式16 位不是直接读出来的数字SDADC 输出的原始值要换算成物理量需要区分数据格式和参考电压。F373 的 SDADC 数据寄存器是 24 位但有效分辨率不是直接等于 24 位。实际有效位数也就是 ENOB是测出来的不是看位数标称。读出的原始码可能是左对齐或右对齐取决于你配置的数据格式。代码里如果用 unsigned short 接收很容易把负数补码读成很大的正数导致换算结果完全错误。单端模式下的换算公式我一般写作voltage (raw_signed / 32768.0) * (Vref / Gain)这里的 raw_signed 是把读取值强制转换为 int16 位或 int32 位后的有符号数Vref 是当前参考电压Gain 是实际生效的增益。差分模式时满量程范围变成正负 Vref/Gain所以公式里 FSR 换成全摆幅其余逻辑一样。这样算出电压后再交给漂移补偿和工程量换算逻辑数据链路就完整了。强调一个低级但常见的坑读取结果寄存器的指针类型。如果库函数返回的是 uint32_t 指针指向的寄存器值千万不要直接赋值给 unsigned short 再按“原始值”用。SDADC 的 24 位数据在寄存器里可能靠左或靠右存放具体看参考手册里的 SHIFT 位。按错了移位方向零点和满量程附近都会出问题。稳妥做法是先用已知电压校准一下观察换算值是否线性。4.3 数字滤波的落地实现在 MCU 端再做一层平滑Sigma-Delta 调制器输出的码流经过片内数字滤波器后已经有一定平滑效果但实际应用中我还是建议在 MCU 端再做一次滑动平均或一阶低通。原因在于读数往往含有低频波动的成分只靠片内滤波滤不干净。滑动平均适合信号变化慢的场景比如温度、应变窗口长度选 4 到 16 个点效果立竿见影。/* 简单滑动平均滤波器窗口长度为 8 */ #define FILTER_WINDOW 8 static int32_t samples[FILTER_WINDOW]; static uint8_t index 0; static int32_t sum 0; int32_t moving_average(int32_t new_value) { sum - samples[index]; /* 去掉最旧的值 */ samples[index] new_value; /* 存入新值 */ sum new_value; /* 累加当前窗口 */ index (index 1) % FILTER_WINDOW; return sum / FILTER_WINDOW; /* 返回平均结果 */ }这段代码的逻辑是一个标准的环形缓冲滑动平均。每进来一个新采样值就把当前窗口里最旧的那个值从累加和中减掉再加进新值最终除以窗口长度 8 得到平均值。这个滤波器能显著压低随机噪声对阶跃信号则会产生固定延时窗口越长延时越大。参数上窗口长度 8 是我在传感器采集里常用的起点。窗口太长会拖慢响应窗口太短滤波效果不明显。如果你的信号是 10Hz 以内变化ADC 输出速率在 1kSPS 左右窗口 8 到 16 点可以接受。如果你要拿这个数据做快速过流保护窗口 8 都嫌多这时更应该靠模拟硬件滤波来扛噪声而不是软件一味平滑。滤波也会影响控制回路的稳定性。如果你后面还要做 PID 闭环过重的滤波会引入相位延迟轻则控制超调重则系统震荡。我通常先把原始数据录下来离线用不同滤波长度对比信噪比和上升时间再敲定一个合适的滤波器参数。别一上来就套 IIR 低通后面调试时会非常被动。5. SDADC 使用避坑从启动失败到数据飘的 5 条血泪经验5.1 启动与校准相关的坑复位之后先校准再使能否则数据是歪的现象把 SDADC 按普通 ADC 的流程初始化后直接读每次上电读到的零点都不一样差值能有几十到几百个码而且把输入短接后仍然有读出随机值。原因SDADC 内部有模拟校准电路上电后校准寄存器里的值不是默认有效状态。不执行校准就启动转换零点偏移和线性误差都没有被修正数据自然不靠谱。解决上电初始化后先执行线性校准再执行偏移校准等待校准完成标志位后再启动转换。如果代码里跳过了某个校准步骤补齐即可。注意校准必须在设定好通道配置之后做因为校准值依赖当前增益和通道配置。还有一种更隐蔽的坑代码在 main 函数里先启动了转换之后才调校准函数等于校准后启动、校准前已经转换最终读数时好时坏。现象切换增益档位后读数持续漂移稳定不下来。原因不同增益档位的内部失调不同校准值并不能跨增益共用。在运行中切换增益后寄存器里的校准结果已经不是当前增益对应的值了。解决任何增益切换之后都应该重新跑一遍校准流程不要觉得“开关一下就没问题”。如果应用需要频繁切增益建议把校准做成状态机的一部分每次切换后阻塞校准完成再放行数据读取。现象SDADC 转换完成标志位一直不置位程序卡死在等待函数里。原因最常见的是时钟配置错误。SDADC 的时钟没有被正确开启或者时钟分频选到了超出工作范围的值导致片上数字滤波器没有运转。其次是通道编号越界或通道配置函数被调用两次把内部状态改乱了。解决先检查 RCC 里 SDADC 外设时钟是否使能再检查时钟树的最终输出频率是否在手册规定范围内。把配置流程打印出来逐一核对初始化顺序时钟、GPIO、SDADC 基础参数、通道参数、校准、启动。任何一个环节失败后面都会卡住。5.2 信号调理相关的坑低输入阻抗和高增益是两回事现象裸测 SDADC 时读数正常接上传感器后读数偏低而且传感器和 ADC 之间的连接线一碰读数就跳。原因SDADC 的采样网络是开关电容结构等效输入阻抗比较低。传感器如果是高输出阻抗比如某些压电传感器或高阻电桥信号会在内部采样电容上被“吃掉”一部分导致衰减。增益越高等效阻抗越低这和高精度外部 ADC 的设计完全不同很多人想当然地把它和常见的逐次逼近 ADC 前端混为一谈。解决在传感器和 SDADC 之间加一级低输出阻抗的缓冲放大器。单位增益运放就可以重点是把源阻抗降下来。如果不想加运放应优先选高增益档并通过 RC 滤波把高频阻抗影响降到最低。还有一种方法是用差分输入把传感器输出端接成差分对共模干扰会被抑制衰减也会对称一些。现象SDADC 读数毛刺很多波形上时常出现单个点跳变而且看起来没什么规律。原因最常见的是布线引入的开关噪声或者是模拟地与数字地没有分开驱动 GPIO 翻转时产生的电流尖峰耦合到了模拟输入。另一个常见原因是 SDADC 的时钟没有做去抖板子上时钟走线过长或穿过数字区域。解决把 SDADC 的模拟输入引脚远离晶振引脚和高频数字线模拟地单点连接采样信号线加 RC 低通滤波截止频率放在信号带宽的 3 到 5 倍。另外关闭不需要的 GPIO 翻转干扰或者在采样窗口内避免切换数字引脚也能显著减少毛刺。现象两个 SDADC 单元同时工作时互相之间出现串扰一路信号跳变会带动另一路读数跳几下。原因虽然三个 SDADC 是独立单元但如果共用了同一个参考电压或同一个电源引脚内部模拟公用通路上的耦合就会显现出来。尤其是它们的采样时钟不同步时调制器会在不同时刻抽取电流在电源网络上形成竞态。解决尽量让两路 SDADC 使用相同配置和采样时钟这样电流抽取节奏一致耦合干扰变成共模影响较小或者将两路采集错开时间让一路转换期间另一路保持空闲。对时钟频率不同的场景可以在电源引脚旁各加一个 0.1uF 和 4.7uF 电容的组合降低高频串扰。6. 进阶用内部基准通道验证 ENOB顺便把三路 SDADC 一起用上如果你已经把单路 SDADC 跑通接下来的功课是验证它到底能采多准。最简单的方法是配置内部通道把内部基准电压直接当成输入来测量。F373 的 SDADC 内部有一个固定连接到基准电压的通道配置它就像配普通通道一样。把增益设为 1用标准采样参数读出一段时间的数据统计均值和标准差。标准差比较低说明这个板子的噪声水平好标准差偏高就说明电源或参考源有可优化的空间。有人问内部基准通道不能作为绝对参考怎么用来验精度它做不到绝对校准但能做一致性验证。同一批板子如果内部基准读数都落在同一小范围内说明各板卡的转换通道工作正常如果某一块读数偏离明显就要回头查那块板子的电源纹波或焊接问题。这个方法在产线上非常实用成本几乎为零。三路 SDADC 一起用也不是难题。把 SDADC1、SDADC2、SDADC3 都初始化成同样的结构只是句柄和通道不同然后按同样的流程启动转换。如果你需要它们同步采样可以利用 DMA 把多组结果块搬到内存配合转换完成中断来做数据对齐。我习惯的做法是建立一个小型采集状态机每个 SDADC 的转换完成中断里把数据压入环形缓冲区再由主循环统一换算和滤波。这样三个通道的代码逻辑一致调试时只要修一个地方。最后分享一个习惯无论 SDADC 还是普通 ADC我都会在固件里加一个自检步骤把内部基准通道的测量值打印出来。如果一个批次里某块板子的基准读数偏离平均值超过几个 LSB就说明它的供电或者模拟前端有问题趁早拦截不要在用户现场出问题再返修。这种自检做起来成本极低但能避免很多看起来像“玄学”的现场故障。希望帮到你。本文还有配套的精品资源点击获取