在之前的章节中,我们测试了用ESP32来接收模拟电压信号,在测试中,读取到的数据与现实存在一定的误差,在这一篇中,我们尝试了解出现误差的原因和解决方法。
对于出现的误差,有多种软件和硬件方面的原因:
一、官方设置原因
二、代码原因
三、硬件原因
四、电源原因
我们对应这几个原因,一步一步来测试并尝试解决。
我们所使用的ESP32版本有许多(esp32,esp32-s2,esp32-c3,esp32-s3等),对于ADC的更新和优化也一直在进行。在本系列章节中所使用的版本为esp32,而该版本的ADC功能在使用过程中,体现是比较差的一个版本。
我们在官方文档中可以查到该版本的ADC功能的量程
ADC_ATTEN_DB_0 100 mV ~ 950 mV (0.1V~0.95V)
ADC_ATTEN_DB_2_5 100 mV ~ 1250 mV (0.1V~1.25V)
ADC_ATTEN_DB_6 150 mV ~ 1750 mV (0.15V~1.75V)
ADC_ATTEN_DB_11 150 mV ~ 3100 mV (0.15V~3.1V)
也就是说,在默认设置下,我们可以读取的电压范围为0.15V~3.1V,但是我们在上一章中所使用的是3.3V的引脚,也就是说,当电压为3.1V时,我们读取到的数据已经等于4095,但电压继续上升时,我们读取到的数据并不会继续增加。
我们可以用代码来测试这个现像,我们用ESP32的DAC功能来生成一个电压,同时用ADC功能来读取这个生成的电压,测试生成的电压和读取的电压是否相同。
#include <esp32-hal-adc.h>
uint8_t dac_value = 0; //DAC值,2^8长度
void setup() {Serial.begin(115200);
}void loop() {dac_value++; //DAC值累加float vout = (dac_value) * 3.3 / 255; //DAC值转为电压值Serial.print("vout = ");Serial.print(vout); //串口输出当前输出的电压值dacWrite(25,dac_value); //25号引脚输出对应电压int adc_value = analogRead(4); //4号引脚读取25号引脚输出的模拟值float vin = (adc_value*3.3) / 4095; //读取到的数据转为电压值Serial.print(" | ");Serial.print("vin = ");Serial.println(vin); //串口输出当前输入的电压值delay(1000);
}
查看串口输出
我们可以观察到
当输出的电压到达0.06V时,读取到的电压为0.01V。误差为0.06
在观察过程中,误差慢慢增加,当输出电压到达2.47V时,读取到的电压为2.12V,误差达到最大误差值,为0.35。
之后,误差慢慢减少,当输出电压到达3.30V时,读取到的电压为3.25V,误差为0.05。
这时,我们发现,这与官方文档所说明的量程为0.15V~3.10V的又出现了偏差。
所以,我们继续尝试用另一个函数:analogReadMillivolts()来读取4号引脚的电压:
#include <esp32-hal-adc.h>
uint8_t dac_value = 0; //DAC值,2^8长度
void setup() {Serial.begin(115200);
}void loop() {dac_value++; //DAC值累加float vout = (dac_value) * 3.3 / 255; //DAC值转为电压值Serial.print("vout = ");Serial.print(vout); //串口输出当前输出的电压值dacWrite(25,dac_value); //25号引脚输出对应电压float vin = analogReadMillivolts(4)/1000.0; //4号引脚读取25号引脚的电压值Serial.print(" | ");Serial.print("vin = ");Serial.print(vin); //串口输出当前输入的电压值Serial.print(" | ");Serial.print("deviation = ");Serial.println(vout - vin); //串口输出当前输出与输入的误差delay(100);
}
读取的结果同样并不完美,同样出现误差的变化,但对比之前,误差已经在一定的范围内
当读取到的电压到达3.1V时,超量了0.04V,误差最大为0.14。
至此,我们大概已经可以了解到,在代码层面出现误差的原因有两点:
一、官方设置的量程问题
二、与analogRead()相比较,使用analogReadMillivolts()函数能更精确地读取对应的电压值。
我们在官方文档中详细地了解一下这两个函数:ADC — Arduino-ESP32 2.0.14 documentation
analogRead()函数的作用是用于读取指定引脚的ADC值,返回的结果未经过校准。
analogReadMillivolts()函数的作用是用于读取指定引脚的ADC值,并返回转换为以毫伏为单位的校准结果。
至此,我们在本章中已经了解了软件方面导致数据出现误差的原因,在下一章中,我们将介绍在硬件方面导致数据出现误差的原因。