1.ADC简介
12位ADC是一种逐次逼近型模拟数字转换器。它有多达18个通道,可测量16个外部和2个内部信号源。各通道的A/D转换可以单次、连续、扫描或间断模式执行。ADC的结果可以左对齐或右对齐方式存储在16位数据寄存器中。
模拟看门狗特性允许应用程序检测输入电压是否超出用户定义的高/低阀值。
ADC的输入时钟不得超过14MHz,它是由PCLK2经分频产生。
- ADC(Analog-Digital Converter)模拟-数字转换器
- ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁
- 12位逐次逼近型ADC,1us转换时间(对应AD转换频率就是1MHz(ADC的最快转换频率))
- 输入电压范围:0 ~ 3.3V,转换结果范围:0 ~ 4095(STM32的ADC是12位的 所以AD结果最大值是4095 也就是2^12-1;0~3.3V对应0~4095)
- 18个输入通道,可测量16个外部和2个内部信号源
- 规则组和注入组两个转换单元
- 模拟看门狗自动监测输入电压范围
- STM32F103C8T6 ADC资源:ADC1外设、ADC2外设,10个外部输入通道(PA0-PA7、PB0、PB1)
2.逐次逼近型ADC
- 左边是通道选择开关 有IN0~IN7 是8路输入通道 通过通道选择开关 选中一路 输入到方框右边界限的箭头初进行转换 下面是地址锁存和译码 就是选中哪个通道 就把通道号放在这三个脚(ADDA、ADDB、ADDC)上 然后给一个锁存信号(ALU) 上面这里对应的通路开关就可以自动拨好了 这部分就相当于一个可以通过模拟信号的数据选择器
-
这个ADC0809只有8个输入通道 我们STM32内部的ADC是有18个输入通道的 所以对应输入电路 就是一个18路输入的多路开关
-
图中有一个电压比较器 它可以判断两个输入信号电压的大小关系 输出一个高低电平指示谁大谁小 它的两个输入端 一个是待测的电压 另一个是这里DAC的电压输出端 DAC是数模转换器 我们之前说过了 给它一个数据 它就可以输出数据对应的电压 DAC内部是使用加权电阻网络来实现的转换
-
我们有一个外部通道输入的未知编码的电压 和一个DAC输出的已知编码的电压 它俩同时输入到电压比较器 进行大小判断 如果DAC输出的电压比较大 我就调小DAC数据;如果DAC输出的电压比较小 我就增大DAC数据 直到DAC输出的电压和外部通道输入的电压近似相等 这样DAC输入的数据就是外部电压的编码数据了 这就是DAC的实现原理 这个电压调节的过程就是这个逐次逼近SAR来完成的
-
EOC是end of convert 转换结束信号 start是开始转换给一个输入脉冲 clock是ADC时钟 因为ADC内部是一步一步进行判断的 所以需要时钟来推动这个过程
3.ADC框图
- 左边是ADC的输入通,包括16个GPIO口 IN0~IN15 和两个内部的通道,一个是内部温度传感器 另一个是VREFINT(V Reference Internal)内部参考电压 总共是18个输入通道
-
模拟多路开关 可以指定我们想要选择的通道 右边是多路开关的输出 进入到模数转换器 这里模数转换器就是刚才讲过的逐次比较的过程,转换结果会直接放在这个数据寄存器里 我们读取寄存器就能知道ADC转换的结果 然后在这里对于普通的ADC 多路开关一般都是只选中一个的 就是选中某个通道开始转换 等待转换完成取出结果 这是普通的流程 但是这里就比较高级了 它可以同时选中多个 而且在转换的时候还分成了两个组 规则通道组和注入通道组
-
注入组和规则组:举个例子 这就像是你去餐厅点菜 普通的ADC是你指定一个菜 老板给你做 然后做好了送给你 这里(指STM32F103C8T6 ADC)就是 你指定一个菜单 这个菜单最多可以填16个菜 然后你直接递个菜单给老板 老板就按照菜单的顺序依次做好 一次性给你端上菜 这样的话就可以大大提高效率 当然 你的菜单也可以只写一个菜 这样这个菜单就简化成了普通的模式了 那对于这个菜单 也有两种 一种是规则组菜单 可以同时上16个菜 但是它有个尴尬的地方 就是这个规则组只有一个数据寄存器 就是这个桌子比较小 最多只能放一个菜 你如果上16个菜 那不好意思 前15个菜都会被挤掉些 你只能得到第16个菜 所以对于规则组转换来说 如果使用这个菜单的话 最好配合DMA来实现 DMA是一个数据转运小帮手 它可以在每上一个菜之后 把这个菜挪到其他地方去 防止被覆盖 那现在我们就知道了 这个规则组虽然可以同时转换16个通道 但是数据寄存器只能存一个结果 如果不想之前的结果被覆盖 那在转换完成之后 就要尽快把结果拿走 接着我们看一下注入组 这个组就比较高级了 它相当于是餐厅的VIP座位 在这个座位上 一次性最多可以点4个菜 并且这里数据寄存器有4个 是可以同时上4个菜的 对于注入组而言 就不用担心数据覆盖的问题了 这就是规则组和注入组的介绍 一般情况下 我们使用规则组就完全足够了 如果要使用规则组的菜单 那就再配合DMA转运数据 这样就不用担心数据覆盖的问题
-
如图是触发转换的部分 也就是这里的START信号 开始转换 那对于STM32的ADC 触发ADC开始转换的信号有两种 一种是软件触发 就是你在程序中手动调用一条代码 就可以启动转换了 另一种是硬件触发 就是这里的这些触发源 上面这些是注入组的触发源 下面这些是规则组的触发源 这些触发源主要是来自于定时器 有定时器的各个通道 还有TRGO定时器主模式的输出
-
左上角这里是VREF+、VREF-、VDDA和VSSA 上面两个是ADC的参考电压 决定了ADC输入电压的范围 下面两个是ADC的供电引脚 一般情况下 VREF+要接VDDA VREF-要接VSSA 在我们这个芯片上 没有VREF+和VREF-的引脚 它在内部就已经和VDDA和VSSA接在一起了。 查看引脚定义 VDDA和VSSA是内部模拟部分的电源(前面讲过分区供电) 比如ADC、RC振荡器、锁相环等 在这里VDDA接3.3V, VSSA接GND 所以ADC的输入电压范围就是0~3.3V
-
这里是ADCCLK是ADC的时钟 也就是这里的CLOCK 是用于驱动内部逐次比较的时钟 这个ADCCLK是来自ADC预分频器 而ADC预分频器是来源于RCC的 APB2时钟72MHz 然后通过ADC预分频器进行分频 得到ADCCLK ADCCLK最大是14MHz 所以这个预分频器就有点尴尬 它可以选择2、4、6、8分频 如果选择2分频 72M/2=36M 超出允许范围了 4分频之后是18M 也超了 所以对于ADC预分频器只能选择6分频 结果是12M 和8分频 结果是9M 这两个值
-
DMA请求是用于触发DMA进行数据转运的
-
两个数据寄存器 用于存放转换结果的 上面这里还有模拟开门狗 它里面可以存一个阈值高线和阈值低线 如果启动了模拟看门狗 并且指定了看门的通道 那个看门狗就会关注他开门的通道 一旦超过这个阈值范围了 它就会乱叫 就会在上面申请一个模拟看门狗的中断 最后通向NVIC 然后对于规则组和注入组而言 它们转换完成之后也会有一个EOC转换完成的信号 在这里EOC是规则组的完成信号 JEOC是注入组完成的信号 这两个信号会在状态寄存器里置一个标志位 我们读取这个标志位就能知道是否转换结束 同时这两个标志位也可以去到NVIC申请中断 如果开启了NVIC对应的通道 它们就会触发中断
4.ADC基本结构
-
左边是输入通道 16个GPIO口 外加两个内部的通道 然后进入AD转换器 AD转换器里有两个组 一个是规则组 一个是注入组 规则组最多可以选中16个通道 注入组最多可以选择4个通道 然后转换的结果可以存放在AD数据寄存器里 其中规则组只有1个数据寄存器 注入组有4个 然后下面这里有触发控制 提供了开始转换这个START信号 触发控制可以选择软件触发和硬件触发 硬件触发主要是来自于定时器 当然也可以选择外部中断的引脚 右边这里是来自于RCC的ADC时钟CLOCK ADC 逐次比较的过程就是由这个时钟推动的 然后上面 可以布置一个模拟看门狗用于监测转换结果的范围 如果超出设定的阈值 就通过中断输出控制 向NVIC申请中断 另外 规则组和注入组转换完成后会有个EOC信号 它会置一个标志位 当然也可以通向NVIC 最后右下角这里还有个开关控制 在库函数中 就是ADC_Cmd函数 用于给ADC上电的
5.输入通道
对比引脚定义图:
- ADC_IN0对应的是PA0引脚 IN1对应PA1引脚 然后IN2、IN3、IN4、IN5、IN6、IN7、IN8、IN9 依次对应的是PA0到PB1 这里只有IN0~IN9 总共只有十个通道 然后其他地方就没有了 所以这个芯片就只能有十个外部输入通道 然后ADC_IN0的意思是 ADC1和ADC2 的IN0都是在PA0上的 然后下面全都是ADC12 这说明ADC1和ADC12的引脚全都是相同的
6.转换模式
6.1 单次转换,非扫描模式
6.2 连续转换,非扫描模式
6.3 单次转换,扫描模式
6.4 连续转换,扫描模式
7.触发控制
-
这个表就是规则组的触发源 也就是ADC总框图中的ADC 在这个表里 有来自定时器的信号 还有这个来自引脚或定时器的信号 这个具体是引脚还是定时器 需要用AFIO重映射来确定 最后是软件控制位 也就是我们之前说的软件触发 这些触发信号怎么选择 可以通过设置右边这个寄存器来完成 当然使用库函数的话 直接给一个参数就行了 这就是触发控制
8.数据对齐
-
这个ADC是12位的 它的转换结果就是一个12位的数据 但是这个数据寄存器是16位的 所以就存在一个数据对齐的问题 这里第一种是数据右对齐 就是12位的数据向右靠高位多出来的几位就补零 第二种是数据左对齐 是12位的数据向左靠 低位多出来的补零 在这里我们一般使用的都是第一种右对齐 这样读取这个16位寄存器 直接就是转换结果 如果选择左对齐直接读的话 得到的数据会比实际的大 因为数据左对齐实际上就是把数据左移了四次 二进制有个特点 就是数据左移一次 就等效于把这个数据乘二 那这里左移四次 就相当于把结果乘16 所以直接读的话会比实际值大16倍
9.转换时间
- 采样保持是因为AD转换(就是后面的量化编码) 是需要一小段时间的 如果在这一小段时间里 输入的电压还在不断变化 那就没法定位输入电压的位置 所以在量化编码之前 我们需要设置一个采样开关 先打开采样开关 收集一下外部的电压 比如可以用一个小容量的电容存储一下这个电压 存储好了之后 断开采样开关 再进行后面的AD转换 这样在量化编码的期间 电压始终保持不变 这样才能精确地定位未知电压的位置 这就是采样保持电路
- 采样保持的过程中 需要闭合采样开关 过一段时间再断开 这里就会产生一个采样时间 那回到这里 我们就得到了第二条 STM32 ADC的总转换时间为TCONV=采样时间+12.5个ADC周期 采样时间是采样保持花费的时间 这个可以在程序中进行配置 采样时间越大 越能避兔一些毛刺信号的干扰 不过转换时间也会相应延长 12.5个ADC周期是量化编码花费的时间 因为是12位的ADC 所以需要花费12个周期 这里多了半个周期 可能是做其他一些东西花的时间 ADC周期就是从RCC分频过来的ADCCLK 这个ADCCLK最大是14MHz
10.校准
11.硬件电路
-
第一个是电位器产生一个可调的电压 这里电位器的两个固定端 一端接3.3V 另一端接GND 这样中间的滑动端就可以输出一个0~3.3伏可调的电压 我们这里可以接ADC的输入通道 比如PA0口 当滑动端往上滑时 电压增大 往下滑时电压减小 另外注意一下这个电阻的阻值不要给太小 因为这个电阻两端也是直接跨接在电源正负极的 如果阻值太小 那这个电阻就会比较费电 再小就有可能发热冒烟 一般至少要接千欧级的电阻 比如这里接的是10k的电路 这是电位器产生可调电压的电路
-
第二个是传感器输出电压的电路 一般来说像光敏电阻、热敏电阻、红外接收管、麦克风等等都可以等效为一个可变电阻 那电阻阻值没法直接测量 所以这里就可以通过和一个固定电阻串联分压 来得到一个反应电阻值电压的电路 那这里传感器阻值变小时下拉作用变强 输出端电压就下降 传感器阻值变大时下拉作用变弱 输出端受上拉电阻的作用 电压就会升高 这个固定电阻一般可以选择和传感器阻值相近的电阻 这样可以得到一个位于中间电压区域比较好的输出 但这里传感器和固定电阻的位置也可以换过来 这样的话输出电压的极性就翻过来了 这就是这个分压方法来输出传感器阻值的电路
-
最后这是一个简单的电压转换电路 比如你想测个0~ 5V的VIN电压 但是ADC只能接收0~ 3.3伏的电压 那就可以搭建一个这样的简易转换电路 在这里还是使用电阻进行分压 上面阻值17k 下面阻值33k加一起是50k 所以根据分压公式 中间的电压就是VIN/50Kx33k 最后得到的电压范围就是0~3.3就可以进入ADC转换了 这就是这个简单的电压转换电路 如果你想采集5V、10V这些电压的话 可以使用这个电压转换电路,但是如果你电压再高一些 就不建议使用这个电路了 那可能会比较危险 高电压采集最好使用一些专用的采集芯片 比如隔离放大器等等 做好高低电压的隔离 保证电路的安全
12.相关API
RCC_ADCCLKConfig
void RCC_ADCCLKConfig(uint32_t RCC_PCLK2);
功能:
配置ADC时钟(ADCCLK)
参数:
RCC_PCLK2:定义ADC时钟分频器 这个钟是由APB2时钟(PCLK2)配置
返回值:
无
13.AD单通道
13.1 步骤
-
第一步,开启RCC时钟,包括ADC和GPIO的时钟,另外这里ADCCLK的分频器,也需要配置一下
-
第二步,配置GPIO。把需要用的GPIO配置成模拟输入的模式
-
第三步,配置这里的多路开关。把左边的通道接入到右边的规则组列表里。这个过程就是我们之前说的点菜,把各个通道的菜,列在菜单里
-
第四步,就是配置ADC转换器了。在库函数里,是用结构体来配置的,可以配置这一大块电路的参数。包括ADC是单次转换还是连续转换、扫描还是非扫描、有几个通道,触发源是什么,数据对齐是左对齐还是右对齐。
-
第五步,就是开关控制,调用一下ADC_Cmd函数,开启ADC,这样ADC就配置完成了,就能正常工作了
13.2 接线图
13.3 相关代码
AD.C
#include "stm32f10x.h" // Device header
void AD_Init(void)
{
/*开启时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);//开启ADC时钟 ADC都是APB2上的设备
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启GPIO时钟
/*设置ADC时钟*/
RCC_ADCCLKConfig(RCC_PCLK2_Div6);//配置ADCCLK分频器 ADCCLK=72MHz/6=12MHz 最大不超过14MHz
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;将PA0引脚初始化为模拟输入 ADC的专属模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
/*规则组通道配置*/
ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);//规则组序列1的位置,配置为通道0
//第三个参数对应规则组里面的16个序列 目前只有PA0一个通道 所以指定通道放在第一个序列1的位置
//第四个参数是采用时间参数 需要更快的转换 就选择小的参数 需要更稳定的转换 就选择大的参数
/*ADC初始化*/
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//指定转换是连续模式还是单次模式 ENABLE是连续模式 DISABLE是单次模式 本节是单次转换
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//数据对齐,选择右对齐
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//触发源,外部触发,本节使用软件触发,不需要外部触发
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//模式,选择独立模式,即单独使用ADC1
ADC_InitStructure.ADC_NbrOfChannel = 1;//为1,仅在扫描模式下,才需要指定大于1的数,在非扫描模式下,只能是1
ADC_InitStructure.ADC_ScanConvMode = DISABLE;//指定转换是扫描模式(多通道)还是非扫描模式(单通道)通道数 ENABLE是多通道 DISABLE是单通道 本节是单通道
ADC_Init(ADC1,&ADC_InitStructure);
/*ADC使能*/
ADC_Cmd(ADC1,ENABLE);//使能ADC1,ADC开始运行
/*ADC校准*/
ADC_ResetCalibration(ADC1);//重置指定的 ADC 的校准寄存器
while (ADC_GetResetCalibrationStatus(ADC1) == SET);//获取 ADC 重置校准寄存器的状态 若等于SET 则还在初始化校准寄存器 若不等于SET 则说明初始化校准寄存器完毕 跳出循环
ADC_StartCalibration(ADC1);//开始指定 ADC 的校准程序
while (ADC_GetCalibrationStatus(ADC1) == SET);//获取指定 ADC 的校准状态 若等于SET 则开始校准 若不等于SET 则说明校准完毕 跳出循环
}
/**
* 函 数:获取AD转换的值
* 参 数:无
* 返 回 值:AD转换的值,范围:0~4095
*/
uint16_t AD_GetValue(void)
{
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //软件触发AD转换一次
while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET); //等待EOC标志位,即等待AD转换结束,等于RESET,转换未完成,等于SET,转换完成,跳出循环
return ADC_GetConversionValue(ADC1); //读数据寄存器,得到AD转换的结果
}
附加程序中几个循环代码内判断的原因:
AD.h
#ifndef __AD_H
#define __AD_H
void AD_Init(void);
uint16_t AD_GetValue(void);
#endif
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
uint16_t ADValue; //定义AD值变量
float Voltage; //定义电压变量
int main(void)
{
OLED_Init(); //OLED初始化
AD_Init(); //AD初始化
/*显示静态字符串*/
OLED_ShowString(1, 1, "ADValue:");
OLED_ShowString(2, 1, "Voltage:0.00V");
while (1)
{
ADValue = AD_GetValue(); //获取AD转换的值
Voltage = (float)ADValue / 4095 * 3.3; //将AD值线性变换到0~3.3的范围,表示电压 AD是int型 需进行强转
OLED_ShowNum(1, 9, ADValue, 4); //显示AD值
OLED_ShowNum(2, 9, Voltage, 1); //显示电压值的整数部分
OLED_ShowNum(2, 11, (uint16_t)(Voltage * 100) % 100, 2); //显示电压值的小数部分
//乘100再取余 比如1.23乘100就是123 再对100取余得到的就是23 成功得到小数部分
Delay_ms(100); //延时100ms,手动增加一些转换的间隔时间
}
}
现象:上电之后 OLED显示屏显示AD值和电压值 通过旋转电位器 电压值和AD值随之改变 两者变化范围分别是0~3.3和0~4095
14.AD多通道
14.1 接线图
14.2 相关代码
AD.c
#include "stm32f10x.h" // Device header
void AD_Init(void)
{
/*开启时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);//开启ADC时钟 ADC都是APB2上的设备
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启GPIO时钟
/*设置ADC时钟*/
RCC_ADCCLKConfig(RCC_PCLK2_Div6);//配置ADCCLK分频器 ADCCLK=72MHz/6=12MHz 最大不超过14MHz
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;将PA0引脚初始化为模拟输入 ADC的专属模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
/*规则组通道配置*/
//ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);//规则组序列1的位置,配置为通道0
//第三个参数对应规则组里面的16个序列 目前只有PA0一个通道 所以指定通道放在第一个序列1的位置
//第四个参数是采用时间参数 需要更快的转换 就选择小的参数 需要更稳定的转换 就选择大的参数
//有多个通道 可直接放在最下面获取AD转换的值里面 在里面进行配置同时获取AD的值
/*ADC初始化*/
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//指定转换是连续模式还是单次模式 ENABLE是连续模式 DISABLE是单次模式 本节是单次转换
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//数据对齐,选择右对齐
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//触发源,外部触发,本节使用软件触发,不需要外部触发
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//模式,选择独立模式,即单独使用ADC1
ADC_InitStructure.ADC_NbrOfChannel = 1;//为1,仅在扫描模式下,才需要指定大于1的数,在非扫描模式下,只能是1
ADC_InitStructure.ADC_ScanConvMode = DISABLE;//指定转换是扫描模式(多通道)还是非扫描模式(单通道)通道数 ENABLE是多通道 DISABLE是单通道 本节是单通道
ADC_Init(ADC1,&ADC_InitStructure);
/*ADC使能*/
ADC_Cmd(ADC1,ENABLE);//使能ADC1,ADC开始运行
/*ADC校准*/
ADC_ResetCalibration(ADC1);//重置指定的 ADC 的校准寄存器
while (ADC_GetResetCalibrationStatus(ADC1) == SET);//获取 ADC 重置校准寄存器的状态 若等于SET 则还在初始化校准寄存器 若不等于SET 则说明初始化校准寄存器完毕 跳出循环
ADC_StartCalibration(ADC1);//开始指定 ADC 的校准程序
while (ADC_GetCalibrationStatus(ADC1) == SET);//获取指定 ADC 的校准状态 若等于SET 则开始校准 若不等于SET 则说明校准完毕 跳出循环
}
/**
* 函 数:获取AD转换的值
* 参 数:无
* 返 回 值:AD转换的值,范围:0~4095
*/
uint16_t AD_GetValue(uint8_t ADC_Channel)
{
ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_55Cycles5); //在每次转换前,根据函数形参灵活更改规则组的通道1
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //软件触发AD转换一次
while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET); //等待EOC标志位,即等待AD转换结束,等于RESET,转换未完成,等于SET,转换完成,跳出循环
return ADC_GetConversionValue(ADC1); //读数据寄存器,得到AD转换的结果
}
AD.h
#ifndef __AD1_H
#define __AD1_H
void AD_Init(void);
uint16_t AD_GetValue(uint8_t ADC_Channel);
#endif
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD1.h"
uint16_t AD0, AD1, AD2, AD3; //定义AD值变量
int main(void)
{
OLED_Init();
AD_Init();
OLED_ShowString(1,1,"AD0:");
OLED_ShowString(2,1,"AD1:");
OLED_ShowString(3,1,"AD2:");
OLED_ShowString(4,1,"AD3:");
while (1)
{
AD0 = AD_GetValue(ADC_Channel_0); //单次启动ADC,转换通道0
AD1 = AD_GetValue(ADC_Channel_1); //单次启动ADC,转换通道1
AD2 = AD_GetValue(ADC_Channel_2); //单次启动ADC,转换通道2
AD3 = AD_GetValue(ADC_Channel_3); //单次启动ADC,转换通道3
OLED_ShowNum(1, 5, AD0, 4); //显示通道0的转换结果AD0
OLED_ShowNum(2, 5, AD1, 4); //显示通道1的转换结果AD1
OLED_ShowNum(3, 5, AD2, 4); //显示通道2的转换结果AD2
OLED_ShowNum(4, 5, AD3, 4); //显示通道3的转换结果AD3
Delay_ms(100); //延时100ms,手动增加一些转换的间隔时间
}
}
现象:上电之后OLED显示4个AD转换值 AD0对应电位器 旋转时AD0值会发生改变 AD1对应光敏传感器模块 遮住光线后AD1值发生改变 AD2对应热敏电阻模块 用手捂住感受器AD2值发生改变 AD3对应反射式传感器模块 当接触东西时其AD3值会发生改变