
1. 为什么我选了STM32F373来做信号采集与控制1.1 从一块“不太主流”的芯片说起STM32F373这颗芯片在STM32家族里算不上明星型号大部分人做信号采集第一反应是F103或者F407做电机控制就直奔F405、G431这些带高级定时器的型号。但我实际用下来F373在混合信号场景里有一个非常独特的生态位——它内置了16位Sigma-Delta ADC也就是SDADC这东西在低速高精度采集场合的性价比几乎无敌。我最初接触F373是因为一个工业现场的小项目需要同时采集4路压力变送器的4-20mA信号精度要求到0.1%同时还要驱动两路比例阀做闭环控制。用外置16位ADC芯片方案光是SPI隔离和基准源就要多花三四十块钱的BOM成本而F373一颗芯片就把三路SDADC最多支持5个差分通道全包了还带PGA可编程增益直接省掉前端运放。这个项目之后我就把F373当成了“低速精密采集中等实时控制”场景的首选。1.2 这颗芯片到底能做什么先把F373的核心资源摆出来方便你判断它适不适合你的场景资源类型具体规格典型用途内核Cortex-M4 72MHz带FPU和DSP指令PID运算、滤波算法SDADC3路独立16位差分输入增益1/2/4/8/16/32压力、称重、热电偶、电流采样12位ADC1路最多16通道1MSPS快速信号、辅助采集DAC2路12位模拟输出、基准设定定时器2个高级定时器TIM1/TIM8带死区和刹车电机控制、PWM输出比较器2个过流保护、窗口检测运放3个可编程增益运放信号调理通信3xUSART、2xSPI、2xI2C、CAN、USB上位机、总线通信这张表里最值钱的是SDADC和内置运放的组合。你想想一个称重传感器输出满量程才20mV直接进12位ADC根本没法看噪声就把信号淹了。F373的SDADC配上PGA32等效分辨率能到19位以上而且差分输入天然抑制共模干扰这在工业现场太重要了。1.3 适合谁来参考这篇内容如果你正在做下面这几类事情这篇经验应该能帮你少走弯路需要采集低速高精度模拟信号称重、压力、温度、电流同时还要做实时控制用STM32做PID闭环但不确定采样率、滤波、控制周期怎么配合想用一颗芯片搞定采集控制通信降低BOM和板子面积从F103过渡到F373被SDADC的配置和校准卡住了我下面会按“整体设计思路→核心细节→实操过程→问题排查”的顺序展开每个环节都附上我实际踩过的坑和验证过的参数。2. 整体方案设计与选型思路拆解2.1 采集与控制为什么要放在一颗芯片里很多人习惯把采集和控制分成两个MCU或者采集用专用ADC芯片MCU控制。这种架构在大型系统里没问题但在中小型设备上会带来几个麻烦两个芯片之间的通信延迟、数据同步问题、以及额外的PCB面积和成本。我举个例子。之前做一个注塑机辅机项目需要采集模腔压力0-10V信号然后控制伺服阀开度。最初方案是STM32F103外置ADS1256结果发现ADS1256的SPI读取周期加上F103的处理时间整个闭环延迟到了8ms左右压力波动明显。后来换成F373SDADC直接DMA到内存控制周期压到1ms波动立刻小了一个数量级。这里的关键在于数据通路要短。F373的SDADC转换结果可以通过DMA直接搬到SRAMCPU只需要在DMA完成中断里做PID运算然后更新PWM占空比。整个链路没有外部总线没有协议开销延迟就是转换时间加运算时间。2.2 SDADC和12位ADC怎么分工F373有两套完全不同的ADC系统很多人一开始会懵到底用哪个我的经验是按信号特性分工SDADC负责的活信号带宽低于1kHz需要高分辨率14位有效信号幅度小需要PGA放大差分信号共模干扰大12位ADC负责的活信号变化快10kHz只需要8-12位精度多通道轮询采集配合DMA做高速缓冲在我的压力采集项目里4路压力信号全部走SDADC因为压力变送器带宽也就100Hz左右但精度要求高。而电机电流采样走12位ADC因为PWM频率16kHz需要同步采样SDADC的转换速率跟不上。注意SDADC的转换速率和分辨率是矛盾的。F373的SDADC在单次转换模式下采样率越高有效位数越低。我实测在50kSPS时有效位数掉到13位左右所以别指望它做高速采集。2.3 控制环路的设计原则采集做好了控制环路的设计决定了整个系统的动态性能。我在F373上跑PID有几个固定原则第一控制周期必须稳定。我用TIM2做1ms定时中断在中断里做PID运算和PWM更新。绝对不要在main循环里做控制因为main循环的周期会被通信、显示等任务拉长导致控制周期抖动。第二采集和控制要同步。如果SDADC的DMA完成中断和TIM2中断不同步你会拿到“旧数据”做控制。我的做法是在TIM2中断里启动SDADC转换转换完成后DMA中断置标志位下一个TIM2中断读取标志位并计算。这样保证每次控制用的都是最新采样值。第三PWM频率要匹配执行器。比例阀的响应时间一般在10-50msPWM频率用1kHz就够了太高反而会让阀芯抖动。但如果是控制电机PWM频率至少要16kHz以上否则会有音频噪声。2.4 工具链和开发环境的选择我用的工具链比较朴素STM32CubeMX做初始化配置Keil MDK做编译调试ST-Link V2做下载。CubeMX对F373的SDADC支持还算完整但有几个参数它不会自动帮你算比如SDADC的校准序列和稳定时间这些后面会详细说。如果你习惯用HAL库F373的HAL库有个坑SDADC的HAL驱动在早期版本里有bug校准函数会卡死。我建议用LL库或者直接操作寄存器来做SDADC配置稳定得多。我现在的做法是CubeMX生成时钟和GPIO初始化SDADC部分自己写寄存器操作。3. 核心细节解析与实操要点3.1 SDADC的配置流程和关键参数SDADC的配置比普通ADC复杂得多因为它是一个Sigma-Delta调制器需要配置调制器时钟、抽取率、增益、校准等。我按实际配置顺序来讲。第一步时钟配置。SDADC的时钟来源是系统时钟经过一个预分频器。F373的SDADC时钟最高6MHz我一般设到3MHz。为什么不用最高因为调制器时钟越高功耗越大而且对电源噪声越敏感。3MHz下50kSPS的抽取率是60信噪比已经足够。第二步通道配置。SDADC有5个差分输入对但通道映射是固定的不能随便换。比如SDADC1的通道0是PA0/PA1通道1是PA2/PA3这个在数据手册的引脚定义表里查。我建议在PCB设计阶段就把信号分配到正确的引脚上否则后面改线很麻烦。第三步增益设置。PGA增益有1、2、4、8、16、32六档。选择依据是信号满量程和SDADC的参考电压。SDADC的差分输入范围是±VREF/增益。假设VREF2.5V增益16那么输入范围是±156mV。如果你的传感器输出是0-20mV增益32对应±78mV刚好覆盖。第四步校准。这是最容易出问题的地方。SDADC需要两种校准偏移校准和增益校准。偏移校准在内部短接输入时进行增益校准需要外部提供精确参考。我通常只做偏移校准增益误差靠软件标定补偿。// SDADC偏移校准的寄存器操作以SDADC1为例 SDADC1-CR1 | SDADC_CR1_ADON; // 使能SDADC while(!(SDADC1-ISR SDADC_ISR_INITRDY)); // 等待稳定 SDADC1-CR2 | SDADC_CR2_CALIB; // 启动校准 while(SDADC1-CR2 SDADC_CR2_CALIB); // 等待校准完成实操心得校准前一定要等SDADC稳定。数据手册说稳定时间是1ms但我实测在电源纹波较大的板子上需要5ms以上。如果校准结果偏差大先检查电源和参考电压。3.2 差分输入的共模范围限制SDADC的差分输入有一个容易被忽略的参数共模电压范围。它不是随便接两个信号就能工作的共模电压必须在一个特定范围内否则转换结果会饱和。F373的SDADC共模范围大概是VREF/2 ± 0.1V左右增益不同略有差异。这意味着如果你的传感器输出是单端信号不能直接接到SDADC的正输入端负输入端接地——这样共模电压就是信号的一半会随信号变化超出范围。正确做法是给负输入端提供一个VREF/2的偏置电压。我通常用一颗精密电阻分压从VREF得到1.25V然后通过一个电压跟随器可以用F373内置的运放缓冲后接到负输入端。这样共模电压固定在1.25V正输入端信号在1.25V±78mV范围内摆动完全在共模范围内。这个细节在数据手册里写得很隐蔽我当初调试时信号一直饱和查了两天才发现是共模问题。3.3 定时器中断和PWM输出的配合控制环路的核心是定时器。我用TIM2做1ms控制周期中断TIM1做PWM输出。两者需要同步否则PWM更新时机会有抖动。TIM1配置成中央对齐模式PWM频率72MHz/(2ARRPSC)。假设PSC0ARR2250PWM频率72M/(2*2250)16kHz。这个频率适合电机控制。如果是比例阀ARR设大一些PWM频率降到1kHz。TIM2配置成向上计数ARR7199PSC9中断频率72M/(7200*10)1kHz即1ms周期。关键点在TIM2中断里计算PID输出后不要直接写TIM1-CCR1而是写到一个缓冲区在TIM1的更新中断里再写入CCR。这样可以避免在PWM周期中间更新占空比导致的脉冲异常。// TIM2中断服务函数1ms周期 void TIM2_IRQHandler(void) { if (TIM2-SR TIM_SR_UIF) { TIM2-SR ~TIM_SR_UIF; // 读取SDADC最新值 int32_t feedback sdadc_buffer[0]; // PID计算 float error setpoint - feedback; integral error * Ki; if (integral INTEGRAL_MAX) integral INTEGRAL_MAX; if (integral INTEGRAL_MIN) integral INTEGRAL_MIN; float output error * Kp integral (error - last_error) * Kd; last_error error; // 输出限幅 if (output OUTPUT_MAX) output OUTPUT_MAX; if (output OUTPUT_MIN) output OUTPUT_MIN; // 写入PWM缓冲区 pwm_duty_buffer (uint16_t)output; } }3.4 信号调理电路的设计要点虽然F373内置了PGA和运放但前端调理电路还是不能省。我一般会加一级RC低通滤波截止频率设在信号带宽的2-3倍。比如压力信号带宽100HzRC截止频率设300HzR1kΩC0.47μF。为什么不用F373内置运放做滤波因为内置运放的带宽和噪声性能有限而且三个运放要分配给不同通道灵活性不够。我通常只用内置运放做共模偏置的电压跟随信号滤波用外部运放或者直接RC。另外4-20mA信号需要一颗精密采样电阻。我用的是一颗0.1%精度、25ppm/°C的100Ω电阻这样4-20mA对应0.4-2.0V正好在SDADC的输入范围内增益1时差分范围±2.5V但共模限制下实际可用范围约±1.2V。注意采样电阻的温漂直接影响精度。如果环境温度变化大一定要选低温漂电阻否则软件标定也救不回来。4. 完整实操过程与核心环节实现4.1 硬件最小系统搭建先列一下我用的核心物料物料型号说明MCUSTM32F373C8T6LQFP48够用且好焊晶振8MHz无源HSEPLL到72MHz基准REF50252.5V3ppm/°C采样电阻100Ω 0.1% 25ppm4-20mA转电压运放OPA333共模偏置缓冲电源AMS1117-3.3数字电源模拟电源LP2985-3.3模拟部分独立供电模拟电源和数字电源一定要分开中间用磁珠或者0Ω电阻单点连接。SDADC对电源噪声非常敏感我试过用同一个LDO供电有效位数直接掉2位。PCB布局上SDADC的输入走线要尽量短远离PWM和数字信号线。参考电压的走线要加屏蔽最好用地线包围。这些细节听起来老生常谈但在16位精度下每一点噪声都会体现在结果里。4.2 CubeMX初始化配置CubeMX里需要配置的模块RCCHSE晶振PLL倍频到72MHzSDADC1时钟使能通道0和1使能增益16单次转换模式TIM1PWM输出中央对齐ARR2250PSC0TIM21ms定时中断ARR7199PSC9DMASDADC1的DMA通道循环模式半字传输USART1115200波特率用于调试输出GPIOPWM输出引脚SDADC输入引脚配置为模拟模式CubeMX生成的SDADC初始化代码需要手动修改因为它的默认配置里校准流程不完整。我通常把SDADC的初始化单独写一个函数在main里调用。4.3 SDADC采集与DMA传输实现SDADC的DMA传输和普通ADC不太一样。SDADC的数据寄存器是16位的但实际有效数据在低16位高16位是符号扩展。DMA配置成半字传输每次传输一个通道的数据。我配置的是扫描模式SDADC1的通道0和通道1轮流转换DMA搬运到数组的前两个位置。转换由TIM2中断触发保证和控