一颗ADC芯片输出的只是数字编码,如何把它变成用户看得懂的电压值?本文从代码层面完整解析D223的ADC数据处理流水线:原始值→滑动平均→量程转换→校准插值→工程单位。
一、数据处理流水线总览
D223的ADC数据从硬件到用户显示,经过以下完整流水线[word_4][note_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]:
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]:
voidmodbus_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]:
// ±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]:
// ±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 / 100000003.2 ±5V量程转换
±5V量程下,系数相应调整[word_4]:
// ±5V量程// 16位: 系数 = 6.5536 = 32768 / 5000// 24位: 系数 = 1.6777216 = 8388608 / 50000003.3 上位机端的转换
C#上位机中的24位ADC数据转换[word_3]:
// Setting.csif(_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]:
// 电流模式(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_4][word_3]:
doublecalc_adcCalibData(doubledata,doubled1,doublea1,doubled2,doublea2){return(((data-d1)*(a2-a1))/(d2-d1))+a1;}参数说明[word_4]:
data:当前ADC原始数字量d1, a1:校准点A的数字量和模拟量d2, a2:校准点B的数字量和模拟量
4.2 校准执行流程
固件中的校准执行[word_4]:
doublegetCalibAdcDatas(u8 widthByte,u8 ch,s32 data){doubleadcFloat=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位elseadcFloat=data*5.0/8388608.0-5.0;// 24位}returnadcFloat;}4.3 校准参数存储
每通道独立存储两组校准数据[word_7]:
// C#上位机中的校准参数结构publicstaticInt32[]calib_adcCalib_digitalA=new Int32[8];// A点数字量publicstaticInt32[]calib_adcCalib_analogA=new Int32[8];// A点模拟量(×100000)publicstaticbyte[]calib_adcCalib_enableA=new byte[8];// A点使能publicstaticInt32[]calib_adcCalib_digitalB=new Int32[8];// B点数字量publicstaticInt32[]calib_adcCalib_analogB=new Int32[8];// B点模拟量publicstaticbyte[]calib_adcCalib_enableB=new byte[8];// B点使能publicstaticInt32[]correctFactor=new Int32[8];// 校正系数(×100000)publicstaticInt32[]calibZero=new Int32[8];// 零点偏移(×100000)模拟量乘以100000存储为整数,避免浮点运算精度损失[word_7]。
4.4 校正系数与零点偏移
在两点校准之后,还叠加校正系数和零点偏移[word_4]:
adcFloat=adcFloat*(correctFactor*0.00001)+(calibZero*0.00001);correctFactor:乘法校正系数,默认100000(即1.0)calibZero:加法零点偏移,默认0
五、Step 4:传感器单位转换
5.1 支持的传感器单位
D223支持以下传感器单位[word_3][word_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]:
publicstaticbyte[]sensorUnit=newbyte[8];// 每通道单位索引publicstaticstring[]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_4][note_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#开发实战:从帧解析到波形显示的完整实现》