{
P1_0 = 1; while( 1 ) {
{
//作为输入引脚,必须先输出高电平 //永远为真,即死循环
//如果按键,则为低电平
//如果一直按着键,则不停地执行该循环,实际是等待松键
if( P1_0 == 0 )
keyPut( 6 ); //保存按键编号值为按键队列 while( P1_0 == 0 );
}
if( keyHit() != 0 )
//如果队列中有按键
//从队列中取出按键值,并显示在数码管上
P2=Seg7Code[ keyGet() ];
}
}
例二:在例一中考虑按键20ms抖动问题。 #include #include “KEY.H” void main( void ) {
P1_0 = 1; while( 1 ) {
{
//作为输入引脚,必须先输出高电平 //永远为真,即死循环
//如果按键,则为低电平
if( P1_0 == 0 )
delay20ms(); //延时20ms,跳过接下抖动 keyPut( 6 ); //保存按键编号值为按键队列 while( P1_0 == 0 );
//如果一直按着键,则不停地执行该循环,实际是等待松键
}
delay20ms(); //延时20ms,跳过松开抖动 if( keyHit() != 0 )
}
例三:在例二中考虑干扰问题。即小于20ms的负脉冲干扰。 #include #include “KEY.H” void main( void ) {
P1_0 = 1; while( 1 ) {
{
//作为输入引脚,必须先输出高电平 //永远为真,即死循环
//如果按键,则为低电平
}
//如果队列中有按键
//从队列中取出按键值,并显示在数码管上
P2=Seg7Code[ keyGet() ];
if( P1_0 == 0 )
delay20ms(); //延时20ms,跳过接下抖动
if( P1_0 == 1 ) continue; //假按键 keyPut( 6 ); //保存按键编号值为按键队列 while( P1_0 == 0 );
//如果一直按着键,则不停地执行该循环,实际是等待松键
}
delay20ms(); //延时20ms,跳过松开抖动 if( keyHit() != 0 )
}
例四:状态图编程法。通过20ms周期中断,扫描按键。
}
//如果队列中有按键
//从队列中取出按键值,并显示在数码管上
P2=Seg7Code[ keyGet() ];
/**************************************************************************************** 采用晶体为12KHz时,指令周期为1ms(即主频为1KHz),这样T0工作在定时器方式2,8位自动重载。计数值为20,即可产生20ms的周期性中断,在中断服务程序中实现按键扫描
*****************************************************************************************/ #include #include “KEY.H” void main( void ) {
TMOD = (TMOD & 0xf0 ) | 0x02; //不改变T1的工作方式,T0为定时器方式2 TH0 = -20; TL0=TH0; TR0=1; ET0=1; EA=1;
{
if( keyHit() != 0 )
}
void timer0int( void ) interrupt 1 {
static unsigned char sts=0; P1_0 = 1; {
case 0: if( P1_0==0 ) sts=1; break; //按键则转入状态1 case 1:
if( P1_0==1 ) sts=0; break;
}
}
case 2: if( P1_0==1 ) sts=3; break; //如果松键,则转状态3 case 3:
if( P1_0==0 ) sts=2; else sts=0;
//假松键,回状态2
//真松键,回状态0,等待下一次按键过程
//假按错,或干扰,回状态0
else{ sts=2; keyPut( 6 ); } //确实按键,键值入队列,并转状态2
//作为输入引脚,必须先输出高电平
switch( sts )
//20ms;T0的中断号为1
}
//如果队列中有按键
//从队列中取出按键值,并显示在数码管上
P2=Seg7Code[ keyGet() ];
//计数周期为20个主频脉,即20ms //先软加载一次计数值 //允许T0开始计数
//允许T0计数溢出时产生中断请求 //允许CPU响应中断请求
while( 1 ) //永远为真,即死循环
例五:状态图编程法。
/**************************************************************************************** 如果采用晶体为12MHz时,指令周期为1us(即主频为1MHz),要产生20ms左右的计时,则计数值达到20000,T0工作必须为定时器方式1,16位非自动重载,即可产生20ms的周期性中断,在中断服务程序中实现按键扫描
*****************************************************************************************/ #include #include “KEY.H”
void main( void ) {
TMOD = (TMOD & 0xf0 ) | 0x01; //不改变T1的工作方式,T0为定时器方式1 TL0 = -20000; TR0=1; ET0=1; EA=1;
{
if( keyHit() != 0 )
}
void timer0int( void ) interrupt 1 {
static unsigned char sts=0; TL0 = -20000;
P1_0 = 1; {
case 0: if( P1_0==0 ) sts=1; break; //按键则转入状态1 case 1:
if( P1_0==1 ) sts=0; break;
}
}
case 2: if( P1_0==1 ) sts=3; break; //如果松键,则转状态3 case 3:
if( P1_0==0 ) sts=2; else sts=0;
//假松键,回状态2
//真松键,回状态0,等待下一次按键过程
//假按错,或干扰,回状态0
else{ sts=2; keyPut( 6 ); } //确实按键,键值入队列,并转状态2
//方式1为软件重载
//右移8位,实际上是取高8位
TH0 = (-20000)>>8; switch( sts )
//20ms;T0的中断号为1
}
//如果队列中有按键
//从队列中取出按键值,并显示在数码管上
P2=Seg7Code[ keyGet() ];
//计数周期为20000个主频脉,自动取低8位 //右移8位,实际上是取高8位 //允许T0开始计数
//允许T0计数溢出时产生中断请求 //允许CPU响应中断请求
TH0 = (-20000)>>8;
while( 1 ) //永远为真,即死循环
//作为输入引脚,必须先输出高电平
例六:4X4按键。
/**************************************************************************************** 由P1端口的高4位和低4位构成4X4的矩阵键盘,本程序只认为单键操作为合法,同时按多键时无效。 这样下面的X,Y的合法值为0x7, 0xb, 0xd, 0xe, 0xf,通过表keyCode影射变换可得按键值
10 P1.0(0xE) C P1.1(0xD) 8 P1.2(0xB) 4 P1.3(0x7) F B 7 3 E A 6 2 D 9 5 1 P1.4(E) P1.5(D) P1.6(B) P1.7(7) *****************************************************************************************/ #include #include “KEY.H”
unsigned char keyScan( void ) {
//返回0表示无按键,或无效按键,其它值为按键编码值
code unsigned char keyCode[16]=
/0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 3, 4, 0 }; unsigned char x, y, retVal; P1=0x0f; x=P1&0x0f; P1=0xf0;
//低四位输入,高四位输出0 //P1输入后,清高四位,作为X值 //高四位输入,低四位输出0
//P1输入后移位到低四位,并清高四位,作为Y值
y=(P1 >> 4) & 0x0f;
retVal = keyCode[x]*4 + keyCode[y]; //根据本公式倒算按键编码 if( retVal==0 ) return(0); else return( retVal-4 ); }
//比如按键‘1’,得X=0x7,Y=0x7,算得retVal= 5,所以返回函数值1。 //双如按键‘7’,得X=0xb,Y=0xd,算得retVal=11,所以返回函数值7。 void main( void ) {
TMOD = (TMOD & 0xf0 ) | 0x01; //不改变T1的工作方式,T0为定时器方式1 TL0 = -20000; TR0=1; ET0=1; EA=1;
{
if( keyHit() != 0 )
//如果队列中有按键
//计数周期为20000个主频脉,自动取低8位 //右移8位,实际上是取高8位 //允许T0开始计数
//允许T0计数溢出时产生中断请求 //允许CPU响应中断请求
TH0 = (-20000)>>8;
while( 1 ) //永远为真,即死循环
P2=Seg7Code[ keyGet() ];
}
void timer0int( void ) interrupt 1 {
static unsigned char sts=0; TL0 = -20000;
P1_0 = 1; {
}
//从队列中取出按键值,并显示在数码管上
//20ms;T0的中断号为1 //方式1为软件重载
//右移8位,实际上是取高8位
TH0 = (-20000)>>8; switch( sts )
//作为输入引脚,必须先输出高电平
case 0: if( keyScan()!=0 ) sts=1; break; //按键则转入状态1 case 1:
if( keyScan()==0 ) sts=0; break;
//假按错,或干扰,回状态0
else{ sts=2; keyPut( keyScan() ); } //确实按键,键值入队列,并转状态2
}
}
case 2: if(keyScan()==0 ) sts=3; break; //如果松键,则转状态3 case 3:
if( keyScan()!=0 ) sts=2; else sts=0;
//假松键,回状态2
//真松键,回状态0,等待下一次按键过程
第六节:低频频率计
实例目的:学时定时器、计数器、中断应用
说明:选用24MHz的晶体,主频可达2MHz。用T1产生100us的时标,T0作信号脉冲计数器。假设晶体频率没有误差,而且稳定不变(实际上可达万分之一);被测信号是周期性矩形波(正负脉冲宽度都不能小于0.5us),频率小于1MHz,大于1Hz。要求测量时标1S,测量精度为0.1%。
解:从测量精度要求来看,当频率超过1KHz时,可采用1S时标内计数信号脉冲个数来测量信号频,而信号频率低于1KHz时,可以通过测量信号的周期来求出信号频率。两种方法自动转换。
对于低于1KHz的信号,信号周期最小为1ms,也就是说超过1000us,而我们用的定时器计时脉冲周期为0.5us,如果定时多计或少计一个脉冲,误差为1us,所以相对误差为1us/1000us=0.1%。信号周期越大,即信号频率越低,相对误差就越小。
从上面描述来看,当信号频率超过1KHz后,信号周期就少于1000us,显然采用上面的测量方法,不能达到测量精度要求,这时我们采用1S单位时间计数信号的脉冲个数,最少能计到1000个脉冲,由于信号频率不超过1MHz,而我们定时脉冲为2MHz,最差多计或少计一个信号脉冲,这样相对误差为1/1000,可见信号频率越高,相对误差越小。
信号除输入到T1(P3.5)外,还输入到INT1(P3.3)。 unsigned int us100; unsigned char Second; unsigned int K64; unsigned char oldT0;
unsigned int oldus, oldK64, oldT1; unsigned long fcy;
//存放频率值,单位为Hz
//对64K单位计数,即有多少个64K
//对100us时间间隔单位计数,即有多少个100us。