2. ADC通道精度校准测试
任务目标
在ESP32量产测试中,ADC通道的精度直接影响产品测量类功能的可靠性。本案例使用可调精密电压源(或高精度稳压模块)作为已知输入,连接至ESP32的多个ADC通道,通过采集数据与实际电压比较,计算各通道的误差,并依据预设允许范围判定通道是否合格。
环境与依赖
- 硬件:ESP32开发板(如ESP32-DevKitC)、可调精密电压源(0~3.3V,精度≥0.1%)、万用表(用于校准电压源输出)
- 软件:ESP-IDF v5.x 或 Arduino ESP32 核心(本案例使用ESP-IDF风格代码,Arduino环境需调整头文件与初始化方式)
- 接线:电压源输出接至ADC通道引脚(如GPIO34/ADC1_CH6、GPIO35/ADC1_CH7、GPIO36/ADC1_CH0),GND共地
实现原理
ESP32的ADC为12位分辨率,理论满量程对应0~3.3V(实际受衰减与参考电压影响存在非线性)。本案例采用多点校准法:对每个通道输入3个已知电压点(如0.5V、1.5V、2.5V),读取ADC原始值并转换为电压,计算每个点的误差与相对误差,取最大误差作为该通道的判定依据。允许范围设定为±2%(相对误差)或±30mV(误差),取两者中较大者作为阈值。
关键完整代码
#include <stdio.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/adc.h"
#include "esp_adc_cal.h"
#define NUM_CHANNELS 3
#define NUM_POINTS 3
// 待测通道配置(ADC1)
static const adc1_channel_t channels[NUM_CHANNELS] = {
ADC1_CHANNEL_6, // GPIO34
ADC1_CHANNEL_7, // GPIO35
ADC1_CHANNEL_0 // GPIO36
};
// 已知标准电压(单位:mV)
static const int standard_mv[NUM_POINTS] = {500, 1500, 2500};
// 允许误差:相对2%或30mV,取较大者
static const float max_rel_err = 0.02f;
static const int max_abs_err_mv = 30;
static esp_adc_cal_characteristics_t adc_chars;
// 读取单通道多次采样取平均
static uint32_t read_adc_avg(adc1_channel_t ch, int samples) {
uint32_t sum = 0;
for (int i = 0; i < samples; i++) {
sum += adc1_get_raw(ch);
}
return sum / samples;
}
// 测试单个通道在所有标准电压点下的误差
static void test_channel(adc1_channel_t ch, int ch_idx) {
float max_err_mv = 0.0f;
int raw_val;
uint32_t voltage_mv;
printf("--- Channel %d (GPIO%d) ---\n", ch_idx,
(ch == ADC1_CHANNEL_6) ? 34 : (ch == ADC1_CHANNEL_7) ? 35 : 36);
for (int i = 0; i < NUM_POINTS; i++) {
// 提示用户设置电压源
printf("Set source to %d mV, then press Enter...", standard_mv[i]);
getchar(); // 等待用户确认电压已设置
raw_val = read_adc_avg(ch, 32);
voltage_mv = esp_adc_cal_raw_to_voltage(raw_val, &adc_chars);
float abs_err = (float)voltage_mv - standard_mv[i];
float rel_err = abs_err / standard_mv[i];
float allowed = (standard_mv[i] * max_rel_err > max_abs_err_mv) ?
standard_mv[i] * max_rel_err : max_abs_err_mv;
printf(" Std=%4d mV, Raw=%4d, Meas=%4d mV, AbsErr=%+6.1f mV, RelErr=%+5.2f%%\n",
standard_mv[i], raw_val, voltage_mv, abs_err, rel_err * 100);
if (abs_err > allowed || abs_err < -allowed) {
printf(" -- FAIL (exceeds tolerance)\n");
return;
}
if (abs_err > max_err_mv) max_err_mv = abs_err;
}
printf(" PASS (max abs err: %.1f mV)\n", max_err_mv);
}
void app_main(void) {
// 初始化ADC1,衰减11dB(量程约0~3.3V)
adc1_config_width(ADC_WIDTH_BIT_12);
adc1_config_channel_atten(ADC1_CHANNEL_6, ADC_ATTEN_DB_11);
adc1_config_channel_atten(ADC1_CHANNEL_7, ADC_ATTEN_DB_11);
adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_DB_11);
// 校准特性(使用eFuse中存储的Vref)
esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_12, 1100, &adc_chars);
printf("ADC Channel Calibration Test\n");
printf("Set voltage source to 0V before starting.\n");
printf("Press Enter to begin...\n");
getchar();
for (int i = 0; i < NUM_CHANNELS; i++) {
test_channel(channels[i], i);
}
printf("All channels tested.\n");
}
编译与运行
- 在ESP-IDF环境中创建项目,将上述代码保存为
main.c,替换默认入口文件。 - 在项目根目录执行
idf.py set-target esp32和idf.py build完成编译。 - 使用
idf.py -p /dev/ttyUSB0 flash monitor烧录并打开串口监视器(波特率115200)。
验证方法与判定标准
- 程序启动后,先将电压源输出调至0V,按回车开始测试。
- 每个测试点会提示设置指定电压,使用万用表确认电压源输出准确后按回车。
- 观察串口输出:每个通道在3个电压点下均显示
PASS,且无FAIL标记,则该通道精度合格。 - 若某点误差超出允许范围(相对2%或30mV取大者),程序立即打印
FAIL并跳过该通道后续测试。 - 全部3个通道均通过,则判定该板ADC精度满足要求。
常见问题
- 读数跳变较大:检查电源纹波,确保共地可靠;可增加采样次数(当前32次)或使用硬件平均。
- 所有通道误差一致偏大:多为参考电压偏差,确认eFuse中Vref校准值是否有效;若无效需手动校准。
- 高电压点(2.5V以上)误差增大:属ADC非线性特性,若超出允许范围需在固件中增加查表补偿。