找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 1813|回复: 22
收起左侧

标准的单片机C语言按键检测代码格式是什么样的?

  [复制链接]
ID:709761 发表于 2022-5-16 11:41 | 显示全部楼层 |阅读模式
//以下两种单片机C语言代码,哪个正确

if(k1 == 0)
  {
                Delay6ms();//按键消抖
                if(k1 == 0)
                {

                //执行按键结果
               }
                while(!k1)//等待按键放开
   }

//--------------------------------------------------------

if(k1 == 0)
  {
                Delay6ms();//按键消抖
                if(k1 == 0)
                {

                //执行按键结果

                while(!k1)//等待按键放开
                }  
            }

回复

使用道具 举报

ID:276663 发表于 2022-5-16 13:09 | 显示全部楼层
回复

使用道具 举报

ID:161164 发表于 2022-5-16 13:17 | 显示全部楼层
都不好,浪费资源

  1. static uint Delay = 0;

  2. if(K1 == 0)

  3. {

  4. if(Delay <= 0xFFFF)Delay++;

  5. if(Delay == 1000)

  6. {

  7. //执行按键结果

  8. }

  9. }else{

  10. Delay=0;

  11. }
复制代码




回复

使用道具 举报

ID:56665 发表于 2022-5-16 13:47 | 显示全部楼层
lkc8210 发表于 2022-5-16 13:17
都不好,浪费资源

这个代码和上面的没有区别,Delay++一样在空耗,要想不浪费资源,延时放在定时器中断里处理。
回复

使用道具 举报

ID:123289 发表于 2022-5-16 14:01 | 显示全部楼层
都不标准,赞同地板的说法。
回复

使用道具 举报

ID:213173 发表于 2022-5-16 14:30 | 显示全部楼层
没有标准的按键代码。用什么方式写按键代码是设计者根据硬件条件、应用场景和编程习惯所采用。最常见的代码不下20种。楼主所列两种其实就是同为一种最低水平的入门级写法,大多见于针对初学者的教材中。优点是初学者容易读懂,用于极简单的程序也无不可。其缺点是效率低下,与其它需要快速响应的程序发生冲突.....。
回复

使用道具 举报

ID:161164 发表于 2022-5-16 14:38 | 显示全部楼层
m182892 发表于 2022-5-16 13:47
这个代码和上面的没有区别,Delay++一样在空耗,要想不浪费资源,延时放在定时器中断里处理。

一个Delay++的消耗比的上Delay6ms和while(!k1)的死等?
我的代码的原理是定时扫描按键的电平
确保按键保持低电平一段时间才输出
每扫描一次
if(K1==0)
if(Delay<0xFFFF)
Delay++;
if(Delay==1000)
这四句只用了5.43us(keil debug mode STC89C52RC@11.0592)

求问有没有更低消耗的消抖算法
回复

使用道具 举报

ID:624769 发表于 2022-5-16 16:06 来自手机 | 显示全部楼层
lkc8210 发表于 2022-5-16 14:38
一个Delay++的消耗比的上Delay6ms和while(!k1)的死等?
我的代码的原理是定时扫描按键的电平
确保按键保 ...

发现你特别不爱用中断。
诚然,表面上看,你这段代码的确只需要5.4us,比起阻塞式while,的确不耗系统资源,但是,你累加1000次,才完成一次按键判断,总共耗费的系统资源,并不比delay6ms节约吧? 随着你程序复杂度的提升,你要不停地修正1000这个值,来均衡消抖和漏判这个平衡点。某种意义上来讲,稳定性还不如阻塞式判断。用个中断不好么?硬件放在那里不用,才是真正的浪费,并不是节约。真要效率最大化,个人觉得,外部中断触发,定时器中断消抖,才是最优方案,系统中断资源不够了,再去考虑不用中断的方法。
回复

使用道具 举报

ID:203661 发表于 2022-5-16 16:22 | 显示全部楼层
你那个是标准教科书写法
回复

使用道具 举报

ID:213173 发表于 2022-5-16 16:43 | 显示全部楼层
本帖最后由 wulin 于 2022-5-16 17:21 编辑
lkc8210 发表于 2022-5-16 14:38
一个Delay++的消耗比的上Delay6ms和while(!k1)的死等?
我的代码的原理是定时扫描按键的电平
确保按键保 ...

“这四句只用了5.43us”不如 if(K1==0){if(++Delay>=1000).....} 效率更高。常见的按键多为选用轻触开关,其抖动时间在2~10ms范围。5.43us的时间难以确保避开抖动时段。在特定的应用场景,消抖不是必须的。诸如某大佬的经典代码,完成长短按就三条语句。
unsigned char Trg;
unsigned char Cont;
void KeyRead( void )
{
    unsigned char ReadData = PINB^0xff;
    Trg = ReadData & (ReadData ^ Cont);
    Cont = ReadData;
}
回复

使用道具 举报

ID:161164 发表于 2022-5-16 16:46 | 显示全部楼层
188610329 发表于 2022-5-16 16:06
发现你特别不爱用中断。
诚然,表面上看,你这段代码的确只需要5.4us,比起阻塞式while,的确不耗系统资 ...

大佬你好

我没说不用中断啊~
如果把代码放到1ms中断中
直接写个if(Delay==20)就可以滤掉99.99%的抖动而不用调节
Delay更可以改用unsigned char更节约步骤

我是考虑到有些新手还没学定时器中断
就推荐一个低消耗,不阻塞,可以不用定时器中断,简单易懂的按键消抖代码

至于关于累积消耗,我更考虑每次大循环的消耗
经常有贴子求问按键令数码管闪烁
就是一些新手用了delayms和while(!K1)这种阻塞式消抖

比起突然来个大消耗,我更愿意选连续而均匀的小消耗

更正:
之前说的5.43 us消耗是没有键按时的计时
有按键时是17.36 us(keil debug mode STC89C52RC@11.0592)
回复

使用道具 举报

ID:161164 发表于 2022-5-16 17:11 | 显示全部楼层
wulin 发表于 2022-5-16 16:43
“这四句只用了5.43us”和只用一句if(K1==0)没有什么区别。常见的按键多为选用轻触开关,其抖动时间在2~1 ...

大佬你好

之前我仿真错了
5.43 us是没有键按时的耗时
有按键时是17.36 us

如果是if(Delay == 1)当然没有用
但用的是if(Delay == 1000)在只计算自己的耗时都有17.36ms
足以滤掉99.99%的抖动

我也知道消抖不是必须的
如一些有自保的开关,限位器等
按着就是按着
不用理会按了多少下

那大佬的代码我也拜读过
第一次看时惊为天人
连说三字妙妙妙
我只知道他可以检测电平的改变来触发短按
但如何触发长按。。。愿闻其详
回复

使用道具 举报

ID:213173 发表于 2022-5-16 17:38 | 显示全部楼层
lkc8210 发表于 2022-5-16 17:11
大佬你好

之前我仿真错了

不好意思,只看了前面“这四句只用了5.43us”就望文生义,确为不妥,帖子已改。长短按就三条语句是真是的。我特别强调的是应用场景。 一款精巧的按键处理程序.doc (36.5 KB, 下载次数: 13)
回复

使用道具 举报

ID:624769 发表于 2022-5-17 14:52 | 显示全部楼层
lkc8210 发表于 2022-5-16 16:46
大佬你好

我没说不用中断啊~

抱歉,没有别的意思,就是看过不少你的回帖,发现你大多都刻意的回避使用中断,之前,也看了你关于旋转编码器的一个回帖(构思很好,非常简洁,但是,因为刻意回避中断,所以有漏洞,当然普通使用是没有什么问题的,这里就不展开了),而此帖,楼主又刻意强调“标准”二字,虽然按键判断,没有几百种都有几十种方式,但是,要说标准的话,排除衍生出来的组合按键,矩阵按键不说,比较有代表性的独立按键而言,用外部中断判断,才是能算比较“标准”的用法吧?
“标准”按键,应该硬件消抖,算了,标准是被不断的拉低的,就当是软件消抖是“标准”吧。窃以为,外部中断+定时器中断,占用系统少。无漏判误判,欢迎指正。

bit    Key_Press;
void main()
{
       TMOD = 0x00;            //13位定时器模式 (不设初值,就用从默认的 0 开始计时 12Mhz 约 8.192Ms 溢出)
       IT0 = 1;               //下降沿触发
       EX0 = 1;
       ET0 = 1;
       EA  = 1;
       while(1)
       {
                if(Key_Press)
                {
                        Key_Press = 0;   //清按键标志
                        //按键后需要执行的代码。
                }
       }
}

void EX0_INT  interrupt 0
{
        Key_Press = 1;    //置位按键标志
        EX0 = 0;       //关外部中断
        TR0 = 1;        //开定时器消抖
}

void T0_INT  interrupt 1
{
        if(IE0)
        {
               IE0 = 0;   //继续消抖
        }
        else
        {
               EX0 = 1;   //消抖完成,重开外部中断
               TR0 = 0;  //关定时器
        }
}
回复

使用道具 举报

ID:883242 发表于 2022-5-17 15:16 | 显示全部楼层
lkc8210 发表于 2022-5-16 17:11
大佬你好

之前我仿真错了

新开关一般不需要消抖,但是用旧了一般都会抖。

曾经遇到过一个水平挺高的高手,居然跟我说按键不需要消抖,后来听熟悉他的人说,毕业之后只做过单片机原厂FAE,给要用单片机的厂家做个demo就行了,从来没有做过一个产品的全寿命周期测试。然后我就找了几个新按键,果然一个抖的都没有。
回复

使用道具 举报

ID:824490 发表于 2022-5-17 17:35 | 显示全部楼层
Hephaestus 发表于 2022-5-17 15:16
新开关一般不需要消抖,但是用旧了一般都会抖。

曾经遇到过一个水平挺高的高手,居然跟我说按键不需要 ...

这里的“抖”不一定是按键产生的,还可能是外部电磁环境产生的尖脉冲。。
回复

使用道具 举报

ID:161164 发表于 2022-5-17 17:40 | 显示全部楼层
wulin 发表于 2022-5-16 17:38
不好意思,只看了前面“这四句只用了5.43us”就望文生义,确为不妥,帖子已改。长短按就三条语句是真是的 ...

文件收藏了,感谢分享
回复

使用道具 举报

ID:161164 发表于 2022-5-17 17:48 | 显示全部楼层
188610329 发表于 2022-5-17 14:52
抱歉,没有别的意思,就是看过不少你的回帖,发现你大多都刻意的回避使用中断,之前,也看了你关于旋转编 ...

代码很巧妙,但为了一个按键占用了一个外中断和一个定时器,算不上占用系统少吧?
而且只能滤掉按下那一刹那的抖动,滤不掉放开那一刹那的抖动吧?
稍微修改一下
  1. bit    Key_Press;
  2. sbit Key_XX = P3^2;
  3. void main()
  4. {
  5.         TMOD = 0x00;            //13位定时器模式 (不设初值,就用从默认的 0 开始计时 12Mhz 约 8.192Ms 溢出)
  6.         IT0 = 1;               //下降沿触发
  7.         EX0 = 1;
  8.         ET0 = 1;
  9.         EA  = 1;
  10.         while(1)
  11.         {
  12.                 if(Key_Press)
  13.                 {
  14.                         Key_Press = 0;   //清按键标志
  15.                         //按键后需要执行的代码。
  16.                 }
  17.         }
  18. }

  19. void EX0_INT  interrupt 0
  20. {
  21.         EX0 = 0;       //关外部中断
  22.         TR0 = 1;        //开定时器消抖
  23. }

  24. void T0_INT  interrupt 1
  25. {
  26.         if(IE0)
  27.         {
  28.                 IE0 = 0;   //继续消抖
  29.         }
  30.         else
  31.         {
  32.                 if(!Key_XX)Key_Press = 1;    //置位按键标志
  33.                 EX0 = 1;   //消抖完成,重开外部中断
  34.                 TR0 = 0;  //关定时器
  35.         }
  36. }
复制代码

回复

使用道具 举报

ID:509408 发表于 2022-5-17 17:55 | 显示全部楼层
void CheckSwitchKey(void)
{
               
                if(cgCheckSwitchTime<3)          //3ms扫描一次,该变量可放入定时器中累加
                         return;       
                //检查前先将该IO口线设置为1
                SWITCH_KEY1 = 1;
                SWITCH_KEY2 = 1;
                cgCheckSwitchTime = 0;
                       
                if( (cgSwitchStatus & 0x01) && (SWITCH_KEY2==0) )
                {
                        //按键前状态=1,目前状态=0        (首次检测按键按下)
                        cglowVotTime = 0;          //按键低电平计时清0
                }
                else
                {
                        if( ((cgSwitchStatus & 0x01)!=0x01) && (SWITCH_KEY2==1) )
                        {
                                //按键前状态=0,目前状态=1         (按键弹起)
                                //低电平时间>5ms 执行按键消息函数
                                if(cglowVotTime>5)                 DealKeyInfo();
                        }
                }
               
#if 0          //状态机写法
                switch(state)
                 {
                        case S0://状态0        等待按键按下
                                if(bgStatusofKey1==0)
                                        state = S1;                //判断输入是否为0,为0转入状态1       
                                break;

                        case S1://状态1        按键按下
                                if(bgStatusofKey1)
                                        state = S0;                //按键弹起? 返回状态0(按键状态不稳定)       
                                else{                                //否则开始计时,转入状态2
                                        state =        S2;
                                        cgPushKeyTime = 0;
                                }break;
                                       
                        case S2://状态2        等待短按释放
                                if(bgStatusofKey1){

                                        state = S0;                        //按键弹起?返回状态0(短按键释放)
                                        bgEv1527Study = 0;
                                        cgFlagBeacon = 0x01;
                                        DealKeyInfo();
                                }
                                else if(cgPushKeyTime>250){ //长按超时?(1s以上)
                                        cgPushKeyTime = 0;
                                        bgEv1527Study = 1;                 //学习状态。
                                        cgFlagBeacon = 0x02;
                                        DealKeyInfo();
                                        state=S3;                        //否则开始计时,计时结束转入状态3
                                }break;
                                               
                        case S3://状态3        等待长按释放
                                if(bgStatusofKey1)                 //长按键释放?返回状态0
                                        state=S0;               
                                else if(++cgPushKeyTime==5)        //否则开始计时,计时结束按键连击
                                {
                                        cgPushKeyTime=0;
                                        /*连击处理函数*/
                                }break;
                                       
                        default:
                                break;                                                       
                }
#endif
                //备份按键口线状态
                bgStatusofKey1 = SWITCH_KEY1;

                if(SWITCH_KEY2==1)   
                        cgSwitchStatus |= 0x01;
                else
                        cgSwitchStatus &= 0xfe;
       
}
回复

使用道具 举报

ID:624769 发表于 2022-5-17 18:19 | 显示全部楼层
lkc8210 发表于 2022-5-17 17:48
代码很巧妙,但为了一个按键占用了一个外中断和一个定时器,算不上占用系统少吧?
而且只能滤掉按下那一 ...

跟你交流很开心, 以下纯粹探讨. 如有空盼回。

为了一个按键占用了一个外中断和一个定时器,算不上占用系统少吧?
我还是那句话,如果中断空着不用,严格意义上来讲,不是节约资源,而是资源浪费,而且外部中断判断按键的话,按键未按下时,在主循环中,连1个时钟的系统时钟都不会占用,这还不算占用系统资源少么?

只能滤掉按下那一刹那的抖动,滤不掉放开那一刹那的抖动吧?
是的,实际上,我自己写的代码,不是这样的,发上来的时候精简了一下,正常自己使用的时候,主程序也不会有按键标志判断的,代码如下:
(另,个人爱好,非工作,所以没有测试设备,大多功能都是自己写代码烧录,然后通过LED状态来测试验证,你这边方便的话可帮忙测试,实际应用中STC15以上系列,IT0 =1 时,释放按键时,只要不是故意手抖,抖不出IE0=1, 即不会触发中断,原因不明,可能和 IT0 的判定变更了有关,你那边有条件的话,不知能否也帮忙验证一下?)

void EX0_INT  interrupt 0
{
         EX0 = 0;       //关外部中断
        TR0 = 1;        //开定时器消抖

        //此处按键按下后的  需要执行的操作
}


sbit  INT0 = P3^2;
void T0_INT  interrupt 1
{
         if((IE0) || (!INT0))    //判断按键释放&消抖
        {
                IE0 = 0;   //继续消抖
        }
         else
         {
                EX0 = 1;   //消抖完成,重开外部中断
               TR0 = 0;  //关定时器
        }
}
回复

使用道具 举报

ID:1026496 发表于 2022-5-17 21:34 | 显示全部楼层
不是是这样的,标准的是没有阻塞的也就是没有while(!key); 标准的就是一个函数返回键值根据当前的io 的状态返回时按键值;延时用定时器的tick +状态机实现
回复

使用道具 举报

ID:161164 发表于 2022-5-18 10:54 | 显示全部楼层
188610329 发表于 2022-5-17 18:19
跟你交流很开心, 以下纯粹探讨. 如有空盼回。

为了一个按键占用了一个外中断和一个定时器,算不上占用 ...

看大佬的贴子和回贴我也学到很多东西
特别是STC EEPROM 的操作

有时间我会试一试放手进中断的情况
回复

使用道具 举报

ID:491577 发表于 2022-5-18 12:37 | 显示全部楼层
这是标准菜鸟写法,等到不用    Delay6ms(); while(!k1)这两个语句也可以写出按键程序的时候才算入门啦
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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