9. ADC
9.1 什么是ADC
ADC 全称为模拟-数字转换器,是一种用于将模拟信号转换为数字信号的模拟数字转换器。我们知道,模拟信号是连续的,其取值可以在一定范围内任意变化,如声音、光信号等。而数字信号则是离散的二进制信号,如计算机中的数据0和1,仅能取有限的值。
ADC的工作原理是将模拟信号通过采样转换为离散的数字信号,然后再通过量化、编码等处理,最终得到对应的数字表示。ADC采样的频率越高,得到的数字信号就越接近原来的模拟信号,也就是保真度越高,但是需要更多的资源和计算功耗。
ADC 通常用于从外部模拟传感器中读取模拟信号,并将其转换为数字信号供嵌入式系统或计算机进行处理,例如测量温度、湿度、压力等物理量。
9.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 负载的变化而变化。一般来讲,芯片内部温度会高于外部温度。
9.3 ADC 的基本参数
ADC 的基本参数通常包括以下几个:
- 分辨率:指 ADC 的数字输出位数,也称为量化位数。例如,12 位 ADC 具有 4096 个离散的数字输出。
- 采样速率:指 ADC 可以进行采样的最大速率。对于 ESP32-S3,最大采样速率为 2.5 MS/s。
- 输入范围:指 ADC 可以测量输入信号的电压范围。对于 ESP32-S3,输入范围为 0-3.3V。
- 噪声:指 ADC 在采集时分辨率的误差和干扰的影响。噪声越小,ADC 测量结果越精准。
- 稳定性:指 ADC 输出的稳定性。稳定性好的 ADC 输出变化小,测量结果更加准确。
9.4 使用ADC流程
9.4.1 包含相关头文件
首先,在你的代码中引入必要的头文件,包括 esp_adc_cal.h
和 driver/adc.h
。
#include "esp_adc_cal.h"
#include "driver/adc.h"
2
9.4.2 配置ADC参数
通过调用函数adc1_config_width()
和adc1_config_channel_atten()
配置所需精度和衰减。adc1_config_width
是 ESP32S3 中的 ADC1 模块的配置函数之一,它用于设置 ADC1 的精度(位数)。通过这个函数,你可以将 ADC1 的精度设置为 9 位、10 位、11 位或 12 位。
void adc1_config_width(adc_bits_width_t width);
其中 width
参数指定要设置的分辨率位数,它可以是以下值之一:
ADC_WIDTH_BIT_9
: 将 ADC1 的精度设置为 9 位ADC_WIDTH_BIT_10
: 将 ADC1 的精度设置为 10 位ADC_WIDTH_BIT_11
: 将 ADC1 的精度设置为 11 位ADC_WIDTH_BIT_12
: 将 ADC1 的精度设置为 12 位
例如,要将 ADC1 的精度设置为 12 位,可以进行以下操作: // 将 ADC1 的精度设置为 12 位
adc1_config_width(ADC_WIDTH_BIT_12);
这个函数应该在使用 ADC1 之前调用,以确保 ADC1 的精度被正确设置。
adc1_config_channel_atten
是 ESP32S3 中的 ADC1 模块配置函数之一,它用于设置 ADC1 特定通道的衰减参数。 该函数的原型如下:
void adc1_config_channel_atten(adc1_channel_t channel, adc_atten_t atten);
函数接受两个参数:
channel
:要配置的 ADC1 通道,类型为adc1_channel_t
。atten
:衰减参数,类型为adc_atten_t
。 衰减参数atten
决定了 ADC 输入信号的电压范围,可选值有:ADC_ATTEN_DB_0
: 0 dB 衰减,输入电压范围为 0-1.1V。ADC_ATTEN_DB_2_5
: 2.5 dB 衰减,输入电压范围为 0-1.5V。ADC_ATTEN_DB_6
: 6 dB 衰减,输入电压范围为 0-2.2V。ADC_ATTEN_DB_11
: 11 dB 衰减,输入电压范围为 0-3.3V。 例如,要将 ADC1 的通道 0 配置为 11 dB 衰减,可以进行以下操作:
// 将 ADC1 的通道 0 配置为 11 dB 衰减
adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_DB_11);
2
这个函数应该在使用 ADC1 的特定通道之前调用,以确保需要的衰减参数设置正确。
9.4.3 读取 ADC 值
- 使用
adc1_get_raw
函数来获取 ADC 值。adc1_get_raw
是 ESP32S3 中的 ADC1 模块函数之一,用于以原始 12 位 ADC 值的形式读取 ADC1 的指定通道的测量值。
函数原型如下:
int adc1_get_raw(adc1_channel_t channel);
该函数只接受一个参数:
channel
: 要读取的 ADC1 通道,类型为adc1_channel_t
。
函数返回值为一个整数,表示 ADC 测量值的原始 12 位 ADC 值。即使经过了 ADC 特征化处理,测量值也以原始的 12 位数字形式提供。
例如,要在 ADC1 的通道 2 上进行 ADC 测量,并将测量值的原始 12 位 ADC 值存储在变量raw_value
中,可以执行以下操作:
// 从 ADC1 的通道 2 中读取测量值的原始 12 位 ADC 值
int raw_value = adc1_get_raw(ADC1_CHANNEL_2);
2
请注意,将原始 ADC 值转换为可读的单位和物理量测量单位需要一些额外的计算和转换过程。要根据实际情况进行处理。
9.5 硬件连接与准备
本示例将GPIO1作为ADC读取引脚,通过GPIO1测试外部电压,再使用板载的串口0打印出ADC读取到的值,并换算成实际电压显示。
来自未连接到任何信号的引脚的ADC读数是随机的。
9.6 ADC读取验证
#include <string.h>
#include <stdio.h>
#include "sdkconfig.h"
#include <esp_log.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "driver/adc.h"
#include "esp_adc_cal.h"
#define DEFAULT_VREF 1100 //默认参考电压,单位mV
static esp_adc_cal_characteristics_t *adc_chars;
#define channel ADC_CHANNEL_0 // ADC测量通道
#define width ADC_WIDTH_BIT_12 // ADC分辨率
#define atten ADC_ATTEN_DB_11 // ADC衰减
#define unit ADC_UNIT_1 // ADC1
void app_main(void)
{
int read_raw_1=0, read_raw_2=0, read_raw_3=0;
uint32_t voltage =0;
float voltage_f = 0;
adc1_config_width(width);// 12位分辨率
//ADC_ATTEN_DB_0:表示参考电压为1.1V
//ADC_ATTEN_DB_2_5:表示参考电压为1.5V
//ADC_ATTEN_DB_6:表示参考电压为2.2V
//ADC_ATTEN_DB_11:表示参考电压为3.3V
//adc1_config_channel_atten( channel,atten);// 设置通道0和3.3V参考电压
// 分配内存
adc_chars = calloc(1, sizeof(esp_adc_cal_characteristics_t));
// 对 ADC 特性进行初始化,使其能够正确地计算转换结果和补偿因素
esp_adc_cal_characterize(unit, atten, width, DEFAULT_VREF, adc_chars);
while(1)
{
//采集三个通道的ADC值
read_raw_1 = adc1_get_raw(ADC1_CHANNEL_0); //GPIO1
read_raw_2 = adc1_get_raw(ADC1_CHANNEL_1); //gpio2
read_raw_3 = adc1_get_raw(ADC1_CHANNEL_2); //gpio3
//将ADC1的通道0(gpio1)的结果转换成电压,单位mV
voltage = esp_adc_cal_raw_to_voltage(read_raw_1, adc_chars);
//输出ADC值 与 ADC1通道0(GPIO1)实际电压值
printf("read_raw_1 = %d\tread_raw_2 = %d\tread_raw_3 = %d\tvoltage: %f\n",read_raw_1,read_raw_2,read_raw_3,voltage/1000.0);
vTaskDelay(100 / portTICK_PERIOD_MS);
}
}
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
以上示例中用到了另一个函数esp_adc_cal_characterize()
,它是用于根据一组参数计算 ADC 的特征化信息,并将结果存储到指定的 esp_adc_cal_characteristics_t
结构体中。在采集时,ADC 硬件模块会将测量信息转换为数字值,通常需要进行一些校准和转换才能得到准确的测量结果。通过使用 esp_adc_cal_characterize()
,可以自动完成这些校准和转换过程,以获取更准确的 ADC 测量值。
9.7 ADC读取效果
将GPIO1和GPIO3接到一个1.48V左右的电压,将GPIO2接到GND上。得出以下结果