信号链路——从采样电阻到电流数值

核心问题:三相电流从电机相线流出来,经过采样电阻、运放、ADC,最后变成代码里的i_fb——这条路每一步做了什么?怎么推导换算系数?

我的板子参数:Rshunt=1mΩ,Gain=50,V_bias=1.5V,Vs=3.3V


1.为什么需要偏置电压

先回答一个根本问题:为什么不能把采样电阻上的电压直接送ADC

因为ADC的输入范围是0~Vs(这里Vs=3.3V),不能接受负电压

但电机的相电流是正弦交流信号——每个电周期里电流自然地在正负之间交替。采样电阻上的电压V_shunt=I×Rshunt自然也有正有负。

I>0→V_shunt>0→ADC能读þ

I=0→V_shunt=0→ADC能读(0V)þ

I<0→V_shunt<0→ADC不能读Q

解法:在运放端叠加一个直流偏置电压V_bias。运放输出:

V_opamp=V_shunt×Gain+V_bias

V_bias把整个信号向上平移。零电流时运放输出不是0V,而是V_bias。电流为正时V_opamp从V_bias向上摆,电流为负时从V_bias向下摆——只要摆幅不超过0~Vs,ADC就能完整读到正负电流。

这就是偏置的核心作用:把双极性信号映射到单极性ADC的输入范围内。

2.正向推导:从电流到ADC数值

完整链路:

I→V_shunt=I×Rshunt→V_opamp=V_shunt×Gain+V_bias→ADC_code=V_opamp/Vs×4095

2.1第一步:采样电阻

V_shunt=I×Rshunt

Rshunt=1mΩ,相电流每变化1A,V_shunt变化1mV:

mV级的电压太小,STM32G4的ADC分辨率是3.3V/4095≈0.8mV/bit——勉强能分辨但精度很差。必须放大。

2.2第二步:运放放大+偏置

V_opamp=V_shunt×Gain+V_bias

代入我的参数(Gain=50,V_bias=1.5V):

V_bias=1.5V来自一个独立的3.0V基准源经电阻分压得到。没有直接从3.3V分压,因为MCU的3.3V供电线上混着数字噪声(几十mV级别),引入V_bias会让零电流点跟着抖。独立的低噪声基准源能保证零电流点稳定。

V_opamp=I×0.001×50+1.5=I×0.05+1.5

2.3第三步:ADC量化

ADC把模拟电压除以Vs,乘以分辨率,得到数字量:

ADC_code=V_opamp/Vs×4095

代入V_opamp=I×0.05+1.5、Vs=3.3V:

ADC_code=(I×0.05+1.5)/3.3×4095

电流I

V_opamp

计算过程

ADC_code

+10A

2.0V

2.0/3.3×4095

2482

+5A

1.75V

1.75/3.3×4095

2172

0A

1.5V

1.5/3.3×4095

1861(理论值)

−5A

1.25V

1.25/3.3×4095

1551

−10A

1.0V

1.0/3.3×4095

1241

3.反向推导:从ADC数值到电流

ADC中断里读到的是整数ADC_code,要还原成电流。从正向公式反推:

正向:

ADC_code=(I×0.05+1.5)/3.3×4095

第一步,两边同时除以4095、乘以Vs,还原V_opamp:

V_opamp=ADC_code/4095×Vs

第二步,减去偏置V_bias,还原放大的信号:

V_signal=V_opamp-V_bias=ADC_code/4095×Vs-V_bias

第三步,除以Gain和Rshunt,还原电流:

I=V_signal/(Rshunt×Gain)=(ADC_code/4095×Vs-V_bias)/(Rshunt×Gain)

代入参数验算:ADC_code=2172(对应+5A)

I=(2172/4095×3.3-1.5)/(0.001×50)=(1.75-1.5)/0.05=0.25/0.05=5.0A

4.代码实现

公式直接翻译成C代码:

```c

//完整公式——每次计算

floatcurrent=(adc_value/4095.0f*VREF-VBIAS)/(RSHUNT*GAIN);

但每次都做两次除法和两次乘法太慢。工程上预计算一个系数:

//预计算系数——编译时算好

//ampere_per_count=Vs/(4095×Rshunt×Gain)

//=3.3/(4095×0.001×50)

//≈0.01612A/count

#defineAMPERE_PER_COUNT(3.3f/(4095.0f*0.001f*50.0f))

//零电流对应的ADC读数(理论值)

//zero_code=V_bias/Vs×4095=1.5/3.3×4095≈1861

//注意:这是从V_bias算出的理论值。

//实际零电流读数会有偏差(运放、电阻精度),

//需通过ADC偏置校准实测得到

#defineZERO_CURRENT_CODE(1861)//→实际使用前务必校准

//运行时——一次减法一次乘法

floatcurrent=(adc_value-ZERO_CURRENT_CODE)*AMPERE_PER_COUNT;

验算:

ADC_code

减偏置

乘系数

电流

2482

2482−1861=621

621×0.01612

+10.0A

2172

2172−1861=311

311×0.01612

+5.01A

1861

1861−1861=0

0×0.01612

0A

1551

1551−1861=−310

−310×0.01612

−5.00A

1241

1241−1861=−620

−620×0.01612

−9.99A

误差来自系数截断,在±0.2%以内,不影响控制精度。


5.几个值得注意的点

这块板子偏置电压不是Vs/2

Vs/2=1.65V能使零电流居中,正负电流对称。我的板子V_bias=1.5V略低于1.65V——这不影响功能,只是正负方向的最大可测电流范围不对称了:

零电流理论值在1861,而不是2048

正向余量:4095−1861=2234counts→2234×0.01612=+36A

负向余量:1861−0=1861counts→1861×0.01612=−30A

正向能测到+36A,负向能测到−30A——对于±10A的工况绰绰有余。

关于ZERO_CURRENT_CODE的取值

上面用了1861。这是从V_bias=1.5V推出来的理论值,用于推导和验算没问题。但实际板子上的零电流ADC读数不会是精确的1861——运放、电阻精度、走线差异都会产生偏差。

所以实际代码里的ZERO_CURRENT_CODE应该通过ADC偏置校准来获得。