STM32笔记(1)——ADC模数转换器原理及单双通道转换详解
一.ADC 模数转换器
1.1 ADC、DAC、PWM
ADC(Analog-Digital Converter),意即模拟-数字转换器,简称模数转换器。ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁。
DAC:数字到模拟的桥梁(PWM控制灯的亮度和电机旋转的速度,DAC的使用只要是在信号发生器、音频解码芯片等
PWM:数字到模拟的桥梁,例如PWM控制灯的亮度和电机旋转的速度,PWM只有完全导通和完全断开两种状态,在这两种状态都没有功率损耗,故直流电机调速这种大功率的应用场景,使用PWM来等效模拟量,是比DAC更好的选择,PWM电路更简单,更常用。
1.2 12位逐次逼近型ADC,1us转换时间
1.3 ADC有18个输入通道
1.4 规则组和注入组两个转换单元
普通的AD转换流程为:先启动一次转换,之后读值,依次循环。
STM32的ADC可以将要转换的通道列为一组,每一次连续转换多个值。
规则组用于常规使用,注入组一般用于突发事件。
一般可以用于测量光线强度、温度这些值
如果光线高于某个预值、低于某个预值,或者温度高于某个预值、低于某个预值时,就会执行一些操作
模拟开门狗可以监测指定的某些通道,当AD值高于他设定的上域值,或者低于下域值时。就会申请中断,然后就可以在中断函数里,执行相应的操作。
1.5 逐次逼近型ADC
下图所示的即为逐次逼近型ADC的工作原理图(ADC0809)。STM32中的ADC的结构与此类似
通过配置ADDA~ADDC可以选择一个通道作为信号输入
通过比较器, DAC逐渐逼近输入信号, DAC的值最终与输入信号十分接近
1.6 STM32中的ADC框图
- 普通的ADC多路开关一般只选中一个,STM32的ADC可以同时选中多个通道进行转换,规则组最多同时选中16个通道,注入组一次最多可以选中4个通道。(以餐厅点菜模型为例,普通模式为每次点一个菜,做好菜后上菜;STM32可以做到每次列出一个菜单,规则组一次最多可以列16个菜,注入组一次最多可以列4个菜,做好后依次上菜)
- STM32中的ADC的转换结果会被存储在对应的数据寄存器中。对于规则组通道,其只有一个数据寄存器(餐桌上只能摆一个菜),后转换的数据会将之前转换的数据覆盖,之前转换的数据就会丢失。对于规则组通道,要想实现同时转换的功能,最好配合DMA来将转换后的数据及时转运,就可以保证转换的数据不会丢失了。对于注入组通道,它拥有4个数据寄存器(餐厅的VIP坐席,餐桌上一次可以摆四个菜)。对于注入组而言,就不用担心数据覆盖的问题了。一般情况下,使用规则组和DMA就可以满足大部分的使用需求。(这里只讲解规则组使用,注入组自行了解即可)
- 框图的左下角为触发转换信号,对应ADC0809的START信号。STM32的触发转换信号来源有两种:软件触发和硬件触发。硬件触发信号可以来自于定时器的各个通道、定时器TRGO主模式的输出,外部中断EXTI。下表列出了ADC1和ADC2的触发源。(其中EXTI线11/TIM8_YRGO事件的选择需要使用AFIO端口重映射来配置)
- 这里ADC的时钟ADCCLK是来自于RCC的APB2时钟。由原理图可得,ADCCLK最大为14MHz,所以ADC预分频器只能选择6分频(得到12MHz)和8分频(得到9MHz)两个值
- ADC可以通过DMA请求信号触发DMA转运数据
- 模拟看门狗的功能是监测指定的通道。可以设置模拟看门狗的阈值高限(12位)、阈值底限(12位)和指定“看门”的通道。只要通道的电压值超过阈值范围,模拟看门狗就会“乱叫”,申请一个模拟看门狗的中断,之后通向NVIC。
- 规则组和注入组在转换完成后会生成一个转换完成的信号。EOC为规则组转换完成的信号,JEOC为注入组转换完成的信号。这两个信号会在状态寄存器中置一个标志位,我们通过读取状态寄存器,就可以知道转换是否完成了。同时这两个标志位也可以通过配置通向NVIC申请中断。
1.7 ADC基本结构
1.8 输入通道
通道 | AD1 | AD2 | AD3 |
---|---|---|---|
通道0 | PA0 | PA0 | PA0 |
通道1 | PA1 | PA1 | PA1 |
通道2 | PA2 | PA2 | PA2 |
通道3 | PA3 | PA3 | PA3 |
通道4 | PA4 | PA4 | PF6 |
通道5 | PA5 | PA5 | PF7 |
通道6 | PA6 | PA6 | PF8 |
通道7 | PA7 | PA7 | PF9 |
通道8 | PB0 | PB0 | PF10 |
通道9 | PB1 | PB1 | |
通道10 | PC0 | PC0 | PC0 |
通道11 | PC1 | PC1 | PC1 |
通道12 | PC2 | PC2 | PC2 |
通道13 | PC3 | PC3 | PC3 |
通道14 | PC4 | PC4 | |
通道15 | PC5 | PC5 | |
通道16 | 温度传感器 | ||
通道17 | 内部参考电压 |
本节课程使用的STM32F103C8T6没有PC0到PC5的引脚,故也就不存在通道10到通道15。
1.9 ADC的四种转换模式
单次转换非扫描模式、连续转换非扫描模式、单次转换扫描模式、连续转换扫描模式
1.9.1单次转换非扫描模式
1.9.2 连续转换非扫描模式
1.9.3 单次转换扫描模式
1.9.4 连续转换扫描模式
一次转换完成后,立刻开始下一次的转换
在扫描模式下还可有一种模式——间断模式,作用是在扫描过程中每隔几个转换,就暂停一次,需要再次触发才能继续
1.10 细节之处
1.10.1 触发控制 
此表为规则组的触发源,也就是上图第2部分,有来自定时器的信号、引脚或定时器的信号(具体是引脚和定时器,需要用AFIO重映射来确定)最后的软件控制位就是软件触发。这些触发信号的选择,可以通过上图1设置表右边的寄存器完成,使用库函数的话只需一个函数给个参数即可。
1.10.2 数据对齐
数据右对齐,即作为转换结果的12位数据向右靠,高位补0;
数据左对齐,即作为转换结果的12位数据向左靠,低位补0。
在使用时通常使用数据右对齐,这样在读取时直接读取寄存器即可。
如果选择左对齐直接读取,得到的数据会比实际的数据大16倍。
当对分辨率的要求不高时(对电压仅作大概的判断即可)可以采用左对齐,将数据寄存器的高8位取出,就相当于舍弃了转换结果的4位的精度,12位的ADC退化位为8位的ADC
1.10.3 转换时间(AD转换很快,一般忽略)
Tconv = 采样时间 + 12.5个ADC周期
(花费12个ADC周期进行量化和编码,多余的0.5个周期完成了其他的工作)
例如:当ADCCLK = 14MHz, 采样时间为1.5个ADC周期
Tconv = 1.5 + 12.5 = 14个ADC周期 = 1us
1.10.4 校准
ADC有一个内置自校准模式。校准可大幅减小因内部电容器组的变化而造成的精准度误差。校准期间, 在每个电容器上都会计算出一个修正码(数字值), 这个码用于消除在随后的转换中每个电容器上产生的误差
建议在每次上电后执行一次校准启动校准前, ADC必须处于关电状态超过至少两个ADC时钟周期由于校准过程是固定的,对于使用者而言只需要在初始化后加上几条代码即可!
二. 硬件电路
ADC外围电路的设计给出以下三个电路图:
图1:电位器产生一个可调的电压的电路
中间的滑动端可以输出一个0~3.3伏可调的电压输出来,上滑时电压增大,下滑时电压减小,若阻值太小,电阻就会比较费电
图2:分压方法输出传感器组织的电路
传感器输出电压的电路,例如光敏电阻、热敏电阻、红外接头管、麦克风等都可以等效为一个可变电阻 那电阻阻值得通过和一个固定电阻串联分压,当传感器阻值变大时,下拉作用变弱,输出端受上拉电阻的作用,电压就会升高 固定电阻建议选择和传感器阻值相近的电阻,才可以得到一个位于中间电压区域比较好的输出 此处传感器和固定电阻的位置也调换,输出电压的极性就反过来了
图3:简单的电压转换电路
想测一个0-5V的VIN电压,到那时ADC只能接收0~3.3V的电压,就可以搭建此类电路。使用电阻分压,上面阻值17K,下面阻值33K,加一起50K,中间的电压就是VIN/50K*33K,得到的电压范围就是0-3.3伏,就可以进入ADC转换了。想要其他范围(如5V、10V)的VIN电压可类似操作,如果电压过高就不建议使用这种电路了,可能比较危险,高电压采集最好使用专用芯片,比如隔离放大器等,做到高低电压隔离保证电路安全。
三. ADC 常用库函数
3.1 ADC的RCC时钟配置函数
该配置函数定义存放在stm32f10x_rcc.h文件中,用来配置ADCCLK分频器。它可以对APB2的72MHz时钟选择2、4、6、8分频,输出到ADCCLK。
1.void RCC_ADCCLKConfig(uint32_t RCC_PCLK2)
3.2 ADC设置
// 恢复ADC缺省配置
void ADC_DeInit(ADC_TypeDef* ADCx);
// ADC初始化
void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);
// ADC配置结构体初始化
void ADC_StructInit(ADC_InitTypeDef* ADC_InitStruct);
// ADC上电工作函数,即开关控制函数
void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState);
// ADC开启DMA输出信号
void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState);
// ADC中断输出控制函数
void ADC_ITConfig(ADC_TypeDef* ADCx, uint16_t ADC_IT, FunctionalState NewState);
// 下面4个函数用于ADC工作前的校准操作,在ADC初始化完成后依次调用即可
// ADC复位校准
void ADC_ResetCalibration(ADC_TypeDef* ADCx);
// ADC获取复位校准状态
FlagStatus ADC_GetResetCalibrationStatus(ADC_TypeDef* ADCx);
// ADC开始校准
void ADC_StartCalibration(ADC_TypeDef* ADCx);
// ADC获取开始校准状态
FlagStatus ADC_GetCalibrationStatus(ADC_TypeDef* ADCx);
// ADC软件触发转换,给CR2的SWSTART置1(开始转换后立即自动清0)
void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
// ADC获取软件触发状态,获取CR2的SWSTART(开始转换规则通道)位
// 不能用它判断转换是否结束,一般不用,了解即可
FlagStatus ADC_GetSoftwareStartConvStatus(ADC_TypeDef* ADCx);
// ADC规则组通道配置,给转换序列的每个位置填写指定的通道
void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);
// ADC外部触发转换控制(是否允许外部触发转换)
void ADC_ExternalTrigConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
// ADC获取转换值,获取AD转换的数据寄存器
uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx);
// ADC获取双模式转换值,读取双ADC模式下ADC的转换结果
uint32_t ADC_GetDualModeConversionValue(void);
// ADC温度传感器、内部参考电压控制,开启内部的两个转换通道
void ADC_TempSensorVrefintCmd(FunctionalState NewState);
// 下面的函数与操作标志位寄存器状态有关
// ADC获取标志位状态,可通过获取EOC标志位判断转换是否结束
FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG);
// 清除标志位
void ADC_ClearFlag(ADC_TypeDef* ADCx, uint8_t ADC_FLAG);
// 获取中断标志位
ITStatus ADC_GetITStatus(ADC_TypeDef* ADCx, uint16_t ADC_IT);
// 清除中断挂起位
void ADC_ClearITPendingBit(ADC_TypeDef* ADCx, uint16_t ADC_IT);
3.3 配置的参数
(1)双ADC工作模式选择,ADC_Mode,其中分别为:
#define ADC_Mode_Independent ((uint32_t)0x00000000)//独立模式
#define ADC_Mode_RegInjecSimult ((uint32_t)0x00010000)//同步规则和同步注入模式
#define ADC_Mode_RegSimult_AlterTrig ((uint32_t)0x00020000)//同步规则和交替触发模式
#define ADC_Mode_InjecSimult_FastInterl ((uint32_t)0x00030000)//同步注入和快速交叉模式
#define ADC_Mode_InjecSimult_SlowInterl ((uint32_t)0x00040000)//同步注入和慢速交叉模式
#define ADC_Mode_InjecSimult ((uint32_t)0x00050000)//同步注入模式
#define ADC_Mode_RegSimult ((uint32_t)0x00060000)//同步规则模式
#define ADC_Mode_FastInterl ((uint32_t)0x00070000)//快速交叉模式
#define ADC_Mode_SlowInterl ((uint32_t)0x00080000)//慢速交叉模式
#define ADC_Mode_AlterTrig ((uint32_t)0x00090000)//交替触发模式
(2)扫描模式选择,ADC_ScanConvMode,ENABLE表示多通道扫描模式,否则为单通道。
(3)连续转换模式选择,ADC_ContinuousConvMode,ENABLE表示连续转换模式,否则为单次转换模式。
(4)ADC转换触发方式选择,ADC_ContinuousConvMode,其中ADC_ExternalTrigConv_None为不使用外部触发,即使用软件触发方式。
(5)ADC转换数据对齐方式选择,ADC_DataAlign,可以左对齐或右对齐
(6) 顺序进行规则转换的ADC通道的数目设置,ADC_NbrOfChannel,可以设置1~16个通道
3.4 ADC间断模式配置
// 下面两个函数用来配置STM32中ADC的间断模式
// 配置每隔几个通道间断依次
void ADC_DiscModeChannelCountConfig(ADC_TypeDef* ADCx, uint8_t Number);
// 开启间断模式
void ADC_DiscModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
四. 程序示例
4.1 ADC单通道转换
1.AD.c(单次转换非扫描)
#include "stm32f10x.h" // Device header
void AD_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//配置ADCCLK分频器,对APB2的72MHz时钟选择2、4、6、8分频,输入到ADCCLK
RCC_ADCCLKConfig(RCC_PCLK2_Div6);//分频后等于72MHz/6=12MHz
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//模拟输入(ADC的专属模式)在AIN,GPIO口无效,断开GPIO口,防止GPIO口的输入输出对模拟电压造成干扰
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//ADC规则组通道配置,给序列的每个位置填写指定的通道,就是填写点菜菜单的过程
//第一个参数是ADCx,第二个是ADC指定的通道(通道0-17)
//第三个是写在序列几的位置,然后第四个指定通道的采样时间
//ADC_SampleTime_55Cycles5表示55.5个ADCCLK的周期
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
//ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 2, ADC_SampleTime_55Cycles5);
//ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 3, ADC_SampleTime_55Cycles5);
//通道可以重复,序列不要重复,需要的话可以多写几个,这是填充菜单的方法
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//工作模式,独立模式:ADC1和ADC2各转各的
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//不使用外部触发转换,即软件触发
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//单次 //ENABLE:连续转换
ADC_InitStructure.ADC_ScanConvMode = DISABLE;//非扫描
ADC_InitStructure.ADC_NbrOfChannel = 1;//总共需要扫描多少个通道
ADC_Init(ADC1, &ADC_InitStructure);
//中断和看门狗如果需要可以在此处定义
//ADC上电
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);//复位校准
//为1时,开始复位校准,复位校准完后,该位就会由硬件自动清0
while (ADC_GetResetCalibrationStatus(ADC1) == SET);//等待复位校准完成
ADC_StartCalibration(ADC1);//开始校准
while (ADC_GetCalibrationStatus(ADC1) == SET);//等待校准完成
/*ADC_SoftwareStartConvCmd(ADC1, ENABLE); //软件触发(连续转换只需要初始化一次即可,所以软件触发的函数可以挪到初始化函数) */
}
uint16_t AD_GetValue(void)
{
// 1. 软件触发开启转换
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //软件触发
// 2. 等待转换完成(获取标志位状态,等待EOC标志位置1)/*连续转换非扫描:不需要判断标志位while这句可删掉
while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);//转换结束,EOC置1// 转换未完成则等待(55.5T + 12.5T = 68T,结果大概为5.6us)
// 3. 读取ADC数据寄存器并返回
return ADC_GetConversionValue(ADC1);// 读取之后会自动清除EOC标志位
}
2、改为连续转换非扫描
好处:无需不断触发,不需要等待转换完成
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//连续 连续转换只需要初始化一次即可,所以软件触发的函数可以挪到初始化函数最后ADC_SoftwareStartConvCmd(ADC1, ENABLE); //软件触发 在初始化完成后触发一次即可 且在AD_GetValue函数中,不需要判断标志位
while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);//转换结束,EOC置1 这一句可以删除
程序如上图所示
3、main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
/*
电位器即滑动变阻器,用电位器产生0~3.3V连续变化的模拟电压信号,然后接到STM32的PA0口上,
之后用STM32内部的ADC读取电压数据,显示在屏幕上
屏幕第一行:模拟数据
屏幕第二行:处理过后显示的电压值
往左拧电位器,AD值减小,对应的电压减小,反之则反
ADC是12位的,AD结果最大值是4095,也就是2^12-1,对应的电压是3.3V
GPIO只能读取高低电平 ,而ADC可以对高低电平之间的任意电压进行量化,最终用一个变量表示
*/
uint16_t ADValue;
float Voltage;
int main(void)
{
OLED_Init();
AD_Init();
OLED_ShowString(1, 1, "ADValue:");
OLED_ShowString(2, 1, "Volatge:0.00V");
while(1)
{
ADValue = AD_GetValue();
Voltage = (float)ADValue/4095*3.3;
//数字与电压映射关系,ADValue为uint16类型除以4095会舍弃小数部分,先强转为float
OLED_ShowNum(1,9,ADValue,4);
//目前的OLED没有显示浮点数的功能,但可用显示整数的借用
OLED_ShowNum(2,9,Voltage,1);//显示整数部分
OLED_ShowNum(2,11,(uint16_t)(Voltage*100)%100,2);//显示小数部分,浮点数不可%取余所以先强转为整型
Delay_ms(100);//限制OLED刷新速度
}
}
4.2 ADC多通道转换
1、思路
在每次触发转换之前,手动更改一下列表第一个位置的通道
比如第一次转换,在序列1先写入通道0,之后触发、等待、读值
第二次转换,在序列1把通道0改成通道1,之后触发、等待、读值
第三次转换,在序列1改成通道2等等
2、AD.c
#include "stm32f10x.h" // Device header
//(单次转换非扫描)
void AD_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//配置ADCCLK分频器,对APB2的72MHz时钟选择2、4、6、8分频,输入到ADCCLK
RCC_ADCCLKConfig(RCC_PCLK2_Div6);//分频后等于72MHz/6=12MHz
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//模拟输入(ADC的专属模式)在AIN,GPIO口无效,断开GPIO口,防止GPIO口的输入输出对模拟电压造成干扰
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//工作模式,独立模式:ADC1和ADC2各转各的
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//不使用外部触发转换,即软件触发
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//单次 //ENABLE:连续转换
ADC_InitStructure.ADC_ScanConvMode = DISABLE;//非扫描
ADC_InitStructure.ADC_NbrOfChannel = 1;//总共需要扫描多少个通道
ADC_Init(ADC1, &ADC_InitStructure);
//中断和看门狗如果需要可以在此处定义
//ADC上电
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);//复位校准
//为1时,开始复位校准,复位校准完后,该位就会由硬件自动清0
while (ADC_GetResetCalibrationStatus(ADC1) == SET);//等待复位校准完成
ADC_StartCalibration(ADC1);//开始校准
while (ADC_GetCalibrationStatus(ADC1) == SET);//等待校准完成
/*ADC_SoftwareStartConvCmd(ADC1, ENABLE); //软件触发(连续转换只需要初始化一次即可,所以软件触发的函数可以挪到初始化函数) */
}
uint16_t AD_GetValue(uint8_t ADC_Channel)
{
ADC_RegularChannelConfig(ADC1,ADC_Channel, 1, ADC_SampleTime_55Cycles5);
// 把通道作为参数填入序列1中,通道的采样周期是55.5个ADCCLK的周期
// 1. 软件触发开启转换
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //软件触发
// 2. 等待转换完成(获取标志位状态,等待EOC标志位置1)/*连续转换非扫描:不需要判断标志位while这句可删掉
while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);//转换结束,EOC置1// 转换未完成则等待(55.5T + 12.5T = 68T,结果大概为5.6us)
// 3. 读取ADC数据寄存器并返回
return ADC_GetConversionValue(ADC1);// 读取之后会自动清除EOC标志位
}
3、main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
/*
DO是数字输出
AO是模拟量输出
*/
uint16_t AD0,AD1,AD2,AD3;//表示四个ADC输入通道的转换结果的接收变量
int main(void)
{
OLED_Init();
AD_Init();
OLED_ShowString(1, 1, "AD0:");
OLED_ShowString(2, 1, "AD1:");
while(1)
{
AD0 = AD_GetValue(ADC_Channel_0);
AD1 = AD_GetValue(ADC_Channel_1);
//根据需要也可设置映射关系,再进行显示
OLED_ShowNum(1,5,AD0,4);
OLED_ShowNum(2,5,AD1,4);
Delay_ms(100);//限制OLED刷新速度
}
}