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

[复制链接]
楼主: jianhong_wu
| 2017-12-15 13:58 | 显示全部楼层
谢谢分享
| 2017-12-16 14:27 | 显示全部楼层
楼主以前是不是在**论坛?
现在来21IC了?
那个控制欲极强的**有时候确实不可理喻。
 楼主 | 2017-12-17 11:37 | 显示全部楼层
第一百节: “行列扫描式”矩阵按键的单个触发(优化版)。

【100.1   “行列扫描式”矩阵按键。】

               
                上图100.1.1  有源蜂鸣器电路


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

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

  1. #include "REG52.H"  

  2. #define KEY_VOICE_TIME   50     

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

  4. void T0_time();
  5. void SystemInitial(void) ;
  6. void Delay(unsigned long u32DelayTime) ;
  7. void PeripheralInitial(void) ;

  8. void BeepOpen(void);   
  9. void BeepClose(void);

  10. void VoiceScan(void);
  11. void KeyScan(void);   
  12. void KeyTask(void);   

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

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

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

  20. volatile unsigned char vGu8BeepTimerFlag=0;  
  21. volatile unsigned int vGu16BeepTimerCnt=0;  


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

  23. void main()
  24. {
  25. SystemInitial();            
  26. Delay(10000);               
  27. PeripheralInitial();      
  28.     while(1)  
  29. {  
  30.     KeyTask();       //按键的任务函数
  31.     }
  32. }

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

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

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

  46.    switch(Su8KeyStep)
  47.    {
  48.      case 1:   
  49.           if(0==Su8ColumnRecord)  //按键扫描输出第一列低电平
  50. {
  51.           COLUMN_OUTPUT1=0;      
  52.           COLUMN_OUTPUT2=1;
  53.           COLUMN_OUTPUT3=1;  
  54. }
  55.           else if(1==Su8ColumnRecord)  //按键扫描输出第二列低电平
  56. {
  57.           COLUMN_OUTPUT1=1;      
  58.           COLUMN_OUTPUT2=0;
  59.           COLUMN_OUTPUT3=1;  
  60. }
  61.           else     //按键扫描输出第三列低电平
  62. {
  63.           COLUMN_OUTPUT1=1;      
  64.           COLUMN_OUTPUT2=1;
  65.           COLUMN_OUTPUT3=0;  
  66. }
  67.           Su16KeyCnt=0;  //延时计数器清零
  68.           Su8KeyStep++;  //切换到下一个运行步骤
  69.           break;

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

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

  99.                       if(0==Su8ColumnRecord)  //第1列输出低电平
  100.                       {
  101.                            vGu8KeySec=1;  //触发1号键 对应S1键
  102.                       }
  103.                       else if(1==Su8ColumnRecord)  //第2列输出低电平
  104.                       {
  105.                            vGu8KeySec=2;  //触发2号键 对应S2键
  106.                       }
  107.                       else if(2==Su8ColumnRecord)  //第3列输出低电平
  108.                       {
  109.                            vGu8KeySec=3;  //触发3号键 对应S3键
  110.                       }
  111.                   }

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

  120.                       if(0==Su8ColumnRecord)  //第1列输出低电平
  121.                       {
  122.                            vGu8KeySec=4;  //触发4号键 对应S4键
  123.                       }
  124.                       else if(1==Su8ColumnRecord)  //第2列输出低电平
  125.                       {
  126.                            vGu8KeySec=5;  //触发5号键 对应S5键
  127.                       }
  128.                       else if(2==Su8ColumnRecord)  //第3列输出低电平
  129.                       {
  130.                            vGu8KeySec=6;  //触发6号键 对应S6键
  131.                       }
  132.                   }   
  133.               }
  134.               else if(1==ROW_INPUT1&&1==ROW_INPUT2&&0==ROW_INPUT3)
  135.               {
  136.                   Su16KeyCnt++;  //去抖动延时计数器
  137.                   if(Su16KeyCnt>=KEY_SHORT_TIME)
  138.                   {
  139.                       Su16KeyCnt=0;
  140.                       Su8KeyLock=1;//自锁置1,避免一直触发,只有松开按键,此标志位才会被清零
  141.                       if(0==Su8ColumnRecord)  //第1列输出低电平
  142.                       {
  143.                            vGu8KeySec=7;  //触发7号键 对应S7键
  144.                       }
  145.                       else if(1==Su8ColumnRecord)  //第2列输出低电平
  146.                       {
  147.                            vGu8KeySec=8;  //触发8号键 对应S8键
  148.                       }
  149.                       else if(2==Su8ColumnRecord)  //第3列输出低电平
  150.                       {
  151.                            vGu8KeySec=9;  //触发9号键 对应S9键
  152.                       }
  153.                   }   
  154.               }

  155.           }
  156.           break;
  157.    }

  158. }

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

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

  168.             vGu8BeepTimerFlag=0;  
  169. vGu16BeepTimerCnt=KEY_VOICE_TIME;  //发出“嘀”一声
  170.             vGu8BeepTimerFlag=1;  

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

  173.    case 2:     //S2触发的任务

  174.             vGu8BeepTimerFlag=0;  
  175. vGu16BeepTimerCnt=KEY_VOICE_TIME;  //发出“嘀”一声
  176.             vGu8BeepTimerFlag=1;  

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

  179.    case 3:     //S3触发的任务

  180.             vGu8BeepTimerFlag=0;  
  181. vGu16BeepTimerCnt=KEY_VOICE_TIME;  //发出“嘀”一声
  182.             vGu8BeepTimerFlag=1;  

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

  185.    case 4:     //S4触发的任务

  186.             vGu8BeepTimerFlag=0;  
  187. vGu16BeepTimerCnt=KEY_VOICE_TIME;  //发出“嘀”一声
  188.             vGu8BeepTimerFlag=1;  

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

  191.    case 5:     //S5触发的任务

  192.             vGu8BeepTimerFlag=0;  
  193. vGu16BeepTimerCnt=KEY_VOICE_TIME;  //发出“嘀”一声
  194.             vGu8BeepTimerFlag=1;  

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

  197.    case 6:     //S6触发的任务

  198.             vGu8BeepTimerFlag=0;  
  199. vGu16BeepTimerCnt=KEY_VOICE_TIME;  //发出“嘀”一声
  200.             vGu8BeepTimerFlag=1;  

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

  203.    case 7:     //S7触发的任务

  204.             vGu8BeepTimerFlag=0;  
  205. vGu16BeepTimerCnt=KEY_VOICE_TIME;  //发出“嘀”一声
  206.             vGu8BeepTimerFlag=1;  

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

  209.    case 8:     //S8触发的任务

  210.             vGu8BeepTimerFlag=0;  
  211. vGu16BeepTimerCnt=KEY_VOICE_TIME;  //发出“嘀”一声
  212.             vGu8BeepTimerFlag=1;  

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

  215.    case 9:     //S9触发的任务

  216.             vGu8BeepTimerFlag=0;  
  217. vGu16BeepTimerCnt=KEY_VOICE_TIME;  //发出“嘀”一声
  218.             vGu8BeepTimerFlag=1;  

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

  221. }
  222. }



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

  227. TH0=0xfc;   
  228. TL0=0x66;   
  229. }


  230. void SystemInitial(void)
  231. {
  232. TMOD=0x01;  
  233. TH0=0xfc;   
  234. TL0=0x66;   
  235. EA=1;      
  236. ET0=1;      
  237. TR0=1;      
  238. }

  239. void Delay(unsigned long u32DelayTime)
  240. {
  241.     for(;u32DelayTime>0;u32DelayTime--);
  242. }

  243. void PeripheralInitial(void)
  244. {

  245. }

  246. void BeepOpen(void)
  247. {
  248. P3_4=0;  
  249. }

  250. void BeepClose(void)
  251. {
  252. P3_4=1;  
  253. }

  254. void VoiceScan(void)
  255. {

  256.           static unsigned char Su8Lock=0;  

  257. if(1==vGu8BeepTimerFlag&&vGu16BeepTimerCnt>0)
  258.           {
  259.                   if(0==Su8Lock)
  260.                   {
  261.                    Su8Lock=1;  
  262. BeepOpen();
  263.      }
  264.     else  
  265. {     

  266.                        vGu16BeepTimerCnt--;         

  267.                    if(0==vGu16BeepTimerCnt)
  268.                    {
  269.                            Su8Lock=0;     
  270. BeepClose();  
  271.                    }

  272. }
  273.           }         
  274. }
复制代码


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
| 2017-12-17 15:04 | 显示全部楼层
收益良多
| 2017-12-18 22:35 | 显示全部楼层
非常感谢。
| 2017-12-18 22:36 | 显示全部楼层
以前搞修理,现在学习单片机,很适合初学者。
| 2017-12-25 20:12 | 显示全部楼层
收益良多.圣诞快乐!
| 2017-12-31 13:03 | 显示全部楼层
感谢分享
 楼主 | 2017-12-31 16:51 | 显示全部楼层
第一百零一节: 矩阵按键鼠标式的单击与双击。

【101.1   矩阵按键鼠标式的单击与双击。】

            
                上图101.1.1  有源蜂鸣器电路

      
                上图101.1.2  LED电路


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

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

  1. #include "REG52.H"  

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

  5. void T0_time();
  6. void SystemInitial(void) ;
  7. void Delay(unsigned long u32DelayTime) ;
  8. void PeripheralInitial(void) ;

  9. void BeepOpen(void);   
  10. void BeepClose(void);
  11. void LedOpen(void);   
  12. void LedClose(void);

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

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

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

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

  25. volatile unsigned char vGu8BeepTimerFlag=0;  
  26. volatile unsigned int vGu16BeepTimerCnt=0;  

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

  30. void main()
  31. {
  32. SystemInitial();            
  33. Delay(10000);               
  34. PeripheralInitial();      
  35.     while(1)  
  36. {  
  37.         SingleKeyTask();    //单击按键任务函数
  38.         DoubleKeyTask();    //双击按键任务函数
  39.     }
  40. }

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

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

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

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

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

  76.    switch(Su8KeyStep)
  77.    {
  78.      case 1:   
  79.           if(0==Su8ColumnRecord)  //按键扫描输出第一列低电平
  80. {
  81.           COLUMN_OUTPUT1=0;      
  82.           COLUMN_OUTPUT2=1;
  83.           COLUMN_OUTPUT3=1;  
  84. }
  85.           else if(1==Su8ColumnRecord)  //按键扫描输出第二列低电平
  86. {
  87.           COLUMN_OUTPUT1=1;      
  88.           COLUMN_OUTPUT2=0;
  89.           COLUMN_OUTPUT3=1;  
  90. }
  91.           else     //按键扫描输出第三列低电平
  92. {
  93.           COLUMN_OUTPUT1=1;      
  94.           COLUMN_OUTPUT2=1;
  95.           COLUMN_OUTPUT3=0;  
  96. }
  97.           Su16KeyCnt=0;  //延时计数器清零
  98.           Su8KeyStep++;  //切换到下一个运行步骤
  99.           break;

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

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

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

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

  137.                       if(0==Su8ColumnRecord)  //第1列输出低电平
  138.                       {
  139.                            Su16KeyIntervalCnt1=0;   //按键有效间隔的时间计数器清零
  140.                            Su8KeyTouchCnt1++;       //记录当前单击的次数
  141.                            if(1==Su8KeyTouchCnt1)   //只按了1次
  142.                            {
  143.                                vGu8SingleKeySec=1;     //单击任务,触发1号键 对应S1键
  144.                            }
  145.                            else if(Su8KeyTouchCnt1>=2)  //连续按了两次以上
  146.                            {
  147.                                 Su8KeyTouchCnt1=0;    //统计按键次数清零
  148.                                 vGu8SingleKeySec=1;   //单击任务,触发1号键 对应S1键
  149. vGu8DoubleKeySec=1;   //双击任务,触发1号键 对应S1键
  150.                            }
  151.                       }
  152.                       else if(1==Su8ColumnRecord)  //第2列输出低电平
  153.                       {
  154.                            vGu8SingleKeySec=2;  //触发2号键 对应S2键
  155.                       }
  156.                       else if(2==Su8ColumnRecord)  //第3列输出低电平
  157.                       {
  158.                            vGu8SingleKeySec=3;  //触发3号键 对应S3键
  159.                       }
  160.                   }

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

  168.                       if(0==Su8ColumnRecord)  //第1列输出低电平
  169.                       {
  170.                            vGu8SingleKeySec=4;  //触发4号键 对应S4键
  171.                       }
  172.                       else if(1==Su8ColumnRecord)  //第2列输出低电平
  173.                       {
  174.                            vGu8SingleKeySec=5;  //触发5号键 对应S5键
  175.                       }
  176.                       else if(2==Su8ColumnRecord)  //第3列输出低电平
  177.                       {
  178.                            vGu8SingleKeySec=6;  //触发6号键 对应S6键
  179.                       }
  180.                   }   
  181.               }
  182.               else if(1==ROW_INPUT1&&1==ROW_INPUT2&&0==ROW_INPUT3)
  183.               {
  184.                   Su16KeyCnt++;  //去抖动延时计数器
  185.                   if(Su16KeyCnt>=KEY_SHORT_TIME)
  186.                   {
  187.                       Su8KeyLock=1;//自锁置1,避免一直触发,只有松开按键,此标志位才会被清零
  188.                       if(0==Su8ColumnRecord)  //第1列输出低电平
  189.                       {
  190.                            vGu8SingleKeySec=7;  //触发7号键 对应S7键
  191.                       }
  192.                       else if(1==Su8ColumnRecord)  //第2列输出低电平
  193.                       {
  194.                            vGu8SingleKeySec=8;  //触发8号键 对应S8键
  195.                       }
  196.                       else if(2==Su8ColumnRecord)  //第3列输出低电平
  197.                       {
  198.                            vGu8SingleKeySec=9;  //触发9号键 对应S9键
  199.                       }
  200.                   }   
  201.               }

  202.           }
  203.           break;
  204.    }

  205. }

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

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

  215.             //通过Gu8LedStatus的状态切换,来反复切换LED的“灭”与“亮”的状态
  216.             if(0==Gu8LedStatus)
  217.             {
  218.                 Gu8LedStatus=1; //标识并且更改当前LED灯的状态。0就变成1。
  219.                 LedOpen();   //点亮LED
  220. }
  221.             else
  222.             {
  223.                 Gu8LedStatus=0; //标识并且更改当前LED灯的状态。1就变成0。
  224.                 LedClose();  //关闭LED
  225. }

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

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

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

  231. }
  232. }

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

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

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

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

  248. }

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

  253. TH0=0xfc;   
  254. TL0=0x66;   
  255. }


  256. void SystemInitial(void)
  257. {
  258. TMOD=0x01;  
  259. TH0=0xfc;   
  260. TL0=0x66;   
  261. EA=1;      
  262. ET0=1;      
  263. TR0=1;      
  264. }

  265. void Delay(unsigned long u32DelayTime)
  266. {
  267.     for(;u32DelayTime>0;u32DelayTime--);
  268. }

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

  275.     //根据Gu8LedStatus的值来初始化LED当前的显示状态,0代表灭,1代表亮
  276. if(0==Gu8LedStatus)
  277. {
  278.     LedClose();  //关闭LED
  279. }
  280. else
  281. {
  282.     LedOpen();   //点亮LED
  283. }
  284. }

  285. void BeepOpen(void)
  286. {
  287. P3_4=0;  
  288. }

  289. void BeepClose(void)
  290. {
  291. P3_4=1;  
  292. }

  293. void LedOpen(void)
  294. {
  295. P1_4=0;  
  296. }

  297. void LedClose(void)
  298. {
  299. P1_4=1;  
  300. }

  301. void VoiceScan(void)
  302. {

  303.           static unsigned char Su8Lock=0;  

  304. if(1==vGu8BeepTimerFlag&&vGu16BeepTimerCnt>0)
  305.           {
  306.                   if(0==Su8Lock)
  307.                   {
  308.                    Su8Lock=1;  
  309. BeepOpen();
  310.      }
  311.     else  
  312. {     

  313.                        vGu16BeepTimerCnt--;         

  314.                    if(0==vGu16BeepTimerCnt)
  315.                    {
  316.                            Su8Lock=0;     
  317. BeepClose();  
  318.                    }

  319. }
  320.           }         
  321. }
复制代码


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
| 2017-12-31 22:16 | 显示全部楼层
很适合初学者。
| 2018-1-3 14:57 | 显示全部楼层
非常感谢
| 2018-1-3 15:00 | 显示全部楼层
这个贴子应该长期置顶
| 2018-1-5 22:36 | 显示全部楼层
 楼主 | 2018-1-7 12:32 | 显示全部楼层
第一百零二节: 两个“任意行输入”矩阵按键的“有序”组合触发。

【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,就认为构造了“有序”组合键,蜂鸣器发出“嘀”的一声。

  1. #include "REG52.H"  

  2. #define KEY_VOICE_TIME   50     
  3. #define KEY_SHORT_TIME  20   

  4. void T0_time();
  5. void SystemInitial(void) ;
  6. void Delay(unsigned long u32DelayTime) ;
  7. void PeripheralInitial(void) ;

  8. void BeepOpen(void);   
  9. void BeepClose(void);
  10. void LedOpen(void);   
  11. void LedClose(void);

  12. void VoiceScan(void);
  13. void KeyScan(void);   
  14. void SingleKeyTask(void);   
  15. void DoubleKeyTask(void);   

  16. sbit P3_4=P3^4;      
  17. sbit P1_4=P1^4;      

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

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

  24. volatile unsigned char vGu8BeepTimerFlag=0;  
  25. volatile unsigned int vGu16BeepTimerCnt=0;  

  26. unsigned char Gu8LedStatus=0;
  27. volatile unsigned char vGu8SingleKeySec=0;  
  28. volatile unsigned char vGu8DoubleKeySec=0;  

  29. void main()
  30. {
  31. SystemInitial();            
  32. Delay(10000);               
  33. PeripheralInitial();      
  34.     while(1)  
  35. {  
  36.         SingleKeyTask();   
  37.         DoubleKeyTask();   
  38.     }
  39. }

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

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

  53.    static unsigned char Su8ColumnRecord=0;  

  54.    switch(Su8KeyStep)
  55.    {
  56.      case 1:   
  57.           if(0==Su8ColumnRecord)  
  58. {
  59.           COLUMN_OUTPUT1=0;      
  60.           COLUMN_OUTPUT2=1;
  61.           COLUMN_OUTPUT3=1;  
  62. }
  63.           else if(1==Su8ColumnRecord)  
  64. {
  65.           COLUMN_OUTPUT1=1;      
  66.           COLUMN_OUTPUT2=0;
  67.           COLUMN_OUTPUT3=1;  
  68. }
  69.           else     
  70. {
  71.           COLUMN_OUTPUT1=1;      
  72.           COLUMN_OUTPUT2=1;
  73.           COLUMN_OUTPUT3=0;  
  74. }
  75.           Su16KeyCnt=0;
  76.           Su8KeyStep++;  
  77.           break;

  78.      case 2:      //等待列输出稳定,但不是去抖动延时
  79.           Su16KeyCnt++;
  80.           if(Su16KeyCnt>=2)
  81.           {
  82.              Su16KeyCnt=0;
  83.              Su8KeyStep++;     
  84.           }
  85.           break;

  86.      case 3:
  87.           if(1==ROW_INPUT1&&1==ROW_INPUT2&&1==ROW_INPUT3)
  88.           {  
  89.              Su8KeyStep=1;  
  90.              Su8KeyLock=0;
  91.              Su16KeyCnt=0;  

  92.              Su8ColumnRecord++;  
  93.              if(Su8ColumnRecord>=3)  
  94.              {
  95.                 Su8ColumnRecord=0;
  96.              }     
  97.           }
  98.           else if(0==Su8KeyLock)
  99.           {
  100.               if(0==ROW_INPUT1&&1==ROW_INPUT2&&1==ROW_INPUT3)
  101.               {
  102.                   Su16KeyCnt++;  
  103.                   if(Su16KeyCnt>=KEY_SHORT_TIME)
  104.                   {
  105.                       Su8KeyLock=1;

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

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

  113.                        Su16KeyCnt=0;  //去抖动延时清零,为下一步计时做准备
  114.                            Su8KeyStep++;   //切换到下一步步骤
  115.                       }
  116.                       else if(1==Su8ColumnRecord)  
  117.                       {
  118.                            vGu8SingleKeySec=2;  
  119.                       }
  120.                       else if(2==Su8ColumnRecord)  
  121.                       {
  122.                            vGu8SingleKeySec=3;  
  123.                       }
  124.                   }

  125.               }
  126.               else if(1==ROW_INPUT1&&0==ROW_INPUT2&&1==ROW_INPUT3)
  127.               {
  128.                   Su16KeyCnt++;  
  129.                   if(Su16KeyCnt>=KEY_SHORT_TIME)
  130.                   {
  131.                       Su8KeyLock=1;

  132.                       if(0==Su8ColumnRecord)  
  133.                       {
  134.                            vGu8SingleKeySec=4;  
  135.                       }
  136.                       else if(1==Su8ColumnRecord)  
  137.                       {
  138.                            vGu8SingleKeySec=5;  
  139.                       }
  140.                       else if(2==Su8ColumnRecord)  
  141.                       {
  142.                            vGu8SingleKeySec=6;  
  143.                       }
  144.                   }   
  145.               }
  146.               else if(1==ROW_INPUT1&&1==ROW_INPUT2&&0==ROW_INPUT3)
  147.               {
  148.                   Su16KeyCnt++;
  149.                   if(Su16KeyCnt>=KEY_SHORT_TIME)
  150.                   {
  151.                       Su8KeyLock=1;
  152.                       if(0==Su8ColumnRecord)  
  153.                       {
  154.                            vGu8SingleKeySec=7;  
  155.                       }
  156.                       else if(1==Su8ColumnRecord)  
  157.                       {
  158.                            vGu8SingleKeySec=8;  
  159.                       }
  160.                       else if(2==Su8ColumnRecord)  
  161.                       {
  162.                            vGu8SingleKeySec=9;  
  163.                       }
  164.                   }   
  165.               }

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

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

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

  186.              Su8KeyStep++;    //切换到下一个步骤,监控S1是否率先已经松开
  187.           }
  188. else if(0==Su8KeyLock)
  189. {   
  190.               if(0==ROW_INPUT1&&1==ROW_INPUT2&&1==ROW_INPUT3) //S2按键被按下
  191.               {
  192.                   Su16KeyCnt++;
  193.                   if(Su16KeyCnt>=KEY_SHORT_TIME)
  194.                   {
  195.                         Su8KeyLock=1;  //组合按键的自锁
  196. vGu8DoubleKeySec=1;   //触发组合按键(S1+S2)
  197.                   }
  198.               }



  199. }
  200.           break;

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

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

  216.               Su8ColumnRecord++;  
  217.               if(Su8ColumnRecord>=3)  
  218.               {
  219.                   Su8ColumnRecord=0;
  220.               }  
  221. }
  222. else
  223. {
  224.               //“马上更新输出列的信号状态”
  225.           COLUMN_OUTPUT1=1;   
  226.           COLUMN_OUTPUT2=0;     //列2输出0,非常关键的代码!
  227.           COLUMN_OUTPUT3=1;  
  228.               Su8KeyStep=4;   //如果S1按键没有松开,继续返回判断S2是否已按下
  229. }
  230.           break;
  231.    }

  232. }

  233. void SingleKeyTask(void)   
  234. {
  235. if(0==vGu8SingleKeySec)
  236. {
  237. return;
  238. }

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

  242.             if(0==Gu8LedStatus)
  243.             {
  244.                 Gu8LedStatus=1;
  245.                 LedOpen();   
  246. }
  247.             else
  248.             {
  249.                 Gu8LedStatus=0;
  250.                 LedClose();  
  251. }

  252. vGu8SingleKeySec=0;  
  253. break;

  254.    default:  

  255. vGu8SingleKeySec=0;  
  256. break;

  257. }
  258. }

  259. void DoubleKeyTask(void)   
  260. {
  261. if(0==vGu8DoubleKeySec)
  262. {
  263. return;
  264. }

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

  268.               vGu8BeepTimerFlag=0;  
  269. vGu16BeepTimerCnt=KEY_VOICE_TIME;
  270.               vGu8BeepTimerFlag=1;  

  271. vGu8DoubleKeySec=0;  
  272. break;
  273. }

  274. }

  275. void T0_time() interrupt 1     
  276. {
  277. VoiceScan();  
  278. KeyScan();   

  279. TH0=0xfc;   
  280. TL0=0x66;   
  281. }


  282. void SystemInitial(void)
  283. {
  284. TMOD=0x01;  
  285. TH0=0xfc;   
  286. TL0=0x66;   
  287. EA=1;      
  288. ET0=1;      
  289. TR0=1;      
  290. }

  291. void Delay(unsigned long u32DelayTime)
  292. {
  293.     for(;u32DelayTime>0;u32DelayTime--);
  294. }

  295. void PeripheralInitial(void)
  296. {
  297. if(0==Gu8LedStatus)
  298. {
  299.     LedClose();  
  300. }
  301. else
  302. {
  303.     LedOpen();   
  304. }
  305. }

  306. void BeepOpen(void)
  307. {
  308. P3_4=0;  
  309. }

  310. void BeepClose(void)
  311. {
  312. P3_4=1;  
  313. }

  314. void LedOpen(void)
  315. {
  316. P1_4=0;  
  317. }

  318. void LedClose(void)
  319. {
  320. P1_4=1;  
  321. }

  322. void VoiceScan(void)
  323. {

  324.           static unsigned char Su8Lock=0;  

  325. if(1==vGu8BeepTimerFlag&&vGu16BeepTimerCnt>0)
  326.           {
  327.                   if(0==Su8Lock)
  328.                   {
  329.                    Su8Lock=1;  
  330. BeepOpen();
  331.      }
  332.     else  
  333. {     

  334.                        vGu16BeepTimerCnt--;         

  335.                    if(0==vGu16BeepTimerCnt)
  336.                    {
  337.                            Su8Lock=0;     
  338. BeepClose();  
  339.                    }

  340. }
  341.           }         
  342. }
复制代码


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
| 2018-1-10 20:14 | 显示全部楼层
学习下载附件
| 2018-1-13 21:35 | 显示全部楼层
好东西,收藏了
 楼主 | 2018-1-14 10:39 | 显示全部楼层
第一百零三节: 两个“任意行输入”矩阵按键的“无序”组合触发。

【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,都认为构造了“无序”组合键,蜂鸣器发出“嘀”的一声。

  1. #include "REG52.H"  

  2. #define KEY_VOICE_TIME   50     
  3. #define KEY_SHORT_TIME  20   

  4. void T0_time();
  5. void SystemInitial(void) ;
  6. void Delay(unsigned long u32DelayTime) ;
  7. void PeripheralInitial(void) ;

  8. void BeepOpen(void);   
  9. void BeepClose(void);
  10. void LedOpen_P1_4(void);   
  11. void LedClose_P1_4(void);
  12. void LedOpen_P1_5(void);   
  13. void LedClose_P1_5(void);

  14. void VoiceScan(void);
  15. void KeyScan(void);   
  16. void SingleKeyTask(void);   
  17. void DoubleKeyTask(void);   

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

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

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

  27. volatile unsigned char vGu8BeepTimerFlag=0;  
  28. volatile unsigned int vGu16BeepTimerCnt=0;  

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

  33. void main()
  34. {
  35. SystemInitial();            
  36. Delay(10000);               
  37. PeripheralInitial();      
  38.     while(1)  
  39. {  
  40.         SingleKeyTask();   
  41.         DoubleKeyTask();   
  42.     }
  43. }

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

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

  60.    static unsigned char Su8ColumnRecord=0;  

  61.    switch(Su8KeyStep)
  62.    {
  63.      case 1:   
  64.           if(0==Su8ColumnRecord)  
  65. {
  66.           COLUMN_OUTPUT1=0;      
  67.           COLUMN_OUTPUT2=1;
  68.           COLUMN_OUTPUT3=1;  
  69. }
  70.           else if(1==Su8ColumnRecord)  
  71. {
  72.           COLUMN_OUTPUT1=1;      
  73.           COLUMN_OUTPUT2=0;
  74.           COLUMN_OUTPUT3=1;  
  75. }
  76.           else     
  77. {
  78.           COLUMN_OUTPUT1=1;      
  79.           COLUMN_OUTPUT2=1;
  80.           COLUMN_OUTPUT3=0;  
  81. }
  82.           Su16KeyCnt=0;
  83.           Su8KeyStep++;  
  84.           break;

  85.      case 2:      //等待列输出稳定,但不是去抖动延时
  86.           Su16KeyCnt++;
  87.           if(Su16KeyCnt>=2)
  88.           {
  89.              Su16KeyCnt=0;
  90.              Su8KeyStep++;     
  91.           }
  92.           break;

  93.      case 3:
  94.           if(1==ROW_INPUT1&&1==ROW_INPUT2&&1==ROW_INPUT3)
  95.           {  
  96.              Su8KeyStep=1;  
  97.              Su8KeyLock=0;
  98.              Su16KeyCnt=0;  

  99.              Su8ColumnRecord++;  
  100.              if(Su8ColumnRecord>=3)  
  101.              {
  102.                 Su8ColumnRecord=0;
  103.              }     
  104.           }
  105.           else if(0==Su8KeyLock)
  106.           {
  107.               if(0==ROW_INPUT1&&1==ROW_INPUT2&&1==ROW_INPUT3)
  108.               {
  109.                   Su16KeyCnt++;  
  110.                   if(Su16KeyCnt>=KEY_SHORT_TIME)
  111.                   {
  112.                       Su8KeyLock=1;

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

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

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

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

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

  138.               }
  139.               else if(1==ROW_INPUT1&&0==ROW_INPUT2&&1==ROW_INPUT3)
  140.               {
  141.                   Su16KeyCnt++;  
  142.                   if(Su16KeyCnt>=KEY_SHORT_TIME)
  143.                   {
  144.                       Su8KeyLock=1;

  145.                       if(0==Su8ColumnRecord)  
  146.                       {
  147.                            vGu8SingleKeySec=4;  
  148.                       }
  149.                       else if(1==Su8ColumnRecord)  
  150.                       {
  151.                            vGu8SingleKeySec=5;  
  152.                       }
  153.                       else if(2==Su8ColumnRecord)  
  154.                       {
  155.                            vGu8SingleKeySec=6;  
  156.                       }
  157.                   }   
  158.               }
  159.               else if(1==ROW_INPUT1&&1==ROW_INPUT2&&0==ROW_INPUT3)
  160.               {
  161.                   Su16KeyCnt++;
  162.                   if(Su16KeyCnt>=KEY_SHORT_TIME)
  163.                   {
  164.                       Su8KeyLock=1;
  165.                       if(0==Su8ColumnRecord)  
  166.                       {
  167.                            vGu8SingleKeySec=7;  
  168.                       }
  169.                       else if(1==Su8ColumnRecord)  
  170.                       {
  171.                            vGu8SingleKeySec=8;  
  172.                       }
  173.                       else if(2==Su8ColumnRecord)  
  174.                       {
  175.                            vGu8SingleKeySec=9;  
  176.                       }
  177.                   }   
  178.               }

  179.           }
  180.           break;

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

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

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

  200.              Su8KeyStep++;    //切换到下一个步骤,监控S1是否率先已经松开
  201.           }
  202. else if(0==Su8KeyLock)
  203. {   
  204.               if(0==ROW_INPUT1&&1==ROW_INPUT2&&1==ROW_INPUT3) //S2按键被按下
  205.               {
  206.                   Su16KeyCnt++;
  207.                   if(Su16KeyCnt>=KEY_SHORT_TIME)
  208.                   {
  209.                         Su8KeyLock=1;  //组合按键的自锁
  210. vGu8DoubleKeySec=1;   //触发组合按键(S1+S2)
  211.                   }
  212.               }



  213. }
  214.           break;

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

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

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

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

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

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

  264.              Su8KeyStep++;    //切换到下一个步骤,监控S2是否率先已经松开
  265.           }
  266. else if(0==Su8KeyLock)
  267. {   
  268.               if(0==ROW_INPUT1&&1==ROW_INPUT2&&1==ROW_INPUT3) //S1按键被按下
  269.               {
  270.                   Su16KeyCnt++;
  271.                   if(Su16KeyCnt>=KEY_SHORT_TIME)
  272.                   {
  273.                         Su8KeyLock=1;  //组合按键的自锁
  274. vGu8DoubleKeySec=1;   //触发组合按键(S1+S2)
  275.                   }
  276.               }



  277. }
  278.           break;

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

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

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


  309.    }

  310. }

  311. void SingleKeyTask(void)   
  312. {
  313. if(0==vGu8SingleKeySec)
  314. {
  315. return;
  316. }

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

  320.             if(0==Gu8LedStatus_P1_4)
  321.             {
  322.                 Gu8LedStatus_P1_4=1;
  323.                 LedOpen_P1_4();   
  324. }
  325.             else
  326.             {
  327.                 Gu8LedStatus_P1_4=0;
  328.                 LedClose_P1_4();  
  329. }

  330. vGu8SingleKeySec=0;  
  331. break;

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

  333.             if(0==Gu8LedStatus_P1_5)
  334.             {
  335.                 Gu8LedStatus_P1_5=1;
  336.                 LedOpen_P1_5();   
  337. }
  338.             else
  339.             {
  340.                 Gu8LedStatus_P1_5=0;
  341.                 LedClose_P1_5();  
  342. }

  343. vGu8SingleKeySec=0;  
  344. break;

  345.    default:  

  346. vGu8SingleKeySec=0;  
  347. break;

  348. }
  349. }

  350. void DoubleKeyTask(void)   
  351. {
  352. if(0==vGu8DoubleKeySec)
  353. {
  354. return;
  355. }

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

  359.               vGu8BeepTimerFlag=0;  
  360. vGu16BeepTimerCnt=KEY_VOICE_TIME;
  361.               vGu8BeepTimerFlag=1;  

  362. vGu8DoubleKeySec=0;  
  363. break;
  364. }

  365. }

  366. void T0_time() interrupt 1     
  367. {
  368. VoiceScan();  
  369. KeyScan();   

  370. TH0=0xfc;   
  371. TL0=0x66;   
  372. }


  373. void SystemInitial(void)
  374. {
  375. TMOD=0x01;  
  376. TH0=0xfc;   
  377. TL0=0x66;   
  378. EA=1;      
  379. ET0=1;      
  380. TR0=1;      
  381. }

  382. void Delay(unsigned long u32DelayTime)
  383. {
  384.     for(;u32DelayTime>0;u32DelayTime--);
  385. }

  386. void PeripheralInitial(void)
  387. {
  388. if(0==Gu8LedStatus_P1_4)
  389. {
  390.     LedClose_P1_4();  
  391. }
  392. else
  393. {
  394.     LedOpen_P1_4();   
  395. }

  396. if(0==Gu8LedStatus_P1_5)
  397. {
  398.     LedClose_P1_5();  
  399. }
  400. else
  401. {
  402.     LedOpen_P1_5();   
  403. }

  404. }

  405. void BeepOpen(void)
  406. {
  407. P3_4=0;  
  408. }

  409. void BeepClose(void)
  410. {
  411. P3_4=1;  
  412. }

  413. void LedOpen_P1_4(void)
  414. {
  415. P1_4=0;  
  416. }

  417. void LedClose_P1_4(void)
  418. {
  419. P1_4=1;  
  420. }

  421. void LedOpen_P1_5(void)
  422. {
  423. P1_5=0;  
  424. }

  425. void LedClose_P1_5(void)
  426. {
  427. P1_5=1;  
  428. }

  429. void VoiceScan(void)
  430. {

  431.           static unsigned char Su8Lock=0;  

  432. if(1==vGu8BeepTimerFlag&&vGu16BeepTimerCnt>0)
  433.           {
  434.                   if(0==Su8Lock)
  435.                   {
  436.                    Su8Lock=1;  
  437. BeepOpen();
  438.      }
  439.     else  
  440. {     

  441.                        vGu16BeepTimerCnt--;         

  442.                    if(0==vGu16BeepTimerCnt)
  443.                    {
  444.                            Su8Lock=0;     
  445. BeepClose();  
  446.                    }

  447. }
  448.           }         
  449. }
复制代码



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
| 2018-1-14 22:08 | 显示全部楼层
期待楼主的资料,就是更新有点慢。
| 2018-1-18 19:27 | 显示全部楼层
感谢楼主的无私奉献
| 2018-1-19 17:36 | 显示全部楼层
Mark
扫描二维码,随时随地手机跟帖
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复

您需要登录后才可以回帖
登录 | 注册
高级模式
我要创建版块 申请成为版主

论坛热帖

关闭

热门推荐上一条 /2 下一条

快速回复 返回顶部 返回列表