Arduino 入门学习笔记(十六):ADC 实验
开发板:正点原子ESP32S3
没有LCD屏可以用串口打印进行测试
例程源码在文章顶部可免费下载
1. ADC 介绍
1.1 ADC 简介
生活中接触到的大多数信息是醉着时间连续变化的物理量,如声音、温度、压力等。表达这些信息的电信号,称为模拟信号(Analog Signal)。 为了方便存储、处理,在计算机系统中,都是数字 0 和 1 信号,将模拟信号(连续信号)转换为数字信号(离散信号)的器件就叫模数转换器(Analog-to-Digital Convert, ADC)。
ADC 转换器可分为: 并行比较型 A/D 转换器(FLASH ADC)、逐次比较型 A/D 转换器(SARADC)和双积分式 A/D 转换器(Double Integral ADC)。
A/D 转换过程通常为 4 步:采样、保持、量化和编码,如下图所示:
- 采样:把时间连续变化的信号变换为时间离散的信号。
- 保持:保持采样信号,使有充分时间转换为数字信号。
- 量化:把采样保持电路的输出信号用单位量化电压的整数倍表示。
- 编码:把量化的结果用二进制代码表示。
采样和保持通常是在采样-保持电路中完成,量化和编码通常在 ADC 数字编码电路中完成。
1.2 ESP32-S3 ADC 介绍
ESP32-S3 集成了两个 12 位 SAR ADC, ADC1 和 ADC2, 支持 20 个模拟通道输入。这 20个模拟通道输入对应着具体的 IO,并不是随意的 IO 都有模拟输入功能,具备模拟输入功能的引脚如下表所示:
ESP32-S3 的 ADC 模块的分辨率为 12 位,所以 AD 转换后的值范围为 0~4095。由于 ESP32-S3 的工作电压为 3.3V,所以当 AD 值为 4095 时,对应的电压为 3.3V;当 AD 值为 0 时,对应的电压为 0V。对于 AD 值和电压值,这里就会有一个简单的关系,如下所示:
1.3 ADC 函数介绍
本小节介绍到的函数可在以下文件中找到:
C:\Users\ 用户名 \AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.11\cores\esp32\esp32-hal-adc.c
接下来,我们介绍一下本章节唯一用到的 analogRead 函数。
analogRead 函数,该函数功能是从指定引脚读取模拟信号,获取返回值。
uint16_t analogRead(uin8_t pin);
参数 pin 为模拟输入引脚,可以查看一下表确认该引脚是否有模拟输入功能;
返回值: ADC 精度是 12bit,默认范围为 0~4095 之间。
2. 硬件设计
2.1 例程功能
使用 ADC 采集通道 7(IO8)上面的电压,在 LCD 上面显示 ADC 转换值以及换算成电压后的电压值。使用短路帽将 P3 端子中的 AIN 和 RV1 排针连接,使得 IO8 连接到电位器上,然后将 ADC 采集到的数据和转换后的电压值在 LCD 屏中显示。用户可以通过调节电位器的旋钮改变电压值。
2.2 硬件资源
- USART0
U0TXD-IO43 U0RXD-IO44 - XL9555
IIC_SDA-IO41
IIC_SCL-IO42 - SPILCD
CS-IO21
SCK-IO12
SDA-IO11
DC-IO40(在 P5 端口,使用跳线帽将 IO_SET 和 LCD_DC 相连)
PWR-IO1_3(XL9555)
RST-IO1_2(XL9555) - ADC
RV-AIN(IO8)(在 P3 端口,使用跳线帽将 RV 和 AIN 相连)
2.3 原理图
ADC 相关原理图,如下图所示:
本实验,我们通过 ADC1 的通道 7(IO8)来采集外部电压值,开发板有一个电位器,可调节的电压范围是: 0~3.3V。我们可以通过跳线帽将 IO8 与电位器连接,如下图所示:
使用跳线帽将 AIN 和 RV 排针连接好后,并下载程序后,就可以用螺丝刀调节电位器变换多种电压值进行测量。
有的朋友可能还想测量其它地方的电压值,我们只需要 1 根杜邦线,一端接到 AIN 排针上,另外一端就接你要测试的电压点。 一定要保证测试点的电压在 0~3.3V 的电压范围,否则可能烧坏我们的 ADC,甚至是整个主控芯片。
3. 软件设计
3.1 程序流程图
下面看看本实验的程序流程图:
3.2 程序解析
ADC 驱动代码
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。 ADC 驱动源码包括两个文件: adc.cpp 和 adc.h。
下面我们先解析 adc.h 的程序。 对模拟输入引脚做了相关定义。
/* 引脚定义 */
#define ADC_IN_PIN 8
我们选择使用 IO8 作为读取模拟信号引脚, 使用到的是 ADC1 通道 7。
下面, 我们继续获取 ADC 值的函数 adc_get,代码如下:
/**
* @brief 获取 ADC 值函数
* @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 之间。
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_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);
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);
delay(500);
}
在 setup 函数中,调用 uart_init 函数完成串口初始化,调用 xl9555_init 函数完成 XL9555 初始化,然后调用 lcd_init 函数完成 LCD 屏初始化,后面就是 LCD 显示实验信息。
在 loop 函数中,调用 adc_get 函数获取 IO8 的 AD 值,然后换算出电压值,通过 LCD 进行显示。
4. 下载验证
下载代码后, 可以看到 LCD 显示如下图所示:
上图中,我们使用跳线帽将 RV 和 AIN 连接,使得 IO8 连接到电位器上,测试得时电位器的电压,并可以通过螺丝刀调节电位器改变电压值,范围: 0~3.3V。
大家也可以用杜邦线将 AIN 连接到其他待测试的电压点,看看测量到的电压值是否准确? 但是需要注意:一定要保证测试点的电压在 0~3.3V 的电压范围,否则可能烧坏我们的ADC,甚至是整个主控芯片。