打印
[51单片机]

从单片机基础到程序框架(连载)

[复制链接]
楼主: jianhong_wu
手机看帖
扫描二维码
随时随地手机跟帖
341
jianhong_wu|  楼主 | 2017-12-17 11:37 | 只看该作者 |只看大图 回帖奖励 |倒序浏览
第一百节: “行列扫描式”矩阵按键的单个触发(优化版)。
第一百节_pdf文件.pdf (109.4 KB)
【100.1   “行列扫描式”矩阵按键。】

               
                上图100.1.1  有源蜂鸣器电路


      
                上图100.1.2  3*3矩阵按键的电路

       写程序,凡是出现“重复性、相似性”的代码,都可以加入“循环,判断,数组”这类语句对代码进行压缩优化。上一节讲的矩阵按键,代码是记流水账式的,出现很多“重复性、相似性”的代码,是没有经过优化的“原始版”,本节的目的是对上一节的代码进行优化,让大家从中发现一些技巧。
       多说一句,我一直认为,只要单片机容量够,代码多一点少一点并不重要,只要不影响运行效率就行。而且有时候,代码写多一点,可读性非常强,修改起来也非常方便。如果一味的追求压缩代码,就会刻意用很多“循环,判断,数组”等元素,代码虽然紧凑了,但是可分离性,可更改性,可阅读性就没那么强。因此,做项目的时候,某些代码要不要进行压缩,是没有绝对标准的,能因敌而取胜者谓之神。
       本节例程实现的功能:9个矩阵按键,每按下1个按键都触发一次蜂鸣器鸣叫。

#include "REG52.H"  

#define KEY_VOICE_TIME   50     

#define KEY_SHORT_TIME  20     //按键去抖动的“滤波”时间

void T0_time();
void SystemInitial(void) ;
void Delay(unsigned long u32DelayTime) ;
void PeripheralInitial(void) ;

void BeepOpen(void);   
void BeepClose(void);

void VoiceScan(void);
void KeyScan(void);   
void KeyTask(void);   

sbit P3_4=P3^4;       //蜂鸣器

sbit ROW_INPUT1=P2^2;  //第1行输入口。
sbit ROW_INPUT2=P2^1;  //第2行输入口。
sbit ROW_INPUT3=P2^0;  //第3行输入口。

sbit COLUMN_OUTPUT1=P2^5;  //第1列输出口。
sbit COLUMN_OUTPUT2=P2^4;  //第2列输出口。
sbit COLUMN_OUTPUT3=P2^3;  //第3列输出口。

volatile unsigned char vGu8BeepTimerFlag=0;  
volatile unsigned int vGu16BeepTimerCnt=0;  


volatile unsigned char vGu8KeySec=0;  //按键的触发序号

void main()
{
SystemInitial();            
Delay(10000);               
PeripheralInitial();      
    while(1)  
{  
    KeyTask();       //按键的任务函数
    }
}

/* 注释一:
*  矩阵按键扫描的详细过程:
*  先输出某1列低电平,其它2列输出高电平,延时等待2ms后(等此3列输出同步稳定),
*  再分别判断3行的输入IO口, 如果发现哪一行是低电平,就说明对应的某个按键被触发。
*  依次循环切换输出的3种状态,并且分别判断输入的3行,就可以检测完9个按键。矩阵按键的
*  去抖动处理方法跟我前面讲的独立按键去抖动方法是一样的,不再重复多讲。
*/

void KeyScan(void)  //此函数放在定时中断里每1ms扫描一次
{
   static unsigned char Su8KeyLock=0;        
   static unsigned int  Su16KeyCnt=0;  
   static unsigned char Su8KeyStep=1;  

   static unsigned char Su8ColumnRecord=0;  //本节多增加此变量用来切换当前列的输出

   switch(Su8KeyStep)
   {
     case 1:   
          if(0==Su8ColumnRecord)  //按键扫描输出第一列低电平
{
          COLUMN_OUTPUT1=0;      
          COLUMN_OUTPUT2=1;
          COLUMN_OUTPUT3=1;  
}
          else if(1==Su8ColumnRecord)  //按键扫描输出第二列低电平
{
          COLUMN_OUTPUT1=1;      
          COLUMN_OUTPUT2=0;
          COLUMN_OUTPUT3=1;  
}
          else     //按键扫描输出第三列低电平
{
          COLUMN_OUTPUT1=1;      
          COLUMN_OUTPUT2=1;
          COLUMN_OUTPUT3=0;  
}
          Su16KeyCnt=0;  //延时计数器清零
          Su8KeyStep++;  //切换到下一个运行步骤
          break;

     case 2:     //延时等待2ms后(等此3列输出同步稳定)。不是按键的去抖动延时。
          Su16KeyCnt++;
          if(Su16KeyCnt>=2)
          {
             Su16KeyCnt=0;
             Su8KeyStep++;     //切换到下一个运行步骤
          }
          break;

     case 3:
          if(1==ROW_INPUT1&&1==ROW_INPUT2&&1==ROW_INPUT3)
          {  
             Su8KeyStep=1;  //如果没有按键按下,返回到第一个运行步骤重新开始扫描!!!!!!
             Su8KeyLock=0;  //按键自锁标志清零
             Su16KeyCnt=0;  //按键去抖动延时计数器清零,此行非常巧妙   
             Su8ColumnRecord++;  //输出下一列
             if(Su8ColumnRecord>=3)  
             {
                Su8ColumnRecord=0; //依次输出完第3列之后,继续从第1列开始输出低电平
             }     
          }
          else if(0==Su8KeyLock)  //有按键按下,且是第一次触发
          {
              if(0==ROW_INPUT1&&1==ROW_INPUT2&&1==ROW_INPUT3)
              {
                  Su16KeyCnt++;  //去抖动延时计数器
                  if(Su16KeyCnt>=KEY_SHORT_TIME)
                  {
                      Su16KeyCnt=0;
                      Su8KeyLock=1;//自锁置1,避免一直触发,只有松开按键,此标志位才会被清零

                      if(0==Su8ColumnRecord)  //第1列输出低电平
                      {
                           vGu8KeySec=1;  //触发1号键 对应S1键
                      }
                      else if(1==Su8ColumnRecord)  //第2列输出低电平
                      {
                           vGu8KeySec=2;  //触发2号键 对应S2键
                      }
                      else if(2==Su8ColumnRecord)  //第3列输出低电平
                      {
                           vGu8KeySec=3;  //触发3号键 对应S3键
                      }
                  }

              }
              else if(1==ROW_INPUT1&&0==ROW_INPUT2&&1==ROW_INPUT3)
              {
                  Su16KeyCnt++;  //去抖动延时计数器
                  if(Su16KeyCnt>=KEY_SHORT_TIME)
                  {
                      Su16KeyCnt=0;
                      Su8KeyLock=1;//自锁置1,避免一直触发,只有松开按键,此标志位才会被清零

                      if(0==Su8ColumnRecord)  //第1列输出低电平
                      {
                           vGu8KeySec=4;  //触发4号键 对应S4键
                      }
                      else if(1==Su8ColumnRecord)  //第2列输出低电平
                      {
                           vGu8KeySec=5;  //触发5号键 对应S5键
                      }
                      else if(2==Su8ColumnRecord)  //第3列输出低电平
                      {
                           vGu8KeySec=6;  //触发6号键 对应S6键
                      }
                  }   
              }
              else if(1==ROW_INPUT1&&1==ROW_INPUT2&&0==ROW_INPUT3)
              {
                  Su16KeyCnt++;  //去抖动延时计数器
                  if(Su16KeyCnt>=KEY_SHORT_TIME)
                  {
                      Su16KeyCnt=0;
                      Su8KeyLock=1;//自锁置1,避免一直触发,只有松开按键,此标志位才会被清零
                      if(0==Su8ColumnRecord)  //第1列输出低电平
                      {
                           vGu8KeySec=7;  //触发7号键 对应S7键
                      }
                      else if(1==Su8ColumnRecord)  //第2列输出低电平
                      {
                           vGu8KeySec=8;  //触发8号键 对应S8键
                      }
                      else if(2==Su8ColumnRecord)  //第3列输出低电平
                      {
                           vGu8KeySec=9;  //触发9号键 对应S9键
                      }
                  }   
              }

          }
          break;
   }

}

void KeyTask(void)    //按键任务函数,放在主函数内
{
if(0==vGu8KeySec)
{
return; //按键的触发序号是0意味着无按键触发,直接退出当前函数,不执行此函数下面的代码
}

switch(vGu8KeySec) //根据不同的按键触发序号执行对应的代码
{
   case 1:     //S1触发的任务

            vGu8BeepTimerFlag=0;  
vGu16BeepTimerCnt=KEY_VOICE_TIME;  //发出“嘀”一声
            vGu8BeepTimerFlag=1;  

vGu8KeySec=0;  //响应按键服务处理程序后,按键编号必须清零,避免一直触发
break;

   case 2:     //S2触发的任务

            vGu8BeepTimerFlag=0;  
vGu16BeepTimerCnt=KEY_VOICE_TIME;  //发出“嘀”一声
            vGu8BeepTimerFlag=1;  

vGu8KeySec=0;  //响应按键服务处理程序后,按键编号必须清零,避免一直触发
break;

   case 3:     //S3触发的任务

            vGu8BeepTimerFlag=0;  
vGu16BeepTimerCnt=KEY_VOICE_TIME;  //发出“嘀”一声
            vGu8BeepTimerFlag=1;  

vGu8KeySec=0;  //响应按键服务处理程序后,按键编号必须清零,避免一直触发
break;

   case 4:     //S4触发的任务

            vGu8BeepTimerFlag=0;  
vGu16BeepTimerCnt=KEY_VOICE_TIME;  //发出“嘀”一声
            vGu8BeepTimerFlag=1;  

vGu8KeySec=0;  //响应按键服务处理程序后,按键编号必须清零,避免一直触发
break;

   case 5:     //S5触发的任务

            vGu8BeepTimerFlag=0;  
vGu16BeepTimerCnt=KEY_VOICE_TIME;  //发出“嘀”一声
            vGu8BeepTimerFlag=1;  

vGu8KeySec=0;  //响应按键服务处理程序后,按键编号必须清零,避免一直触发
break;

   case 6:     //S6触发的任务

            vGu8BeepTimerFlag=0;  
vGu16BeepTimerCnt=KEY_VOICE_TIME;  //发出“嘀”一声
            vGu8BeepTimerFlag=1;  

vGu8KeySec=0;  //响应按键服务处理程序后,按键编号必须清零,避免一直触发
break;

   case 7:     //S7触发的任务

            vGu8BeepTimerFlag=0;  
vGu16BeepTimerCnt=KEY_VOICE_TIME;  //发出“嘀”一声
            vGu8BeepTimerFlag=1;  

vGu8KeySec=0;  //响应按键服务处理程序后,按键编号必须清零,避免一直触发
break;

   case 8:     //S8触发的任务

            vGu8BeepTimerFlag=0;  
vGu16BeepTimerCnt=KEY_VOICE_TIME;  //发出“嘀”一声
            vGu8BeepTimerFlag=1;  

vGu8KeySec=0;  //响应按键服务处理程序后,按键编号必须清零,避免一直触发
break;

   case 9:     //S9触发的任务

            vGu8BeepTimerFlag=0;  
vGu16BeepTimerCnt=KEY_VOICE_TIME;  //发出“嘀”一声
            vGu8BeepTimerFlag=1;  

vGu8KeySec=0;  //响应按键服务处理程序后,按键编号必须清零,避免一直触发
break;

}
}



void T0_time() interrupt 1     
{
VoiceScan();  
KeyScan();    //按键识别的驱动函数

TH0=0xfc;   
TL0=0x66;   
}


void SystemInitial(void)
{
TMOD=0x01;  
TH0=0xfc;   
TL0=0x66;   
EA=1;      
ET0=1;      
TR0=1;      
}

void Delay(unsigned long u32DelayTime)
{
    for(;u32DelayTime>0;u32DelayTime--);
}

void PeripheralInitial(void)
{

}

void BeepOpen(void)
{
P3_4=0;  
}

void BeepClose(void)
{
P3_4=1;  
}

void VoiceScan(void)
{

          static unsigned char Su8Lock=0;  

if(1==vGu8BeepTimerFlag&&vGu16BeepTimerCnt>0)
          {
                  if(0==Su8Lock)
                  {
                   Su8Lock=1;  
BeepOpen();
     }
    else  
{     

                       vGu16BeepTimerCnt--;         

                   if(0==vGu16BeepTimerCnt)
                   {
                           Su8Lock=0;     
BeepClose();  
                   }

}
          }         
}


使用特权

评论回复
342
ly33xy| | 2017-12-17 15:04 | 只看该作者
收益良多

使用特权

评论回复
343
Dalarl2008| | 2017-12-18 22:35 | 只看该作者
非常感谢。

使用特权

评论回复
344
Dalarl2008| | 2017-12-18 22:36 | 只看该作者
以前搞修理,现在学习单片机,很适合初学者。

使用特权

评论回复
345
arima| | 2017-12-25 20:12 | 只看该作者
收益良多.圣诞快乐!

使用特权

评论回复
346
蔬木果| | 2017-12-31 13:03 | 只看该作者
感谢分享

使用特权

评论回复
347
jianhong_wu|  楼主 | 2017-12-31 16:51 | 只看该作者
第一百零一节: 矩阵按键鼠标式的单击与双击。
第一百零一节_pdf文件.pdf (127.59 KB)
【101.1   矩阵按键鼠标式的单击与双击。】

            
                上图101.1.1  有源蜂鸣器电路

      
                上图101.1.2  LED电路


      
                上图101.1.3  3*3矩阵按键的电路

        矩阵按键与前面章节独立按键的单击与双击的处理思路是一样的,本节讲矩阵按键的单击与双击,也算是重温之前章节讲的内容。
        鼠标的左键,可以触发单击,也可以触发双击。双击的规则是这样的,两次单击,如果第1次单击与第2次单击的时间比较“短”的时候,则这两次单击就构成双击。编写这个程序的最大亮点是如何控制好第1次单击与第2次单击的时间间隔。程序例程要实现的功能是:以S1按键为例,(1)单击改变LED灯的显示状态。单击一次LED从原来“灭”的状态变成“亮”的状态,或者从原来“亮”的状态变成“灭”的状态,依次循环切换。(2)双击则蜂鸣器发出“嘀”的一声。代码如下:

#include "REG52.H"  

#define KEY_VOICE_TIME   50     
#define KEY_SHORT_TIME  20     //按键去抖动的“滤波”时间
#define KEY_INTERVAL_TIME  80  //连续两次单击之间的最大有效时间。因为是矩阵,80不一定是80ms

void T0_time();
void SystemInitial(void) ;
void Delay(unsigned long u32DelayTime) ;
void PeripheralInitial(void) ;

void BeepOpen(void);   
void BeepClose(void);
void LedOpen(void);   
void LedClose(void);

void VoiceScan(void);
void KeyScan(void);   
void SingleKeyTask(void);   //单击按键任务函数,放在主函数内
void DoubleKeyTask(void);   //双击按键任务函数,放在主函数内

sbit P3_4=P3^4;       //蜂鸣器
sbit P1_4=P1^4;       //LED

sbit ROW_INPUT1=P2^2;  //第1行输入口。
sbit ROW_INPUT2=P2^1;  //第2行输入口。
sbit ROW_INPUT3=P2^0;  //第3行输入口。

sbit COLUMN_OUTPUT1=P2^5;  //第1列输出口。
sbit COLUMN_OUTPUT2=P2^4;  //第2列输出口。
sbit COLUMN_OUTPUT3=P2^3;  //第3列输出口。

volatile unsigned char vGu8BeepTimerFlag=0;  
volatile unsigned int vGu16BeepTimerCnt=0;  

unsigned char Gu8LedStatus=0; //记录LED灯的状态,0代表灭,1代表亮
volatile unsigned char vGu8SingleKeySec=0;  //单击按键的触发序号
volatile unsigned char vGu8DoubleKeySec=0;  //双击按键的触发序号

void main()
{
SystemInitial();            
Delay(10000);               
PeripheralInitial();      
    while(1)  
{  
        SingleKeyTask();    //单击按键任务函数
        DoubleKeyTask();    //双击按键任务函数
    }
}

/* 注释一:
*  矩阵按键扫描的详细过程:
*  先输出某1列低电平,其它2列输出高电平,延时等待2ms后(等此3列输出同步稳定),
*  再分别判断3行的输入IO口, 如果发现哪一行是低电平,就说明对应的某个按键被触发。
*  依次循环切换输出的3种状态,并且分别判断输入的3行,就可以检测完9个按键。矩阵按键的
*  去抖动处理方法跟我前面讲的独立按键去抖动方法是一样的。
*/

/* 注释二:
* 双击按键扫描的详细过程:
* 第一步:平时没有按键被触发时,按键的自锁标志,去抖动延时计数器一直被清零。
*         如果之前已经有按键触发过1次单击,那么启动时间间隔计数器Su16KeyIntervalCnt1,
*         在KEY_INTERVAL_TIME这个允许的时间差范围内,如果一直没有第2次单击触发,
*         则把累加按键触发的次数Su8KeyTouchCnt1也清零,上一次累计的单击数被清零,
*         就意味着下一次新的双击必须重新开始累加两次单击数。
* 第二步:一旦有按键被按下,去抖动延时计数器开始在定时中断函数里累加,在还没累加到
*         阀值KEY_SHORT_TIME时,如果在这期间由于受外界干扰或者按键抖动,而使
*         IO口突然瞬间触发成高电平,这个时候马上把延时计数器Su16KeyCnt
*         清零了,这个过程非常巧妙,非常有效地去除瞬间的杂波干扰,以后凡是用到开关感应器的时候,
*         都可以用类似这样的方法去干扰。
* 第三步:如果按键按下的时间超过了阀值KEY_SHORT_TIME,马上把自锁标志Su8KeyLock置1,
*         防止按住按键不松手后一直触发。与此同时,累加1次按键次数,如果按键次数累加有2次,
*         则认为触发双击按键,并把编号vGu8DoubleKeySec赋值。
* 第四步:等按键松开后,自锁标志Su8KeyLock及时清零解锁,为下一次自锁做准备。并且累加间隔时间,
*         防止两次按键的间隔时间太长。如果连续2次单击的间隔时间太长达到了KEY_INTERVAL_TIME
*         的长度,立即清零当前按键次数的计数器,这样意味着上一次的累加单击数无效,下一次双击
*         必须重新累加新的单击数。
*/

void KeyScan(void)  //此函数放在定时中断里每1ms扫描一次
{
   static unsigned char Su8KeyLock=0;        
   static unsigned int  Su16KeyCnt=0;  
   static unsigned char Su8KeyStep=1;  

   static unsigned char Su8ColumnRecord=0;  //用来切换当前列的输出

   static unsigned char Su8KeyTouchCnt1;       //S1按键的次数记录
   static unsigned int  Su16KeyIntervalCnt1;   //S1按键的间隔时间计数器

   switch(Su8KeyStep)
   {
     case 1:   
          if(0==Su8ColumnRecord)  //按键扫描输出第一列低电平
{
          COLUMN_OUTPUT1=0;      
          COLUMN_OUTPUT2=1;
          COLUMN_OUTPUT3=1;  
}
          else if(1==Su8ColumnRecord)  //按键扫描输出第二列低电平
{
          COLUMN_OUTPUT1=1;      
          COLUMN_OUTPUT2=0;
          COLUMN_OUTPUT3=1;  
}
          else     //按键扫描输出第三列低电平
{
          COLUMN_OUTPUT1=1;      
          COLUMN_OUTPUT2=1;
          COLUMN_OUTPUT3=0;  
}
          Su16KeyCnt=0;  //延时计数器清零
          Su8KeyStep++;  //切换到下一个运行步骤
          break;

     case 2:     //延时等待2ms后(等此3列输出同步稳定)。不是按键的去抖动延时。
          Su16KeyCnt++;
          if(Su16KeyCnt>=2)
          {
             Su16KeyCnt=0;
             Su8KeyStep++;     //切换到下一个运行步骤
          }
          break;

     case 3:
          if(1==ROW_INPUT1&&1==ROW_INPUT2&&1==ROW_INPUT3)
          {  
             Su8KeyStep=1;  //如果没有按键按下,返回到第一个运行步骤重新开始扫描!!!!!!
             Su8KeyLock=0;  //按键自锁标志清零
             Su16KeyCnt=0;  //按键去抖动延时计数器清零,此行非常巧妙

if(Su8KeyTouchCnt1>=1) //之前已经有按键触发过一次,启动间隔时间的计数器
{
Su16KeyIntervalCnt1++; //按键间隔的时间计数器累加
if(Su16KeyIntervalCnt1>=KEY_INTERVAL_TIME) //达到最大允许的间隔时间,溢出无效
{
Su16KeyIntervalCnt1=0; //时间计数器清零
Su8KeyTouchCnt1=0;     //清零按键的按下的次数,因为间隔时间溢出无效
}
}

             Su8ColumnRecord++;  //输出下一列
             if(Su8ColumnRecord>=3)  
             {
                Su8ColumnRecord=0; //依次输出完第3列之后,继续从第1列开始输出低电平
             }     
          }
          else if(0==Su8KeyLock)  //有按键按下,且是第一次触发
          {
              if(0==ROW_INPUT1&&1==ROW_INPUT2&&1==ROW_INPUT3)
              {
                  Su16KeyCnt++;  //去抖动延时计数器
                  if(Su16KeyCnt>=KEY_SHORT_TIME)
                  {
                      Su8KeyLock=1;//自锁置1,避免一直触发,只有松开按键,此标志位才会被清零

                      if(0==Su8ColumnRecord)  //第1列输出低电平
                      {
                           Su16KeyIntervalCnt1=0;   //按键有效间隔的时间计数器清零
                           Su8KeyTouchCnt1++;       //记录当前单击的次数
                           if(1==Su8KeyTouchCnt1)   //只按了1次
                           {
                               vGu8SingleKeySec=1;     //单击任务,触发1号键 对应S1键
                           }
                           else if(Su8KeyTouchCnt1>=2)  //连续按了两次以上
                           {
                                Su8KeyTouchCnt1=0;    //统计按键次数清零
                                vGu8SingleKeySec=1;   //单击任务,触发1号键 对应S1键
vGu8DoubleKeySec=1;   //双击任务,触发1号键 对应S1键
                           }
                      }
                      else if(1==Su8ColumnRecord)  //第2列输出低电平
                      {
                           vGu8SingleKeySec=2;  //触发2号键 对应S2键
                      }
                      else if(2==Su8ColumnRecord)  //第3列输出低电平
                      {
                           vGu8SingleKeySec=3;  //触发3号键 对应S3键
                      }
                  }

              }
              else if(1==ROW_INPUT1&&0==ROW_INPUT2&&1==ROW_INPUT3)
              {
                  Su16KeyCnt++;  //去抖动延时计数器
                  if(Su16KeyCnt>=KEY_SHORT_TIME)
                  {
                      Su8KeyLock=1;//自锁置1,避免一直触发,只有松开按键,此标志位才会被清零

                      if(0==Su8ColumnRecord)  //第1列输出低电平
                      {
                           vGu8SingleKeySec=4;  //触发4号键 对应S4键
                      }
                      else if(1==Su8ColumnRecord)  //第2列输出低电平
                      {
                           vGu8SingleKeySec=5;  //触发5号键 对应S5键
                      }
                      else if(2==Su8ColumnRecord)  //第3列输出低电平
                      {
                           vGu8SingleKeySec=6;  //触发6号键 对应S6键
                      }
                  }   
              }
              else if(1==ROW_INPUT1&&1==ROW_INPUT2&&0==ROW_INPUT3)
              {
                  Su16KeyCnt++;  //去抖动延时计数器
                  if(Su16KeyCnt>=KEY_SHORT_TIME)
                  {
                      Su8KeyLock=1;//自锁置1,避免一直触发,只有松开按键,此标志位才会被清零
                      if(0==Su8ColumnRecord)  //第1列输出低电平
                      {
                           vGu8SingleKeySec=7;  //触发7号键 对应S7键
                      }
                      else if(1==Su8ColumnRecord)  //第2列输出低电平
                      {
                           vGu8SingleKeySec=8;  //触发8号键 对应S8键
                      }
                      else if(2==Su8ColumnRecord)  //第3列输出低电平
                      {
                           vGu8SingleKeySec=9;  //触发9号键 对应S9键
                      }
                  }   
              }

          }
          break;
   }

}

void SingleKeyTask(void)    //按键单击的任务函数,放在主函数内
{
if(0==vGu8SingleKeySec)
{
return; //按键的触发序号是0意味着无按键触发,直接退出当前函数,不执行此函数下面的代码
}

switch(vGu8SingleKeySec) //根据不同的按键触发序号执行对应的代码
{
   case 1:     //S1按键的单击任务

            //通过Gu8LedStatus的状态切换,来反复切换LED的“灭”与“亮”的状态
            if(0==Gu8LedStatus)
            {
                Gu8LedStatus=1; //标识并且更改当前LED灯的状态。0就变成1。
                LedOpen();   //点亮LED
}
            else
            {
                Gu8LedStatus=0; //标识并且更改当前LED灯的状态。1就变成0。
                LedClose();  //关闭LED
}

vGu8SingleKeySec=0;  //响应按键服务处理程序后,按键编号必须清零,避免一直触发
break;

   default:  //其它按键触发的单击

vGu8SingleKeySec=0;  //响应按键服务处理程序后,按键编号必须清零,避免一直触发
break;

}
}

void DoubleKeyTask(void)    //双击按键任务函数,放在主函数内
{
if(0==vGu8DoubleKeySec)
{
return; //按键的触发序号是0意味着无按键触发,直接退出当前函数,不执行此函数下面的代码
}

switch(vGu8DoubleKeySec) //根据不同的按键触发序号执行对应的代码
{
        case 1:     //S1按键的双击任务

              vGu8BeepTimerFlag=0;  
vGu16BeepTimerCnt=KEY_VOICE_TIME;  //触发双击后,发出“嘀”一声
              vGu8BeepTimerFlag=1;  

vGu8DoubleKeySec=0;  //响应按键服务处理程序后,按键编号必须清零,避免一致触发
break;
}

}

void T0_time() interrupt 1     
{
VoiceScan();  
KeyScan();    //按键识别的驱动函数

TH0=0xfc;   
TL0=0x66;   
}


void SystemInitial(void)
{
TMOD=0x01;  
TH0=0xfc;   
TL0=0x66;   
EA=1;      
ET0=1;      
TR0=1;      
}

void Delay(unsigned long u32DelayTime)
{
    for(;u32DelayTime>0;u32DelayTime--);
}

void PeripheralInitial(void)
{
/* 注释三:
* 把LED的初始化放在PeripheralInitial而不是放在SystemInitial,是因为LED显示内容对上电
* 瞬间的要求不高。但是,如果是控制继电器,则应该把继电器的输出初始化放在SystemInitial。
*/

    //根据Gu8LedStatus的值来初始化LED当前的显示状态,0代表灭,1代表亮
if(0==Gu8LedStatus)
{
    LedClose();  //关闭LED
}
else
{
    LedOpen();   //点亮LED
}
}

void BeepOpen(void)
{
P3_4=0;  
}

void BeepClose(void)
{
P3_4=1;  
}

void LedOpen(void)
{
P1_4=0;  
}

void LedClose(void)
{
P1_4=1;  
}

void VoiceScan(void)
{

          static unsigned char Su8Lock=0;  

if(1==vGu8BeepTimerFlag&&vGu16BeepTimerCnt>0)
          {
                  if(0==Su8Lock)
                  {
                   Su8Lock=1;  
BeepOpen();
     }
    else  
{     

                       vGu16BeepTimerCnt--;         

                   if(0==vGu16BeepTimerCnt)
                   {
                           Su8Lock=0;     
BeepClose();  
                   }

}
          }         
}


使用特权

评论回复
348
Dalarl2008| | 2017-12-31 22:16 | 只看该作者
很适合初学者。

使用特权

评论回复
349
SEAN_DZH| | 2018-1-3 14:57 | 只看该作者
非常感谢

使用特权

评论回复
350
SEAN_DZH| | 2018-1-3 15:00 | 只看该作者
这个贴子应该长期置顶

使用特权

评论回复
351
Dalarl2008| | 2018-1-5 22:36 | 只看该作者

使用特权

评论回复
352
jianhong_wu|  楼主 | 2018-1-7 12:32 | 只看该作者
第一百零二节: 两个“任意行输入”矩阵按键的“有序”组合触发。
第一百零二节_pdf文件.pdf (114.93 KB)
【102.1   “异行输入”“同行输入”“有序”。】

            
                上图102.1.1  有源蜂鸣器电路

      
                上图102.1.2  LED电路


   
                上图102.1.3  3*3矩阵按键的电路

       “任意行输入”是指能兼容“异行输入”与“同行输入”这两种按键状态。
        何谓“异行输入”何谓“同行输入”?如上图矩阵按键,P2.2,P2.1,P2.0是输入行,P2.5,P2.4,P2.3是输出列。以S1按键为例,很明显,S2和S3都是属于S1的“同行输入”,都是属于P2.2的输入行。除了S2和S3以外,其它所有的按键都是S1的“异行输入”,比如S5按键就是S1的“异行输入”,因为S1是属于P2.2的输入行,而S5是属于P2.1的输入行。
何谓“有序”组合触发?就是两个按键的触发必须遵守“先后顺序”才能构成“组合触发”。比如,像电脑的复制快捷键(Ctrl+C),你必须先按住Ctrl再按住C此时“复制快捷键”才有效,如果你先按住C再按住Ctrl此时“复制快捷键”无效。
       “异行输入”与“同行输入”,相比之下,“同行输入”更难更有代表性,如果把“同行输入”的程序写出来了,那么完全按“同行输入”的思路,就可以把“异行输入”的程序写出来。因此,只要把“同行输入”的程序写出来了,也就意味着“任意行输入”的程序也就实现了。本节以S1和S2的“同行输入”按键为例,S1是主键,类似复制快捷键的Ctrl键;S2是从键,类似复制快捷键的C键。要触发组合键(S1+S2),必须先按住S1再按S2才有效。功能如下:(1)S1每单击一次,LED要么从“灭”变成“亮”,要么从“亮”变成“灭”,在两种状态之间切换。(2)如果先按住S1再按S2,就认为构造了“有序”组合键,蜂鸣器发出“嘀”的一声。

#include "REG52.H"  

#define KEY_VOICE_TIME   50     
#define KEY_SHORT_TIME  20   

void T0_time();
void SystemInitial(void) ;
void Delay(unsigned long u32DelayTime) ;
void PeripheralInitial(void) ;

void BeepOpen(void);   
void BeepClose(void);
void LedOpen(void);   
void LedClose(void);

void VoiceScan(void);
void KeyScan(void);   
void SingleKeyTask(void);   
void DoubleKeyTask(void);   

sbit P3_4=P3^4;      
sbit P1_4=P1^4;      

sbit ROW_INPUT1=P2^2;  //第1行输入口。
sbit ROW_INPUT2=P2^1;  //第2行输入口。
sbit ROW_INPUT3=P2^0;  //第3行输入口。

sbit COLUMN_OUTPUT1=P2^5;  //第1列输出口。
sbit COLUMN_OUTPUT2=P2^4;  //第2列输出口。
sbit COLUMN_OUTPUT3=P2^3;  //第3列输出口。

volatile unsigned char vGu8BeepTimerFlag=0;  
volatile unsigned int vGu16BeepTimerCnt=0;  

unsigned char Gu8LedStatus=0;
volatile unsigned char vGu8SingleKeySec=0;  
volatile unsigned char vGu8DoubleKeySec=0;  

void main()
{
SystemInitial();            
Delay(10000);               
PeripheralInitial();      
    while(1)  
{  
        SingleKeyTask();   
        DoubleKeyTask();   
    }
}

/* 注释一:
*  两个“任意行输入”矩阵按键“有序”触发的两个最关键地方:
*  (1)当S1按键被按下单击触发之后, “马上更新输出列的信号状态”,然后切换到后面的步骤。
*  (2)在后面的步骤里,进入到S1和S2两个按键的轮番循环监控之中,如果发现S1按键率先
*   被松开了,就把步骤切换到开始的第一步,重新开始新一轮的按键扫描。
*  (3)按照这个模板,只需“更改不同的列输出,判断不同的行输入”,就可以实现“任意行输入”
*   矩阵按键的“有序”组合触发。
*/

void KeyScan(void)  //此函数放在定时中断里每1ms扫描一次
{
   static unsigned char Su8KeyLock=0;        
   static unsigned int  Su16KeyCnt=0;  
   static unsigned char Su8KeyStep=1;  

   static unsigned char Su8ColumnRecord=0;  

   switch(Su8KeyStep)
   {
     case 1:   
          if(0==Su8ColumnRecord)  
{
          COLUMN_OUTPUT1=0;      
          COLUMN_OUTPUT2=1;
          COLUMN_OUTPUT3=1;  
}
          else if(1==Su8ColumnRecord)  
{
          COLUMN_OUTPUT1=1;      
          COLUMN_OUTPUT2=0;
          COLUMN_OUTPUT3=1;  
}
          else     
{
          COLUMN_OUTPUT1=1;      
          COLUMN_OUTPUT2=1;
          COLUMN_OUTPUT3=0;  
}
          Su16KeyCnt=0;
          Su8KeyStep++;  
          break;

     case 2:      //等待列输出稳定,但不是去抖动延时
          Su16KeyCnt++;
          if(Su16KeyCnt>=2)
          {
             Su16KeyCnt=0;
             Su8KeyStep++;     
          }
          break;

     case 3:
          if(1==ROW_INPUT1&&1==ROW_INPUT2&&1==ROW_INPUT3)
          {  
             Su8KeyStep=1;  
             Su8KeyLock=0;
             Su16KeyCnt=0;  

             Su8ColumnRecord++;  
             if(Su8ColumnRecord>=3)  
             {
                Su8ColumnRecord=0;
             }     
          }
          else if(0==Su8KeyLock)
          {
              if(0==ROW_INPUT1&&1==ROW_INPUT2&&1==ROW_INPUT3)
              {
                  Su16KeyCnt++;  
                  if(Su16KeyCnt>=KEY_SHORT_TIME)
                  {
                      Su8KeyLock=1;

                      if(0==Su8ColumnRecord)  
                      {
                           vGu8SingleKeySec=1;     //单击任务,触发1号键 对应S1键

                           //“马上更新输出列的信号状态”
                       COLUMN_OUTPUT1=1;   
                       COLUMN_OUTPUT2=0;   //列2也输出0,非常关键的代码!
                       COLUMN_OUTPUT3=1;  

                       Su16KeyCnt=0;  //去抖动延时清零,为下一步计时做准备
                           Su8KeyStep++;   //切换到下一步步骤
                      }
                      else if(1==Su8ColumnRecord)  
                      {
                           vGu8SingleKeySec=2;  
                      }
                      else if(2==Su8ColumnRecord)  
                      {
                           vGu8SingleKeySec=3;  
                      }
                  }

              }
              else if(1==ROW_INPUT1&&0==ROW_INPUT2&&1==ROW_INPUT3)
              {
                  Su16KeyCnt++;  
                  if(Su16KeyCnt>=KEY_SHORT_TIME)
                  {
                      Su8KeyLock=1;

                      if(0==Su8ColumnRecord)  
                      {
                           vGu8SingleKeySec=4;  
                      }
                      else if(1==Su8ColumnRecord)  
                      {
                           vGu8SingleKeySec=5;  
                      }
                      else if(2==Su8ColumnRecord)  
                      {
                           vGu8SingleKeySec=6;  
                      }
                  }   
              }
              else if(1==ROW_INPUT1&&1==ROW_INPUT2&&0==ROW_INPUT3)
              {
                  Su16KeyCnt++;
                  if(Su16KeyCnt>=KEY_SHORT_TIME)
                  {
                      Su8KeyLock=1;
                      if(0==Su8ColumnRecord)  
                      {
                           vGu8SingleKeySec=7;  
                      }
                      else if(1==Su8ColumnRecord)  
                      {
                           vGu8SingleKeySec=8;  
                      }
                      else if(2==Su8ColumnRecord)  
                      {
                           vGu8SingleKeySec=9;  
                      }
                  }   
              }

          }
          break;
     case 4:             //等待列输出稳定,但不是去抖动延时
          Su16KeyCnt++;
          if(Su16KeyCnt>=2)   
          {
              Su16KeyCnt=0;
Su8KeyLock=0;   //关键语句!自锁清零,为下一步自锁组合按键做准备
              Su8KeyStep++;     
          }
          break;

     case 5:    //判断S2按键
          if(1==ROW_INPUT1&&1==ROW_INPUT2&&1==ROW_INPUT3) //S2按键没有被按下
          {  
             Su8KeyLock=0;
             Su16KeyCnt=0;  

             //“马上更新输出列的信号状态”
         COLUMN_OUTPUT1=0;     //列1输出0,非常关键的代码!
         COLUMN_OUTPUT2=1;  
         COLUMN_OUTPUT3=1;  

             Su8KeyStep++;    //切换到下一个步骤,监控S1是否率先已经松开
          }
else if(0==Su8KeyLock)
{   
              if(0==ROW_INPUT1&&1==ROW_INPUT2&&1==ROW_INPUT3) //S2按键被按下
              {
                  Su16KeyCnt++;
                  if(Su16KeyCnt>=KEY_SHORT_TIME)
                  {
                        Su8KeyLock=1;  //组合按键的自锁
vGu8DoubleKeySec=1;   //触发组合按键(S1+S2)
                  }
              }



}
          break;

     case 6:       //等待列输出稳定,但不是去抖动延时
          Su16KeyCnt++;
          if(Su16KeyCnt>=2)   
          {
              Su16KeyCnt=0;
Su8KeyLock=0;   //关键语句!自锁清零,为下一步自锁组合按键做准备
              Su8KeyStep++;     
          }
          break;

     case 7:    //监控S1按键是否率先已经松开
          if(1==ROW_INPUT1&&1==ROW_INPUT2&&1==ROW_INPUT3)
{
              Su16KeyCnt=0;
Su8KeyLock=0;   
              Su8KeyStep=1;   //如果S1按键已经松开,返回到第一个运行步骤重新开始扫描

              Su8ColumnRecord++;  
              if(Su8ColumnRecord>=3)  
              {
                  Su8ColumnRecord=0;
              }  
}
else
{
              //“马上更新输出列的信号状态”
          COLUMN_OUTPUT1=1;   
          COLUMN_OUTPUT2=0;     //列2输出0,非常关键的代码!
          COLUMN_OUTPUT3=1;  
              Su8KeyStep=4;   //如果S1按键没有松开,继续返回判断S2是否已按下
}
          break;
   }

}

void SingleKeyTask(void)   
{
if(0==vGu8SingleKeySec)
{
return;
}

switch(vGu8SingleKeySec)
{
   case 1:     //S1按键的单击任务,更改LED灯的显示状态

            if(0==Gu8LedStatus)
            {
                Gu8LedStatus=1;
                LedOpen();   
}
            else
            {
                Gu8LedStatus=0;
                LedClose();  
}

vGu8SingleKeySec=0;  
break;

   default:  

vGu8SingleKeySec=0;  
break;

}
}

void DoubleKeyTask(void)   
{
if(0==vGu8DoubleKeySec)
{
return;
}

switch(vGu8DoubleKeySec)
{
        case 1:     //S1与S2的组合按键触发,发出“嘀”一声

              vGu8BeepTimerFlag=0;  
vGu16BeepTimerCnt=KEY_VOICE_TIME;
              vGu8BeepTimerFlag=1;  

vGu8DoubleKeySec=0;  
break;
}

}

void T0_time() interrupt 1     
{
VoiceScan();  
KeyScan();   

TH0=0xfc;   
TL0=0x66;   
}


void SystemInitial(void)
{
TMOD=0x01;  
TH0=0xfc;   
TL0=0x66;   
EA=1;      
ET0=1;      
TR0=1;      
}

void Delay(unsigned long u32DelayTime)
{
    for(;u32DelayTime>0;u32DelayTime--);
}

void PeripheralInitial(void)
{
if(0==Gu8LedStatus)
{
    LedClose();  
}
else
{
    LedOpen();   
}
}

void BeepOpen(void)
{
P3_4=0;  
}

void BeepClose(void)
{
P3_4=1;  
}

void LedOpen(void)
{
P1_4=0;  
}

void LedClose(void)
{
P1_4=1;  
}

void VoiceScan(void)
{

          static unsigned char Su8Lock=0;  

if(1==vGu8BeepTimerFlag&&vGu16BeepTimerCnt>0)
          {
                  if(0==Su8Lock)
                  {
                   Su8Lock=1;  
BeepOpen();
     }
    else  
{     

                       vGu16BeepTimerCnt--;         

                   if(0==vGu16BeepTimerCnt)
                   {
                           Su8Lock=0;     
BeepClose();  
                   }

}
          }         
}


使用特权

评论回复
353
电子小童| | 2018-1-10 20:14 | 只看该作者
学习下载附件

使用特权

评论回复
354
alexzhou88| | 2018-1-13 21:35 | 只看该作者
好东西,收藏了

使用特权

评论回复
355
jianhong_wu|  楼主 | 2018-1-14 10:39 | 只看该作者
第一百零三节: 两个“任意行输入”矩阵按键的“无序”组合触发。
第一百零三节_pdf文件.pdf (113.65 KB)
【103.1   “无序”组合触发。】

            
                上图103.1.1  有源蜂鸣器电路

     
                上图103.1.2  LED电路


      
                上图103.1.3  3*3矩阵按键的电路

       “无序”是指两个组合按键不分先后顺序,都能构成组合触发。比如,要触发组合键(S1+S2),先按S1再按S2,或者先按S2再按S1,功能都是一样的。
       本节程序功能如下:(1)S1每单击一次,P1.4所在的LED要么从“灭”变成“亮”,要么从“亮”变成“灭”,在两种状态之间切换。(2)S2每单击一次,P1.5所在的LED要么从“灭”变成“亮”,要么从“亮”变成“灭”,在两种状态之间切换。(3)如果先按住S1再按S2,或者先按住S2再按S1,都认为构造了“无序”组合键,蜂鸣器发出“嘀”的一声。

#include "REG52.H"  

#define KEY_VOICE_TIME   50     
#define KEY_SHORT_TIME  20   

void T0_time();
void SystemInitial(void) ;
void Delay(unsigned long u32DelayTime) ;
void PeripheralInitial(void) ;

void BeepOpen(void);   
void BeepClose(void);
void LedOpen_P1_4(void);   
void LedClose_P1_4(void);
void LedOpen_P1_5(void);   
void LedClose_P1_5(void);

void VoiceScan(void);
void KeyScan(void);   
void SingleKeyTask(void);   
void DoubleKeyTask(void);   

sbit P3_4=P3^4;      
sbit P1_4=P1^4;    //P1.4所在的LED
sbit P1_5=P1^5;    //P1.5所在的LED

sbit ROW_INPUT1=P2^2;  //第1行输入口。
sbit ROW_INPUT2=P2^1;  //第2行输入口。
sbit ROW_INPUT3=P2^0;  //第3行输入口。

sbit COLUMN_OUTPUT1=P2^5;  //第1列输出口。
sbit COLUMN_OUTPUT2=P2^4;  //第2列输出口。
sbit COLUMN_OUTPUT3=P2^3;  //第3列输出口。

volatile unsigned char vGu8BeepTimerFlag=0;  
volatile unsigned int vGu16BeepTimerCnt=0;  

unsigned char Gu8LedStatus_P1_4=0;  //P1.4所在的LED的状态
unsigned char Gu8LedStatus_P1_5=0;  //P1.5所在的LED的状态
volatile unsigned char vGu8SingleKeySec=0;  
volatile unsigned char vGu8DoubleKeySec=0;  

void main()
{
SystemInitial();            
Delay(10000);               
PeripheralInitial();      
    while(1)  
{  
        SingleKeyTask();   
        DoubleKeyTask();   
    }
}

/* 注释一:
*  矩阵按键“无序”触发的两个最关键地方:
*  (1)如果是S1按键先被按下并且单击触发之后,“马上更新输出列的信号状态”,然后切换到
*   “S1后面所在的步骤里”,进入到S1和S2两个按键的轮番循环监控之中,如果发现S1按键率先
*   被松开了,就把步骤切换到开始的第一步,重新开始新一轮的按键扫描。
*  (2)如果是S2按键先被按下并且单击触发之后,“马上更新输出列的信号状态”,然后切换到
*   “S2后面所在的步骤里”,进入到S1和S2两个按键的轮番循环监控之中,如果发现S2按键率先
*   被松开了,就把步骤切换到开始的第一步,重新开始新一轮的按键扫描。
*  (3)上面两个描述中的两种步骤,“S1后面所在的步骤里”和“S2后面所在的步骤里”是分开的,
*   不共用的,这是本节破题的关键。
*/

void KeyScan(void)  //此函数放在定时中断里每1ms扫描一次
{
   static unsigned char Su8KeyLock=0;        
   static unsigned int  Su16KeyCnt=0;  
   static unsigned char Su8KeyStep=1;  

   static unsigned char Su8ColumnRecord=0;  

   switch(Su8KeyStep)
   {
     case 1:   
          if(0==Su8ColumnRecord)  
{
          COLUMN_OUTPUT1=0;      
          COLUMN_OUTPUT2=1;
          COLUMN_OUTPUT3=1;  
}
          else if(1==Su8ColumnRecord)  
{
          COLUMN_OUTPUT1=1;      
          COLUMN_OUTPUT2=0;
          COLUMN_OUTPUT3=1;  
}
          else     
{
          COLUMN_OUTPUT1=1;      
          COLUMN_OUTPUT2=1;
          COLUMN_OUTPUT3=0;  
}
          Su16KeyCnt=0;
          Su8KeyStep++;  
          break;

     case 2:      //等待列输出稳定,但不是去抖动延时
          Su16KeyCnt++;
          if(Su16KeyCnt>=2)
          {
             Su16KeyCnt=0;
             Su8KeyStep++;     
          }
          break;

     case 3:
          if(1==ROW_INPUT1&&1==ROW_INPUT2&&1==ROW_INPUT3)
          {  
             Su8KeyStep=1;  
             Su8KeyLock=0;
             Su16KeyCnt=0;  

             Su8ColumnRecord++;  
             if(Su8ColumnRecord>=3)  
             {
                Su8ColumnRecord=0;
             }     
          }
          else if(0==Su8KeyLock)
          {
              if(0==ROW_INPUT1&&1==ROW_INPUT2&&1==ROW_INPUT3)
              {
                  Su16KeyCnt++;  
                  if(Su16KeyCnt>=KEY_SHORT_TIME)
                  {
                      Su8KeyLock=1;

                      if(0==Su8ColumnRecord)  
                      {
                           vGu8SingleKeySec=1;     //单击任务,触发1号键 对应S1键

                           //“马上更新输出列的信号状态”
                       COLUMN_OUTPUT1=1;   
                       COLUMN_OUTPUT2=0;   //列2也输出0,下一步监控S2,非常关键的代码!
                       COLUMN_OUTPUT3=1;  

                       Su16KeyCnt=0;  //去抖动延时清零,为下一步计时做准备
                           Su8KeyStep=4;   //切换到“S1后面所在的步骤里”,破题的关键!!!
                      }
                      else if(1==Su8ColumnRecord)  
                      {
                           vGu8SingleKeySec=2;    //单击任务,触发2号键 对应S2键

                           //“马上更新输出列的信号状态”
                       COLUMN_OUTPUT1=0;  //列1也输出0,下一步监控S1,非常关键的代码!
                       COLUMN_OUTPUT2=1;  
                       COLUMN_OUTPUT3=1;  

                       Su16KeyCnt=0;  //去抖动延时清零,为下一步计时做准备
                           Su8KeyStep=8;   //切换到“S2后面所在的步骤里”,破题的关键!!!
                      }
                      else if(2==Su8ColumnRecord)  
                      {
                           vGu8SingleKeySec=3;  
                      }
                  }

              }
              else if(1==ROW_INPUT1&&0==ROW_INPUT2&&1==ROW_INPUT3)
              {
                  Su16KeyCnt++;  
                  if(Su16KeyCnt>=KEY_SHORT_TIME)
                  {
                      Su8KeyLock=1;

                      if(0==Su8ColumnRecord)  
                      {
                           vGu8SingleKeySec=4;  
                      }
                      else if(1==Su8ColumnRecord)  
                      {
                           vGu8SingleKeySec=5;  
                      }
                      else if(2==Su8ColumnRecord)  
                      {
                           vGu8SingleKeySec=6;  
                      }
                  }   
              }
              else if(1==ROW_INPUT1&&1==ROW_INPUT2&&0==ROW_INPUT3)
              {
                  Su16KeyCnt++;
                  if(Su16KeyCnt>=KEY_SHORT_TIME)
                  {
                      Su8KeyLock=1;
                      if(0==Su8ColumnRecord)  
                      {
                           vGu8SingleKeySec=7;  
                      }
                      else if(1==Su8ColumnRecord)  
                      {
                           vGu8SingleKeySec=8;  
                      }
                      else if(2==Su8ColumnRecord)  
                      {
                           vGu8SingleKeySec=9;  
                      }
                  }   
              }

          }
          break;

/*--------------“S1后面所在的步骤里”------------------*/
     case 4:             //等待列输出稳定,但不是去抖动延时
          Su16KeyCnt++;
          if(Su16KeyCnt>=2)   
          {
              Su16KeyCnt=0;
Su8KeyLock=0;   //关键语句!自锁清零,为下一步自锁组合按键做准备
              Su8KeyStep++;     
          }
          break;

     case 5:    //判断S2按键
          if(1==ROW_INPUT1&&1==ROW_INPUT2&&1==ROW_INPUT3) //S2按键没有被按下
          {  
             Su8KeyLock=0;
             Su16KeyCnt=0;  

             //“马上更新输出列的信号状态”
         COLUMN_OUTPUT1=0;     //列1输出0,下一步监控S1,非常关键的代码!
         COLUMN_OUTPUT2=1;  
         COLUMN_OUTPUT3=1;  

             Su8KeyStep++;    //切换到下一个步骤,监控S1是否率先已经松开
          }
else if(0==Su8KeyLock)
{   
              if(0==ROW_INPUT1&&1==ROW_INPUT2&&1==ROW_INPUT3) //S2按键被按下
              {
                  Su16KeyCnt++;
                  if(Su16KeyCnt>=KEY_SHORT_TIME)
                  {
                        Su8KeyLock=1;  //组合按键的自锁
vGu8DoubleKeySec=1;   //触发组合按键(S1+S2)
                  }
              }



}
          break;

     case 6:       //等待列输出稳定,但不是去抖动延时
          Su16KeyCnt++;
          if(Su16KeyCnt>=2)   
          {
              Su16KeyCnt=0;
Su8KeyLock=0;   //关键语句!自锁清零,为下一步自锁组合按键做准备
              Su8KeyStep++;     
          }
          break;

     case 7:    //监控S1按键是否率先已经松开
          if(1==ROW_INPUT1&&1==ROW_INPUT2&&1==ROW_INPUT3)
{
              Su16KeyCnt=0;
Su8KeyLock=0;   
              Su8KeyStep=1;   //如果S1按键已经松开,返回到第一个运行步骤重新开始扫描

              Su8ColumnRecord++;  
              if(Su8ColumnRecord>=3)  
              {
                  Su8ColumnRecord=0;
              }  
}
else
{
              //“马上更新输出列的信号状态”
          COLUMN_OUTPUT1=1;   
          COLUMN_OUTPUT2=0;     //列2输出0,下一步监控S2,非常关键的代码!
          COLUMN_OUTPUT3=1;  
              Su8KeyStep=4;   //如果S1按键没有松开,继续返回判断S2是否已按下
}
          break;

/*--------------“S2后面所在的步骤里”------------------*/
     case 8:             //等待列输出稳定,但不是去抖动延时
          Su16KeyCnt++;
          if(Su16KeyCnt>=2)   
          {
              Su16KeyCnt=0;
Su8KeyLock=0;   //关键语句!自锁清零,为下一步自锁组合按键做准备
              Su8KeyStep++;     
          }
          break;

     case 9:    //判断S1按键
          if(1==ROW_INPUT1&&1==ROW_INPUT2&&1==ROW_INPUT3) //S1按键没有被按下
          {  
             Su8KeyLock=0;
             Su16KeyCnt=0;  

             //“马上更新输出列的信号状态”
         COLUMN_OUTPUT1=1;     
         COLUMN_OUTPUT2=0;  //列2输出0,下一步监控S2,非常关键的代码!
         COLUMN_OUTPUT3=1;  

             Su8KeyStep++;    //切换到下一个步骤,监控S2是否率先已经松开
          }
else if(0==Su8KeyLock)
{   
              if(0==ROW_INPUT1&&1==ROW_INPUT2&&1==ROW_INPUT3) //S1按键被按下
              {
                  Su16KeyCnt++;
                  if(Su16KeyCnt>=KEY_SHORT_TIME)
                  {
                        Su8KeyLock=1;  //组合按键的自锁
vGu8DoubleKeySec=1;   //触发组合按键(S1+S2)
                  }
              }



}
          break;

     case 10:       //等待列输出稳定,但不是去抖动延时
          Su16KeyCnt++;
          if(Su16KeyCnt>=2)   
          {
              Su16KeyCnt=0;
Su8KeyLock=0;   //关键语句!自锁清零,为下一步自锁组合按键做准备
              Su8KeyStep++;     
          }
          break;

     case 11:    //监控S2按键是否率先已经松开
          if(1==ROW_INPUT1&&1==ROW_INPUT2&&1==ROW_INPUT3)
{
              Su16KeyCnt=0;
Su8KeyLock=0;   
              Su8KeyStep=1;   //如果S2按键已经松开,返回到第一个运行步骤重新开始扫描

              Su8ColumnRecord++;  
              if(Su8ColumnRecord>=3)  
              {
                  Su8ColumnRecord=0;
              }  
}
else
{
              //“马上更新输出列的信号状态”
          COLUMN_OUTPUT1=0;    //列1输出0,下一步监控S1,非常关键的代码!
          COLUMN_OUTPUT2=1;     
          COLUMN_OUTPUT3=1;  
              Su8KeyStep=8;   //如果S2按键没有松开,继续返回判断S1是否已按下
}
          break;


   }

}

void SingleKeyTask(void)   
{
if(0==vGu8SingleKeySec)
{
return;
}

switch(vGu8SingleKeySec)
{
   case 1:     //S1按键的单击任务,更改P1.4所在的LED灯的显示状态

            if(0==Gu8LedStatus_P1_4)
            {
                Gu8LedStatus_P1_4=1;
                LedOpen_P1_4();   
}
            else
            {
                Gu8LedStatus_P1_4=0;
                LedClose_P1_4();  
}

vGu8SingleKeySec=0;  
break;

   case 2:     //S2按键的单击任务,更改P1.5所在的LED灯的显示状态

            if(0==Gu8LedStatus_P1_5)
            {
                Gu8LedStatus_P1_5=1;
                LedOpen_P1_5();   
}
            else
            {
                Gu8LedStatus_P1_5=0;
                LedClose_P1_5();  
}

vGu8SingleKeySec=0;  
break;

   default:  

vGu8SingleKeySec=0;  
break;

}
}

void DoubleKeyTask(void)   
{
if(0==vGu8DoubleKeySec)
{
return;
}

switch(vGu8DoubleKeySec)
{
        case 1:     //S1与S2的组合按键触发,发出“嘀”一声

              vGu8BeepTimerFlag=0;  
vGu16BeepTimerCnt=KEY_VOICE_TIME;
              vGu8BeepTimerFlag=1;  

vGu8DoubleKeySec=0;  
break;
}

}

void T0_time() interrupt 1     
{
VoiceScan();  
KeyScan();   

TH0=0xfc;   
TL0=0x66;   
}


void SystemInitial(void)
{
TMOD=0x01;  
TH0=0xfc;   
TL0=0x66;   
EA=1;      
ET0=1;      
TR0=1;      
}

void Delay(unsigned long u32DelayTime)
{
    for(;u32DelayTime>0;u32DelayTime--);
}

void PeripheralInitial(void)
{
if(0==Gu8LedStatus_P1_4)
{
    LedClose_P1_4();  
}
else
{
    LedOpen_P1_4();   
}

if(0==Gu8LedStatus_P1_5)
{
    LedClose_P1_5();  
}
else
{
    LedOpen_P1_5();   
}

}

void BeepOpen(void)
{
P3_4=0;  
}

void BeepClose(void)
{
P3_4=1;  
}

void LedOpen_P1_4(void)
{
P1_4=0;  
}

void LedClose_P1_4(void)
{
P1_4=1;  
}

void LedOpen_P1_5(void)
{
P1_5=0;  
}

void LedClose_P1_5(void)
{
P1_5=1;  
}

void VoiceScan(void)
{

          static unsigned char Su8Lock=0;  

if(1==vGu8BeepTimerFlag&&vGu16BeepTimerCnt>0)
          {
                  if(0==Su8Lock)
                  {
                   Su8Lock=1;  
BeepOpen();
     }
    else  
{     

                       vGu16BeepTimerCnt--;         

                   if(0==vGu16BeepTimerCnt)
                   {
                           Su8Lock=0;     
BeepClose();  
                   }

}
          }         
}



使用特权

评论回复
356
renwanbin| | 2018-1-14 22:08 | 只看该作者
期待楼主的资料,就是更新有点慢。

使用特权

评论回复
357
pguangchun| | 2018-1-18 19:27 | 只看该作者
感谢楼主的无私奉献

使用特权

评论回复
358
eatpeanut| | 2018-1-19 17:36 | 只看该作者
Mark

使用特权

评论回复
359
hymope| | 2018-1-19 19:50 | 只看该作者
谢谢 感觉很有用

使用特权

评论回复
360
luwei0503| | 2018-1-21 17:31 | 只看该作者
楼主太棒了,为执着精神点赞

使用特权

评论回复
发新帖 本帖赏金 72.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则