# 4.电池电压测量-学会使用ADC 你好,我是爱吃鱼香ROS的小鱼。上面两节通过LED和按键学习了GPIO的输出和输入。 但这种输入和输出只有两种状态`HIGH`或者`LOW`,我们称这种为数字逻辑,这也是输入输出函数称为数字输入和数字输出的原因。 但是电池的电压是一个在一个范围内不断变化的值,明显无法通过`HIGH`和`LOW`来表示,所以本节我们学习使用ADC,将电压值这个模拟信号转换成数字信号。 ## 一、ADC介绍 ![查看源图像](4.%E7%94%B5%E6%B1%A0%E7%94%B5%E5%8E%8B%E6%B5%8B%E9%87%8F-%E5%AD%A6%E4%BC%9A%E4%BD%BF%E7%94%A8ADC/imgs/c-2.jpg) ADC(analog to digital converter)模数转换器是一种非常常见的外设,用于**将电压等模拟信号转换为数字形式**,以便微控制器可以读取和处理。 ADC在控制和监控应用中非常有用,因为大多数传感器(例如温度、压力、力)都是输出的模拟电压,所以我们需要掌握ADC。 > 与ADC相对应的DAC——用于将数字信号转换成模拟信号,比如将一段二进制的音乐文件转换成一段连续的电压信号播放出来就需要DAC。 ## 二、电池电压测量原理 在我们的开发板所使用的ESP32单片机上,自带了ADC模块,我们只需要将需要测量的模拟电压接入相应引脚,接着调用ADC相关API即可读取。 因为ADC原理是采用电压比较方式进行测量,而我们的单片机的供电电压为3.3V,所以测量的电压范围最大不能超过3.3V,但我们采用的电池电压和板子的供电电压分别是12V和5V的。 所以想要使用单片机测量电池电压,那么就要想办法将板子测量的电压按照比例缩小即可,根据初中物理知识,串联分压原理,小鱼就设计了这样的电路。 ![image-20230104151803596](4.%E7%94%B5%E6%B1%A0%E7%94%B5%E5%8E%8B%E6%B5%8B%E9%87%8F-%E5%AD%A6%E4%BC%9A%E4%BD%BF%E7%94%A8ADC/imgs/image-20230104151803596.png) 串联分压,左边接电机电压输入引脚,右侧接地,R18是40.2千欧阻值的电阻,R19是10千欧的,假如此时VMOTOR的输入电压为5V,那么ESP_IO34的电压就是 $$ V_{ESPIO34}=V_{VMOTOR}*(10/(40.2+10))=V_{VMOTOR}/5.02 $$ 那么如果此时通过ADC测量出$V_{ESPIO34}$上的电压,通过下面的等式就可以算出$V_{VMOTOR}$的电压值。 $$ V_{VMOTOR} =V_{ESPIO34}*5.02 $$ ## 三、Arduino ADC API 了解了原理,我们来看看Arduino为我们提供了哪些API可以直接获取到引脚上的电压。 ### 3.1 设置ADC衰减系数(analogReadResolution) 这个函数用来设置ADC读取时的衰减系数。 输入到引脚的电压在输入到ADC之前可能会衰减。有 4 种可用的衰减选项,衰减越高,可测量的输入电压就越高,为了能够测量到12V以上的电压,我们采用最高的衰减比-ADC_11db。 ``` typedef enum { ADC_0db, ADC_2_5db, ADC_6db, ADC_11db, } adc_attenuation_t; ``` ### 3.2 读取ADC值(analogRead) 此函数用于获取给定引脚或ADC通道的ADC原始值,默认是12位分辨率,所以这个读出来的值的范围就是0-2^12,也就是最大4096。 ``` uint16_t analogRead(uint8_t pin); ``` - `pin` 要读取ADC值的GPIO 引脚 返回值:ADC原始值 ### 3.3 读取电压值(analogReadMillivolts) 此函数用于获取给定引脚或ADC 通道的 ADC 值(以毫伏为单位)。 ``` uint32_t analogReadMilliVolts(uint8_t pin); ``` - `pin` 要读取ADC值的GPIO 引脚 返回值:此函数将以毫伏为单位返回模拟值。 ## 四、编写程序 利用上面的三个API和计算公式就可以实现电压的测量,新建`example04_adc`工程,在`main.cpp`输入代码。 ```cpp /** * @file main.cpp * @author fishros@foxmail.com * @brief 4.电池电压测量-学会使用ADC * @version 0.1 * @date 2023-01-04 * * @copyright Copyright(c) fishros.com 2023 * */ #include void setup() { Serial.begin(115200); pinMode(34, INPUT); analogSetAttenuation(ADC_11db); } void loop() { int analogValue = analogRead(34); // 读取原始值0-4096 int analogVolts = analogReadMilliVolts(34); // 读取模拟电压,单位毫伏 float realVolts = 5.02 * ((float)analogVolts * 1e-3); // 计算实际电压值 Serial.printf("ADC analog value = %d\n", analogValue); Serial.printf("ADC millivolts value = %d\n", analogVolts); Serial.printf("realVolts value = %f\n", realVolts); delay(100); } ``` ## 五、测试 根据第三节中的原理图,我们测量的是VMOTOR的电压,VMOTOR是通过一个跳线帽选择连接到5V还是12V上的,详情可以看下面的原理图。 ![image-20230104160525961](4.%E7%94%B5%E6%B1%A0%E7%94%B5%E5%8E%8B%E6%B5%8B%E9%87%8F-%E5%AD%A6%E4%BC%9A%E4%BD%BF%E7%94%A8ADC/imgs/image-20230104160525961-16728195261322.png) 对应板子上的位置 ![image-20230104161156619](4.%E7%94%B5%E6%B1%A0%E7%94%B5%E5%8E%8B%E6%B5%8B%E9%87%8F-%E5%AD%A6%E4%BC%9A%E4%BD%BF%E7%94%A8ADC/imgs/image-20230104161156619.png) 这里我们把跳线帽调整到左侧,让VMOTOR和5V连接,接着打开串口观察测量到的电压值。 ![image-20230104161606070](4.%E7%94%B5%E6%B1%A0%E7%94%B5%E5%8E%8B%E6%B5%8B%E9%87%8F-%E5%AD%A6%E4%BC%9A%E4%BD%BF%E7%94%A8ADC/imgs/image-20230104161606070.png) 可以看到我们成功的测量到了实际的电压值为5.045V,符合正常电压值。 如果你有整台FishBot小车,可以将跳线帽调整到12V的位置,打开电池电源开关,看看是否可以正常测量到12V的电压。 ## 六、总结 本节我们通过电池电压测量的例程,学习了ADC的使用,电池电压测量对我们机器人来说是非常重要的,当电压低的时候可以进行自动返回充电,然后等充电完成后再继续工作。