数码之家

 找回密码
 立即注册
搜索
查看: 447|回复: 4

[C51] 小白学习单片机之按键检测、蜂鸣器哔哔哔

[复制链接]
发表于 2025-2-4 14:18:25 | 显示全部楼层 |阅读模式

爱科技、爱创意、爱折腾、爱极致,我们都是技术控

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

x
本帖最后由 wangxiangtan2 于 2025-2-4 14:26 编辑

这种加锁的程序大家觉得怎么样?
按键检测采用计数法25ms消抖,看起来很强大的样子:
  1. #include "REG52.H"
  2. #define KEY_VOICE_TIME 50 //按键触发后发出的声音长度
  3. #define KEY_FILTER_TIME 25 //按键滤波的“稳定时间”25ms
  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 KEY_INPUT1=P2^2; //K1 按键识别的输入口。
  15. sbit KEY_INPUT2=P2^1; //K2 按键识别的输入口。
  16. volatile unsigned char vGu8BeepTimerFlag=0;
  17. volatile unsigned int vGu16BeepTimerCnt=0;
  18. volatile unsigned char vGu8KeySec=0; //按键的触发序号,全局变量意味着是其它函数的接口。
  19. void main()
  20. {
  21. SystemInitial();
  22. Delay(10000);
  23. PeripheralInitial();
  24. while(1)
  25. {
  26. KeyTask(); //按键任务函数
  27. }
  28. }
  29. void T0_time() interrupt 1
  30. {
  31. VoiceScan();
  32. KeyScan(); //按键识别的驱动函数
  33. TH0=0xfc;
  34. TL0=0x66;}
  35. void SystemInitial(void)
  36. {
  37. TMOD=0x01;
  38. TH0=0xfc;
  39. TL0=0x66;
  40. EA=1;
  41. ET0=1;
  42. TR0=1;
  43. }
  44. void Delay(unsigned long u32DelayTime)
  45. {
  46. for(;u32DelayTime>0;u32DelayTime--);
  47. }
  48. void PeripheralInitial(void)
  49. {
  50. }
  51. void BeepOpen(void)
  52. {
  53. P3_4=0;
  54. }
  55. void BeepClose(void)
  56. {
  57. P3_4=1;
  58. }
  59. void VoiceScan(void)
  60. {
  61. static unsigned char Su8Lock=0;
  62. if(1==vGu8BeepTimerFlag&&vGu16BeepTimerCnt>0)
  63. {
  64. if(0==Su8Lock)
  65. {
  66. Su8Lock=1;
  67. BeepOpen();
  68. }
  69. else
  70. {
  71. vGu16BeepTimerCnt--;
  72. if(0==vGu16BeepTimerCnt)
  73. {
  74. Su8Lock=0;
  75. BeepClose();
  76. }
  77. }
  78. }
  79. }
  80. /* 注释一:
  81. * 独立按键扫描的详细过程,以按键 K1 为例,如下:
  82. * 第一步:平时没有按键被触发时,按键的自锁标志,去抖动延时计数器一直被清零。
  83. * 第二步:一旦有按键被按下,去抖动延时计数器开始在定时中断函数里累加,在还没累加到
  84. * 阀值 KEY_FILTER_TIME 时,如果在这期间由于受外界干扰或者按键抖动,而使
  85. * IO 口突然瞬间触发成高电平,这个时候马上把延时计数器 Su16KeyCnt1 清零了,这个过程
  86. * 非常巧妙,非常有效地去除瞬间的杂波干扰。以后凡是用到开关感应器的时候,
  87. * 都可以用类似这样的方法去干扰。
  88. * 第三步:如果按键按下的时间达到阀值 KEY_FILTER_TIME 时,则触发按键,把编号 vGu8KeySec 赋值。
  89. * 同时,马上把自锁标志 Su8KeyLock1 置 1,防止按住按键不松手后一直触发。
  90. * 第四步:等按键松开后,自锁标志 Su8KeyLock1 及时清零(解锁),为下一次自锁做准备。
  91. * 第五步:以上整个过程,就是识别按键 IO 口下降沿触发的过程。
  92. */
  93. void KeyScan(void) //此函数放在定时中断里每 1ms 扫描一次
  94. {
  95. static unsigned char Su8KeyLock1; //1 号按键的自锁
  96. static unsigned int Su16KeyCnt1; //1 号按键的计时器
  97. static unsigned char Su8KeyLock2; //2 号按键的自锁
  98. static unsigned int Su16KeyCnt2; //2 号按键的计时器
  99. //1 号按键
  100. if(0!=KEY_INPUT1)//IO 是高电平,说明按键没有被按下,这时要及时清零一些标志位
  101. {
  102. Su8KeyLock1=0; //按键解锁
  103. Su16KeyCnt1=0; //按键去抖动延时计数器清零,此行非常巧妙,是全场的亮点。
  104. }
  105. else if(0==Su8KeyLock1)//有按键按下,且是第一次被按下。这行很多初学者有疑问,请看专题分析。
  106. {
  107. Su16KeyCnt1++; //累加定时中断次数
  108. if(Su16KeyCnt1>=KEY_FILTER_TIME) //滤波的“稳定时间”KEY_FILTER_TIME,长度是 25ms。
  109. {
  110. Su8KeyLock1=1; //按键的自锁,避免一直触发
  111. vGu8KeySec=1; //触发 1 号键
  112. }
  113. }
  114. //2 号按键
  115. if(0!=KEY_INPUT2)
  116. {
  117. Su8KeyLock2=0;
  118. Su16KeyCnt2=0;
  119. }
  120. else if(0==Su8KeyLock2)
  121. {
  122. Su16KeyCnt2++;
  123. if(Su16KeyCnt2>=KEY_FILTER_TIME)
  124. {
  125. Su8KeyLock2=1;
  126. vGu8KeySec=2; //触发 2 号键
  127. }
  128. }
  129. }
  130. void KeyTask(void) //按键任务函数,放在主函数内
  131. {
  132. if(0==vGu8KeySec)
  133. {
  134. return; //按键的触发序号是 0 意味着无按键触发,直接退出当前函数,不执行此函数下面的代码
  135. }
  136. switch(vGu8KeySec) //根据不同的按键触发序号执行对应的代码
  137. {
  138. case 1: //1 号按键
  139. vGu8BeepTimerFlag=0;
  140. vGu16BeepTimerCnt=KEY_VOICE_TIME; //触发按键后,发出固定长度的声音
  141. vGu8BeepTimerFlag=1;
  142. vGu8KeySec=0; //响应按键服务处理程序后,按键编号必须清零,避免一直触发
  143. break;
  144. case 2: //2 号按键
  145. vGu8BeepTimerFlag=0;
  146. vGu16BeepTimerCnt=KEY_VOICE_TIME; //触发按键后,发出固定长度的声音
  147. vGu8BeepTimerFlag=1;
  148. vGu8KeySec=0; //响应按键服务处理程序后,按键编号必须清零,避免一直触发
  149. break;
  150. }
  151. }
复制代码





发表于 2025-2-4 21:33:07 来自手机浏览器 | 显示全部楼层
一个按键程序有必要这么复杂吗?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2025-2-5 09:37:20 | 显示全部楼层
taotaoliu199 发表于 2025-2-4 21:33
一个按键程序有必要这么复杂吗?

何止是复杂,光按键作者写了十几章,普通按键的长按、短按、正常按,无序组合按,有序组合按;矩阵键盘的长按、短按、正常按,无序组合按,有序组合按
回复 支持 反对

使用道具 举报

发表于 2025-2-5 15:16:33 | 显示全部楼层
初学写复杂了没关系  多熟悉就知道了
回复 支持 反对

使用道具 举报

发表于 2025-2-7 14:10:46 | 显示全部楼层
中断服务子程序不要太长,在主程序里面用状态机定时轮询最好
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

APP|手机版|小黑屋|关于我们|联系我们|法律条款|技术知识分享平台

闽公网安备35020502000485号

闽ICP备2021002735号-2

GMT+8, 2025-5-23 09:34 , Processed in 0.171601 second(s), 10 queries , Redis On.

Powered by Discuz!

© 2006-2025 MyDigit.Net

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