【风电控制】FPGA采集Vdc的ADC增益系数解析------从数字码到实际电压的桥梁
一、问题本质
FPGA采集直流母线电压(Vdc)时,ADC输出的是数字码(如0~65535),而不是实际电压值(如1800V)。需要一个**增益系数(gain)**将数字码转换为实际电压。
实际物理世界 ADC硬件 FPGA数字域
Vdc = 1800V ──→ 数字码 = 41268 ──→ 实际Vdc = 41268 × 0.0436041090223524 = 1800V
ADC输出 乘以gain后
(无物理单位) (有物理单位: V)
增益系数 0.0436041090223524 的含义:ADC每输出1个数字码,对应的实际电压是 0.0436V。
二、ADC增益系数的计算原理
2.1 基本公式
实际电压=数字码×gain \text{实际电压} = \text{数字码} \times \text{gain} 实际电压=数字码×gain
反推:
gain=实际电压数字码 \text{gain} = \frac{\text{实际电压}}{\text{数字码}} gain=数字码实际电压
2.2 gain的完整推导链
gain不是凭空来的,它由硬件电路决定,需要从实际电压一直推导到数字码:
实际电压 Vdc (如1800V)
│
▼
┌─────────────────────────────────────────────────────┐
│ 电压调理电路 (分压 + 滤波 + 钳位) │
│ │
│ 分压比 K_div = R_low / (R_high + R_low) │
│ 如: R_high = 1MΩ, R_low = 5kΩ │
│ K_div = 5k / (1M + 5k) ≈ 0.004975 │
│ │
│ ADC输入电压 = Vdc × K_div │
│ 如: 1800V × 0.004975 ≈ 8.955V │
│ │
│ (实际中还需经过运放调理,调整到ADC的输入范围) │
└─────────────────────┬───────────────────────────────┘
▼
┌─────────────────────────────────────────────────────┐
│ ADC量化 │
│ │
│ ADC参考电压 Vref (如3.3V) │
│ ADC分辨率 N (如16位) │
│ 最大数字码 D_max = 2^N - 1 = 65535 │
│ │
│ 数字码 = ADC输入电压 / Vref × D_max │
│ 如: 8.955V / 3.3V × 65535 ≈ ??? │
│ │
│ 等等, 8.955V > 3.3V? 这超出了ADC范围! │
│ 需要更精细的分压或运放调理... │
└─────────────────────────┬───────────────────────────┘
▼
2.3 反推gain = 0.0436041090223524的硬件参数
已知:
- 额定电压 P_Dat_NomDcVolt = 1800V
- gain = 0.0436041090223524 V/count
- 对应的额定数字码 = 1800 / 0.0436041090223524 ≈ 41268
反推ADC输入电压(假设16位ADC,Vref = 3.3V):
VADC,input=4126865535×3.3V=2.077V V_{ADC,input} = \frac{41268}{65535} \times 3.3V = 2.077V VADC,input=6553541268×3.3V=2.077V
反推分压比:
Kdiv=VADC,inputVdc=2.0771800=0.001154 K_{div} = \frac{V_{ADC,input}}{V_{dc}} = \frac{2.077}{1800} = 0.001154 Kdiv=VdcVADC,input=18002.077=0.001154
反推分压电阻(假设R_high = 390kΩ × 2):
Rlow=Kdiv×Rhigh1−Kdiv=0.001154×780k1−0.001154≈900Ω R_{low} = \frac{K_{div} \times R_{high}}{1 - K_{div}} = \frac{0.001154 \times 780k}{1 - 0.001154} \approx 900\Omega Rlow=1−KdivKdiv×Rhigh=1−0.0011540.001154×780k≈900Ω
完整的信号链:
Vdc = 1800V
│
▼
分压电阻: R_high = 780kΩ, R_low = 900Ω
│
│ V_adc = 1800 × 900 / (780000 + 900) = 2.074V
▼
运放调理 (可能有增益/偏移调整)
│
│ V_adc_conditioned ≈ 2.077V
▼
ADC量化 (16位, Vref = 3.3V)
│
│ 数字码 = 2.077 / 3.3 × 65535 ≈ 41268
▼
增益系数: gain = 1800 / 41268 = 0.0436041090223524 V/count
三、增益系数的精确含义
3.1 gain包含了整条信号链的总增益
gain=Vdc,ratedDrated=Vdc,ratedVdc,rated×Kdiv×GopampVref×(2N−1) \text{gain} = \frac{V_{dc,rated}}{D_{rated}} = \frac{V_{dc,rated}}{\frac{V_{dc,rated} \times K_{div} \times G_{opamp}}{V_{ref}} \times (2^N - 1)} gain=DratedVdc,rated=VrefVdc,rated×Kdiv×Gopamp×(2N−1)Vdc,rated
简化:
gain=VrefKdiv×Gopamp×(2N−1) \boxed{\text{gain} = \frac{V_{ref}}{K_{div} \times G_{opamp} \times (2^N - 1)}} gain=Kdiv×Gopamp×(2N−1)Vref
其中:
- VrefV_{ref}Vref:ADC参考电压(如3.3V)
- KdivK_{div}Kdiv:分压电阻比
- GopampG_{opamp}Gopamp:运放增益
- NNN:ADC位数(如16位)
gain是整条硬件信号链的"等效转换系数"------它把分压、运放、ADC量化三个环节的增益合并为一个数字。
3.2 为什么gain这么精确(小数点后14位)?
0.0436041090223524 这个精度看起来夸张,但有其工程原因:
| 原因 | 说明 |
|---|---|
| 出厂标定 | 每台设备在出厂前用标准电压源标定,记录精确的gain |
| 电阻精度 | 分压电阻有±0.1%的公差,需要标定补偿 |
| 运放偏移 | 运放有输入偏置电压(mV级),需要标定补偿 |
| ADC非线性 | ADC存在DNL/INL误差,精确gain可以部分补偿 |
| 温度漂移 | 不同温度下的gain略有不同,可能需要温度补偿表 |
实际上,这个精度可能是过度标定的结果。在工程中,保留6~8位有效数字就足够了(如0.0436041)。
四、FPGA中的实现
4.1 乘法实现
verilog
module adc_to_voltage (
input clk,
input [15:0] adc_code, // ADC数字码 (0~65535)
input [31:0] gain_fixed, // 增益系数 (Q格式定点数)
output reg [31:0] voltage_fixed // 实际电压 (Q格式定点数)
);
// gain = 0.0436041090223524
// 用Q16定点表示: 0.0436041090223524 × 2^16 ≈ 2858
// 或更精确: 0.0436041090223524 × 2^24 ≈ 731335
// 定点乘法: 电压 = adc_code × gain
always @(posedge clk) begin
// 16位 × 24位 = 40位乘法
voltage_fixed <= adc_code * gain_fixed;
// 结果右移24位得到实际电压 (Q0格式)
end
endmodule
4.2 C2000 DSP中的实现
c
// 在DSP中, 通常用浮点乘法
#define VDC_ADC_GAIN 0.0436041090223524f
float Convert_ADC_to_Vdc(uint16_t adc_code) {
return (float)adc_code * VDC_ADC_GAIN;
}
// 或使用Q格式定点 (节省计算时间)
// gain_fixed = 0.0436041090223524 × 2^15 = 1429 (Q15格式)
#define VDC_ADC_GAIN_Q15 1429
int32_t Convert_ADC_to_Vdc_Q15(uint16_t adc_code) {
return ((int32_t)adc_code * VDC_ADC_GAIN_Q15) >> 15;
// 结果单位: 0.01V (需要进一步处理得到实际电压)
}
4.3 SafeDivide模块的作用
在系统中,SafeDivide模块用于计算额定电压对应的数字码:
CoefNomDcVolt=P_Dat_NomDcVoltP_HW_UnitDcVoltAdcGain=18000.0436041090223524≈41268.5 \text{CoefNomDcVolt} = \frac{\text{P\_Dat\_NomDcVolt}}{\text{P\_HW\_UnitDcVoltAdcGain}} = \frac{1800}{0.0436041090223524} \approx 41268.5 CoefNomDcVolt=P_HW_UnitDcVoltAdcGainP_Dat_NomDcVolt=0.04360410902235241800≈41268.5
这个系数的用途:
| 用途 | 说明 |
|---|---|
| 标定验证 | 41268应该是额定电压下的ADC读数,用于验证硬件标定是否正确 |
| 归一化基准 | 将ADC读数归一化为标幺值:Vdc,pu=adc_code/CoefNomDcVoltV_{dc,pu} = \text{adc\_code} / \text{CoefNomDcVolt}Vdc,pu=adc_code/CoefNomDcVolt |
| 保护阈值 | 过压保护阈值 = CoefNomDcVolt × 1.1(如45395对应1980V) |
| 增益校验 | 如果实际ADC读数与41268偏差较大,说明硬件需要重新标定 |
c
// 归一化计算示例
float CoefNomDcVolt = P_Dat_NomDcVolt / P_HW_UnitDcVoltAdcGain; // 41268.5
// 将ADC读数转为标幺值
float Vdc_pu = (float)adc_code / CoefNomDcVolt;
// 额定运行时: Vdc_pu ≈ 1.0
// 过压保护: Vdc_pu > 1.1 → 报警
// 欠压保护: Vdc_pu < 0.85 → 报警
五、完整的Vdc采集信号链
┌──────────────────────────────────────────────────────────────────┐
│ 物理层 │
│ │
│ 直流母线正极 ──┬──[R_high 780kΩ]──┬──[运放]──┬──[ADC]──→ FPGA │
│ │ │ │ │
│ └──[R_low 900Ω]───┘ │ │
│ Vref=3.3V │
│ 直流母线负极 ────────────────────────────────┘ │
│ │
│ 信号变化: │
│ Vdc = 1800V → 分压 → 2.077V → ADC → 41268 → ×gain → 1800V │
└──────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────┐
│ FPGA逻辑层 │
│ │
│ 1. ADC接口: SPI读取数字码 (41268) │
│ 2. 校准: 减去零偏 (如 adc_code - offset) │
│ 3. 增益转换: Vdc = adc_code × gain (0.0436041090223524) │
│ 4. 滤波: 低通滤波去除PWM开关纹波 │
│ 5. 保护: 与阈值比较 (如 > 1980V → 过压报警) │
│ 6. 输出: 将Vdc值传给DSP (通过共享RAM或SPI) │
└──────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────┐
│ DSP算法层 │
│ │
│ 1. 接收Vdc值 │
│ 2. 电压环PI: VdcRef - Vdc → id_ref │
│ 3. 弱磁控制: Vdc作为电压极限的基准 │
│ 4. 保护判断: Vdc用于过压/欠压保护 │
└──────────────────────────────────────────────────────────────────┘
六、增益系数的标定方法
6.1 工厂标定流程
Step 1: 施加已知标准电压 (如用高精度电压源施加1000V)
Step 2: 记录ADC数字码 (如 adc_code = 22927)
Step 3: 计算gain = 1000 / 22927 = 0.043617...
Step 4: 施加另一个标准电压 (如1500V)
Step 5: 记录ADC数字码 (如 adc_code = 34393)
Step 6: 验证: 34393 × 0.043617 ≈ 1500.1V ✓
Step 7: 多点标定, 取平均或拟合直线
Step 8: 将gain写入EEPROM: P_HW_UnitDcVoltAdcGain = 0.0436041090223524
6.2 在线自校准
c
// 利用已知的参考电压进行在线校准
void ADC_SelfCalibrate(void) {
// 在停机状态下, 直流母线电压由预充电电路确定
// 已知预充电电压 = V_precharge (如1000V)
float V_precharge = 1000.0f; // 标准电压
uint16_t adc_code_measured = ADC_Read(CH_VDC); // 读取ADC
// 更新gain
float gain_new = V_precharge / (float)adc_code_measured;
// 与存储的gain比较, 如果偏差 > 1%, 报警
if (fabsf(gain_new - gain_stored) / gain_stored > 0.01f) {
FAULT_Set(FAULT_ADC_CALIBRATION);
}
// 更新gain (可选, 需要谨慎)
gain_stored = 0.9f * gain_stored + 0.1f * gain_new; // 缓慢更新
}
七、常见问题
问题1:gain会随温度变化吗?
会。 分压电阻和运放都有温度系数。对于高精度应用(如计量级),需要温度补偿:
gain(T)=gain(25°C)×(1+α×(T−25)) \text{gain}(T) = \text{gain}(25°C) \times (1 + \alpha \times (T - 25)) gain(T)=gain(25°C)×(1+α×(T−25))
其中 α\alphaα 为温度系数(通常 < 50ppm/°C)。
问题2:为什么不用整数比例?
gain=VrefKdiv×Gopamp×(2N−1) \text{gain} = \frac{V_{ref}}{K_{div} \times G_{opamp} \times (2^N - 1)} gain=Kdiv×Gopamp×(2N−1)Vref
由于分压电阻、运放增益、ADC参考电压都是物理量,它们的比值几乎不可能是"整齐"的数字。0.0436041090223524 这个"丑陋"的数字恰恰反映了硬件的真实特性。
问题3:gain精度需要多少位?
| 应用场景 | gain精度要求 | 说明 |
|---|---|---|
| 保护判断 | ±1% | 只需4位有效数字 |
| 控制反馈 | ±0.1% | 需要6位有效数字 |
| 功率计量 | ±0.05% | 需要8位有效数字 |
| 电能质量分析 | ±0.01% | 需要10位以上 |
工程建议:对于变流器控制,gain保留8位有效数字(如0.04360411)即可,多余的精度被ADC本身的DNL/INL误差淹没。
八、总结
| 问题 | 答案 |
|---|---|
| gain是什么? | ADC数字码到实际电压的转换系数,单位 V/count |
| gain = 0.0436041090223524 的含义? | ADC每输出1个数字码,对应 0.0436V 的实际电压 |
| gain怎么来的? | 由硬件信号链决定:分压比 × 运放增益 × ADC量化系数的总等效增益 |
| gain用在哪里? | ADC数字码 × gain = 实际Vdc;Vdc / gain = 额定数字码(用于标定和保护) |
| gain需要多精确? | 变流器控制:8位有效数字足够 |
| gain会变吗? | 会随温度和器件老化漂移,高精度应用需要在线校准 |
一句话:增益系数是连接"模拟世界"和"数字世界"的桥梁------它让FPGA知道,ADC输出的数字码41268到底代表多少伏特的电压。