跳转至

ADC 模数转换器

概要

本文讲解了ADC的概念,以及MicroPython-ESP32中的ADC实现, 最后在综合例程里面,使用ADC采集点位计的数值,然后通过PWM控制LED的亮度。

keywords ADC Encoder PWM LED sample

ADC是什么

ADC的英文全称是Analog / Digital Converter,是将模拟信号转换为数字信号的转换器,ADC是单片机读取传感器信号的常见方式。

GPIO输出与控制LED亮灭这一章里面,我们知道了数字信号与模拟信号之间的区别。 我们日常生活中的信号,例如光照强度,声波,电池电压 这些都是模拟值。 如果我们想通过单片机对模拟信号(电压,光照强度,声波)进行测量,用数字信号进行表达,这个时候我们就需要ADC 模拟数字信号转换器。

ADC-常用API

硬件资源

在ESP32里面ADC一共有两组,分为ADC 1 还有ADC 2, 其中又因为ESP32的ADC2不能与WIFI联网同时存在,当连接WIFI的时候ADC 2的管脚就不能进行ADC采样,所以MicroPython-ESP32固件里面,并没有实现对ADC 2的支持,大家看引脚图的时候留意一下这个问题。

NodeMCU-32S的32-39管脚具备ADC功能:

GPIO编号 ADC编号 通道编号
39 1 3
36 1 0
35 1 7
33 1 5
34 1 6
32 1 4

为了方便大家和实际的管脚布局进行比对,我们1Z实验室为大家制作了下图:

此图待ZR PS

实例化

machine模块里面导入两个类 Pin 还有ADC

from machine import Pin,ADC

第一步还是创建一个Pin的对象:

adc_pin = Pin(34)

解下来创建一个ADC的对象,直接把pin对象传入到ADC的构造器里面。

adc = ADC(adc_pin)

设置衰减比

衰减器在功能上和放大器相反,放大器提供大于1的增益,衰减器提供小于1的增益。原理上衰减器由简单的分压电路组成。

ADC采样有两个重要的参数,一个是满量程电压是多少,显然不可能超过ESP32的管脚电压3.3v. 可以设定0-3.3v之间的一个值。

宏定义 数值 满量程电压
ADC.ATTN_0DB 0 1.2v
ADC.ATTN_2_5DB 1 1.5v
ADC.ATTN_6DB 2 2.0v
ADC.ATTN_11DB 3 3.3v

演示样例:

adc.atten(ADC.ATTN_11DB)

设置分辨率

ADC的分辨率是指能够将采集的模拟信号转化为数字信号的精度,通常我们用“位”来表述,比如8位就是指ADC可以将制定量程内的电压信号,分别对应到0 - 2^8-1,即 0-255这256个数字上。分辨率位数越高,能够表示的也就越精确,信息丢失的也就越少。

再举个例子,如果分辨率是10位,那么ADC采样的取值范围就是0 - 1023.

1023 = 2^10 - 1

宏定义 数值 分辨率
ADC.WIDTH_9BIT 0 9
ADC.WIDTH_10BIT 1 10
ADC.WIDTH_11BIT 2 11
ADC.WIDTH_12BIT 3 12

ADC采样

使用read函数进行ADC采样

adc.read()

ADC采集电位计的数据

如图便是一个简单的电位计。 电位计

注意

这种电位计很多都有上下两排引脚,按照上图所标注的,选择一排接线即可

将电位计的OUT管脚与ESP32的34号引脚相连。以下是笔者为大家画出的面包板接线图:

按照上面的接线,我们编写如下的代码配置好一个ADC:

from machine import ADC,Pin
import utime


adc = ADC(Pin(34)) # 声明ADC对象 设置D34号引脚作为ADC采样引脚
adc.atten(ADC.ATTN_11DB) # 设置衰减比 满量程3.3v
adc.width(ADC.WIDTH_10BIT) # 设置数据宽度为10bit

运行以上的代码,我们便可以使用read()函数来读取电位计的数值了,你可以试着转动你的电位计到某一个位置,看看读数:

你是不是也发现了,尽管你没有碰电位计了,但是每次的ADC采样,得到的读数都不一样。在模拟信号转化为数字信号的过程中,会存在诸多的因素导致信号中可能存在噪声,使得结果带有一定的误差,因此我们需要一些方法或手段来减小这种误差对我们带来的负面作用,接下来就为大家介绍最朴素的均值滤波。

ADC采样均值滤波

检测ADC采样值是否发生变化,如果发生变化就打印出来, 采用均值滤波对噪声进行过滤。 所谓均值滤波就是多次采集数据然后求取平均值。

原理如此简单,那我们就尝试着写一个均值滤波的函数吧

def adc_mean(adc, sample_times):
    # 注意,这行代码很不适合给Python新手观看(属于Python装逼炫技)
    # 因为普通的循环累加再求平均,谁都会写
    # 所以笔者在这里使用Python的列表推倒式来完成这些事
    return int(sum([adc.read() for i in range(sample_times)])/sample_times)

简单的解释:

  • sum()函数,用于求和,可以接受列表作为参数, 返回求和之后的值

  • [adc.read() for i in range(sample_times)] 这时Python的列表推倒式,返回一个列表,列表中的元素为10次循环中每次adc读取的值。

挑战:电位计控制LED亮度

现在你已经学习完了定时器,延时函数以及最基本的ESP32的GPIO控制,PWM等内容,为什么不尝试尝试使用电位计来控制控制LED灯的亮度呢?