摘要:本文主要介绍如何使用ESP32的ADC功能,读取物联网智能小车的电池电压
今天介绍一个ESP32的新功能——如何利用ESP32的引脚,采集模拟量信息。在前面GPIO的学习中我们知道,可以利用GPIO的引脚读取外部设备输出的高低电平信号,这种只有2种高低电平的信号是数字信号,而具有连续数值的信号则属于模拟信号,比如我们今天要读取的电源电压就属于模拟信号。
ADC(Analog to Digital Converter:模数转换器)的功能就是将外部设备的模拟电压信号,转换成数字量,从而让单片机能够识别和进行处理。在前面GPIO学习的时候,已经强调过了,虽然除了电源引脚之外的引脚都是GPIO引脚,但有些引脚只能输入,有些引脚已经被特殊功能给占用了,开发者是不能使用的。ADC功能同样如此,也是需要注意有哪些引脚可以使用,具体的对应关系如下表所示:
GPIO | 模拟功能 | 注释 |
GPIO0 | ADC2_CH1 | Strapping 管脚 |
GPIO2 | ADC2_CH2 | Strapping 管脚 |
GPIO4 | ADC2_CH0 | |
GPIO12 | ADC2_CH5 | Strapping 管脚;JTAG |
GPIO13 | ADC2_CH4 | JTAG |
GPIO14 | ADC2_CH6 | JTAG |
GPIO15 | ADC2_CH3 | Strapping 管脚;JTAG |
GPIO25 | ADC2_CH8 | |
GPIO26 | ADC2_CH9 | |
GPIO27 | ADC2_CH7 | |
GPIO32 | ADC1_CH4 | |
GPIO33 | ADC1_CH5 | |
GPIO34 | ADC1_CH6 | GPI |
GPIO35 | ADC1_CH7 | GPI |
GPIO36 | ADC1_CH0 | GPI |
GPIO37 | ADC1_CH1 | GPI |
GPIO38 | ADC1_CH2 | GPI |
GPIO39 | ADC1_CH3 | GPI |
通过上表可以看到,ESP32芯片有两个ADC单元,分别是ADC1和ADC2,ADC单元就相当于可以独立完成ADC功能的机器,而这两个ADC单元可以工作在不同的工作模式,两者不会互相影响。
上表中一共列举了18个GPIO引脚,这就相当于给ADC单元供货的18个传送带,它们负责把外部的模拟信号,传递给ADC单元,这18个传送带的学名叫通道(Channel),与ADC1连接的有8个通道,分别命名为CH0、CH1、CH2……直到CH7。与ADC2连接的有10个通道,分别命名为CH0、CH1、CH2……直到CH9。所以,在将一个模拟信号接入到GPIO引脚的时候,一定要知道连接的是哪一个通道以及哪一个ADC单元。关于ADC通道引脚,有以下几个事项需要特别注意:
ESP32这两个ADC都是12位SAR(逐次逼近)ADC。也就是最高的精度为12位,即为 2^12 = 4096,先记住4096这个数字,后边会进一步的讲解。在实际使用中,也可以将ADC的精度通过软件设置位11位、10位或者9位。
ESP32的ADC单元有两种工作模式:
好了,ADC功能先基本介绍到这里了,可能初次接触的一下觉得有些复杂,不好理解。下面就来看一下我们是如何实现电源电压采集的吧,使用的方法还是很简单的。
在上边已经介绍过了,ESP32的ADC通道引脚最多支持3.3V的输入电压,而我们的电源电压是12V左右的,是不能直接连接到GPIO引脚的。在这里我们就需要使用到分压模块,该模块的作用就是将电压分压到原来的五分之一,样子如下图所示,这样我们的电源电压就转换成了0~3V以内了。没有这个模块的,可以直接按照串联电阻分压原理,找2个合适的电阻,自己搭建一个也是可以的。
这个分压模块的原理如下图所示。
根据原理图,我们只需要把左侧的VCC连接电池的正极,GND连接电池的负极。右侧的“-”端连接到ESP32开发板的GND引脚,“S”端连接到ESP32的P35引脚(也可以使用别的引脚)就可以了。
接下来就可以使用Mixly进行开发读取电源电压值的程序了。在这个功能中,我们主要用到了“输入/输出”功能模块区的“模拟输入”功能,该功能可以直接将指定引脚的电压信号转换成数字信号。我们在程序中将这个采集的数字输出出来,就可以计算出我们电池的电源电压了。最简单的读取电压的程序如下图所示:
下面就运行一下,看看输入结果吧。我的输出结果如下图所示:
我们看到,每一次的输出,都是一个4位数的整数,那么如何转换成我们想要的电压值呢?这就要用到我们前面介绍的ADC转换精度的知识了。默认情况下,ADC使用的是12位的转换精度,那么转换的结果就是0~4095,也就是说0对应0V电压,4095对应3.3V的电压。那么把采集的数除以4095,然后再乘以3.3V就是GPIO引脚的输入电压了。最后别忘了,我们使用了分压电路,计算的结果还要再乘以5才是真正的电源电压。具体计算公式如下所示:
2855/4095*3.3*5=11.5
这就说明,我们电源的电压是11.5V。是不是很简单?你成功了吗?
最后,我们再看一下Mixly的源代码,了解一下用程序代码是如何工作的吧。
void setup(){ ??Serial.begin(115200); } void loop(){ ??Serial.println(analogRead(35)); } |
只需要调用analogRead()函数就把P35引脚的电压信号转换成数字值了。但实际上ADC的工作不是那么简单的,在读取模拟信号值之前也是要进行一系列配置的,只不过这个配置过程都由开发工具在背后默默的完成了,其使用到的参数都是参数的默认值。当我们需要进行个性定制话ADC转换的时候,就没这么简单了。这在以后遇到了再详细的讲解吧。
前边用Mixly生成C语言的方式获得了供电电压的数值。接下来再用MicroPython语言也实现这个功能,其实现方法和之前的C语言基本一样,只不过增加了一个参考电压的设置,参考电压的值越大,ADC可测量的输入的模拟信号的电压值越高,我们将其配置为3.3V,具体开发方法如下所示:
上传该程序后,在串口窗口输出了返回的结果,如下图所示:
输出值为45755,啊!和前面的输出值不同啊?别担心,没出问题。这是因为MicroPython将ADC的结果转换为了在0~65535之间,所以,这个测量结果在计算被测电压的时候,要除以的数值是65535,而不是4095了。具体计算公式如下:
45755/65535*3.3*5=11.5
没错,两次测量的结果是一样的。但是C语言与MicroPython在这里对处理结果的差别要注意一下,不然,计算出来的结果就不对了。Mixly生成的MicroPython代码如下所示:
import machine adc35 = machine.ADC(machine.Pin(35)) adc35.atten(machine.ADC.ATTN_11DB) print(adc35.read_u16()) |
自己尝试一下读懂它吧?