10.ADC
10.1 什么是ADC
ADC 全称为模拟-数字转换器,是一种用于将模拟信号转换为数字信号的模拟数字转换器。我们知道,模拟信号是连续的,其取值可以在一定范围内任意变化,如声音、光信号等。而数字信号则是离散的二进制信号,如计算机中的数据0和1,仅能取有限的值。
ADC的工作原理是将模拟信号通过采样转换为离散的数字信号,然后再通过量化、编码等处理,最终得到对应的数字表示。ADC采样的频率越高,得到的数字信号就越接近原来的模拟信号,也就是保真度越高,但是需要更多的资源和计算功耗。
ADC 通常用于从外部模拟传感器中读取模拟信号,并将其转换为数字信号供嵌入式系统或计算机进行处理,例如测量温度、湿度、压力等物理量。
10.2 ESP32S3的ADC介绍
ESP32-S3 集成了两个 12 位 SAR ADC,每个ADC有10个通道,共支持 20 个模拟通道输入,可测量最多来自 20 个管脚的模拟信号以及内部电压等内部信号。其中,为了实现更低功耗,ESP32-S3 的 ULP 协处理器也可以在睡眠方式下测量电压,此时,可通过设置阈值或其他触发方式唤醒 CPU。
ADC可以转换的电压范围由VREF决定,对于ESP32-S3通常是0到3.3V,但需要注意的是,ESP32具体的最大额定输入电压可能略有不同,检查相应数据手册的具体信息非常重要,通常会建议使用衰减器(Attenuation)功能或者额外的硬件如电压分压器来保证输入电压在规定范围内。
ESP32-S3内部还带了一个温度传感器,用于生成一个随温度变化的电压。内部 ADC 将传感器电压转化为一个数字量,温度传感器的测量范围为–20 °C 到 110 °C。温度传感器适用于监测芯片内部温度的变化,该温度值会随着微控制器时钟频率或 IO 负载的变化而变化。一般来讲,芯片内部温度会高于外部温度。
支持ADC采集的引脚如下:
10.3 ADC 的基本参数
ADC 的基本参数通常包括以下几个:
- 通道和引脚: 不是所有的GPIO都可以用于ADC读取,需要查看ESP32S3的引脚图来确定哪些引脚支持ADC功能。
- 分辨率:指 ADC 的数字输出位数,也称为量化位数。例如,12 位 ADC 具有 4096 个离散的数字输出。
- 采样速率:指 ADC 可以进行采样的最大速率。对于 ESP32-S3,最大采样速率为 2.5 MS/s。
- 输入范围:指 ADC 可以测量输入信号的电压范围。对于 ESP32-S3,输入范围为 0-3.3V。
- 噪声:指 ADC 在采集时分辨率的误差和干扰的影响。噪声越小,ADC 测量结果越精准。
- 稳定性:指 ADC 输出的稳定性。稳定性好的 ADC 输出变化小,测量结果更加准确。
10.4 使用ADC流程
10.4.1 使用arduino内部库函数读取值
analogRead()
是 Arduino 中用于读取模拟输入信号的函数之一。它的原型如下:
int analogRead(uint8_t pin);
参数 pin
是一个表示要读取的模拟引脚的整数值。在 ESP32-S3 上,可以使用不同的引脚作为模拟引脚,但是只支持GPIO1到GPIO20之间。analogRead()
函数返回一个整数值,表示从模拟输入引脚读取到的 ADC 值。对于 ESP32-S3 来说,这个值的范围是 0 到 4095,对应于 12 位 ADC 的分辨率。
以下是一个使用 analogRead() 函数读取模拟输入的简单示例:
//定义模拟量读取引脚为GPIO1
const int analogPin = 1;
int sensorValue = 0;
void setup() {
//设置开启调试串口0,设置波特率为9600
Serial.begin(9600);
//设置 GPIO1 引脚 为浮空输入模式
pinMode(analogPin, INPUT);
}
void loop() {
//读取GPIO1的模拟值
sensorValue = analogRead(analogPin);
//通过串口0 打印出读取到的值
Serial.println(sensorValue);
//延时1秒
delay(1000);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
在上面的示例中,我们将 GPIO1设置为模拟输入引脚,并在 loop()
函数中通analogRead(analogPin)
读取模拟输入的值,并将其打印到串口监视器中。延迟 1 秒后,再次读取并打印值。循环重复执行,实现连续的模拟输入读取。
10.4.2 通过ADC外设库读取值
打开 esp32_hal_adc.h
文件之后,我们可以看到 ADC 的所有控制函数:
analogReadResolution(bits)
:用于设置模拟输入的分辨率。对于ESP32-S3(和ESP32),默认的ADC(模拟-数字转换)分辨率是12位,范围是0到4095。如果你调用了该函数,你可以更改这个分辨率。其中bits
是你希望设定的分辨率的位数。它可以是一个介于 9位(0 - 511)和 12 位(0 - 4095)之间的值。例如,如果你将分辨率设置为10位,analogRead()
将返回一个范围是0到1023的值。
改变读数的分辨率可以适应需要不同精度的应用场景。较低的分辨率可以使得ADC读数更快,较高的分辨率则可以获得更详细的电压读数,但会牺牲一些性能。需要注意的是,提高分辨率并不会改善ADC的精度,ADC的准确度还受限于硬件和噪声。analogSetWidth(width)
:用于设置 ADC 的位宽,有效的位宽参数通常是从 9 到 12 位,默认是 12 位。这个函数的作用跟analogReadResolution
函数似乎作用类似,但它们来自不同的上下文。analogSetWidth(width)
是针对 ESP32 平台特有的一个函数,它允许你设定 ESP32 内部 ADC 的位宽。该函数直接设定了 ADC 硬件的采样位宽。analogReadResolution(resolution)
是在 Arduino 环境中更常见的函数,用于设定analogRead()
函数返回的数值的分辨率。在一些具有可变 ADC 分辨率的 Arduino 兼容板上,这个函数可以用来改变analogRead()
的返回值位宽。
简单来说,analogSetWidth()
是特定于ESP32的函数,它实际上改变了硬件ADC模块的工作方式。而analogReadResolution()
是Arduino API的一部分,用来设置analogRead()
函数返回值的分辨率。在不同的设备和库版本间,两者的具体实现可能有所不同。
例如,在ESP32上使用analogSetWidth(10)
会将ADC的位宽改为10位,这意味着ADC会以10位的精度进行模拟信号的数字转换。而在支持analogReadResolution()
的Arduino兼容设备上,如果你调用analogReadResolution(10)
,则表示接下来使用analogRead()
函数时,它将返回一个最大为10位宽的数字值(即0到1023之间),但是硬件实际上返回的是12位的数据。
所以需要注意的是,对于某些Arduino兼容板,改变analogReadResolution()
并不会实际改变ADC硬件自身的分辨率,而是改变了返回给Arduino程序的数值的范围。如果硬件ADC的分辨率实际上是12位的,当你设置analogReadResolution(10)
,Arduino API会将12位原生ADC值映射(或缩放)到10位的范围。而在ESP32上,analogSetWidth()
实际上是改变了内部ADC硬件的位宽。analogSetCycles(cycles)
:设置每个样本的循环次数。默认是 8。取值范围:1 ~ 255。analogSetSamples(samples)
:设置范围内的样本数量。默认为 1 个样本。它有增加灵敏度的作用。analogSetClockDiv(attenuation)
:设置ADC时钟的分压器。默认值为1。取值范围:1 ~ 255。adcAttachPin(pin)
:附加一个引脚到 ADC(也清除任何其他模拟模式可能是 on)。返回TRUE或FALSE结果。analogSetAttenuation(attenuation)
:用于设置模拟数字转换器(ADC)的衰减(Attenuation),以便能够读取更广泛范围的输入电压。衰减是一个减少信号强度的过程,在 ESP32S3 的 ADC 中,这是通过硬件上的衰减器实现的,可以增加ADC能够测量的最大电压,允许较高的电压信号在不超出 ADC 输入限制的情况下进行测量。正是这种功能扩展了ESP32S3 ADC的输入电压范围。ESP32 SDK 通常提供以下衰减选项:
ADC_ATTEN_DB_0
:不衰减,最大电压范围为 1.1V(在ESP32S3中,0 dB 衰减对应的最大电压为大约 1.1V)。ADC_ATTEN_DB_2_5
:约 1.5V 的最大可测电压。ADC_ATTEN_DB_6
:约 2.2V 的最大可测电压。ADC_ATTEN_DB_11
:约 3.9V 的最大可测电压。如果你想测量比 1.1V 更高的电压,必须设置一个衰减值。例如,如果你要测量一个最高 3.3V 的信号,你可以将衰减设置为
ADC_ATTEN_DB_11
。analogSetPinAttenuation(pin, attenuation)
:设置指定引脚的输入衰减。默认是 ADC_11db。衰减值与前一个函数相同;
示例:
#define POT 1
// 初始化模拟输入值
int pot_value;
void setup() {
//初始化串口0,波特率为9600
Serial.begin(9600);
//初始化ADC引脚
pinMode(POT,INPUT);
// 设置 ADC 分辨率
analogReadResolution(12);
// 配置衰减器
analogSetAttenuation(ADC_11db);
}
void loop() {
// 获取模拟输入值
pot_value = analogRead(POT);
// 串口0打印ADC值
Serial.println(pot_value);
delay(1000);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
上面的示例中,初始化了串口0用于打印采集到的ADC值;初始化GPIO1为ADC接口,用于采集外部的模拟量;
10.5 硬件连接与准备
本示例将GPIO1作为ADC读取引脚,通过GPIO1测试外部电压,再使用板载的串口0打印出ADC读取到的值,并换算成实际电压显示。
10.6 ADC读取验证
#define POT 1
// 初始化模拟输入值
int adc_value;
void setup() {
//初始化串口0,波特率为9600
Serial.begin(9600);
//初始化ADC引脚
pinMode(POT,INPUT);
// 设置 ADC 分辨率
analogReadResolution(12);
// 配置衰减器
analogSetAttenuation(ADC_11db);
}
void loop() {
// 获取模拟输入值
adc_value= analogRead(POT);
// 串口0打印ADC值
Serial.write("ADC_value = ");
Serial.println(adc_value);
// 串口0打印 实际电压值
Serial.write("voltage(V) = ");
Serial.println(adc_value/4095.0\*3.3);
delay(1000);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
10.7 ADC读取效果
打开串口调试助手,设置波特率为9600,查看数据。将GPIO1引脚通过杜邦线接到3.3V和GND进行测试。