ADC
ADC介绍
ADC(ADC,Analog-to-Digital Converter) 全称为模拟-数字转换器,是一种用于将模拟信号转换为数字信号的模拟数字转换器。我们知道,模拟信号是连续的,其取值可以在一定范围内任意变化,如声音、光信号等。而数字信号则是离散的二进制信号,如计算机中的数据0和1,仅能取有限的值。
ADC的工作原理是将模拟信号通过采样
转换为离散的数字信号,然后再通过量化
、编码
等处理,最终得到对应的数字表示。ADC采样的频率越高
,得到的数字信号就越接近原来的模拟信号
,也就是保真度越高,但是需要更多的资源和计算功耗。
ADC 通常用于从外部模拟传感器中读取模拟信号,并将其转换为数字信号供嵌入式系统或计算机进行处理,例如测量温度、湿度、压力等物理量。
ADC的基本工作原理
采样
:ADC通过采样(Sampling)将连续的模拟信号在特定的时间间隔上进行采样,得到一系列离散的模拟值。
量化
:采样后的模拟信号被量化(Quantization)为有限个离散的数值。量化过程会引入一定的量化误差。
编码
:量化后的数值被编码(Encoding)为二进制数,形成数字信号输出。
关键参数
-
分辨率(Resolution)
:分辨率是指ADC输出的数字信号的位数。例如,8位、10位、12位、16位等。分辨率越高
,ADC的精度越高
。例如,12位ADC的分辨率为2^12 = 4096
,这意味着它可以将输入信号分为4096个不同的等级。
-
采样率(Sampling Rate)
:采样率是指ADC每秒钟采样的次数
,单位是赫兹(Hz)
。采样率越高,ADC能够捕捉的信号变化就越快
。根据奈奎斯特采样定理,采样率至少要是输入信号最高频率的两倍,以防止混叠(Aliasing)。 -
输入范围(Input Range)
:ADC能够接受的输入电压范围。例如,一些ADC的输入范围是0到3.3V或-1.5V到1.5V。 -
信噪比(SNR, Signal-to-Noise Ratio)
:信噪比是输入信号功率与噪声功率的比值。较高的信噪比表示ADC能够更准确地测量输入信号
,而不受噪声干扰。 -
总谐波失真(THD, Total Harmonic Distortion)
:THD是输入信号的谐波失真的度量,较低的THD
表示ADC对输入信号的失真较小
。 -
无失码(No Missing Codes)
:无失码表示ADC在其转换范围内,每一个可能的输出代码都可以在某个输入电压范围内产生
。
ADC的类型
-
逐次逼近寄存器(SAR, Successive Approximation Register)ADC
:这种类型的ADC通过逐步逼近的方法,将输入信号与DAC生成的电压进行比较,逐步逼近最终的数字值。SAR ADC通常具有中等的分辨率和采样率,适用于很多通用应用。
-
ΔΣ(Delta-Sigma)ADC
:ΔΣ ADC通过超采样和噪声整形来实现高分辨率。它们通常具有很高的分辨率
,但采样率较低
,适用于高精度的测量。
-
流水线(Pipeline)ADC
:这种类型的ADC通过多级逐步逼近和并行处理实现高速和高分辨率
。它们适用于高速信号处理,如视频和通信
。 -
闪存(Flash)ADC
:Flash ADC通过并行比较器将输入信号与所有可能的电压级别进行比较
,实现非常高的采样率
,但其分辨率和功耗较高
。
应用领域
- 音频处理:如音频录音和数字音频工作站。
- 传感器接口:如温度传感器、压力传感器和加速度计。
- 通信:如无线电接收和数字通信。
- 医疗设备:如心电图(ECG)和脑电图(EEG)。
- 工业自动化:如过程控制和数据采集系统。
ESP32的ADC介绍
笔者使用的是ESP32-Wroom-32的开发板
ESP32-Wroom-32的通道如下图所示:
查阅其他资料时发现ESP32-S3的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 负载的变化而变化。一般来讲,芯片内部温度会高于外部温度。
ESP32-S3 的参数及其属性
使用ADC
包含相关头文件
#include "esp_adc_cal.h"
#include "driver/adc.h"
第一个是配置adc1的精度,esp32中一共有俩adc,分别是adc1和adc2,按理说我们都可以用。但是我们最好就是用adc1,每个adc有8个通道,所以一个也是够用的。
至于为什么不用adc2,这是因为adc2和WiFi是冲突的,而WiFi基本上是咱物联网的标配,因此为了避免冲突,咱就是用adc1。
配置ADC参数
通过调用函数adc1_config_width()
和adc1_config_channel_atten()
配置所需精度
和衰减
。
adc1_config_width()
adc1_config_width
是 ESP32 中的 ADC1 模块的配置函数之一,它用于设置 ADC1 的精度(位数)。通过这个函数,你可以将 ADC1 的精度设置为 9 位、10 位、11 位或 12 位。
对应分辨率,分辨率越高
,ADC的精度越高
。例如,12位ADC的分辨率为2^12 = 4096
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_config_channel_atten()
adc1_config_channel_atten()
是 ESP32 中的 ADC1 模块配置函数之一,它用于设置 ADC1 特定通道的衰减参数。衰减参数,衰减越大,可接受的电压越大。
void adc1_config_channel_atten(adc1_channel_t channel, adc_atten_t atten);
第一个参数选择通道,我们知道ESP32-Wroom-32 ADC1都有8个通道,因此可选的有8个。
- channel:要配置的 ADC1 通道,类型为 adc1_channel_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。
这个根据自己手上的传感器的电气参数来选择,不清楚的话就先选11db的,这样就算电压范围不一样也不会导致板子烧坏。
读取ADC的值
- 使用
adc1_get_raw
函数来获取 ADC
值。
adc1_get_raw
是 ESP32 中的 ADC1 模块函数之一,用于以原始 12 位 ADC 值的形式读取 ADC1 的指定通道的测量值。
函数原型如下:
int adc1_get_raw(adc1_channel_t channel);
代码
myADC.c
#include <string.h>
#include <stdio.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"
#include "myADC.h"
#define DEFAULT_VREF 1100 // 默认参考电压,单位mV
static esp_adc_cal_characteristics_t *adc_chars; // 分配内存使用
#define myADC_heat_sensitive_channel ADC_CHANNEL_6 // GPIO34 热敏传感器Ao输入
#define myADC_light_channel ADC_CHANNEL_7 // GPIO35 光敏传感器Ao输入
#define AD_Value1_Channnel myADC_heat_sensitive_channel // 重新定义一个, 为例后续更好的更改
#define AD_Value2_Channnel myADC_light_channel // 重新定义
#define width ADC_WIDTH_BIT_12 // ADC分辨率 12位
#define atten ADC_ATTEN_DB_11 // ADC衰减 11 dB 衰减,输入电压范围为 0-3.3V。
#define unit ADC_UNIT_1 // ADC1
#define NO_OF_SAMPLES 128 // 采样次数, 目的: 多次采样, 滤波
/**
* @description: ADC初始化
* @return {无}
*/
void myADC_Init() {
adc1_config_width(ADC_WIDTH_BIT_12); // 12位分辨率
adc1_config_channel_atten(AD_Value1_Channnel, atten); // 配置ADC通道
adc1_config_channel_atten(AD_Value2_Channnel, atten); // 配置ADC通道
adc_chars = calloc(1, sizeof(esp_adc_cal_characteristics_t)); // 分配内存
esp_adc_cal_characterize(unit, atten, width, DEFAULT_VREF, adc_chars); // 对ADC特性进行初始化
}
/**
* @description: 获取传感器的AD值和电压值
* @param {int16_t} *AD_Value1 第一个传感器的AD值的地址
* @param {int16_t} *AD_Value2 第二个传感器的AD值的地址
* @param {float*} Voltage1 第一个传感器的电压值的地址
* @param {float*} Voltage2 第二个传感器的电压值的地址
* @return {*}
*/
void myADC_GetAdAndVoltage_Value(int16_t *AD_Value1, int16_t *AD_Value2, float* Voltage1, float* Voltage2) {
uint32_t adc_reading1 = 0;
uint32_t adc_reading2 = 0;
for (int i = 0; i < NO_OF_SAMPLES; i++) { // 多次采样, 滤波
adc_reading1 += adc1_get_raw(AD_Value1_Channnel); // 采集热敏传感器Ad值
adc_reading2 += adc1_get_raw(AD_Value2_Channnel); // 采集光照值
}
adc_reading1 /= NO_OF_SAMPLES; // 得到滤波后的数据
adc_reading2 /= NO_OF_SAMPLES; // 得到滤波后的数据
// esp_adc_cal_raw_to_voltage返回的是mV, 想得到单位V, 除以1000
*AD_Value1 = (int16_t)adc_reading1; // 转换为int16_t类型
*AD_Value2 = (int16_t)adc_reading2; // 转换为int16_t类型
*Voltage1 = esp_adc_cal_raw_to_voltage(adc_reading1, adc_chars) / 1000.0; // 获取热敏传感器电压值
*Voltage2 = esp_adc_cal_raw_to_voltage(adc_reading2, adc_chars) / 1000.0; // 获取光照电压值
}
myADC.h
#ifndef _MYADC__H
#define _MYADC__H
#include <stdint.h>
void myADC_Init();
// 获取ADC值和电压值
void myADC_GetAdAndVoltage_Value(int16_t *AD_Value1, int16_t *AD_Value2, float* Voltage1, float* Voltage2);
#endif
main.c
#include <stdio.h>
#include "myADC.h"
#include <freeRTOS/FreeRTOS.h>
#include <esp_log.h>
int16_t heat_sensitive_AdVal, light_AdVal;
float heat_sensitive_Voltage, light_Voltage;
void app_main(void)
{
myADC_Init();
while (1)
{
myADC_GetAdAndVoltage_Value(&heat_sensitive_AdVal, &light_AdVal, &heat_sensitive_Voltage, &light_Voltage);
ESP_LOGI("main","热敏传感器的AD值: %d\t 光敏的AD: %d \n",heat_sensitive_AdVal,light_AdVal);
ESP_LOGI("main","热敏传感器电压值: %.2f\t 光敏电压值: %.2f ",heat_sensitive_Voltage,light_Voltage);
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
项目结构
项目调试