找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 9536|回复: 20
收起左侧

51单片机独立按键程序 实现长按、短按功能

  [复制链接]
ID:743654 发表于 2021-9-30 11:26 | 显示全部楼层 |阅读模式
独立按键 通过定时器实现长按、短按功能:

程序每条语句都附带注释,易理解,方便移植。

本人亲测有效!

单片机源程序如下:
  1. /*************************************************************************************
  2. 实验现象:下载程序后按下K1按键可以对led_0/led_1小灯状态取反
  3.            短按控制led_0;长按控制led_1                                                  
  4. *************************************************************************************
  5. */

  6. #include "reg52.h"               //此文件中定义了单片机的一些特殊功能寄存器

  7. typedef unsigned int u16;          //对数据类型进行声明定义
  8. typedef unsigned char u8;

  9. bit press_flag; //按键标志位
  10. u16 count;      //计数器标志位
  11. sbit k1=P3^1;         //定义P31口是k1
  12. sbit led_0=P2^0; //定义P20口是led_0
  13. sbit led_1=P2^1; //定义P20口是led_1

  14. void Timer0Init()
  15. {
  16.         TMOD|=0X01;//选择为定时器0模式,工作方式1,仅用TR0打开启动。

  17.         TH0=0XFC;        //给定时器赋初值,定时1ms
  18.         TL0=0X18;       
  19.         ET0=1;         //打开定时器0中断允许
  20.         EA=1;         //打开总中断
  21.         TR0=1;//打开定时器                       
  22. }

  23. void Timer0() interrupt 1
  24. {
  25.         TH0=0XFC;        //给定时器赋初值,定时1ms
  26.         TL0=0X18;
  27.         count++;  //计数器累加
  28. }


  29. /*******************************************************************************
  30. * 函 数 名         : delay
  31. * 函数功能                   : 延时函数,i=1时,大约延时10us
  32. *******************************************************************************/
  33. void delay(u16 i)
  34. {
  35.         while(i--);       
  36. }

  37. /*******************************************************************************
  38. * 函 数 名         : keypros
  39. * 函数功能                   : 按键处理函数,判断按键K1是否按下
  40. *******************************************************************************/
  41. void keypros()
  42. {
  43.         if(k1==0)                     //检测按键K1是否按下
  44.         {       
  45.                 delay(1000);   //消除抖动 一般大约10ms
  46.                 if(k1==0)             //再次判断按键是否按下
  47.                 {
  48.                   press_flag=1; //按键标志位
  49.                   TR0=1;        //打开中断
  50.                   while(!k1);   //按键松手检测
  51.                   TR0=0;        //关闭中断
  52.                 }
  53.            if(press_flag)  //若有按键
  54.           {
  55.             if(count>=1500) //长按>=1.5s
  56.             {
  57.               led_1=~led_1;        //led_1灯状态取反 ,此处可以换成别的处理事件
  58.              }
  59.             else            //短按<1.5s
  60.             {
  61.               led_0=~led_0;         //led_0灯状态取反 ,此处可以换成别的处理事件
  62.             }
  63.             count=0;               //计数器清零
  64.             press_flag=0;        //按键标志位清零
  65.           }                
  66.         }
  67. }

  68. /*******************************************************************************
  69. * 函 数 名       : main
  70. * 函数功能                 : 主函数
  71. * 输    入       : 无
  72. * 输    出             : 无
  73. *******************************************************************************/
  74. void main()
  75. {       
  76.         Timer0Init();//定时器初始化
  77.         led_0=1;     //led_0熄灭
  78.         led_1=1;     //led_1熄灭
  79.         while(1)     //主循环
  80.         {       
  81.           keypros();  //按键处理函数       
  82.         }               
  83. }
复制代码


评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

回复

使用道具 举报

ID:1042234 发表于 2022-8-17 10:32 | 显示全部楼层
好像不太对 第一次短按会判断为长按
回复

使用道具 举报

ID:1039671 发表于 2022-8-17 16:34 | 显示全部楼层
stcmcu2 发表于 2022-8-17 10:32
好像不太对 第一次短按会判断为长按

做了实验,第一次开机LED1会亮,仔细分析了下代码,在定时器初始化里面把TR0=1改为TR0=0,就可以解决这个问题。然后感觉有一点还是不完美,就是按键长按不松开LED1不会点亮,需要释放按键才能点亮。
回复

使用道具 举报

ID:161164 发表于 2022-8-17 17:32 | 显示全部楼层
stcmcu2 发表于 2022-8-17 10:32
好像不太对 第一次短按会判断为长按

26行 改为 TR0 = 0;
但不建议用长时间的阻塞式消抖/while(!K1)
回复

使用道具 举报

ID:814525 发表于 2022-8-17 20:17 | 显示全部楼层
一看到while(!k1)这句,就没有研究的价值。
回复

使用道具 举报

ID:1042234 发表于 2022-8-18 08:42 | 显示全部楼层
lkc8210 发表于 2022-8-17 17:32
26行 改为 TR0 = 0;
但不建议用长时间的阻塞式消抖/while(!K1)

双击的怎么样去操作
回复

使用道具 举报

ID:161164 发表于 2022-8-18 10:00 | 显示全部楼层
  1. /*************************************************************************************
  2. 实验现象:下载程序后按下K1按键可以对led_0/led_1小灯状态取反
  3.            短按控制led_0;长按控制led_1
  4. *************************************************************************************
  5. */

  6. #include "reg52.h"               //此文件中定义了单片机的一些特殊功能寄存器

  7. typedef unsigned int u16;          //对数据类型进行声明定义
  8. typedef unsigned char u8;

  9. u16 count = 0;      //计数器标志位
  10. u8 KeyVal = 0;//按键标志位
  11. sbit k1=P3^1;         //定义P31口是k1
  12. sbit led_0=P2^0; //定义P20口是led_0
  13. sbit led_1=P2^1; //定义P20口是led_1
  14. bit Flag_1ms = 0;//1ms标志位
  15. void Timer0Init()
  16. {
  17.         TMOD|=0X01;//选择为定时器0模式,工作方式1,仅用TR0打开启动。

  18.         TH0=0XFC;        //给定时器赋初值,定时1ms
  19.         TL0=0X18;
  20.         ET0=1;         //打开定时器0中断允许
  21.         EA=1;         //打开总中断
  22.         TR0=1;//打开定时器
  23. }

  24. void Timer0() interrupt 1
  25. {
  26.         TH0=0XFC;        //给定时器赋初值,定时1ms
  27.         TL0=0X18;
  28.         Flag_1ms = 1;
  29. }

  30. /*******************************************************************************
  31. * 函 数 名         : keypros
  32. * 函数功能                   : 按键处理函数,判断按键K1是否按下
  33. *******************************************************************************/
  34. void keypros()
  35. {
  36.         if(Flag_1ms==1)
  37.         {
  38.                 Flag_1ms = 0;
  39.                 if(k1==0)             //判断按键是否按下
  40.                 {
  41.                         if(count<0xFFFF)count++;
  42.                         if(count==1500)
  43.                         {
  44.                                 KeyVal = 10;
  45.                         }
  46.                 }
  47.                 else
  48.                 {
  49.                         if((count>= 20) && (count<1500))
  50.                         {
  51.                                 KeyVal = 1;
  52.                         }
  53.                         count=0;
  54.                 }
  55.                 if(KeyVal > 0)  //若有按键
  56.                 {
  57.                         if(KeyVal==10) //长按>=1.5s
  58.                         {
  59.                                 led_1=~led_1;        //led_1灯状态取反 ,此处可以换成别的处理事件
  60.                         }
  61.                         else            //短按<1.5s
  62.                         {
  63.                                 led_0=~led_0;         //led_0灯状态取反 ,此处可以换成别的处理事件
  64.                         }
  65.                         KeyVal=0;
  66.                 }
  67.         }
  68. }

  69. /*******************************************************************************
  70. * 函 数 名       : main
  71. * 函数功能                 : 主函数
  72. * 输    入       : 无
  73. * 输    出             : 无
  74. *******************************************************************************/
  75. void main()
  76. {
  77.         Timer0Init();//定时器初始化
  78.         led_0=1;     //led_0熄灭
  79.         led_1=1;     //led_1熄灭
  80.         while(1)     //主循环
  81.         {
  82.                 keypros();  //按键处理函数
  83.         }
  84. }
复制代码


回复

使用道具 举报

ID:161164 发表于 2022-8-18 10:22 | 显示全部楼层
stcmcu2 发表于 2022-8-18 08:42
双击的怎么样去操作
  1. /*************************************************************************************
  2. 实验现象:下载程序后按下K1按键可以对led_0/led_1小灯状态取反
  3.            短按控制led_0;长按控制led_1
  4. *************************************************************************************
  5. */

  6. #include "reg52.h"               //此文件中定义了单片机的一些特殊功能寄存器

  7. typedef unsigned int u16;          //对数据类型进行声明定义
  8. typedef unsigned char u8;

  9. u16 PressDelay = 0;     //按下计数器标志位
  10. u16 ReleaseDelay = 0;   //放开计数器标志位
  11. u8 KeyVal = 0;                        //按键标志位
  12. u8 ClickCount = 0;                //按键次数
  13. sbit k1=P3^2;                 //定义P32口是k1
  14. sbit led_0=P2^0;                 //定义P20口是led_0
  15. sbit led_1=P2^1;                 //定义P20口是led_1
  16. bit Flag_1ms = 0;                //1ms标志位
  17. void Timer0Init()
  18. {
  19.         TMOD|=0X01;//选择为定时器0模式,工作方式1,仅用TR0打开启动。

  20.         TH0=0XFC;       //给定时器赋初值,定时1ms
  21.         TL0=0X18;
  22.         ET0=1;                 //打开定时器0中断允许
  23.         EA=1;                 //打开总中断
  24.         TR0=1;                        //打开定时器
  25. }

  26. void Timer0() interrupt 1
  27. {
  28.         TH0=0XFC;        //给定时器赋初值,定时1ms
  29.         TL0=0X18;
  30.         Flag_1ms = 1;         //1ms标志位
  31. }

  32. /*******************************************************************************
  33. * 函 数 名         : keypros
  34. * 函数功能                   : 按键处理函数,判断按键K1是否按下
  35. *******************************************************************************/
  36. void keypros()
  37. {
  38.         if(Flag_1ms==1)
  39.         {
  40.                 Flag_1ms = 0;
  41.                 if(k1==0)             //判断按键是否按下
  42.                 {
  43.                         if(PressDelay<0xFFFF)PressDelay++;
  44.                         if(PressDelay==20)//20ms消抖
  45.                         {
  46.                                 ClickCount++;
  47.                         }
  48.                         if(PressDelay==1500)
  49.                         {
  50.                                 KeyVal = 10;
  51.                         }
  52.                         ReleaseDelay = 0;
  53.                 }
  54.                 else
  55.                 {
  56.                         if(ReleaseDelay<0xFF)ReleaseDelay++;
  57.                         if(ReleaseDelay==200)
  58.                         if(PressDelay<1500)
  59.                         {
  60.                                 KeyVal = ClickCount;
  61.                                 ClickCount = 0;
  62.                         }
  63.                         PressDelay=0;
  64.                 }
  65.                 if(KeyVal > 0)  //若有按键
  66.                 {
  67.                         if(KeyVal==10) //长按>=1.5s
  68.                         {
  69.                                 led_1=~led_1;        //led_1灯状态取反 ,此处可以换成别的处理事件
  70.                         }
  71.                         else if(KeyVal==1)
  72.                         {
  73.                                 led_0=0;
  74.                         }
  75.                         else if(KeyVal==2)
  76.                         {
  77.                                 led_0=1;
  78.                         }
  79.                         KeyVal=0;
  80.                 }
  81.         }
  82. }

  83. /*******************************************************************************
  84. * 函 数 名       : main
  85. * 函数功能                 : 主函数
  86. * 输    入       : 无
  87. * 输    出             : 无
  88. *******************************************************************************/
  89. void main()
  90. {
  91.         Timer0Init();//定时器初始化
  92.         led_0=1;     //led_0熄灭
  93.         led_1=1;     //led_1熄灭
  94.         while(1)     //主循环
  95.         {
  96.                 keypros();  //按键处理函数
  97.         }
  98. }
复制代码


回复

使用道具 举报

ID:119977 发表于 2022-8-18 10:31 | 显示全部楼层
delay(1000);   //消除抖动 一般大约10ms   //一般我看到用延时消抖我都觉得水平一般   
回复

使用道具 举报

ID:1042234 发表于 2022-8-18 15:01 | 显示全部楼层

1.按钮长按松开的时候会误判为短按
2.双击的时候第二下如果时间长一点的话会误判为长按
回复

使用道具 举报

ID:1042234 发表于 2022-8-18 15:11 | 显示全部楼层

你好  实际在测试的时候发现 1.按钮长按抬起的时候会误判为短按
2.双击的时候第二下长按的时候会误判为长按  
回复

使用道具 举报

ID:161164 发表于 2022-8-18 17:26 | 显示全部楼层
stcmcu2 发表于 2022-8-18 15:01
1.按钮长按松开的时候会误判为短按
2.双击的时候第二下如果时间长一点的话会误判为长按
  1. void keypros()
  2. {
  3.         if(Flag_1ms==1)
  4.         {
  5.                 Flag_1ms = 0;
  6.                 if(k1==0)             //判断按键是否按下
  7.                 {
  8.                         if(PressDelay<0xFFFF)PressDelay++;
  9.                         if(PressDelay==20)//20ms消抖
  10.                         {
  11.                                 ClickCount++;
  12.                         }
  13.                                                 if(ClickCount<2)//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  14.                                                         if(PressDelay==1500)
  15.                                                         {
  16.                                                                         KeyVal = 10;
  17.                                                                         ClickCount = 0;//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  18.                                                         }
  19.                         ReleaseDelay = 0;
  20.                 }
  21.                 else
  22.                 {
  23.                         if(ReleaseDelay<0xFF)ReleaseDelay++;
  24.                         if(ReleaseDelay==200)
  25.                                                         if(ClickCount)//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  26.                                                         {
  27.                                                                         KeyVal = ClickCount;
  28.                                                                         ClickCount = 0;
  29.                                                         }
  30.                         PressDelay=0;
  31.                 }
  32.                 if(KeyVal > 0)  //若有按键
  33.                 {
  34.                         if(KeyVal==10) //长按>=1.5s
  35.                         {
  36.                                 led_1=~led_1;        //led_1灯状态取反 ,此处可以换成别的处理事件
  37.                         }
  38.                         else if(KeyVal==1)
  39.                         {
  40.                                 led_0=0;
  41.                         }
  42.                         else if(KeyVal==2)
  43.                         {
  44.                                 led_0=1;
  45.                         }
  46.                         KeyVal=0;
  47.                 }
  48.         }
  49. }
复制代码


回复

使用道具 举报

ID:1042234 发表于 2022-8-19 09:36 | 显示全部楼层

有时间我试一试 效果
回复

使用道具 举报

ID:1039671 发表于 2022-8-19 10:03 | 显示全部楼层

这个好,长按不需要松手就可以完成动作,收藏了
回复

使用道具 举报

ID:1042234 发表于 2022-8-19 11:30 | 显示全部楼层

短按 抬起  ok
双击 抬起ok
长按 ok
回复

使用道具 举报

ID:699774 发表于 2022-8-19 12:38 | 显示全部楼层
hewayking 发表于 2022-8-18 10:31
delay(1000);   //消除抖动 一般大约10ms   //一般我看到用延时消抖我都觉得水平一般

确实真正写程序谁会用delay让mcu死在这里循环等待呢
回复

使用道具 举报

ID:1093578 发表于 2023-9-14 20:21 | 显示全部楼层

试了一下,确实OK
回复

使用道具 举报

ID:320250 发表于 2023-9-15 12:58 | 显示全部楼层
用定时器来检测,有点浪费
回复

使用道具 举报

ID:1093578 发表于 2023-9-16 10:11 | 显示全部楼层
疯城浪子 发表于 2023-9-15 12:58
用定时器来检测,有点浪费

我很疑惑,他这个功能实现是OK的,但是TR0=1 打开后,再也没关闭。定时器一直在跑,有什么弊端?
回复

使用道具 举报

ID:1093740 发表于 2023-9-16 17:21 | 显示全部楼层

这个COUNT延时,是不是还得算上程序运行时间了。额。应该没啥影响吧。
回复

使用道具 举报

ID:1093740 发表于 2023-9-16 17:22 | 显示全部楼层
Tony5658 发表于 2023-9-16 10:11
我很疑惑,他这个功能实现是OK的,但是TR0=1 打开后,再也没关闭。定时器一直在跑,有什么弊端?

这定时器一直跑,没啥影响啊
回复

使用道具 举报

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

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

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