第十二节:按住一个独立按键不松手的连续步进触发。

开场白:
上一节讲了同一个按键短按与长按的区别触发功能,这节要教会大家两个知识点:
第一个知识点:如何在上一节的基础上,略作修改,就可以实现按住一个独立按键不松手的连续步进触发。
第二个知识点:在单片机的C语言编译器中,当无符号数据0减去1时,就会溢出,变成这个类型数据的最大值。比如是unsigned int类型的0减去1就等于65535(0xffff),unsigned char类型的0减去1就等于255(0xff)。这个常识经常要用在判断数据临界点的地方。比如一个数最大值是20,最小值是0。这个数据一直往下减,当我们发现它突然大于20的时候,就知道它溢出了,这个时候要及时把它赋值成0就达到我们的目的。

具体内容,请看源代码讲解。

(1)硬件平台:基于朱兆祺51单片机学习板。用矩阵键盘中的S1和S5号键作为独立按键,记得把输出线P0.4一直输出低电平,模拟独立按键的触发地GND。

(2)实现功能:两个独立按键S1和S5,S1键作为加键。S5键做为减键。每按一次S1键则被设置参数uiSetNumber自加1。如果按住S1键不松手超过1秒钟,被设置参数uiSetNumber以每0.25秒的时间间隔往上自加1,一直加到20为止。每按一次S5键则被设置参数uiSetNumber自减1。如果按住S5键不松手超过1秒钟,被设置参数uiSetNumber以每0.25秒的时间间隔往下自减1,一直减到0为止。当被设置参数uiSetNumber小于10的时候,LED灯灭;当大于或者等于10的时候,LED灯亮。

(3)源代码讲解如下:

  1. #include "REG52.H"  
  2.   
  3. #define const_voice_short  40   //蜂鸣器短叫的持续时间  
  4.   
  5. #define const_key_time1  20    //按键去抖动延时的时间  
  6. #define const_key_time2  20    //按键去抖动延时的时间  
  7.   
  8. #define const_time_0_25s  111   //0.25秒钟的时间需要的定时中断次数  
  9. #define const_time_1s     444   //1秒钟的时间需要的定时中断次数  
  10.   
  11.   
  12.   
  13. void initial_myself();      
  14. void initial_peripheral();  
  15. void delay_long(unsigned int uiDelaylong);  
  16. void T0_time();  //定时中断函数  
  17. void key_service(); //按键服务的应用程序  
  18. void key_scan(); //按键扫描函数 放在定时中断里  
  19. void led_run();  //led灯的应用程序  
  20.   
  21. sbit key_sr1=P0^0; //对应朱兆祺学习板的S1键  
  22. sbit key_sr2=P0^1; //对应朱兆祺学习板的S5键  
  23. sbit key_gnd_dr=P0^4; //模拟独立按键的地GND,因此必须一直输出低电平  
  24.   
  25. sbit beep_dr=P2^7; //蜂鸣器的驱动IO口  
  26.   
  27. sbit led_dr=P3^5;  //LED的驱动IO口  
  28.   
  29.   
  30. unsigned char ucKeySec=0;   //被触发的按键编号  
  31.   
  32. unsigned int  uiKeyTimeCnt1=0; //按键去抖动延时计数器  
  33. unsigned int  uiKeyCtntyCnt1=0;  //按键连续触发的间隔延时计数器  
  34. unsigned char ucKeyLock1=0; //按键触发后自锁的变量标志  
  35.   
  36.   
  37. unsigned int  uiKeyTimeCnt2=0; //按键去抖动延时计数器  
  38. unsigned int  uiKeyCtntyCnt2=0;  //按键连续触发的间隔延时计数器  
  39. unsigned char ucKeyLock2=0; //按键触发后自锁的变量标志  
  40.   
  41. unsigned int  uiVoiceCnt=0;  //蜂鸣器鸣叫的持续时间计数器  
  42.   
  43. unsigned int  uiSetNumber=0; //设置的数据  
  44.   
  45. void main()   
  46.   {  
  47.    initial_myself();    
  48.    delay_long(100);     
  49.    initial_peripheral();   
  50.    while(1)    
  51.    {   
  52.        key_service(); //按键服务的应用程序  
  53.            led_run();  //led灯的应用程序  
  54.    }  
  55.   
  56. }  
  57.   
  58. void led_run()  //led灯的应用程序  
  59. {  
  60.    if(uiSetNumber<10)  //如果被设置的参数uiSetNumber小于10,LED灯则灭。否则亮。  
  61.    {  
  62.       led_dr=0;  //灭  
  63.    }  
  64.    else  
  65.    {  
  66.       led_dr=1;  //亮  
  67.    }  
  68. }  
  69.   
  70.   
  71. void key_scan()//按键扫描函数 放在定时中断里  
  72. {    
  73. /* 注释一: 
  74. * 独立按键扫描的详细过程: 
  75. * 第一步:平时没有按键被触发时,按键的自锁标志,去抖动延时计数器,以及时间间隔延时计数器一直被清零。 
  76. * 第二步:一旦有按键被按下,去抖动延时计数器开始在定时中断函数里累加,在还没累加到 
  77. *         阀值const_key_time1时,如果在这期间由于受外界干扰或者按键抖动,而使 
  78. *         IO口突然瞬间触发成高电平,这个时候马上把延时计数器uiKeyTimeCnt1 
  79. *         清零了,这个过程非常巧妙,非常有效地去除瞬间的杂波干扰。这是我实战中摸索出来的。 
  80. *         以后凡是用到开关感应器的时候,都可以用类似这样的方法去干扰。 
  81. * 第三步:如果按键按下的时间超过了阀值const_key_time1,则触发按键,把编号ucKeySec赋值。 
  82. *         同时,马上把自锁标志ucKeyLock1置位,防止按住按键不松手后一直触发。 
  83. * 第四步:如果此时触发了一次按键后,一直不松手,去抖动延时计时器继续累加,直到超过了1秒钟。进入连续触发模式的程序 
  84. * 第五步:在连续触发模式的程序中,连续累加延时计数器开始累加,每0.25秒就触发一次。 
  85. * 第六步:等按键松开后,自锁标志ucKeyLock1和两个延时计时器及时清零,为下一次自锁做准备。 
  86. */  
  87.   if(key_sr1==1)//IO是高电平,说明按键没有被按下,这时要及时清零一些标志位  
  88.   {  
  89.      ucKeyLock1=0; //按键自锁标志清零  
  90.      uiKeyTimeCnt1=0;//按键去抖动延时计数器清零,此行非常巧妙,是我实战中摸索出来的。      
  91.      uiKeyCtntyCnt1=0; //连续累加的时间间隔延时计数器清零  
  92.   }  
  93.   else if(ucKeyLock1==0)//有按键按下,且是第一次被按下  
  94.   {  
  95.      uiKeyTimeCnt1++; //累加定时中断次数  
  96.      if(uiKeyTimeCnt1>const_key_time1)  
  97.      {  
  98.         uiKeyTimeCnt1=0;   
  99.         ucKeyLock1=1;  //自锁按键置位,避免一直触发  
  100.         ucKeySec=1;    //触发1号键  
  101.      }  
  102.   }  
  103.   else if(uiKeyTimeCnt1<const_time_1s) //按住累加到1秒  
  104.   {  
  105.      uiKeyTimeCnt1++;  
  106.   }  
  107.   else  //按住累加到1秒后仍然不放手,这个时候进入有节奏的连续触发  
  108.   {  
  109.      uiKeyCtntyCnt1++; //连续触发延时计数器累加  
  110.          if(uiKeyCtntyCnt1>const_time_0_25s)  //按住没松手,每0.25秒就触发一次  
  111.          {  
  112.              uiKeyCtntyCnt1=0; //  
  113.          ucKeySec=1;    //触发1号键  
  114.          }  
  115.      
  116.   }  
  117.   
  118.   
  119.   
  120.   if(key_sr2==1)  
  121.   {  
  122.      ucKeyLock2=0;   
  123.      uiKeyTimeCnt2=0;  
  124.          uiKeyCtntyCnt2=0;  
  125.   }  
  126.   else if(ucKeyLock2==0)  
  127.   {  
  128.      uiKeyTimeCnt2++; //累加定时中断次数  
  129.      if(uiKeyTimeCnt2>const_key_time2)  
  130.      {  
  131.         uiKeyTimeCnt2=0;  
  132.         ucKeyLock2=1;   
  133.         ucKeySec=2;     //触发2号键  
  134.      }  
  135.   }  
  136.   else if(uiKeyTimeCnt2<const_time_1s)  
  137.   {  
  138.       uiKeyTimeCnt2++;  
  139.   }  
  140.   else  
  141.   {  
  142.       uiKeyCtntyCnt2++;  
  143.           if(uiKeyCtntyCnt2>const_time_0_25s)  
  144.           {  
  145.              uiKeyCtntyCnt2=0;  
  146.                  ucKeySec=2;     //触发2号键  
  147.           }  
  148.   }  
  149.   
  150. }  
  151.   
  152.   
  153. void key_service() //第三区 按键服务的应用程序  
  154. {  
  155.   switch(ucKeySec) //按键服务状态切换  
  156.   {  
  157.     case 1:// 1号键 连续加键  对应朱兆祺学习板的S1键    
  158.               uiSetNumber++; //被设置的参数连续往上加  
  159.                           if(uiSetNumber>20) //最大是20  
  160.                           {  
  161.                             uiSetNumber=20;  
  162.                           }  
  163.               uiVoiceCnt=const_voice_short; //按键声音触发,滴一声就停。  
  164.               ucKeySec=0;  //响应按键服务处理程序后,按键编号清零,避免一致触发  
  165.           break;          
  166.     case 2:// 2号键 连续减键  对应朱兆祺学习板的S5键  
  167. /* 注释二: 
  168. * 在单片机的C语言编译器中,当无符号数据0减去1时,就会溢出,变成这个类型数据的最大值。 
  169. * 比如是unsigned int的0减去1就等于65535(0xffff),unsigned char的0减去1就等于255(0xff) 
  170. */  
  171.               uiSetNumber--; //被设置的参数连续往下减  
  172.                           if(uiSetNumber>20) //最小是0.为什么这里用20?因为0减去1就是溢出变成了65535(0xffff)  
  173.                           {  
  174.                             uiSetNumber=0;  
  175.                           }  
  176.               uiVoiceCnt=const_voice_short; //按键声音触发,滴一声就停。  
  177.               ucKeySec=0;  //响应按键服务处理程序后,按键编号清零,避免一致触发  
  178.           break;                      
  179.   }                  
  180. }  
  181.   
  182.   
  183.   
  184. void T0_time() interrupt 1  
  185. {  
  186.   TF0=0;  //清除中断标志  
  187.   TR0=0; //关中断  
  188.   
  189.   key_scan(); //按键扫描函数  
  190.   
  191.   if(uiVoiceCnt!=0)  
  192.   {  
  193.      uiVoiceCnt--; //每次进入定时中断都自减1,直到等于零为止。才停止鸣叫  
  194.          beep_dr=0;  //蜂鸣器是PNP三极管控制,低电平就开始鸣叫。  
  195.   }  
  196.   else  
  197.   {  
  198.      ; //此处多加一个空指令,想维持跟if括号语句的数量对称,都是两条指令。不加也可以。  
  199.            beep_dr=1;  //蜂鸣器是PNP三极管控制,高电平就停止鸣叫。  
  200.   }  
  201.   
  202.   
  203.   TH0=0xf8;   //重装初始值(65535-2000)=63535=0xf82f  
  204.   TL0=0x2f;  
  205.   TR0=1;  //开中断  
  206. }  
  207.   
  208.   
  209. void delay_long(unsigned int uiDelayLong)  
  210. {  
  211.    unsigned int i;  
  212.    unsigned int j;  
  213.    for(i=0;i<uiDelayLong;i++)  
  214.    {  
  215.       for(j=0;j<500;j++)  //内嵌循环的空指令数量  
  216.           {  
  217.              ; //一个分号相当于执行一条空语句  
  218.           }  
  219.    }  
  220. }  
  221.   
  222.   
  223. void initial_myself()  //第一区 初始化单片机  
  224. {  
  225. /* 注释三: 
  226. * 矩阵键盘也可以做独立按键,前提是把某一根公共输出线输出低电平, 
  227. * 模拟独立按键的触发地,本程序中,把key_gnd_dr输出低电平。 
  228. * 朱兆祺51学习板的S1和S5两个按键就是本程序中用到的两个独立按键。 
  229. */  
  230.   key_gnd_dr=0; //模拟独立按键的地GND,因此必须一直输出低电平  
  231.   
  232.   
  233.   beep_dr=1; //用PNP三极管控制蜂鸣器,输出高电平时不叫。  
  234.   led_dr=0;  //LED灯灭  
  235.   
  236.   TMOD=0x01;  //设置定时器0为工作方式1  
  237.   
  238.   
  239.   TH0=0xf8;   //重装初始值(65535-2000)=63535=0xf82f  
  240.   TL0=0x2f;  
  241.   
  242. }  
  243. void initial_peripheral() //第二区 初始化外围  
  244. {  
  245.   EA=1;     //开总中断  
  246.   ET0=1;    //允许定时中断  
  247.   TR0=1;    //启动定时中断  
  248.   
  249. }   

总结陈词:
本程序可以有节奏地快速往上加或者快速往下减。假如被设置数据的范围不是20,而是1000。如果按0.25秒的节奏往上加,那不是累死人了?如果直接把0.25秒的节奏调快到0.01秒,那么到达999的时候,还来不及松手就很容易超过头,不好微调。有没有完整的方案解决这个问题?当然有。欲知详情,请听下回分解-----按住一个独立按键不松手的加速匀速触发。

 

永不止步步 发表于11-19 21:40 浏览65535次
分享到:

已有0条评论

暂时还没有回复哟,快来抢沙发吧

添加一条新评论

只有登录用户才能评论,请先登录注册哦!

话题作者

永不止步步
金币:67417个|学分:345841个
立即注册
畅学电子网,带你进入电子开发学习世界
专业电子工程技术学习交流社区,加入畅学一起充电加油吧!

x

畅学电子网订阅号