第十九章 ADC实验 本章,我们将学习ESP32-S3的ADC外设,教会大家如何利用ADC去测量可调变阻器的电压值。 本章分为如下几个小节: 19.1 ADC介绍 19.2 硬件设计 19.3 软件设计 19.4 下载验证
19.1 ADC介绍 19.1.1 ADC简介 生活中接触到的大多数信息是醉着时间连续变化的物理量,如声音、温度、压力等。表达这些信息的电信号,称为模拟信号(Analog Signal)。为了方便存储、处理,在计算机系统中,都是数字0和1信号,将模拟信号(连续信号)转换为数字信号(离散信号)的器件就叫模数转换器(Analog-to-Digital Convert,ADC)。 ADC转换器可分为:并行比较型A/D转换器(FLASH ADC)、逐次比较型A/D转换器(SAR ADC)和双积分式A/D转换器(Double Integral ADC)。 A/D转换过程通常为4步:采样、保持、量化和编码,如下图所示。 图19.1.1.1 A/D转换过程图 采样:把时间连续变化的信号变换为时间离散的信号。 保持:保持采样信号,使有充分时间转换为数字信号。 量化:把采样保持电路的输出信号用单位量化电压的整数倍表示。 编码:把量化的结果用二进制代码表示。 采样和保持通常是在采样-保持电路中完成,量化和编码通常在ADC数字编码电路中完成。 19.1.2 ESP32-S3 ADC介绍 ESP32-S3集成了两个12位SAR ADC,ADC1和ADC2,支持20个模拟通道输入。这20个模拟通道输入对应着具体的IO,并不是随意的IO都有模拟输入功能,具备模拟输入功能的引脚如下表所示。 表19.1.2.1 具备模拟输入功能的引脚 ESP32-S3的ADC模块的分辨率为12位,所以AD转换后的值范围为0~4095。由于ESP32-S3的工作电压为3.3V,所以当AD值为4095时,对应的电压为3.3V;当AD值为0时,对应的电压为0V。对于AD值和电压值,这里就会有一个简单的关系,如下所示。 19.1.3 ADC函数介绍 本小节介绍到的函数可在以下文件中找到: Arduino15\packages\esp32\hardware\esp32\2.0.11\cores\esp32\esp32-hal-adc.c 接下来,我们介绍一下本章节唯一用到的analogRead函数。 analogRead函数,该函数功能是从指定引脚读取模拟信号,获取返回值。 uint16_t analogRead(uin8_t pin); 参数pin为模拟输入引脚,可以查看一下表19.1.2.1确认该引脚是否有模拟输入功能; 返回值:ADC精度是12bit,默认范围为0~4095之间。 19.2 硬件设计 1. 例程功能 使用ADC采集通道7(IO8)上面的电压,在LCD上面显示ADC转换值以及换算成电压后的电压值。使用短路帽将P3端子中的AIN和RV1排针连接,使得IO8连接到电位器上,然后将ADC采集到的数据和转换后的电压值在LCD屏中显示。用户可以通过调节电位器的旋钮改变电压值。 2. 硬件资源 1)USART0 U0TXD-IO43 U0RXD-IO44 2)XL9555 IIC_SDA-IO41 IIC_SCL-IO42 3)SPILCD CS-IO21 SCK-IO12 SDA-IO11 DC-IO40(在P5端口,使用跳线帽将IO_SET和LCD_DC相连) PWR-IO1_3(XL9555) RST-IO1_2(XL9555) 4)ADC RV-AIN(IO8)(在P3端口,使用跳线帽将RV和AIN相连) 3. 原理图 ADC相关原理图,如下图所示。 图19.2.1 ADC原理图 本实验,我们通过ADC1的通道7(IO8)来采集外部电压值,开发板有一个电位器,可调节的电压范围是:0~3.3V。我们可以通过跳线帽将IO8与电位器连接,如下图所示: 图19.2.2 IO8(对应AIN排针)与电位器示意图 使用跳线帽将AIN和RV排针连接好后,并下载程序后,就可以用螺丝刀调节电位器变换多种电压值进行测量。 有的朋友可能还想测量其它地方的电压值,我们只需要1根杜邦线,一端接到AIN排针上,另外一端就接你要测试的电压点。一定要保证测试点的电压在0~3.3V的电压范围,否则可能烧坏我们的ADC,甚至是整个主控芯片。 19.3 软件设计 19.3.1 程序流程图 下面看看本实验的程序流程图: 图19.3.1 程序流程图
19.3.2 程序解析 1. ADC驱动代码这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。ADC驱动源码包括两个文件:adc.cpp和adc.h。 下面我们先解析adc.h的程序。对模拟输入引脚做了相关定义。 /* 引脚定义 */ #define ADC_IN_PIN 8 我们选择使用IO8作为读取模拟信号引脚,使用到的是ADC1通道7。 下面,我们继续获取ADC值的函数adc_get,代码如下: /** * @param adc_pin:adc引脚 * @retval 获取到的ADC值 */ uint16_t adc_get(uint8_t adc_pin) { return analogRead(adc_pin); /* 返回ADC值 */ } 在程序中,我们并不需要对ADC进行配置,就可以直接调用analogRead函数进行读取了。由于ADC的分辨率是12位,所以最终读取的值的范围在0 ~ 4095之间。 2. 13_adc.ino代码在13_adc.ino里面编写如下代码: #include "uart.h" #include "xl9555.h" #include "spilcd.h" #include "adc.h" uint16_t adc_value = 0; float adc_vol = 0; /** * @brief 当程序开始执行时,将调用setup()函数,通常用来初始化变量、函数等 * @param 无 * @retval 无 */ void setup() { uart_init(0, 115200); /* 串口0初始化 */ xl9555_init(); /* IO扩展芯片初始化 */ lcd_init(); /* LCD初始化 */ lcd_show_string(30, 50, 200, 16, LCD_FONT_16, "ESP32-S3", RED); lcd_show_string(30, 70, 200, 16, LCD_FONT_16, "ADC TEST", RED); lcd_show_string(30, 90, 200, 16, LCD_FONT_16, "ATOM@ALIENTEK", RED); lcd_show_string(30, 110, 200, 16, LCD_FONT_16, "ADC_VAL:", BLUE); lcd_show_string(30, 130, 200, 16, LCD_FONT_16, "ADC_VOL:0.000V", BLUE); } /** * @brief 循环函数,通常放程序的主体或者需要不断刷新的语句 * @param 无 * @retval 无 */ void loop() { adc_value = adc_get(ADC_IN_PIN); /* 读取GPIO8引脚的ADC值 */ lcd_show_xnum(94, 110, adc_value, 5, LCD_FONT_16, NUM_SHOW_NOZERO, BLUE); /* 显示ADC采样后的原始值 */ adc_vol = (float)adc_value * 3.3 / 4095; /* 换算得到电压值 */ adc_value = adc_vol; /* 赋值整数部分给adc_value变量,因为adc_value为u16整形 */ lcd_show_xnum(94,130,(uint16_t)adc_value,1,LCD_FONT_16,NUM_SHOW_NOZERO,BLUE); /* 显示电压值的整数部分,3.1111的话,这里就是显示3 */ adc_vol -= adc_value; /* 把已显示整数部分去掉,留下小数部分,如3.1111-3=0.1111 */ adc_vol *= 1000; /* 小数部分乘1000,0.1111转换为111.1,相当于保留三位小数 */ lcd_fill(110, 130, 110 + 24, 130 + 16, WHITE); lcd_show_xnum(110, 130, adc_vol, 3, LCD_FONT_16, NUM_SHOW_ZERO, BLUE); /* 显示小数部分(前面转换为了整形显示),这里显示的就是111 */ delay(500); } 在setup函数中,调用uart_init函数完成串口初始化,调用xl9555_init函数完成XL9555初始化,然后调用lcd_init函数完成LCD屏初始化,后面就是LCD显示实验信息。 在loop函数中,调用adc_get函数获取IO8的AD值,然后换算出电压值,通过LCD进行显示。 19.4 下载验证 下载代码后,可以看到LCD显示如下图所示。 图19.4.1 ADC实验测试图
|