在ADC模数转换的基础上,我们可以设计一个导航按键案例,通过将模拟电压转化为数字电压,根据数字电压值进行相应的处理。
电流图:
说明:左边有6个电阻,加起来700Ω,当按下/松开/拨动导航按键时,A/D转换输入的模拟电压有0、100、200、300、400、500、700六种组合。
实现效果:程序主要是对ADC进行操作,并将寄存器相应位取出分别用8位二极管和数码管显示。第一位数码管显示8位转换结果中前三位值,最后两位数码管显示后五位值。数码管下方的发光二极管与数码管对应显示。
程序运行效果说明:
在数码管最高位(命名Seg0)显示转换结果高三位,点亮对应的发光二极管L7—L5。
数码管后两位(命名Seg6-Seg7)显示转换结果低五位,点亮对应的发光二极管L4—L0。
误差来源
:电阻的工艺使得电阻会有一定的误差,导致实际数值有一点误差
解决办法
:我们在做导航按键的判断时,都只是取高三位的值,也就是数码管Seg0的值。
数码管及二极管显示:
操作 | seg0 | seg6-seg7参考值 (实际值) |
---|---|---|
无操作 | 7 | 31(0-31) |
按下KEY3 | 0 | 00(0-31) |
向右 | 1 | 06(0-31) |
向下 | 2 | 12(0-31) |
向里 | 3 | 17(0-31) |
向左 | 4 | 22(0-31) |
向右 | 5 | 25(0-31) |
变量定义:定义引脚别名,AD值相关变量和数码管显示相关的量。
#include
#include //_cror_();
#define uint unsigned int
#define ulint unsigned long
#define uchar unsigned char
#define ADC_FLAG 0x10
/*---------引脚别名---------*/
sbit sbtSel0 = P2 ^ 0;
sbit sbtSel1 = P2 ^ 1;
sbit sbtSel2 = P2 ^ 2; //位选
sbit sbtLedSel = P2 ^ 3;
/*---------变量定义---------*/
uint uiSampleNum = 0; //采样次数
ulint uiAdSum = 0; //AD值累加和
uint uiAdDate8 = 0; //AD值八位数据
uint uiAdHigh3 = 0; //AD值高3位
uint uiAdLow5 = 0; //AD值低5位
uchar ucSegState; //数码管扫描状态
//段选
char arrSegSelect[] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71};
//位选
uchar arrDigSelect[] = {0x00, 0x06, 0x07};
初始化函数:设置推挽输出、中断和AD转换控制寄存器
// 初始化单片机SYS
void InitSYS()
{
P0M1 = 0x00;
P0M0 = 0xff;
P2M1 = 0x00;
P2M0 = 0x0E;
ucSegState = 0;
TMOD = 0x00; //中断设置
EA = 1;
TH0 = ( 65535 - 1000 ) / 256;
TL0 = ( 65535 - 1000 ) % 256;
ET0 = 1; //打开定时器
TR0 = 1; //启动定时器
}
//初始化ADC
void InitADC()
{
P1ASF = 0x80; //选择P1.7作为A/D使用=>可以检测到KEY3按下后的低电平
ADC_RES = 0;//清零ADC寄存器
ADC_CONTR = 0X8F; //打开power 540周期转换一次 选择P1.7作为A/D输入来用
CLK_DIV = 0X00; //ADRJ = 0 ADC_RES存放高8位结果
EADC = 1; //允许ADC中断
PADC = 1; //ADC中断优先级为1//ADCÖжÏÓÅÏȼ¶Îª1
}
处理子函数:控制数码管显示、ADC的数据采集和处理
void Divide3and5() //分离AD转换结果的高3位
{
uiAdHigh3 = uiAdDate8 & 0xE0; //将8位转换结果的低5位清零
uiAdHigh3 = _cror_( uiAdHigh3, 5 ); //循环右移5位
uiAdLow5 = uiAdDate8 & 0x1F; //将8位转换结果高5位清零
}
说明:_cror_循环右移函数,包含在头文件
_crol_(var, n)
:将var循环左移n位
_cror_(var, n)
:将var循环右移n位
// 定时器T0中断
void T0_Process() interrupt 1
{
ucSegState++;
if( ucSegState == 4 )
ucSegState = 0;
P0 = 0x00;
switch( ucSegState )
{
case 0:
sbtLedSel = 0; //选择数码管亮
P2 = arrDigSelect[ucSegState];
P0 = arrSegSelect[uiAdHigh3]; //显示高三位对应的十进制
break;
case 1:
sbtLedSel = 0;
P2 = arrDigSelect[ucSegState];
P0 = arrSegSelect[uiAdLow5 / 10]; //显示低5位对应的十进制的十位
break;
case 2:
sbtLedSel = 0;
P2 = arrDigSelect[ucSegState];
P0 = arrSegSelect[uiAdLow5 % 10]; //显示低5位对应的十进制的个位
break;
case 3:
sbtLedSel = 1; // 发光二极管亮
P0 = uiAdDate8;
break;
}
}
// ADC中断处理
void ADC_Process() interrupt 5
{
IE = 0x00; // 关闭中断
ADC_CONTR &= ~0X10; // ADC_FLAG清零
uiSampleNum++;
if( uiSampleNum > 1000 ) //采集数据1000次后
{
uiAdDate8 = ( uiAdSum + 500 ) / 1000; //四舍五入
Divide3and5(); //分离高3位AD值
uiSampleNum = 0;
uiAdSum = 0;
}
uiAdSum += ADC_RES;
ADC_CONTR |= 0X08; // 将ADC_START置位
IE = 0xa2; //重新打开中断
}
主函数:初始化+死循环
void main()
{
InitSYS();
InitADC();
while( 1 ) ;
}
将持续烧写到实验板中,当我们拨动导航按键时,数码管第一位显示不同的数字,最后两位显示30/31(存在工艺误差),由于选择P1.7作为A/D输入来用,所以当按下KEY3时,数码管显示0 00
完结 cheers! ??