ESP32-S3 读取ADC

ESP32-S3 的 ADC(模数转换器)核心原理是将连续变化的模拟电压,转换为离散的数字值,供数字系统(如CPU)处理。其内部采用了逐次逼近型(SAR) 架构,在转换速度和精度之间取得了良好平衡。

提醒:进入博主 牛瘪汤 主页了解更多。



1. ADC 转换介绍

ESP32-S3 的 ADC(模数转换器)核心原理是将连续变化的模拟电压,转换为离散的数字值,供数字系统(如CPU)处理。其内部采用了逐次逼近型(SAR) 架构,在转换速度和精度之间取得了良好平衡。

ESP32-S3 的 ADC集成了2个12位SAR ADC (ADC1, ADC2),共20个通道。ADC将输入的模拟电压(0~参考电压/衰减电压)量化为数字值。默认12位分辨率对应0~4095的数值范围。分辨率(9~12位可调)、采样率(最高2.5 MS/s)、输入范围(通过衰减器可扩展至0~3.3V左右)。

⚙️ ADC转换的核心原理

ADC转换的核心是将模拟电压量化为数字。在ESP32-S3上,这个过程主要遵循以下步骤:

  1. 采样与量化:ADC在每个采样周期内,对输入引脚的模拟电压进行测量。默认情况下,它使用12位分辨率,这意味着它会将测量范围精细地划分为 4096 (2¹²) 个离散的电压等级。因此,读取到的原始值(通常称为 raw_data)就在 0 到 4095 之间。

  2. 数字值到电压的换算:读取到的原始值本身只是一个数字,要将其转换为实际的电压值,需要参考一个关键参数——参考电压(( V_{ref} ))。在ESP32-S3的设计中,内部参考电压 ( V_{ref} ) 被设定为 1100 mV (1.1V) 。在不使用衰减的情况下,转换公式如下:
    [ V_{data} = \frac{data}{2^{bitwidth} - 1} \times V_{ref} ]
    举例来说,如果你使用12位分辨率,读取到的原始值是2048,那么对应的电压大约是 ( 2048 / 4095 \times 1100 \text{ mV} \approx 550 \text{ mV} )。

重要说明:由于芯片制造过程中的微小差异,每颗ESP32-S3的实际 ( V_{ref} ) 值可能在1000 mV到1200 mV之间浮动。这正是官方驱动提供校准功能的原因,通过校准可以获得更精确的电压读数。

🔧 关键机制详解

为了让ADC适应不同的应用场景,ESP32-S3引入了几个关键机制。

1. 衰减(Attenuation):扩展测量范围

从上面的原理我们知道,如果不做任何处理,ADC的最大测量范围就是0~1.1V。为了能测量更高的电压(比如常见的3.3V传感器信号),就必须使用衰减器。衰减器本质上是在ADC内部对输入信号进行按比例缩减,让高电压也能落在ADC的测量范围内。

ESP32-S3提供了四种衰减级别,对应的测量范围如下表所示:

衰减设置可测量电压范围(近似)适用场景
0 dB0 ~ 1.1 V测量1.1V以下的信号,精度最高
2.5 dB0 ~ 1.5 V测量1.5V以下的信号
6 dB0 ~ 2.2 V测量2.2V以下的信号
11 dB0 ~ 3.3 V测量3.3V以下的信号(最常用)

注意:虽然11dB衰减支持到3.3V,但实际应用中,为了留出余量和保证线性度,官方文档建议将可靠测量的上限视为约3.1V左右。绝对不要输入超过3.3V的电压,否则可能损坏芯片。

2. 两种工作模式:单次与连续

根据你的应用需求,可以选择不同的驱动模式:

  • 单次模式(Oneshot Mode):适用于一次性或低频采样。比如,每秒钟读取一次温度传感器的值。这种模式下,每次转换都由CPU发起,转换完成后读取结果。
  • 连续模式(Continuous Mode):适用于高频、连续的数据采集,比如音频采样或振动分析。此模式会利用DMA(直接内存访问) 技术,将ADC转换结果自动写入内存,无需CPU频繁干预,极大提高了数据吞吐量和效率。
3. 校准(Calibration):获得精确电压

如前所述,由于制造差异,直接用公式计算电压可能会有误差。ESP-IDF(乐鑫的物联网开发框架)提供了ADC校准驱动。它会读取芯片出厂时烧录在eFuse(一种存储器)中的特征值,通过软件算法自动修正原始读数,从而输出更准确的电压值(单位可以是mV或μV)。在要求稍高的场景,建议都使用校准功能。

2. 读取 ADC 电压

Arduino环境提供了非常简便的API,analogReadMilliVolts() 函数更是直接帮你完成了校准,返回毫伏单位的电压值,非常推荐。

// Arduino 示例
const int adcPin = 7; // 使用GPIO7,它对应ADC1的通道

void setup() {
  Serial.begin(115200);
  // 在Arduino中,analogRead默认已配置为12位和11dB衰减,
	analogReadResolution(adcPin);
}

void loop() {
  int adcValue = analogRead(adcPin);           // 读取原始值 (0-4095)
  int voltage_mV = analogReadMilliVolts(adcPin); // 读取校准后的电压值 (mV)

  Serial.print("ADC Raw: ");
  Serial.print(adcValue);
  Serial.print(", Voltage: ");
  Serial.print(voltage_mV);
  Serial.println(" mV");
  delay(500);
}

analogRead(pin):最简单的读取方式,返回0-4095的原始值 。
analogReadMilliVolts(pin):ESP32的增强函数,可以直接返回校准后的毫伏电压值,推荐使用 。
analogSetAttenuation(attenuation):用于设置全局的衰减值 。

3. 读取多组 ADC 电压

确保ESP32-S3开发板已连接Arduino IDE。多通道ADC读取需要明确每个通道对应的GPIO引脚。ESP32-S3的ADC1和ADC2支持多路复用,但需注意部分引脚可能受限制。

// 定义ADC通道对应的GPIO引脚
const int adcPins[] = {1, 2, 3, 4}; // 示例引脚,根据实际硬件调整
const int numChannels = sizeof(adcPins) / sizeof(adcPins[0]);

// 使用滑动平均滤波
const int samples = 5;
int adcReadWithFilter(int pin) {
  int sum = 0;
  for (int i = 0; i < samples; i++) {
    sum += analogRead(pin);
    delay(2);
  }
  return sum / samples;
}

void setup() {
  Serial.begin(115200);
  analogReadResolution(12);
  // 可选:设置衰减器(根据输入电压范围)
  analogSetAttenuation(ADC_11db); // ADC_0db, ADC_2_5db, ADC_6db, ADC_11db
}

void loop() {
  for (int i = 0; i < numChannels; i++) {
    int filteredValue = adcReadWithFilter(adcPins[i]);
    Serial.printf("Channel %d: %d\n", adcPins[i], filteredValue);
  }
  delay(200);
}

4. 触摸按键

ESP32-S3的ADC触摸功能通过内置的触摸传感器通道实现,需调用touchRead()函数读取原始电容值。ESP32-S3支持多个触摸通道(TOUCH0-TOUCH14),无触摸时,数值较高(通常>50,具体与环境电容相关);触摸时:数值显著下降(可能<30);阈值需通过实验校准,不同硬件布局会影响基准值。

触摸按键硬件设计。无需外部电路,直接触碰GPIO焊盘或连接的金属部分即可。若需稳定检测,可增加导电面积(如铜箔)。

#include <Arduino.h>

// 定义触摸传感器引脚(根据ESP32-S3开发板手册选择)
#define TOUCH_PIN 4  // 示例:GPIO4作为触摸输入

// 添加滤波和去抖
#define SAMPLES 5
int getFilteredTouchValue(int pin) {
  int sum = 0;
  for (int i = 0; i < SAMPLES; i++) {
    sum += touchRead(pin);
    delay(2);
  }
  return sum / SAMPLES;
}

void setup() {
  Serial.begin(115200);
  delay(1000);
  Serial.println("ESP32-S3 Touch Sensor Test");
}

void loop() {
  int touchValue = touchRead(TOUCH_PIN);  // 读取原始电容值
  int filteredValue = getFilteredTouchValue(TOUCH_PIN);
  Serial.printf("Touch Value: %d\n", touchValue);
  Serial.printf("Touch Filter Value: %d\n", filteredValue );

  // 判断触摸阈值(需根据实际环境调整)
  if (touchValue < 20) {  // 值越小表示触摸越强
    Serial.println("Touched!");
  }
  delay(200);
}

💡 提高ADC读数准确性的实用技巧

在实际应用中,你可能会发现ADC读数会有些跳动,这是由噪声引起的。可以尝试以下方法改善:

  • 硬件滤波:在ADC输入引脚和GND之间并联一个0.1uF(100nF)的小电容,可以有效滤除高频噪声。
  • 软件滤波:在代码中对同一通道进行多次采样,然后取平均值。这是最简单有效的软件滤波方法。

结语

文章都看到完了,就不点个关注吗,老铁!
欢迎关注博主–牛瘪汤

更多推荐