一颗ADC芯片输出的只是数字编码,如何把它变成用户看得懂的电压值?本文从代码层面完整解析D223的ADC数据处理流水线:原始值→滑动平均→量程转换→校准插值→工程单位。
一、数据处理流水线总览
D223的ADC数据从硬件到用户显示,经过以下完整流水线word_4note_63:
AD7606原始16位数据
│
├── 256点滑动平均滤波
│ ├── 16位均值 → getAdcFilter16[]
│ └── 24位累加和 → getAdcFilter24[]
│
├── 量程转换(±5V / ±10V)
│ ├── 16位 → mV
│ ├── 24位 → μV
│ └── 电流模式 → μA
│
├── 两点线性插值校准
│ └── 消除系统误差
│
├── 校正系数与零点偏移
│ └── 微调
│
└── 传感器单位转换
└── V, mV, A, mA, ℃, N, kg, Pa ...
二、Step 1:256点滑动平均滤波
2.1 数据结构
D223的ADC数据缓冲区word_4:
c
u16 getAdc_Buf256[8][256]; // 8通道×256点滑动窗口
u16 getAdcFilter16[8]; // 16位滤波值
u32 getAdcFilter24[8]; // 24位精度提升值
u16 getAdc_Buf200[8][200]; // 波形显示缓存
u16 getAdc_Buf1000[8][1000]; // 连续数据读取缓存
2.2 滑动平均算法
每收到一次新的ADC采样,数据进入滑动窗口,计算256点累加和word_4:
c
void modbus_getDeviceDatas(void) {
for(u8 i = 0; i < 8; i++) {
// Step1: 256点滑动平均
u32 sumTotal = 0;
for(u16 j = 0; j < 256; j++)
sumTotal += _uadc.getAdc_Buf256[i][j];
_uadc.getAdcFilter16[i] = sumTotal / 256; // 16位均值
_uadc.getAdcFilter24[i] = sumTotal; // 24位累加和(不除!)
}
}
2.3 为什么不除以256就能得到24位?
这是整个流水线中最精妙的设计word_4:
- AD7606是16位ADC,单次采样值范围:0~65535(2^16)
- 256个采样值累加:最大 65535 × 256 = 16,776,960 ≈ 2^24
- 累加结果不除以256,直接保留24位精度
效果:
- 16位均值:
sumTotal / 256,范围0~65535,精度±1 LSB - 24位累加:
sumTotal,范围0~16,776,960,精度提升到±1/256 LSB - 有效位数(ENOB)提升约4位(log2(256) = 8,实际约4位有效提升)
- 信噪比提升约24dB
关键:24位精度不是ADC硬件直接给出的,而是通过256次过采样累加获得的。代价是采样率降低256倍------以时间换精度word_4。
2.4 滤波效果
256点滑动平均等效于一个低通FIR滤波器:
- 截止频率约为采样率/512
- 对白噪声的抑制:20×log10(√256) = 24dB
- 对50Hz工频干扰也有一定抑制效果
三、Step 2:量程转换
3.1 ±10V量程转换
AD7606在±10V量程下的数据编码word_4:
| ADC原始值 | 对应电压 |
|---|---|
| 0x0000 (0) | -10V |
| 0x8000 (32768) | 0V |
| 0xFFFF (65535) | +10V |
16位转换公式word_4:
c
// ±10V量程
int16 adc16 = (int16)_uadc.getAdcFilter16[i] - 32768; // 去偏置
_modbus._inputReg.adc_mV_Data16[i] = adc16 / 3.2768; // → mV
// 3.2768 = 32768 / 10000(满量程10V = 10000mV)
24位转换公式word_4:
c
// ±10V量程
int32 adc24 = (int32)_uadc.getAdcFilter24[i] - 8388608; // 去偏置
_modbus._inputReg.adc_uV_Data24[i] = adc24 / 0.8388608 / 10; // → μV
// 8388608 = 2^23(24位中位值)
// 0.8388608 = 8388608 / 10000000
3.2 ±5V量程转换
±5V量程下,系数相应调整word_4:
c
// ±5V量程
// 16位: 系数 = 6.5536 = 32768 / 5000
// 24位: 系数 = 1.6777216 = 8388608 / 5000000
3.3 上位机端的转换
C#上位机中的24位ADC数据转换word_3:
csharp
// Setting.cs
if (_prxData.adcRange == 1) // ±5V量程
value = (SWAPS32(adcValue) - 8388608) / 83886.08 / 10;
else // ±10V量程
value = (SWAPS32(adcValue) - 8388608) / 167772.16 / 10;
// 8388608 = 2^23, 24位ADC中位值
3.4 Modbus寄存器编码
D223/G511的Modbus寄存器中,24位ADC原始值用两个16位寄存器表示word_17:
| 量程 | 0x000000 | 0x00800000 | 0x00FFFFFF |
|---|---|---|---|
| ±5V | -5V | 0V | +5V |
| ±10V | -10V | 0V | +10V |
3.5 电流信号转换
当信号类型为电流时,通过240Ω取样电阻将电流转换为电压word_4:
c
// 电流模式(4-20mA环路,240Ω取样电阻)
_modbus._inputReg.adc_uA_Data16[i] = adc16 / 3.2768 / 240 * 1000; // → μA
转换链:ADC数字值 → mV电压 → ÷240Ω × 1000 → μA电流word_4。
四、Step 3:两点线性插值校准
4.1 校准公式
两点线性插值是D223校准的核心算法word_4word_3:
c
double calc_adcCalibData(double data, double d1, double a1,
double d2, double a2) {
return (((data - d1) * (a2 - a1)) / (d2 - d1)) + a1;
}
参数说明word_4:
data:当前ADC原始数字量d1, a1:校准点A的数字量和模拟量d2, a2:校准点B的数字量和模拟量
4.2 校准执行流程
固件中的校准执行word_4:
c
double getCalibAdcDatas(u8 widthByte, u8 ch, s32 data) {
double adcFloat = 0;
if(校准点A使能 && 校准点B使能) {
// 使用用户标定的两点进行线性插值
adcFloat = calc_adcCalibData(data,
_framDatas._adcCalibDatasPointA[ch].calibDigital,
_framDatas._adcCalibDatasPointA[ch].calibAnalog,
_framDatas._adcCalibDatasPointB[ch].calibDigital,
_framDatas._adcCalibDatasPointB[ch].calibAnalog);
adcFloat *= 0.00001; // 缩放到实际工程单位
} else {
// 使用默认理想转换曲线(无校准)
if(±10V量程)
adcFloat = data * 10.0 / 8388608.0 - 10.0; // 24位
else
adcFloat = data * 5.0 / 8388608.0 - 5.0; // 24位
}
return adcFloat;
}
4.3 校准参数存储
每通道独立存储两组校准数据word_7:
c
// C#上位机中的校准参数结构
public static Int32[] calib_adcCalib_digitalA = new Int32[8]; // A点数字量
public static Int32[] calib_adcCalib_analogA = new Int32[8]; // A点模拟量(×100000)
public static byte[] calib_adcCalib_enableA = new byte[8]; // A点使能
public static Int32[] calib_adcCalib_digitalB = new Int32[8]; // B点数字量
public static Int32[] calib_adcCalib_analogB = new Int32[8]; // B点模拟量
public static byte[] calib_adcCalib_enableB = new byte[8]; // B点使能
public static Int32[] correctFactor = new Int32[8]; // 校正系数(×100000)
public static Int32[] calibZero = new Int32[8]; // 零点偏移(×100000)
模拟量乘以100000存储为整数,避免浮点运算精度损失word_7。
4.4 校正系数与零点偏移
在两点校准之后,还叠加校正系数和零点偏移word_4:
c
adcFloat = adcFloat * (correctFactor * 0.00001) + (calibZero * 0.00001);
correctFactor:乘法校正系数,默认100000(即1.0)calibZero:加法零点偏移,默认0
五、Step 4:传感器单位转换
5.1 支持的传感器单位
D223支持以下传感器单位word_3word_6:
| 类别 | 单位 |
|---|---|
| 电压 | V, mV |
| 电流 | A, mA |
| 温度 | ℃, m℃, ℉ |
| 力/重量 | N, kg, g |
| 转速 | G, K, mK |
| 压力 | MPa, kPa, hPa, Pa |
共17种预置单位word_3。
注意:D223支持最大256个自定义单位,可以任意修改note_259。这意味着用户可以根据实际传感器定义任意工程单位。
5.2 上位机单位选择
C#上位机中,每个通道可以独立选择传感器单位word_6:
csharp
public static byte[] sensorUnit = new byte[8]; // 每通道单位索引
public static string[] sensorUnitList = {
"V", "mV", "A", "mA", "℃", "m℃",
"N", "kg", "g", "G", "K", "mK",
"MPa", "kPa", "hPa", "Pa", "℉"
};
六、完整转换示例
示例:±10V量程下测量1V电压
假设AD7606输出16位原始值 = 39322(对应约1V)word_5:
Step1: 256点滑动平均
假设256次采样的累加和 = 39322 × 256 = 10,066,432
getAdcFilter16 = 10,066,432 / 256 = 39322 (16位均值)
getAdcFilter24 = 10,066,432 (24位累加和)
Step2: 量程转换(±10V)
16位mV值 = (39322 - 32768) / 3.2768 = 6554 / 3.2768 = 2000 mV = 2V
24位μV值 = (10066432 - 8388608) / 0.8388608 / 10 = 1677824 / 8.388608 = 200000 μV = 0.2V
注意:这里的示例假设了理想线性,实际值取决于信号和噪声
Step3: 两点校准(假设已标定)
校准点A: d1=32768, a1=0V(0V输入时的ADC值)
校准点B: d2=65535, a2=10V(10V输入时的ADC值)
校准值 = (39322 - 32768) × (10 - 0) / (65535 - 32768) + 0 = 2.003V
Step4: 单位转换
传感器选择"V" → 显示 2.003 V
传感器选择"mV" → 显示 2003 mV
七、精度层次总结
D223的ADC数据处理实现了多层精度保障word_4note_244:
| 层次 | 方法 | 精度 |
|---|---|---|
| AD7606硬件 | 16位SAR ADC | ±0.5 LSB INL/DNL |
| 256点过采样 | 软件滑动平均 | 24位等效精度 |
| 量程转换 | 数学运算 | 无损(浮点计算) |
| 两点校准 | 线性插值 | 消除系统误差 |
| 校正系数 | 乘法+加法微调 | 微米级微调 |
| 硬件精度 | 未标定 | ±0.05% |
| 标定后精度 | 两点标定 | 0.01% FSR |
八、与其他型号的数据处理对比
| 特性 | DABL7606 | DABL-G511 | DABM-D223 |
|---|---|---|---|
| 过采样方式 | 256点滑动平均 | 256点滑动平均 | 256点滑动平均 |
| 精度模式 | 16位/24位 | 16位/24位 | 16位/24位 |
| 校准方式 | 两点线性插值 | 两点线性插值 | 两点线性插值 |
| 参数存储 | FRAM | FRAM | FRAM |
| 自定义单位 | 17种预置 | 17种预置 | 256种自定义 |
| 数据流 | SPI1 DMA直读 | SPI1 DMA直读 | QSPI四线DMA |
三款产品的数据处理算法基本一致,核心差异在于数据传输通道------D223通过QSPI四线DMA读取FPGA FIFO,带宽远高于SPI1直读note_63。
九、总结
D223的ADC数据处理流水线是一个从硬件到软件、从原始值到工程值的完整转换链:
AD7606 16位原始值
↓ 256点滑动平均(+8位精度)
24位累加值
↓ 量程转换(±5V/±10V → mV/μV)
工程电压值
↓ 两点线性插值(消除系统误差)
校准电压值
↓ 校正系数 + 零点偏移(微调)
最终电压值
↓ 传感器单位转换(V/mV/℃/Pa...)
用户显示值
每一步都有明确的数学公式和代码实现,整个流水线透明可控,便于用户理解和定制。
下篇预告:《D223上位机C#开发实战:从帧解析到波形显示的完整实现》