找回密码
 立即注册

QQ登录

只需一步,快速开始

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

一个单片机按键长按,短按程序设计,为什么没法实现功能?

  [复制链接]
ID:858009 发表于 2021-9-8 15:34 | 显示全部楼层 |阅读模式
本帖最后由 qwettyuio 于 2021-9-8 17:33 编辑

#include <reg51.h>
#include <intrins.h>

sbit LED = P1;
sbit KEY1 = P3^0;

typedef unsigned int uint;
typedef unsigned int uchar;
unsigned char modeFlag = 0;   //短按
unsigned char SetFlag  = 1;   //长按

void delay(uint x)    //延时程序函数
{
        uint i,j;
        for(i=x;i>0;i--)
        for(j=110;j>0;j--);
}
void key()
{
        uchar count;
        if(KEY1==0)
        {
                delay(10);
                if(KEY1==0)
                {
                        count=0;
                        while(KEY1==0)
                        {
                                count++;
                        }
                        if(count<500)
                        {
                                modeFlag++;
                                if(modeFlag>=4)
                                        modeFlag=0;
                        }
                        else
                        {
                                SetFlag++;
                                if(SetFlag>=3)
                                        SetFlag=1;
                        }
                }
        }
}
void main()
{
       
        while(1)
        {
                key();
                if(modeFlag==1)
                {
                        P1=0xfe;
                }
                else if(modeFlag==2)
                {
                        P1=0xfd;
                }
                else if(modeFlag==3)
                {
                        P1=0xfc;
                }
                else if(SetFlag==1)
                {
                        P1=0xfb;
                }
                else if(SetFlag==2)
                {
                        P1=0xfa;
                }
        }
}
回复

使用道具 举报

ID:514901 发表于 2021-9-8 16:09 | 显示全部楼层
while(KEY1==0)
                        {
                                count++;
                        }
这里没加延时,你按一下,count估计要溢出好几百次
回复

使用道具 举报

ID:609524 发表于 2021-9-8 16:15 | 显示全部楼层
把key函数放到while(1)里面呀,还有最好不要用while判断按键,
回复

使用道具 举报

ID:624769 发表于 2021-9-8 16:18 | 显示全部楼层
  if(KEY1==0)
         {
                 delay(10);         这里你是为了消抖对吧?
                 if(KEY1==0)      那么,这里 == 0  的话,你手一定摁着
                 {
                         count=0;     这里 12 个机器 时钟  (89C51前提下)
                         while(KEY1==0)    这里每次判断12个机器时钟   (89C51前提下)
                         {
                                 count++;     这里每24个 时钟会累加一次     (89C51前提下)
                         }                                  也就是说,即便你用89C51, 晶振12MHz, 2微秒+1 就算你1毫秒就收手,也500次了。
回复

使用道具 举报

ID:858009 发表于 2021-9-8 16:30 | 显示全部楼层
188610329 发表于 2021-9-8 16:18
if(KEY1==0)
         {
                 delay(10);         这里你是为了消抖对吧?

怎么修改才可以
回复

使用道具 举报

ID:858009 发表于 2021-9-8 16:32 | 显示全部楼层
杨天想 发表于 2021-9-8 16:15
把key函数放到while(1)里面呀,还有最好不要用while判断按键,

把他放进去,就实现不了短按和长按控制,目的是一个按键实现短按切换三个模式,长按控制;两个电机
回复

使用道具 举报

ID:858009 发表于 2021-9-8 16:33 | 显示全部楼层
188610329 发表于 2021-9-8 16:18
if(KEY1==0)
         {
                 delay(10);         这里你是为了消抖对吧?

目的是一个按键实现短按切换三个模式,长按控制切换两个电机
回复

使用道具 举报

ID:858009 发表于 2021-9-8 16:35 | 显示全部楼层
郑汉松 发表于 2021-9-8 16:09
while(KEY1==0)
                        {
                                count++;

加延时也不可以,目的是一个按键实现短按切换三个模式,长按控制;两个电机
回复

使用道具 举报

ID:624769 发表于 2021-9-8 16:38 | 显示全部楼层

我长按短按,都是在定时器里面判断的.
回复

使用道具 举报

ID:624769 发表于 2021-9-8 16:48 | 显示全部楼层


按键.png
这是我以前做电压表的时候, 用到的部分代码, 里面有判断长按短按,你可以参考.

由于是按键的端口复用, 1个IO口 通过LED的公共端的周期点亮,实现多个按键的判断,你可以参考,也可以忽略。
T2定时器是 5毫秒进入一次。由于3个公用按键轮循,实际每个按键是15毫秒判断一次,并且+1
回复

使用道具 举报

ID:858009 发表于 2021-9-8 16:48 | 显示全部楼层
188610329 发表于 2021-9-8 16:38
我长按短按,都是在定时器里面判断的.

有例子吗
回复

使用道具 举报

ID:858009 发表于 2021-9-8 16:49 | 显示全部楼层
188610329 发表于 2021-9-8 16:38
我长按短按,都是在定时器里面判断的.

不是用51单片机的,用的是ch552单片机
回复

使用道具 举报

ID:624769 发表于 2021-9-8 16:57 | 显示全部楼层
qwettyuio 发表于 2021-9-8 16:49
不是用51单片机的,用的是ch552单片机

道理是一样的, CH552 也是 51内核, 我用过阉割版 CH551, 少个ADC功能而以, 和89C51 基本一样的编程,就是CH552多了些硬件,比如SPI什么的。还多了128字节的 Eeprom 真正的Eeprom 而不是 89C51那种用 IAP模拟的 Eeprom
回复

使用道具 举报

ID:858009 发表于 2021-9-8 17:04 | 显示全部楼层
188610329 发表于 2021-9-8 16:57
道理是一样的, CH552 也是 51内核, 我用过阉割版 CH551, 少个ADC功能而以, 和89C51 基本一样的编程,就 ...

你用过552,我想问一下我用定时器控制pwm输出,按键脉宽怎么不能实现的
#include "CH552.H"                                                  
#include "Debug.H"
#include "Timer.H"
#include "stdio.h"
#include "GPIO.H"

#pragma  NOAREGS

//UINT8 FLAG;
//UINT16 Cap[8] = {0};
//typedef unsigned int uint16_t;
sbit LED1 = P3^4;
sbit led2 = P1^3;
sbit key = P1^4;
unsigned int condition=0;//这里高电平占一份
unsigned int cnt;                  //整个周期
void button();

void main( )
{
    CfgFsys( );                                                                //CH554时钟选择配置   
    mDelaymS(5);                                                               //修改主频,建议稍加延时等待主频稳定        
        Port3Cfg(1,4);
        Port1Cfg(1,3);
        Port1Cfg(3,4);
    mTimer0Clk12DivFsys();                                                           //T0定时器时钟设置
    mTimer_x_ModInit(0,2);                                                     //T0 定时器模式设置
    mTimer_x_SetData(0,0x5555);                                                       //T0定时器赋值
    mTimer0RunCTL(1);                                                          //T0定时器启动       
    ET0 = 1;                                                                   //T0定时器中断开启               
    EA = 1;
    while(1)
        {
                if(cnt>=100)
                {
                        LED1=0;
                }
                if(cnt<condition)
                {
                        LED1=1;
                }
                else
                {
                        LED1=0;
                }
                button();
        }
}
void Time0Interrupt(void) interrupt INT_NO_TMR0 using 1
{
        mTimer_x_SetData(0,0x5555);                                                       //T0定时器赋值
    cnt++;
}
void button()
{
        if(key==0)
        {
                mDelaymS(5);
                if(key==0)
                {
                        condition=condition+30;         //调制脉宽
            led2=!led2;                                //led灯反转
            if(condition>=90)                //如果脉宽等于总周期了等于0
                        {
                                condition=90;
            }
            while(!key);                          //等待按键松手
                }
        }
}
回复

使用道具 举报

ID:769980 发表于 2021-9-8 17:24 | 显示全部楼层
在这里面加 延时试试吧,                       
while(KEY1==0)
                        {
                            if(count<200)  //防止溢出吧
                               {
                                count++;
                                 }
                                delay(10); //具体延时多久,你慢慢调试咯
                        }

看着你到处都是delay(10),LED显示延时,检测延时,这样的程序,能实际使用吗?
回复

使用道具 举报

ID:624769 发表于 2021-9-8 17:29 | 显示全部楼层
qwettyuio 发表于 2021-9-8 16:49
不是用51单片机的,用的是ch552单片机

我忽然想起一个问题, CH552,属于 “产品向” 的单片机, 看你的代码,你用 “学习向” 的单片机应该更好些。
回复

使用道具 举报

ID:213173 发表于 2021-9-8 17:31 | 显示全部楼层
给你一款简单实用的按键长短按程序
  1. #include <REG51.H>
  2. #define uint unsigned int                         //宏定义数据类型uint
  3. #define uchar unsigned char                 //宏定义数据类型uchar
  4. #define key_S 1000                                        //宏定义短按(约20ms)
  5. #define key_L key_S*50                                //宏定义长按(约1s)

  6. sbit key=P3^6;
  7. sbit LED1=P1^0;
  8. sbit LED2=P1^4;

  9. void keyscan()                                                //按键扫描
  10. {
  11.         static uint count=0;                        //计数变量
  12.         if(!key)   
  13.         {  
  14.                 count++;  
  15.                 if(count==key_L)                        //长按
  16.                         LED2=!LED2;                                //长按任务
  17.                 if(count>key_L)                                //防止count溢出
  18.                         count=key_L+1;          
  19.         }  
  20.         else                                                        //按键抬起
  21.         {  
  22.                 if(count>key_S && count<key_L)//短按
  23.                         LED1=!LED1;                                //短按任务
  24.                 count=0;                                        //count清0
  25.         }   
  26. }
  27. void main()
  28. {
  29.         while(1)
  30.         {
  31.                 keyscan();       
  32.         }
  33. }
复制代码
回复

使用道具 举报

ID:123289 发表于 2021-9-9 14:49 | 显示全部楼层
思路不清,故程序也写不好,教你一下:
要点:
1、不要用DELAY()函数,何也?在DELAY这段时间里就无法做其它事,这段时间里与死机没有区别。
2、不要在【按下】时做界定,而要在【弹起】时做界定。何也?这样才知道是【短按】【中按】【长按】……
实现方法:
1、设置N个标记,分别表示:这里就用【短按】【长按】两种吧,【短按】有效B_DA=1,【长按】有效B_CA=1。更多种按法如法炮制……
2、设置N个计时器,分别计时【短按】R_DA(如0.05-0.3秒)、【长按】R_CA(如>2秒)时间界定值。
3、做个定时器,用于计时,例如:10ms中断一次。(这是避免用DELAY的方法!)
4、初始化参数:开机时将上述各参数清0。
5、主程序中执行如下程序:(这里享受结果,而且只需测试一下标记即可)
如果【短按】B_DA=1,就认为【短按】了,执行短按要做的事。一定要记住!:做完后将B_DA清0(这是做没做过事件处理的关键所在)。如果B_DA=0就当什么也未发生,程序跳过。自然【长按】也类似处理,相信这点楼主不用教。
6、【最关键的部分:】定时中断时执行如下程序:(在定时器中断服务程序中处理,就避免用DELAY了,而且是最快完成界定)
6.1、如果B_DA=1,什么也不做(【短按】已被认可,所以无需再处理,当事件被处理后,主程序会将它清0的,未清0则说明还未处理,或正在处理过程中还未处理完毕)。【长按】类同。
6.2、如果【按键】按下了,则将R_DA+1,这里有个要点:需要设置一个极限,防止溢出,如253,到了253就不要再加了。R_CA类同!(这里只在按下时,才做按键的计时,但只计时,不做界定!)
6.3、如果【按键】弹起了,则做裁定:(要点:界定在弹起时做)
6.3.1、干扰裁定:R_DA<50ms(<5次),不做裁定,清R_DA=0,重新计时。R_CA同。(时间太短,作弹动处理。这个方法也可用于抗按键弹动处理程序,比用DEALY强多了!!)
6.3.2、有效裁定:R_DA=0.05-0.3秒(5-30次,时间在什么范围内,你自己定),裁定为【短按】令B_DA=1,清R_DA=0,重计为下次做准备。R_CA>2秒(>200次,时间大于多少,你自己定),裁定为【长按】令B_CA=1,清R_CA=0,重新计时,为下次做准备。

妙吧:
中断服务程序只做裁定(决定标记是否为1,键有效),但不做事件的处理!
主程序只识别处理事件(根据标记,决定做不做事件处理,做完后清除标记),却不做裁定。
回复

使用道具 举报

ID:123289 发表于 2021-9-9 14:54 | 显示全部楼层

如果不考虑按键交叉(两种或多种事件同时产生),只用一个计时器也可以识别【短按】【中按】【长按】……,这样程序会更简单,相信楼主自己会考虑清楚的。
回复

使用道具 举报

ID:887202 发表于 2021-9-14 16:44 | 显示全部楼层
建议你去搜索一下按键状态机,实现按键长按短按双击等都是比较好的,而且实用性也很好,类似于你的这种通过延时去抖的方式实现按键其实是不太好的
回复

使用道具 举报

ID:130230 发表于 2021-9-17 14:00 | 显示全部楼层
状态机,网上大把
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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