ESP32-S3模数转换器(ADC)
什么是模数转换器(ADC)🔍?
模数转换器(ADC)是一种将模拟信号(如电压)转换为数字信号的设备。在ESP32-S3中,ADC用于将模拟电压转换为数字值,以便微控制器可以处理。
ADC 转换器可分为:
- 并行比较型A/D 转换器(FLASH ADC)
- 逐次比较型A/D 转换器(SAR ADC)
- 双积分式A/D 转换器(Double Integral ADC)
ESP32-S3中集成了两个SAR ADC
A/D 转换过程通常为 4 步:采样、保持、量化和编码:
-
采样:在这个阶段,连续的模拟信号被转换为离散的信号。这是通过在特定的时间间隔内测量信号的值来完成的。
-
保持:在这个阶段,采样值被“保持”或固定,以便进行下一步的量化。这是必要的,因为量化过程需要一定的时间,而我们不希望在这个过程中输入信号发生变化。
-
量化:在这个阶段,保持的采样值被近似为一组预定义的离散值。这是通过将输入信号的范围划分为一组小的区间(每个区间对应一个数字值)来完成的。
-
编码:在这个阶段,量化的值被转换为数字二进制代码,以便于数字系统处理。
这四个步骤共同构成了模数转换的过程,使得模拟信号可以被数字系统处理和理解。
衰减值和分辨率的概念🎊
衰减值
衰减值(Attenuation)在模数转换器(ADC)中,指的是在将模拟信号送入ADC进行转换之前,通过电子电路(如分压器)有意降低信号幅度的过程。这样做主要是为了匹配ADC的输入范围,确保信号不会超过ADC的最高可测量电压(满量程电压)。如果原始信号的电压范围超过了ADC的输入电压限制,不进行衰减可能会导致信号失真或者损坏ADC。
对于衰减值11dB意味着信号功率或电压幅度减少大约一半。在分贝(dB)的表达中,功率衰减遵循公式:
dB = 10 ⋅ log 10 ( P 1 P 2 ) \text{dB} = 10 \cdot \log_{10} \left( \frac{P_1}{P_2} \right) dB=10⋅log10(P2P1)
至于电压衰减,鉴于功率与电压平方成正比 P ∝ V 2 P \propto V^2 P∝V2,相应的转换公式为:
dB = 20 ⋅ log 10 ( V 1 V 2 ) \text{dB} = 20 \cdot \log_{10} \left( \frac{V_1}{V_2} \right) dB=20⋅log10(V2V1)
因此,若信号遭受11dB的衰减,这意指着电压幅度减小约 1 0 11 20 \sqrt{10^{\frac{11}{20}}} 102011倍,等价于更直观的百分比形式,即信号维持了原初幅度的大约79.4%,或者说损失了20.6%。更精确地,
V 2 = V 1 ⋅ 1 0 − 11 20 ≈ 0.891 V 1 V_2 = V_1 \cdot 10^{-\frac{11}{20}} \approx 0.891V_1 V2=V1⋅10−2011≈0.891V1
因此,经历11dB衰减的信号,其电压水平将减至初始值的约89.1%,相应地,电压幅度减少了大约10.9%。
分辨率
在模数转换器(ADC)领域,分辨率是指ADC能够区分输入模拟信号细微差异的能力,即能够检测的最小电压变化量。简而言之,它体现了ADC输出数字信号时的精度。
分辨率通常由ADC的位数来表述。一个N位的ADC能够输出 2 N 2^N 2N个不同的数字量。例如,一个8位的ADC能够输出从0到255即 ( 2 8 − 1 ) (2^8 - 1) (28−1) 的数字值,而一个12位的ADC则能输出0到4095即 ( 2 12 − 1 ) (2^{12} - 1) (212−1) 的值。这意味着,12位的ADC相比8位ADC,在相同的电压范围内能够提供更精细的量化。
分辨率与实际电压的对应关系可通过以下公式计算:
分辨率 = 满量程电压 2 N − 1 \text{分辨率} = \frac{\text{满量程电压}}{2^N - 1} 分辨率=2N−1满量程电压
假设一个ADC的工作电压范围是0至3.3V,对于一个12位的ADC:
分辨率 = 3.3 V 4095 ≈ 0.000806 V 或者 0.806 m V \text{分辨率} = \frac{3.3V}{4095} \approx 0.000806V 或者 0.806mV 分辨率=40953.3V≈0.000806V或者0.806mV
这意味着,如果输入电压有任何大于0.806毫伏的变化,12位的ADC就能检测到并反映在输出的数字值上。因此,高分辨率ADC适合用于需要高度精确测量的应用场景,如精密仪器、传感器读数或是音频处理等领域。
ESP32-S3的ADC特性
ESP32-S3 集成了两个 12 位 SAR(逐次逼近寄存器) ADC,ADC1 和 ADC2,支持20 个模拟通道输入。这 20个模拟通道输入对应着具体的 IO。
ADC1型:
- 10 通道:GPIO1 - GPIO10
ADC2型:
- 10 通道:GPIO11 - GPIO20
ESP32-S3的ADC模块的分辨率为12位,所以AD转换后的值范围为0~4095。由于ESP32-S3的工作电压为3.3V,所以当AD值为4095时,对应的电压为3.3V;当AD值为0时,对应的电压为0V。
这里的关系可以用一个简单的公式来表示,即:
引脚输入的电压 = 引脚 A D 的值 4095 × 3.3 V 引脚输入的电压 = \frac{引脚AD的值}{4095} \times 3.3V 引脚输入的电压=4095引脚AD的值×3.3V
如何使用ESP32-S3的ADC
1️⃣配置 ADC
该函数用于配置 ADC 各项参数,其函数原型如下所示:
esp_err_t adc_digi_controller_configure(const adc_digi_configuration_t *config)
该函数的形参描述如下:
形参 | 描述 |
---|---|
config | 指向 ADC 配置结构体的指针,需自行定义,并根据 ADC 的配置参数填充结构体中的成员变量 |
该函数的返回值描述如下:
返回值 | 描述 |
---|---|
ESP_OK | 返回:0,配置成功 |
ESP_ERR_INVALID_ARG | 无效参数 |
ESP_ERR_INVALID_STATE | 驱动程序状态无效 |
该函数使用 adc_digi_configuration_t
类型的结构体变量传入 ADC 外设的配置参数,该结构体的定义如下所示:
typedef struct {bool conv_limit_en;uint32_t conv_limit_num;uint32_t pattern_num;adc_digi_pattern_config_t *adc_pattern;uint32_t sample_freq_hz;adc_digi_convert_mode_t conv_mode;adc_digi_output_format_t format;
} adc_digi_configuration_t;
该函数的使用示例如下:
#include "driver/gpio.h"void example_fun(void)
{adc_digi_pattern_config_t adc1_digi_pattern_config; // 定义 ADC1 的模式配置结构体adc_digi_configuration_t adc1_init_config; // 定义 ADC1 的初始化配置结构体/* 配置 ADC1 */adc1_digi_pattern_config.atten = ADC_ATTEN_DB_11; // 设置衰减为 11dBadc1_digi_pattern_config.channel = ADC_ADCX_CHY; // 设置通道为 ADC_ADCX_CHYadc1_digi_pattern_config.unit = ADC_UNIT_1; // 设置单元为 ADC_UNIT_1adc1_digi_pattern_config.bit_width = ADC_BITWIDTH_12; // 设置位宽为 12 位adc1_init_config.adc_pattern = &adc1_digi_pattern_config; // 将 ADC1 的模式配置结构体赋值给初始化配置结构体的 adc_pattern 成员adc_digi_controller_configure(&adc1_init_config); // 调用 adc_digi_controller_configure 函数配置 ADC 数字控制器
}
当然,以下是整理后的格式:
2️⃣读取 ADC 原始数据
该函数用于读取 ADC 原始数据,其函数原型如下所示:
int adc1_get_raw(adc1_channel_t channel);
该函数的形参描述如下:
形参 | 描述 |
---|---|
channel | ADC 通道 |
该函数的返回值描述如下:
返回值 | 描述 |
---|---|
-1 | 读取失败 |
其他值 | ADC 的原始数值 |
该函数的使用示例如下:
uint32_t adc_get_result_average(uint32_t ch, uint32_t times)
{uint32_t temp_val = 0; // 定义一个临时变量用于存储 ADC 读取的值uint8_t t;for (t = 0; t < times; t++) // 循环 times 次{temp_val += adc1_get_raw(ch); // 读取 ADC 的原始值并累加到 temp_valvTaskDelay(5); // 延时 5ms}return temp_val / times; // 返回平均值
}
程序设计 💻
首先配置ADC以从通道7(即GPIO8)读取电压,然后将读取到的电压值打印到串口。
#include "driver/adc.h"
#include "esp_adc_cal.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"#define ADC_CHANNEL ADC_CHANNEL_7 // GPIO8
#define ADC_ATTEN ADC_ATTEN_DB_11
#define ADC_UNIT ADC_UNIT_1
#define ADC_WIDTH ADC_WIDTH_BIT_12
#define NUM_SAMPLES 64 // 多次采样
#define TAG "ADC"void example_fun(void)
{adc_digi_pattern_config_t adc1_digi_pattern_config; // 定义 ADC1 的模式配置结构体adc_digi_configuration_t adc1_init_config; // 定义 ADC1 的初始化配置结构体adc1_digi_pattern_config.atten = ADC_ATTEN_DB_11; // 设置衰减为 11dBadc1_digi_pattern_config.channel = ADC_CHANNEL; // 设置通道为 ADC_CHANNELadc1_digi_pattern_config.unit = ADC_UNIT_1; // 设置单元为 ADC_UNIT_1adc1_digi_pattern_config.bit_width = ADC_BITWIDTH_12; // 设置位宽为 12 位adc1_init_config.adc_pattern = &adc1_digi_pattern_config; // 将 ADC1 的模式配置结构体赋值给初始化配置结构体的 adc_pattern 成员adc_digi_controller_configure(&adc1_init_config); // 调用 adc_digi_controller_configure 函数配置 ADC 数字控制器
}uint32_t adc_get_result_average(uint32_t ch, uint32_t times)
{uint32_t temp_val = 0; // 定义一个临时变量用于存储 ADC 读取的值uint8_t t;for (t = 0; t < times; t++) // 循环 times 次{temp_val += adc1_get_raw(ch); // 读取 ADC 的原始值并累加到 temp_valvTaskDelay(5); // 延时 5ms}return temp_val / times; // 返回平均值
}void app_main(void)
{example_fun(); // 配置 ADCwhile (1) // 无限循环{uint32_t voltage = adc_get_result_average(ADC_CHANNEL, NUM_SAMPLES); // 读取 ADC 的平均值ESP_LOGI(TAG, "平均ADC读数: %d", voltage); // 将 ADC 的平均值打印到串口vTaskDelay(pdMS_TO_TICKS(1000)); // 延时 1s}
}
总结
参考资料
正点原子DNESP32S3 开发板教程-IDF 版
乐鑫ESP32-IDF