效果
程序设计目标:利用STC15F2K60S2芯片的ADC口对来自导航按键不同方向的电压值进行采集,并将采集后的转换结果用数码管显示。程序主要是对ADC进行操作,并将寄存器相应位取出分别用8位二极管和数码管显示。第一位数码管显示8位转换结果中前三位值,最后两位数码管显示后五位值。数码管下方的发光二极管与数码管对应显示。
程序运行效果说明:根据用户对导航按键的操作情况,相应产生的ADC转换结果,在数码管最高位(命名Seg0)显示转换结果高三位,点亮对应的发光二极管L7—L5。数码管后两位(命名Seg6-Seg7)显示转换结果低五位,点亮对应的发光二极管L4—L0。具体显示情况如表1所示。注:实际数值如果有一点误差,是由于电阻的工艺使得电阻会有一定的误差,关系不大,我们在做导航按键的判断时,都只是取高三位的值,也就是数码管Seg0的值。
按键3对应的数码管及二极管显示情况对照表
程序实现相关电路及工作原理说明
1.导航按键电路及工作原理说明
本案例需要使用ADC进行导航按键的电压采集,并将采集后的转换结果送到数码管及发光二极管的显示。
导航按键在上图的标注为MINI_KEY5,导航按键的每一个方向被按下,都会引起实际电压的改变,从而可以根据这个原理,与A/D转换器配合,可以判断哪个方位被按下,获取按下后A/D转换的结果。
2.AD数据采集电路及采集步骤说明
ADC数据采集的步骤:
1.将ADC0~7的模拟量送到比较器中,用DAC(数/模转换器)转换的模拟量与输入的模拟量通过比较器进行比较。
2.转换结束后,将比较结果放入转换结果寄存器(ADC_RES和ADC_RESL)。
3.同时,需要将ADC_FLAG软件清零。
4.注意硬件会自动将ADC_START清零,如果需要进行下一次转换,则需要将ADC_START置位。
特别说明:
(1)数码管所显示的ADC转换结果并不是电压值,而是电压进行转换后所得的一个值。如果需要实际的电压值可以参照STC15F2K60S2数据手册的760页上面的公式进行计算得出。
(2)ADC转换结果是一个10位数据,若ADRJ=0,则ADC_RES存放高八位,ADC_RESL存放低两位。若ADRJ=1,则ADC_RESL存放高八位,ADC_RES存放低两位。本案例采用的是ADRJ=0,而且只取了高八位结果。
3.数码管和发光二极管说明
数码管和发光二极管的具体电路和工作原理说明请参考“流水灯测试”和“8位数码管动态扫描测试”。这个案例中,使用了一个技巧将发光二极管和3位数码管进行同时显示:将发光二极管作为数码管位选的第四位,从而在数码管显示的同时,发光二极管也能同时显示。
用途
可以作为按键控制时钟、收音机等。
代码如下:
#include<STC15F2K60S2.H>
#include<intrins.h> //_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};
//初始化单片机SYS
void InitSYS()
{
P0M1=0x00;
P0M0=0xff;
P2M1=0x00;
P2M0=0x0E;
ucSegState=0;
TMOD=0x00;//中断设置
EA=1;
TH0=(65536-1000)/256;
TL0=(65536-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
}
void Divide3and5()//分离AD转换结果的高3位
{
uiAdHigh3=uiAdDate8&0xE0;//将8位转换结果的低5位清除
uiAdHigh3=_cror_(uiAdHigh3,5);//循环右移5位
uiAdLow5=uiAdDate8&0x1F;//将8位转换结果高3位清零
}
void T0_Process()interrupt 1//定时器T0中断
{
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];//显示低五位对应的十进制的十位
break;
case 2:
sbtLedSel=0;
P2=arrDigSelect[ucSegState];
P0=arrSegSelect[uiAdLow5%10];//显示低五位对应的十进制的个位
break;
case 3:
sbtLedSel=1;
P0=uiAdDate8;//发光二极管亮
break;
}
}
void ADC_Process()interrupt 5//ADC中断处理
{
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);
}
说明:
头文件<intrins.h>中,包含两个函数:
_crol_(var,n):将var循环左移n位
_cror_(var,n):将var循环右移n位