天天

[转]AVR扫描矩阵键盘

0
阅读(5179)

AVR扫描矩阵键盘,即是把某一条(只有一条)行线置为低电平,而列线全部置为输入方向,然后检测列线,如果检测到某一条列线是低电平,那么就表示位于这条列线与输出低电平的行线的交点处的按键被按下了。要扫描16个按键,就依次以这样的方法扫描16次,之后就可以确定哪一个按键被按下了。当然这里也少不了延时消除按键抖动的环节。以下是详细步骤(以此图为例):


① 设置PA口的高四位为输入,最好使能上拉电阻,抗干扰,低四位为输出;
③ 先扫描第一行,则使PA0输出低电平;
②  检测各个列线,是否有低电平的现象?
③ 如果有,则调用延时函数后再次检测列线,如果某列线仍然被检测到低电平,则表示有对应按键按下,进入按键赋值。
④ 如果没有,依次扫描第二行,使PA1输出低电平……
其他几行的扫描都和第一行的一摸一样,我们只需要变化行线的电平就可以了,需要注意的是每次只扫描一行,即只有一根行线输出低电平。
如果读者理解了独立键盘的原理话,那么矩阵键盘也应该是很简单的,但是程序的编写才是关键。有详细注释的源程序附件形式上传,数码管显示函数的功能解释在《事无巨细,数码管闪亮》一文有详细解释:
[hide][/hide]
十分重要的几个地方:
① 在设置行线电平的时候,一定要先设置PORTx再设置DDRx,否则会导致按键扫描的全盘失败。(对此笔者也是十分无解,试验多次都是一样的结果)
② 读取IO的电平之后,要注意把不需要的位全部屏蔽掉。如果不屏蔽会导致按键扫描有高丢失率。

③ 关于消抖的延时时间,一般是20ms,要注意结合自己的晶振频率来编写。MEGA单片机默认为内部1M晶振源,但是外部晶振要比内部晶振来得准确。但是无论如何,要注意单片机的内部熔丝位和GCC的MakeFile文件对应好。
④ 最好把列线设置为带上拉电阻的输入状态,这样会在引脚悬空的时候会由上拉电阻拉成高电平,可以有效减少干扰。

 uchar keyscan(void)
{
 uchar temp=0;
 
 PORTA=0xef;
 DDRA=0xf0;          //设置行线电平,注意先PORTx再DDRx
 temp=PINA;          //读取列线电平            
 temp&=0x0f;        //屏蔽行线电平
 if(temp!=0x0f)      //判断是否有按键按下
 {
  _delay_ms(20);               //有按键按下,延时消抖
  temp=PINA;                 //消抖后再读列线电平
  temp&=0x0f;                //屏蔽行线电平
  if(temp!=0x0f)              //判断是否抖动
  {
   temp=PINA;         //确定不是抖动,读取列线
   switch(temp)        //根据列线电平给按键赋值
   {
    case 0xee:key=0;break;
    case 0xed:key=1;break;
    case 0xeb:key=2;break;
    case 0xe7:key=3;break; 
   }
  }
  while(temp!=0x0f)                //松手检测
  {
   temp=PINA;
   temp&=0x0f;   
  }
 }
  //以下为第二到第四行扫描,只需要更改行线电平
 PORTA=0xdf;                                    
 DDRA=0xf0;
 temp=PINA;
 temp&=0x0f;
 if(temp!=0x0f)
 {
  _delay_ms(20);
  temp=PINA;
  temp&=0x0f;
  if(temp!=0x0f)
  {
   temp=PINA;
   switch(temp)
   {
    case 0xde:key=4;break;
    case 0xdd:key=5;break;
    case 0xdb:key=6;break;
    case 0xd7:key=7;break;    
   }
  }
  while(temp!=0x0f)
  {
   temp=PINA;
   temp&=0x0f;   
  }
 }
 
 PORTA=0xbf;
 DDRA=0xf0;
 temp=PINA;
 temp&=0x0f;
 if(temp!=0x0f)
 {
  _delay_ms(20);
  temp=PINA;
  temp&=0x0f;
  if(temp!=0x0f)
  {
   temp=PINA;
   switch(temp)
   {
    case 0xbe:key=8;break;
    case 0xbd:key=9;break;
    case 0xbb:key=10;break;
    case 0xb7:key=11;break;    
   }
  }
  while(temp!=0x0f)
  {
   temp=PINA;
   temp&=0x0f;   
  }
 }
 
 PORTA=0x7f;
 DDRA=0xf0;
 temp=PINA;
 temp&=0x0f;
 if(temp!=0x0f)
 {
  _delay_ms(20);
  temp=PINA;
  temp&=0x0f;
  if(temp!=0x0f)
  {
   temp=PINA;
   switch(temp)
   {
    case 0x7e:key=12;break;
    case 0x7d:key=13;break;
    case 0x7b:key--;break;
    case 0x77:key++;break;    
   }
  }
  while(temp!=0x0f)
  {
   temp=PINA;
   temp&=0x0f;   
  }
 }
 return key;
}