以下是STM32 ADC校准的详细技术说明,包含实际操作步骤和注意事项:
一、ADC校准的必要性
-
误差来源分析:
- 零点偏移误差(Offset Error):输入0V时输出不为0
- 增益误差(Gain Error):满量程时的线性偏差
- 非线性误差(DNL/INL):转换曲线的阶梯偏差
- 温度漂移(典型值±2℃时±4LSB)
-
校准目标:
- 12位ADC的有效精度达到±1LSB
- 减少芯片个体差异影响
- 补偿供电电压波动带来的误差
二、STM32内置校准流程
1. 校准寄存器操作流程
// 适用于STM32F1/F4系列的标准校准流程
void ADC_Calibration(ADC_TypeDef* ADCx) {
// 1. 使能ADC复位校准
ADCx->CR2 |= ADC_CR2_RSTCAL;
while(ADCx->CR2 & ADC_CR2_RSTCAL); // 等待复位完成
// 2. 启动校准过程
ADCx->CR2 |= ADC_CR2_CAL;
while(ADCx->CR2 & ADC_CR2_CAL); // 等待校准完成
// 3. 获取校准因子(F4系列需要)
#if defined(STM32F4xx)
uint32_t cal_value = ADCx->CALFACT;
#endif
}
2. 校准时序要求
步骤 | 最小时间 | 典型值 | 条件 |
---|---|---|---|
电源稳定等待 | 10μs | 20μs | 上电后首次使用前 |
复位校准时间 | 2个ADC周期 | - | 内部自动控制 |
校准过程时间 | 12位模式:28个周期 | ≈7μs @14MHz | 与ADC时钟频率相关 |
三、外部校准进阶方法
1. 两点校准法(推荐)
// 使用标准电压源校准
#define VREF_3V0 3.000f // 外部精密参考电压
#define ADC_RES 4095.0f // 12位分辨率
void Advanced_Calibration() {
// 连接已知电压到ADC通道
float measured_0V = Read_ADC(0); // 短接GND
float measured_3V = Read_ADC(VREF); // 连接3V标准源
// 计算校准系数
float scale = VREF_3V0 / (measured_3V - measured_0V);
float offset = measured_0V * scale;
// 存储到Flash
Save_CalibrationParams(offset, scale);
}
float Get_CalibratedValue(uint16_t raw) {
return (raw * saved_scale) - saved_offset;
}
2. 校准参数存储
推荐使用Flash的最后页存储校准参数:
typedef struct {
float offset;
float scale;
uint32_t crc;
} ADC_CalibData;
#define CALIB_ADDR 0x0800F000 // Flash最后一页
void Save_CalibrationParams(float offset, float scale) {
ADC_CalibData data = {
.offset = offset,
.scale = scale,
.crc = Calculate_CRC32(&data, sizeof(data)-4)
};
FLASH_Unlock();
FLASH_ProgramWord(CALIB_ADDR, *(uint32_t*)&data);
//... 连续写入多个word
FLASH_Lock();
}
四、温度补偿校准
当工作温度变化超过±10℃时需进行补偿:
// 读取内部温度传感器(需先校准)
float Read_Temperature() {
// 启用温度传感器通道
ADC->CCR |= ADC_CCR_TSVREFE;
// 读取温度传感器原始值
uint16_t raw = Read_ADC(ADC_CHANNEL_TEMP);
// 应用校准参数(从Flash读取)
float vsense = raw * VREF/4095.0f;
float temperature = ((vsense - V25)/Avg_Slope) + 25;
return temperature;
}
// 温度补偿公式
void Temp_Compensation() {
static float coeff[3] = {1.0, 0.003, 0.00002}; // 典型补偿系数
float temp = Read_Temperature();
current_scale = saved_scale * (coeff[0] + coeff[1]*temp + coeff[2]*temp*temp);
}
五、校准验证方法
-
静态测试:
// 测试代码 void Test_ADC_Accuracy() { Connect_KnownVoltage(1.65f); // 连接精准1.65V uint32_t sum = 0; for(int i=0; i<1000; i++){ sum += Read_ADC(); } float avg = sum / 1000.0f; float error = fabs((avg * 3.3f/4095) - 1.65f); printf("实测误差:%.2f mV", error*1000); }
-
动态性能测试:
- 使用信号发生器输入正弦波
- 通过FFT分析ENOB(有效位数)
- 计算THD(总谐波失真)应小于-60dB
六、软件滤波增强
结合硬件校准使用数字滤波:
#define FILTER_DEPTH 16
typedef struct {
uint16_t buffer[FILTER_DEPTH];
uint8_t index;
} MovingAverage_Filter;
uint16_t Filter_ADC_Value(uint16_t new_val) {
static MovingAverage_Filter filter = {0};
filter.buffer[filter.index] = new_val;
filter.index = (filter.index + 1) % FILTER_DEPTH;
uint32_t sum = 0;
for(int i=0; i<FILTER_DEPTH; i++){
sum += filter.buffer[i];
}
return sum / FILTER_DEPTH;
}
七、工程实践建议
-
校准周期:
- 首次出厂校准
- 每6个月定期校准
- 检测到温度突变>15℃时触发重新校准
-
PCB布局要点:
- ADC电源引脚添加10μF+0.1μF去耦电容
- 模拟走线与数字走线间距>3倍线宽
- 敏感信号采用guard ring保护
-
异常处理机制:
#define ADC_OVERSHOOT_THRESHOLD 4050 // 3.3V对应4095 void ADC_Check_Abnormal() { uint16_t val = Read_ADC(); if(val > ADC_OVERSHOOT_THRESHOLD) { ADC_Reinit(); // 重新初始化ADC NVIC_SystemReset(); // 严重错误重启 } }
-
低功耗模式校准:
void Enter_LowPowerMode() { ADC->CR2 &= ~ADC_CR2_ADON; // 关闭ADC PWR_EnterSTOPMode(); // 进入STOP模式 // 唤醒后重新校准 ADC_Calibration(ADC1); ADC_Cmd(ADC1, ENABLE); }
这些校准技术的组合应用可使STM32 ADC的实际测量精度达到:
- 常温下±0.5% FSR
- 全温度范围±1.2% FSR
- 长期稳定性±0.8% FSR
建议在浇花系统中对土壤湿度传感器每24小时执行一次自动零点校准(短接传感器输入),并结合温度补偿算法实现全天候精准测量。