做直流电机驱动器的时候,电流采样是躲不掉的第一道坎。电压、转速、位置都能靠估算凑合,电流不行——启动冲击、堵转、换向瞬间,任何一个环节电流失控,功率管可能在几毫秒内直接烧掉。我最早用的是采样电阻加普通运放,小电流测试时波形干干净净,一上大电流就原形毕露:地线上全是开关毛刺,ADC读数跳得没法用,PID调得再好也白搭。后来换成AMC1200隔离运放,才算真正把电流反馈这条路走通。
这篇就把整个过程完整过一遍:从采样电阻选型、AMC1200外围电路设计,到STM32侧ADC+DMA配置、软件滤波和标定方法,既有原理也有可直接抄的代码。适用对象是H桥驱动的直流电机,如果你后面想入门无刷电机FOC,这套采集链路的基本逻辑也是通用的,换汤不换药。
1. 为什么电机电流采样必须“隔离”:从地弹噪声说起
1.1 电机驱动里的“脏地”会毁掉高速ADC读数
很多人第一次做电机驱动,习惯性地把采样电阻的一端直接接到功率地,再用运放放大,输出给单片机的ADC。小电流下确实能工作,但一旦母线电压升高、电流变大,问题就出来了。
原因在于功率地不是理想的“0V”。MOSFET开关瞬间,电流在PCB寄生电感上会产生很大的di/dt压降,这个压降会让局部地电位瞬间跳动几伏甚至几十伏,也就是常说的“地弹”。普通运放的参考地往往是模拟地,和功率地在PCB上做不到完全电位一致,两地之间存在电位差,运放输出里就混入了这个差值。更麻烦的是功率地到模拟地之间还会通过寄生电容耦合高频噪声,这些噪声直接叠加在采样信号上,ADC读到的数值就成了一堆乱码。
隔离运放解决的就是这个问题:输入侧和输出侧之间没有电气连接,信号通过隔离栅以差分方式传递。对AMC1200来说,输入侧的GND1接功率地,输出侧的GND2接单片机模拟地,两侧地完全分开,功率地上的噪声再凶,也很难直接串进ADC的参考地回路里。
用生活类比就是:普通运放等于两个人共用一根电话线,这边说话那边能串音;隔离运放等于给两边各装了独立电话机,中间隔了道墙,声音只以规定的方式传过去。所以电机驱动里做电流采样,只要电流超过一两安培、母线电压超过24V,隔离方案基本是必须的,不是可选项。
1.2 AMC1200与霍尔、隔离ADC的取舍
做隔离电流采样,市面上主流有三条路线:
| 方案 | 原理 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 隔离运放(AMC1200) | 采样电阻压降经Σ-Δ调制后通过电容隔离传输,输出为模拟差分电压 | 带宽高、成本适中、精度可控、体积小 | 需要隔离电源、需要外围滤波、信号需差分处理 | 中小功率电机、FOC相电流采样 |
| 霍尔电流传感器 | 通过磁场感应电流,无需采样电阻 | 完全隔离、无插入损耗、不怕过流 | 带宽低、温漂大、体积大、成本高 | 大功率、低频、对损耗敏感的场景 |
| 隔离式ADC | 采样电阻压降直接在隔离侧完成数字化,通过SPI等接口传给MCU | 抗干扰强、数字接口简单、精度高 | 成本高、带宽受限于采样率、需隔离电源 | 高精度、多通道同步采集场合 |
霍尔传感器最大的问题是带宽和相移,高速动态响应跟不上;隔离式ADC精度高但贵,而且调试多一层。AMC1200算是平衡点:固定增益8倍,差分输入范围约±250mV,带宽约100kHz,足够覆盖绝大多数直流电机和无刷电机的电流环带宽需求,成本也比霍尔低不少。
要注意AMC1200本身不带隔离电源,输入侧的VDD1和输出侧的VDD2必须分别供电,这两个电源之间也要隔离。常见做法是用一个隔离DC-DC模块或者带隔离输出的电源芯片,从单片机侧电源隔离出一路给输入侧用。这一条很多人第一次做时会漏,导致上电后隔离运放完全不工作或者信号乱跳。
2. 采样电阻选型与AMC1200外围电路的设计要点
2.1 采样电阻阻值、功率与开尔文连接
采样电阻是整个采样链路的源头,取值直接决定信噪比和发热。选型要按峰值电流而不是额定电流来算,原则是让峰值电流在电阻上的压降落在AMC1200线性输入范围的60%到80%左右,也就是150mV到200mV附近。
举例来说,一个额定10A、峰值20A的直流电机,选10mΩ采样电阻,20A峰值时压降是200mV,刚好在AMC1200的线性范围内;连续10A时压降100mV,对应隔离运放输出差分电压0.8V,信噪比依然充足。
此时电阻的功率也要算清楚。连续电流下的功耗是I²R:10A×10mΩ算下来是1W,选2W到3W的合金电阻才安全;如果工况有持续堵转或频繁启动,建议直接上5W。峰值状态下的瞬态功耗虽然高,但持续时间短,热容量足够就行。
还有一个很容易被忽略的点:大电流流过PCB铜箔时,铜箔本身的电阻也会产生压降,这部分压降会和采样电阻的真实压降混在一起,导致采样不准。正确做法是采用四线制开尔文连接,从采样电阻两端单独引两条细线到AMC1200的输入引脚,让采样式走线和大电流路径完全分离。原理上就是让电压采样回路尽量不流过负载电流,只承担微安级别的测量电流,线阻上的压降就可以忽略不计了。
2.2 AMC1200的供电、输入和输出接线
AMC1200的引脚不算多,但接线很容易踩坑。输入侧VDD1和输出侧VDD2各自供电,范围是3.0V到5.5V,一般都用3.3V。两边电源都必须加去耦电容,建议在电源引脚附近放一个0.1μF陶瓷电容加一个1μF到10μF的钽电容或大容量陶瓷电容,防止高频噪声串进供电回路。
输入侧需要注意的是共模电压范围。AMC1200是差分输入,VINP和VINN之间可测量的差分为±250mV,但这两个引脚相对GND1不能超出供电范围太多,否则输入级会饱和或损坏。所以在H桥驱动直流电机的场景里,比较稳妥的拓扑是双低边采样电阻:分别在H桥左下管和右下管的源极到功率地之间各放一个采样电阻,这样每个电阻上的压降始终是单方向的,AMC1200可以直接差分测量,不会出现负压。
具体接法:采样电阻靠近MOSFET源极的一端接VINP,靠近功率地的一端接VINN,VINN同时连到GND1附近,确保输入共模不会飘得太离谱。两个通道各用一个AMC1200,分别采集两路电流。软件里根据当前PWM占空比和换向逻辑判断哪一路是有效电流,或者直接用两路之和。
输出侧VOUTP和VOUTN是差分输出,0输入时两脚电压都约等于VDD2的一半,比如3.3V供电时大约1.65V。差分输出电压等于输入差分电压乘以固定增益8。所以AMC1200输出侧的共模电压很高,信号以差分形式存在于1.65V的偏置之上,这就直接决定了下游电路必须用差分方式处理,不能当普通单端信号用。
2.3 RC滤波参数怎么定才不伤信号
AMC1200内部是Σ-Δ调制再解调的结构,输出信号上会残留高频调制噪声,必须在输出侧加低通滤波器。典型做法是在VOUTP和VOUTN之间并联一个电容,配合两侧串联的电阻构成差分低通。
我常用的参数是:每一路串一个100Ω电阻,两路之间跨接一个1nF电容。这样形成的差分滤波器截止频率大约是1/(2π×2RC),把两个100Ω等效看成串联200Ω,算下来约800kHz,足够滤掉调制噪声,又不会对100kHz以内的有效信号造成明显衰减。
输入侧也可以加一个RC低通,用来滤掉开关节点引起的毛刺。这里要注意:输入电阻太大会和AMC1200内部的输入电容构成分压,带来增益误差。建议输入串联电阻控制在10Ω到100Ω之间,电容1nF到10nF,截止频率选在几百kHz到几MHz这个区间,不要为了滤波而把信号衰减掉。
滤波参数不是一个固定值,它和你的控制频率强相关。如果做FOC,电流环频率通常在10k到20kHz,滤波截止频率建议不低于100kHz,否则相位延迟会拉低控制带宽。如果只是做简单的过流保护和慢速调速,可以把截止频率压低一些,换取更干净的波形。
3. 输出端接STM32的方式:差分采样才是正确姿势
3.1 单端采样的坑:共模1.65V和满量程饱和
我见过不少人在AMC1200输出端犯了同一个错:图省事,只把VOUTP接到STM32的ADC引脚上,VOUTN直接悬空或者接地,然后发现电流一上去ADC值就顶满。
这个问题的根源在于AMC1200输出共模电压是VDD2的一半。3.3V供电时,零输入状态下VOUTP大约1.65V,满量程差分信号是±2V(±250mV乘以8),这意味着VOUTP的理论摆动范围大约是从0.65V到2.65V。细算你会发现,正向满量程的2.65V还在ADC量程内,但留给正方向的余量只有约2V的一半左右,如果输入信号稍微超一点或电源电压有波动,输出就会超过3.3V,ADC直接饱和。
最要命的是负向电流。如果只采VOUTP这一路,负电流对应的电压是从1.65V往下走,到0.65V以下就失真了,哪怕负方向电流本身在AMC1200的线性范围内,反映到单端信号上也没有表达空间。所以单端采样根本没把AMC1200的差分能力用起来,等于白花了这个芯片的钱。
3.2 双ADC通道同步采集与软件相减
正确姿势是把VOUTP和VOUTN分别接到STM32的两个ADC通道,用同步模式同时采样,然后在代码里做差值运算。这样共模电压被完全扣掉,剩下的就是真实的差分信号乘以固定增益。
具体接法很简单:VOUTP经过RC滤波后接ADC_IN0,VOUTN经过同样的滤波网络后接ADC_IN1。注意两路的RC参数必须严格一致,否则两条路径的相位和增益会有差异,软件相减后会有残余误差。电阻电容最好用同一批次、精度1%的元件。
软件里拿到两个ADC值后,电流原始值可以这样表达:
int16_t diff_raw = (int16_t)adc_buf[0] - (int16_t)adc_buf[1];为什么要用int16_t来承接?因为两路ADC值相减的结果可能是负的,无符号变量直接相减会溢出,必须先转成有符号型再算。这个细节在整型运算里特别容易埋坑。
有一个替代方案是用带差分ADC的STM32型号,比如STM32G4或部分F3系列,它们内部有真正的差分ADC输入,可以直接把VOUTP和VOUTN接成差分对,由硬件完成相减。但从通用性和成本考虑,普通双ADC通道加软件处理的方案依然很能打,代码里多做一步减法而已,实测性能和硬件差分ADC差别不算大,前提是两路采样时刻足够接近。
4. STM32工程配置与代码实现
4.1 CubeMX里ADC+DMA配置要点
我平时用STM32CubeMX生成初始化代码,再在用户代码区里补业务逻辑。以STM32F401为例,ADC1开启两个通道,分别对应PA0和PA1,使用扫描模式加DMA循环传输。
配置时有几个关键点:
- ADC分辨率选12位,采样时间尽量选长一档,比如12到15个周期以上,降低源阻抗对采样保持电容的影响;
- 开启扫描模式,通道顺序设置为 sequence1: IN0, sequence2: IN1;
- DMA设置为循环模式,数据宽度为半字(16位),这是ADC12位输出的默认宽度;
- 开一个定时器触发ADC启动,不要用软件连续转换,后面会细说为什么。
DMA配置好以后,ADC转换结果会自动写入一个数组,CPU不需要关心数据搬运。这里有个隐藏前提:DMA缓冲区类型要定义成uint16_t数组,并且确保它在内存中的对齐没问题,否则部分型号的DMA会在总线访问上卡顿。
CubeMX生成的MX_ADC1_Init函数已经够用,但有一点要改:默认情况下DMA工作在Normal模式,转换一轮就停了。要改成Circular模式,不然采一次就断,后续数据不更新。
4.2 中点触发采样:避开MOSFET开关毛刺
电机PWM驱动下,电流波形存在周期性开关噪声,MOSFET开通和关断的瞬间噪声最强。如果让ADC随机采样,大概率采到噪声尖峰,数据抖动会非常大。
解决方法是利用PWM定时器的TRGO事件作为ADC触发源,让采样点精确落在PWM周期的中间时刻。因为PWM中点通常是电流纹波的均值位置,同时也是开关暂态结束、信号相对稳定的区间,在这里采样可以拿到一周期内最有代表性的电流值。
以高级定时器TIM1为例,配置如下逻辑:
- TIM1输出PWM的中心对齐模式1或模式2;
- TIM1的更新事件或者某个比较通道匹配事件映射到TRGO;
- ADC1的触发源选择Timer 1 Trigger Out event;
- ADC工作在注入组或规则组都可以,但采样触发外部事件要开启。
中心对齐模式下,PWM周期中点恰好是计数器的周期值附近,TRGO在计数器上溢或下溢时产生,天然对应每个PWM周期的中心。把ADC触发到这个点上,就避开了开关沿的高噪声区间。
代码上不用额外做太多事,主要是在初始化时把ADC外部触发使能打开:
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; // 修改前 hadc1.Init.ExternalTrigConv = ADC_EXTERNAL_TRIGCONV_T1_TRGO; // 修改后 hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNAL_TRIGCONVEDGE_RISING;注意:如果ADC配置为主从多通道扫描,DMA要配合外部触发一起工作,触发一次转换一个序列。触发频率就是PWM频率,每个周期采一组VOUTP和VOUTN,数据量完全够电流环使用。
4.3 数据处理:均值滤波、零漂消除与电流换算
拿到ADC差分原始值之后,不能直接当电流用,要经过三步处理:滤波、去零漂、换算成物理电流。
一阶低通滤波是最常用的动态滤波方式,公式很简单:
float current_filtered = 0.9f * current_filtered + 0.1f * (float)diff_raw;系数0.1可以根据实际噪声水平调节,噪声大可以降到0.05,动态响应要求高就提到0.2。这个滤波主要是为了削掉高频残余毛刺,过度滤波会引入相位延迟,影响电流环带宽,要平衡。
零漂消除是隔离运放特有的问题。AMC1200的输出级有失调电压,STM32的ADC也有零点误差,两路通道不完全对称时,差分结果本身就不是严格的0。所以必须在系统上电后、电机未启动时采集一组数据,存为offset值,在后续每次采样中都减去它。
int16_t zero_offset = adc_buf[0] - adc_buf[1]; // 电机关断时采集 int16_t current_adc = adc_buf[0] - adc_buf[1] - zero_offset;物理电流换算,最简单的标定思路是先理论计算:
- ADC满量程4096对应3.3V;
- 隔离运放输出差分电压 = 输入差分电压 × 8;
- 输入差分电压 = 电流 × 采样电阻阻值。
所以合并起来,电流与ADC值的关系是:
float voltage = (float)current_adc / 4096.0f * 3.3f; float sense_voltage = voltage / 8.0f; float current = sense_voltage / 0.01f; // 采样电阻10mΩ这个计算依赖3.3V参考电压和增益8的准确性,实际标定时会有一定偏差,所以更推荐用已知电流做两点标定,下面的实测章节会讲。
5. 实测标定与高频噪声处理的经验
5.1 零电流偏置标定与死区处理
零电流标定听着简单,做起来有讲究。光在代码里“电机关断时采一次”不够,因为电机关断瞬间母线电容还在放电,漏电流可能不为零。最稳妥的方法是在电机上电初始化阶段,确认PWM输出为高阻或全部下拉之后,连续采集几十个周期的差值,取平均作为零点偏移。
我一般会在main函数开头加一段自校准逻辑:
uint32_t sum = 0; for (uint16_t i = 0; i < 64; i++) { sum += (uint16_t)(adc_buf[0] - adc_buf[1]); HAL_Delay(1); } zero_offset = (int16_t)(sum / 64);取平均的意义是消除单次采样的随机噪声,64次平均基本能把几毫伏的随机波动压下去。如果硬件上两路采样通道对称性好,这个offset会非常接近0x0000,但也可能测出几十个LSB的偏移,这是正常现象,不需要担心。
死区处理是针对小电流场景的。电机零电流附近,采样信号的噪声可能比真实信号还大,如果不做处理,系统会把这个噪声当成电流反馈,轻则产生电流环抖动,重则让电机在停止时出现微小的嗡嗡声。可以在软件中加一个死区判断:当差分ADC值绝对值小于某个阈值时,强制输出0。
if (current_adc > -10 && current_adc < 10) { current_adc = 0; }阈值大小取决于你的噪声水平,需要通过示波器或串口监视实际数据来确定。我是建议这个阈值宁小勿大,太大会损失低速下的电流解析力,造成控制不平顺。
5.2 增益标定与温度漂移应对
理论计算的电流换算公式能给出数量级正确的电流值,但如果要做闭环控制,精度不够。AMC1200的增益有典型值8,实际可能是7.9或8.1,采样电阻精度只有1%或5%,3.3V参考电压也有误差,这些误差会叠加到最终电流上。
标定方法很简单:让电机工作在恒定负载下,用一块精度高的钳形电流表或万用表读出实际电流,同时记录代码里滤波后的ADC值,计算出一个增益系数。实际操作时可以在两个不同电流点各采一次,做一个线性拟合。
float current_lsb = (current_ref1 - current_ref2) / (adc_avg1 - adc_avg2);增益标定最好在系统稳定运行温度下进行,因为采样电阻是合金材料,温漂系数一般不大,但AMC1200的增益温漂和失调温漂是存在的。如果对温漂敏感,可以在采样电阻旁边加一个温度传感器做软件补偿,但这对绝大多数应用来说都过度设计了,硬件布局上让采样电阻远离发热器件是更实用的做法。
5.3 ADC读数抖动时的排查思路
实际调试中遇到ADC读数抖动,不要一上来就改滤波参数。先区分抖动来源,比盲目堆滤波更高效。
排查顺序我一般是这样:
用示波器看滤波后的输出电压波形。如果波形毛刺频率和PWM开关频率一致,问题在采样时刻没避开开关沿,重点检查PWM触发配置;如果毛刺是宽频杂散信号,大概率是电源或布局问题。
检查VOUTP和VOUTN两路ADC的共模是否一致。在零输入时分别打印两路原始ADC值,如果差值不为零且在数十字节范围内跳动,多半是供电噪声或参考电压不稳定。给AMC1200输出侧电源单独加一级LC滤波往往能明显改善。
断开电机,只给单片机供电,观察ADC输入是否依然抖动。如果依然抖,说明问题在单片机侧的参考电压或地线布局,和电机部分无关;如果断开后变干净,说明噪声是从功率侧耦合进来的,重点检查隔离电源和布局。
采样电容采样保持时间太短也会引起读数异常。如果源阻抗很高,ADC内部采样电容还没充满就开始转换了,读数就会偏小且不稳定。适当增加ADC采样时间,比如配到几十个周期,能稳妥解决这个问题。
如果做完这些还是抖,最后再考虑加大RC滤波的时间常数或提高均值滤波的次数。但记住一点:滤波器只是掩盖问题,前期把布局和触发做好,后期根本不需要太强的滤波。
我个人的体会是,AMC1200这类隔离运放,硬件上只要把电源滤波、差分走线和RC参数这三件事做好,软件上把差分采样和零漂校准做对,整个电流采样链路基本不会出幺蛾子。真正会出问题的往往不是芯片本身,而是外围那些看着不起眼的细节:采样电阻功率不够、单端采样、滤波参数拍脑袋、零点不校准,每隔一段时间就会碰到有人栽在其中一个上面。这篇的经验是我反复踩坑后沉淀下来的,按这个流程走一遍,至少能避开我在调试时交过学费的那些坑。