## ESP32 MicroPython ADC功能详解
### 一、ADC基本原理与配置
ESP32的ADC(模数转换器)用于将模拟电压信号转换为数字值。在MicroPython中,ADC功能通过`machine.ADC`模块实现,支持12位分辨率(0-4095)[ref_2]。
| 特性 | 说明 |
|------|------|
| 分辨率 | 12位(默认),可通过`ADC.width()`调整 |
| 参考电压 | 通常为3.3V(ESP32内部参考) |
| 衰减设置 | 控制输入电压范围,避免损坏ADC引脚 |
**基本配置代码示例:**
```python
from machine import ADC, Pin
# 1. 创建ADC对象(以GPIO34为例)
adc_pin = Pin(34, Pin.IN) # 配置为输入模式
adc = ADC(adc_pin) # 创建ADC对象
# 2. 设置衰减(输入电压范围)
# 可选值:ADC.ATTN_0DB(0-1.1V), ADC.ATTN_2_5DB(0-1.5V),
# ADC.ATTN_6DB(0-2.2V), ADC.ATTN_11DB(0-3.3V)
adc.atten(ADC.ATTN_11DB) # 设置11dB衰减,支持0-3.3V输入
# 3. 设置分辨率(ESP32支持9-12位)
adc.width(ADC.WIDTH_12BIT) # 12位分辨率(默认)
# 4. 读取ADC值
value = adc.read() # 返回0-4095的原始值
voltage = value * 3.3 / 4095 # 转换为电压值
print(f"ADC原始值: {value}, 电压: {voltage:.2f}V")
```
### 二、ADC引脚分布与通道映射
ESP32不同型号的ADC引脚分布有所差异,以下是常见型号的ADC通道对应关系:
| ESP32型号 | ADC1通道 | ADC2通道 | 可用引脚(示例) |
|-----------|----------|----------|------------------|
| ESP32-S3 | 0-9 | 0-9 | GPIO1-10, GPIO11-20 |
| ESP32 | 0-7 | 0-9 | GPIO32-39(ADC1), GPIO0,2,4,12-15,25-27(ADC2) |
| 注意 | ADC2在WiFi活动时不可用 | ADC1始终可用 | 具体以开发板手册为准 |
**引脚选择注意事项:**
1. **ADC2限制**:当WiFi功能启用时,ADC2通道无法使用[ref_1]
2. **专用ADC引脚**:部分引脚(如GPIO34-39)仅支持输入模式,不能配置为输出
3. **电源引脚**:避免将ADC连接到VCC(3.3V)或GND以外的电源引脚
### 三、实际应用示例:电池电量检测
以下是一个完整的电池电量检测系统实现,包含电压读取、电量计算和显示功能[ref_2][ref_6]:
```python
from machine import ADC, Pin, Timer
import time
class BatteryMonitor:
def __init__(self, adc_pin=34, voltage_divider_ratio=2.0):
"""
初始化电池监测器
:param adc_pin: ADC引脚编号
:param voltage_divider_ratio: 分压电阻比例(R1+R2)/R2
"""
self.adc = ADC(Pin(adc_pin))
self.adc.atten(ADC.ATTN_11DB) # 0-3.3V范围
self.adc.width(ADC.WIDTH_12BIT) # 12位分辨率
self.ratio = voltage_divider_ratio
self.full_voltage = 4.2 # 锂电池满电电压
self.empty_voltage = 3.0 # 锂电池最低电压
def read_voltage(self, samples=10):
"""读取平均电压值,减少噪声影响"""
total = 0
for _ in range(samples):
total += self.adc.read()
time.sleep_ms(5) # 采样间隔
raw_value = total / samples
# 转换为实际电压(考虑分压电路)
measured_voltage = (raw_value * 3.3 / 4095) * self.ratio
return measured_voltage
def calculate_percentage(self, voltage):
"""根据电压计算电量百分比"""
if voltage >= self.full_voltage:
return 100
elif voltage <= self.empty_voltage:
return 0
else:
# 线性估算(实际应用中应根据电池放电曲线调整)
percentage = ((voltage - self.empty_voltage) /
(self.full_voltage - self.empty_voltage)) * 100
return max(0, min(100, percentage))
def get_battery_info(self):
"""获取完整的电池信息"""
voltage = self.read_voltage()
percentage = self.calculate_percentage(voltage)
return {
"voltage": voltage,
"percentage": percentage,
"status": "充电中" if voltage > 4.1 else "正常"
}
# 使用示例
def main():
# 初始化电池监测(假设使用分压电路,比例2:1)
battery = BatteryMonitor(adc_pin=34, voltage_divider_ratio=2.0)
# 定时检测(每5秒一次)
def check_battery(timer):
info = battery.get_battery_info()
print(f"电池电压: {info['voltage']:.2f}V")
print(f"剩余电量: {info['percentage']:.1f}%")
print(f"状态: {info['status']}")
print("-" * 30)
# 创建定时器(每5秒执行一次)
timer = Timer(0)
timer.init(period=5000, mode=Timer.PERIODIC, callback=lambda t: check_battery(t))
# 保持程序运行
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
timer.deinit()
print("监测停止")
if __name__ == "__main__":
main()
```
### 四、常见问题与解决方案
| 问题现象 | 可能原因 | 解决方案 |
|----------|----------|----------|
| ADC读数不稳定 | 电源噪声、信号干扰 | 1. 增加滤波电容(0.1μF)<br>2. 软件多次采样取平均<br>3. 使用`ADC.ATTN_11DB`衰减 |
| 读数始终为0或4095 | 引脚配置错误、电压超出范围 | 1. 检查引脚是否支持ADC<br>2. 确认电压在0-3.3V范围内<br>3. 检查衰减设置是否合适 |
| ADC2无法使用 | WiFi功能冲突 | 1. 优先使用ADC1通道<br>2. 在读取ADC2前暂时关闭WiFi<br>3. 使用`adc2_read()`专用函数 |
| 精度不足 | 参考电压波动、分辨率设置 | 1. 使用外部基准电压源<br>2. 设置`ADC.width(ADC.WIDTH_12BIT)`<br>3. 校准ADC(如有校准参数) |
**精度优化代码示例:**
```python
def read_adc_with_filter(adc_pin, samples=100, interval_ms=10):
"""带滤波的ADC读取函数"""
adc = ADC(Pin(adc_pin))
adc.atten(ADC.ATTN_11DB)
# 丢弃前几个不稳定读数
for _ in range(5):
adc.read()
time.sleep_ms(interval_ms)
# 多次采样取中位值
values = []
for _ in range(samples):
values.append(adc.read())
time.sleep_ms(interval_ms)
# 排序并取中间部分平均值(去除异常值)
values.sort()
start = samples // 4
end = 3 * samples // 4
filtered_values = values[start:end]
return sum(filtered_values) / len(filtered_values)
# 使用优化后的读取函数
stable_value = read_adc_with_filter(34, samples=50, interval_ms=5)
print(f"滤波后ADC值: {stable_value}")
```
### 五、高级应用:多通道ADC与传感器集成
ESP32支持多通道ADC读取,可以同时监测多个模拟传感器:
```python
from machine import ADC, Pin
import ujson
class MultiSensorSystem:
def __init__(self):
# 初始化多个ADC通道
self.sensors = {
"temperature": ADC(Pin(32)), # 温度传感器
"light": ADC(Pin(33)), # 光敏电阻
"potentiometer": ADC(Pin(34)), # 电位器
"battery": ADC(Pin(35)) # 电池电压
}
# 统一配置所有ADC
for sensor in self.sensors.values():
sensor.atten(ADC.ATTN_11DB)
sensor.width(ADC.WIDTH_12BIT)
def read_all(self):
"""读取所有传感器数据"""
data = {}
for name, adc in self.sensors.items():
# 每个通道读取3次取平均
values = [adc.read() for _ in range(3)]
avg_value = sum(values) / len(values)
voltage = avg_value * 3.3 / 4095
# 根据传感器类型转换(示例)
if name == "temperature":
# NTC热敏电阻转换公式(需根据具体型号调整)
data[name] = self.convert_to_temperature(voltage)
elif name == "light":
# 光强转换(示例)
data[name] = voltage * 100 # 百分比
else:
data[name] = voltage
return data
def convert_to_temperature(self, voltage):
"""电压转温度(示例函数)"""
# 实际应用中应根据传感器数据手册实现
# 这里使用简化线性转换
return (voltage - 0.5) * 100 # 示例公式
def log_to_file(self, filename="sensor_log.json"):
"""记录传感器数据到文件"""
data = self.read_all()
data["timestamp"] = time.time()
# 读取现有数据
try:
with open(filename, "r") as f:
existing = ujson.load(f)
except:
existing = []
# 添加新数据(限制最多100条)
existing.append(data)
if len(existing) > 100:
existing = existing[-100:]
# 保存数据
with open(filename, "w") as f:
ujson.dump(existing, f)
return data
# 系统使用示例
sensor_system = MultiSensorSystem()
# 定时采集数据
while True:
sensor_data = sensor_system.read_all()
print("传感器数据:", sensor_data)
sensor_system.log_to_file()
time.sleep(60) # 每分钟采集一次
```
### 六、注意事项与最佳实践
1. **电源稳定性**:ADC参考电压来自内部LDO,电源波动会影响精度。对于高精度应用,建议使用外部基准源[ref_6]。
2. **分压电路设计**:测量高于3.3V的电压时,必须使用分压电路。典型设计:
```
Vin --[R1]--+--[R2]--GND
|
ADC引脚
分压比 = (R1 + R2) / R2
```
3. **WiFi与ADC2的冲突**:当需要同时使用WiFi和ADC2时,可以采用时间分片策略:
```python
import network
def read_adc2_with_wifi(adc2_pin):
# 暂时禁用WiFi
wifi = network.WLAN(network.STA_IF)
wifi.active(False)
# 快速读取ADC2
adc = ADC(Pin(adc2_pin))
value = adc.read()
# 恢复WiFi
wifi.active(True)
return value
```
4. **校准与补偿**:ESP32 ADC存在非线性误差,可通过两点校准法改善:
```python
def calibrate_adc(adc_pin, known_voltage1, known_voltage2):
"""两点校准法"""
# 在已知电压点1测量
input("连接已知电压1,按回车继续")
raw1 = adc.read()
# 在已知电压点2测量
input("连接已知电压2,按回车继续")
raw2 = adc.read()
# 计算校准参数:y = kx + b
k = (known_voltage2 - known_voltage1) / (raw2 - raw1)
b = known_voltage1 - k * raw1
return lambda raw: k * raw + b
```
5. **抗干扰措施**:
- 在ADC引脚添加0.1μF陶瓷电容到GND
- 使用屏蔽线连接模拟信号源
- 避免ADC引脚靠近数字信号线
- 在软件中实现数字滤波(移动平均、中值滤波等)