找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 4028|回复: 8
打印 上一主题 下一主题
收起左侧

单片机定时器中断按键扫描程序如何检测长按状态?

[复制链接]
跳转到指定楼层
楼主
ID:601478 发表于 2019-12-24 16:50 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

unsignedchar Trg;

unsignedchar Cont;

voidKeyRead( void )

{

   unsigned char ReadData = PINB^0xff;   // 1

   Trg = ReadData & (ReadData ^ Cont);      // 2

   Cont =ReadData;                               // 3

}

网上流传很广的3句语句实现按键扫描,自己用却不会用,只能做到检测单击按键,长按和双击检测不会做,求大佬指点


分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:601478 发表于 2019-12-24 17:24 | 只看该作者
void key_scan()
{
        uchar S,b;
        S = P1^0xff;
  if(0<S && S == R)
        {
                b = T;
                T = S&(S^R);
                R = S;
                if(b != T)
                {
                        if(S == 0x20) {key = 1;        return;}
                  if(S == 0x10) {key = 2; return;}
            if(S == 0x08) {key = 3; return;}
                  if(S == 0x18) {key = 8; return;}
                }
                else
                {
                        if(S == 0x20) {key = 4; return;}
                  if(S == 0x10) {key = 5; return;}
                  if(S == 0x08) {key = 6; return;}
                  if(S == 0x18) {key = 9; return;}
                }
        }
        else
        {
                T = S&(S^R);
                R = S;
        }
}
1238为对应按键的单击标识,4569为对应按键的长按标识,共4个按键, key_scan()放在中断函数里面,20MS扫描一次
while(1)
{
        switch (key)
        {
            case......
        }
}循环执行返回按键标识的对应程序
问题:无法辨别是单击或是长按,按了几下后卡死
回复

使用道具 举报

板凳
ID:213173 发表于 2019-12-24 23:17 | 只看该作者
不是简单的3条代码就搞定的,这只是读键过程,还要有其配套服务程序解读才能生效。
应用示例:
  1. /*********************************************************
  2. 按时间片运行读键与触发的单片机程序,定时器0工作在时间片
  3. 定时器1做蜂鸣器振荡器,LED演示任务状态。
  4. *********************************************************/
  5. #include "STC89xx.H"                         //单片机头文件
  6. #include <intrins.h>                        //
  7. #define uint unsigned int                 //宏定义变量uint为无符号整型数据(0-65535)
  8. #define uchar unsigned char         //宏定义变量uchar为无符号字符型数据(0-255)
  9. #define KEY_MODE 0x80                        //按键宏定义
  10. #define KEY_PLUO 0x80                        //按键宏定义
  11. #define KEY_MODE1 0x40                        //按键宏定义
  12. #define KEY_PLUO1 0x40                        //按键宏定义
  13. #define LED P1                                        //LED宏定义
  14. sbit Buzzer=P2^3;                                //蜂鸣器端口定义
  15. uchar Trg;                                                //Trg(triger) 单击键值全局变量
  16. uchar Cont;                                                //Cont(continue)长击键值全局变量
  17. uchar Repeat;                                        //蜂鸣器重复次数变量
  18. uchar Intrcnt;                                        //定时器1中断时标
  19. bit Buzzer_sign=0;                                //蜂鸣器使能标志位
  20. void KeyRead();                                        //按键扫描程序
  21. void KeyProc();                                        //按键服务程序
  22. void Timer_Init();                                //初始化定时器0/1
  23. void BuzzerSign();                                //蜂鸣器驱动程序

  24. void main()                                                //主函数
  25. {                                                                //初始化数据       
  26.         LED=0xFE;                                        //LED初始化
  27.         Buzzer=1;                                        //蜂鸣器端口初始化
  28.         Timer_Init();                                //初始化定时器初始化

  29.         while(1)                                        //每20ms 执行一次大循环
  30.         {
  31.                 KeyRead();              //将每个子程序都扫描一遍
  32.                 KeyProc();
  33.                 BuzzerSign();
  34. /**************************************************/
  35.                 while(1)
  36.                 {
  37.                         if(Intrcnt>100)                // 一直在等,直到20ms时间到
  38.                         {
  39.                                 Intrcnt=0;
  40.                                 break;               // 返回主循环
  41.                         }
  42.                 }
  43.         }
  44. }

  45. void KeyRead()                                        //读键获取键值
  46. {
  47.     uchar ReadData = P3^0xFF;   //ReadData 临时变量,"^"按位异或运算:如果a、b两个值不相同!
  48.                                                                 //则异或结果为1。如果a、b两个值相同,异或结果为0。
  49.     Trg = ReadData & (ReadData ^ Cont); //获取短按键值
  50.     Cont = ReadData;                    //获取长按键值
  51. }

  52. void KeyProc()                                                //判断键值执行相应任务
  53. {
  54.         uchar cnt_plus,cnt_plus1;                //定义2个计时变量
  55.         if (Trg & KEY_MODE)                         // 如果按下的是KEY_MODE,而且你长按这按键也没有用,
  56.         {
  57.                 Repeat = 1;                                        //赋值蜂鸣器重复次数变量
  58.                 LED=_cror_(LED,1);                        //LED循环右移1位// 可执行任何代码
  59.         }
  60.         if (Cont & KEY_PLUO)                         //如果KEY_PLUO按着不放
  61.         {
  62.                 cnt_plus++;                              // 计时
  63.                 if(cnt_plus > 50)                         // 20ms*100 = 2S 如果时间到执行长按功能
  64.                 {
  65.                         cnt_plus = 50;                        //计数限制
  66.                         cnt_plus1++;
  67.                         if (cnt_plus1 >= 10)                //20ms*10 = 0.2S(长按功能启动后每0.2S执行一次下层功能)
  68.                         {
  69.                                 cnt_plus1=0;
  70.                                 LED=_crol_(LED,1);        //LED循环左移1位
  71.                                 Repeat = 1;                        //赋值蜂鸣器重复次数变量
  72.                         }
  73.                 }         
  74.         }
  75.         else cnt_plus=cnt_plus1=0;                 //计时变量清0       
  76. }
  77. void BuzzerSign()                        //蜂鸣器驱动程序
  78. {
  79.         if (Repeat & (Repeat ^ Buzzer_sign))//如果蜂鸣器重复次数变量Repeat>0、
  80.                 //蜂鸣器使能标志Buzzer_sign=0
  81.                 TR1 = 1;                        //开启定时器1
  82. }

  83. void Timer_Init()                        //200微秒@11.0592MHz
  84. {
  85. //        AUXR &= 0xBF;                        //定时器时钟12T模式(1T要设置AUXR)
  86.         TMOD &= 0xFF;                        //设置定时器模式8位重载
  87.         TMOD |= 0x22;                        //设置定时器模式8位重载
  88.         TL1 = 0x48;                                //设置定时初值
  89.         TH1 = 0x48;                                //设置定时重载值
  90.         TL0 = 0x48;                                //设置定时初值
  91.         TH0 = 0x48;                                //设置定时重载值
  92.         TF1 = 0;                                        //清除TF1标志
  93.         TR0 = 1;                                        //定时器0开始计时
  94. //        TR1 = 1;                                        //定时器1开始计时
  95.         EA=1;                                                //开总中断
  96.         ET0=1;                                        //开定时器0中断       
  97.         ET1=1;                                        //开定时器1中断       
  98. }

  99. void timer0() interrupt        1        //中断服务程序(定时器中断函数timer0)
  100. {                                                                        //interrupt        0:外部中断0,1:定时器中断0,
  101.                                                                         //2:外部中断1,3:定时器中断1、串行口中断
  102.                                                                         //中断程序不需要声明
  103.         Intrcnt++;                                        //定时器中断变量200us自+1
  104. }
  105. void timer1() interrupt        3        //中断服务程序(定时器1中断函数)
  106. {
  107.         uint Cnt200us;                        //定时器中断计数变量
  108.         Cnt200us++;                                //中断变量Cnt200us自+1

  109.         if(Cnt200us<=300)
  110.                 Buzzer=~Buzzer;                //蜂鸣器端口取反
  111.         if(Cnt200us>300)                //检测中断的次数>300                       
  112.                 Buzzer=1;                        //蜂鸣器端口清0
  113.                 Buzzer_sign=1;                //蜂鸣器使能标志位置1
  114.         if(Cnt200us>=900)                //检测中断的次数>=600
  115.         {                                                        //两次鸣响之间间隔时间
  116.                 Buzzer_sign=0;                //蜂鸣器使能标志位清0
  117.                 Cnt200us=0;                        //计数清0
  118.                 TR1=0;                                //关闭定时器
  119.                 Repeat--;                        //蜂鸣器重复次数变量自-1                              
  120.                 if(Repeat<=0)                //判断蜂鸣器重复次数
  121.                         Repeat=0;                //蜂鸣器重复次数变量清0
  122.         }
  123. }
复制代码
回复

使用道具 举报

地板
ID:601478 发表于 2019-12-25 08:58 | 只看该作者
wulin 发表于 2019-12-24 23:17
不是简单的3条代码就搞定的,这只是读键过程,还要有其配套服务程序解读才能生效。
应用示例:

我最搞不懂的就是这个按键扫描是怎样做到消抖的,扫描我是放在中断里面执行的,一旦有触发信号(假设0x01),read Trg Con的值都为0x01,下一个中断到来时如果是真的按下(read继续读到0x01)那经过这个按键扫描算法的处理就变为read 0x01     Trg 0x00  Con 0x01 ,那判断两次读取的值一样才算消抖啊,但是这样就判断不了长按了,因为一直长按 R  T  C 的值都没变的,还是read 0x01     Trg 0x00  Con 0x01 ,这样我就搞不懂了
回复

使用道具 举报

5#
ID:213173 发表于 2019-12-25 10:38 | 只看该作者
PDDDF 发表于 2019-12-25 08:58
我最搞不懂的就是这个按键扫描是怎样做到消抖的,扫描我是放在中断里面执行的,一旦有触发信号(假设0x01 ...

这是原作者的解释,是在特定的程序构架下才有效。
一款精巧的按键处理程序.doc (36.5 KB, 下载次数: 51)

回复

使用道具 举报

6#
ID:601478 发表于 2019-12-25 11:50 | 只看该作者
wulin 发表于 2019-12-25 10:38
这是原作者的解释,是在特定的程序构架下才有效。

谢谢大佬
回复

使用道具 举报

7#
ID:475247 发表于 2019-12-25 13:39 | 只看该作者
其实思路很简单,在定时中断中判断一个按健按下来开始计数,然后这样:if((K1<40) && (K1>5) && (ON_flag==1))//消抖时间+去抖时间+开关标记,后面处理好清0事宜就好了
回复

使用道具 举报

8#
ID:584103 发表于 2019-12-25 18:37 | 只看该作者
太好了  又学到了新知识  好评
回复

使用道具 举报

9#
ID:7485 发表于 2019-12-25 18:54 | 只看该作者
和处理键抖动一回事,N次检测仍未释放就是长按......,N次加或减1,2N次加减2......,快速双击道理一样,检测的时间间隔不同。
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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