找回密码
 立即注册

QQ登录

只需一步,快速开始

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

求程序思路 单片机按键次数控制流水灯

[复制链接]
跳转到指定楼层
楼主
ID:895128 发表于 2021-4-7 22:51 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
由于本论坛禁止直接求程序,禁止伸手党,所以向大家请教一下大致的实现方法与思路,理清头绪后我自己来写程序去实现,谢谢大家

例如按一下全亮,连续按两下流水灯,连续三下换一种流水灯。
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏1 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:895128 发表于 2021-4-7 22:53 | 只看该作者
我暂时的思路是定时计数,定时3s内统计按键次数在用按键次数去控制不同的灯动作。但是这样的话会有很大的延迟,每次都要等3s才会停止计数的话。太浪费时间了。
回复

使用道具 举报

板凳
ID:752974 发表于 2021-4-8 08:35 | 只看该作者
对按键计数就可以,1全亮,2流水,3换一个流水,并清零变量。

评分

参与人数 1黑币 +20 收起 理由
admin + 20 回帖助人的奖励!

查看全部评分

回复

使用道具 举报

地板
ID:584814 发表于 2021-4-8 08:36 | 只看该作者
Dieouy 发表于 2021-4-7 22:53
我暂时的思路是定时计数,定时3s内统计按键次数在用按键次数去控制不同的灯动作。但是这样的话会有很大的延 ...

那么问题来了:你按了1次或2次后怎么才知道你不是要按3次呢 ?

评分

参与人数 1黑币 +20 收起 理由
admin + 20 回帖助人的奖励!

查看全部评分

回复

使用道具 举报

5#
ID:592807 发表于 2021-4-8 08:48 | 只看该作者
Dieouy 发表于 2021-4-7 22:53
我暂时的思路是定时计数,定时3s内统计按键次数在用按键次数去控制不同的灯动作。但是这样的话会有很大的延 ...

3S?你这个东西是给老年人用的?你自己用个计时器算一下3S你能按多少下按键,连续按键间隔超过0.5秒都不叫连续。得到按键信号0.5秒内,没有下一个按键信号就可以处理了

评分

参与人数 1黑币 +20 收起 理由
admin + 20 回帖助人的奖励!

查看全部评分

回复

使用道具 举报

6#
ID:487569 发表于 2021-4-8 09:40 | 只看该作者
思路就是松开按键执行动作。判断到按键按下,先暂存按键的键值,按下次数+1,然后给一个变量赋值用作延时判断。等到判断按键松开延时就开始倒计,倒计为0的时候,把暂存的键值取出来,并执行判断是哪个按键,按下几次。

评分

参与人数 1黑币 +20 收起 理由
admin + 20 回帖助人的奖励!

查看全部评分

回复

使用道具 举报

7#
ID:895128 发表于 2021-4-8 10:40 | 只看该作者
unsigned char key_read(void)
{
    static unsigned char key_m = key_state_0, key_time_1 = 0;
    unsigned char key_return = N_key,key_temp;
     
    key_temp = key_driver();
     
    switch(key_m)
    {
        case key_state_0:
            if (key_temp == S_key )
            {
                 key_time_1 = 0;               // 第1次单击,不返回,到下个状态判断后面是否出现双击
                 key_m = key_state_1;
            }
            else
                 key_return = key_temp;        // 对于无键、长键,返回原事件
            break;

        case key_state_1:
            if (key_temp == S_key)             // 又一次单击(间隔肯定<500ms)
            {
                 key_return = D_key;           // 返回双击键事件,回初始状态
                 key_m = key_state_0;
            }
            else                                
            {                                  // 这里500ms内肯定读到的都是无键事件,因为长键>1000ms,在1s前低层返回的都是无键
                 if(++key_time_1 >= 50)
                 {
                      key_return = S_key;      // 500ms内没有再次出现单键事件,返回上一次的单键事件
                      key_m = key_state_0;     // 返回初始状态
                 }
             }
             break;
    }
    return key_return;
}     



这个怎么添加第n次的判断啊
回复

使用道具 举报

8#
ID:895128 发表于 2021-4-8 11:03 | 只看该作者
已经做出来了,谢谢各位大佬。



#include<reg52.h>
#define N_key    0              //无键
#define S_key    1              //单键
#define D_key    2              //双键
#define L_key    3              //长键
#define C_key    4              //三击键
#define E_key    5              //四击键
#define key_state_0 0
#define key_state_1 1
#define key_state_2 2
#define key_state_3 3
#define key_state_4 4
#define key_state_5 5
sbit key_input = P2^0;    // 按键输入口
unsigned char time_10ms_ok = 0;
unsigned char key = 0;

unsigned char key_driver(void)
{
    static unsigned char key_state = key_state_0, key_time = 0;
    unsigned char  key_return = N_key;
        bit key_press;

    key_press = key_input;                    // 读按键I/O电平

    switch (key_state)
    {
      case key_state_0:                              // 按键初始态
        if (!key_press) key_state = key_state_1;      // 键被按下,状态转换到按键消抖和确认状态
        break;
      
      case key_state_1:                      // 按键消抖与确认态
        if (!key_press)
        {
             key_time = 0;                   //  
             key_state = key_state_2;   // 按键仍然处于按下,消抖完成,状态转换到按下键时间的计时状态,但返回的还是无键事件
        }
        else
             key_state = key_state_0;   // 按键已抬起,转换到按键初始态。此处完成和实现软件消抖,其实按键的按下和释放都在此消抖的。
        break;
      
      case key_state_2:
        if(key_press)
        {
             key_return = S_key;        // 此时按键释放,说明是产生一次短操作,回送S_key
             key_state = key_state_0;   // 转换到按键初始态
        }
        else if (++key_time >= 100)     // 继续按下,计时加10ms(10ms为本函数循环执行间隔)
        {
             key_return = L_key;        // 按下时间>1000ms,此按键为长按操作,返回长键事件
             key_state = key_state_3;   // 转换到等待按键释放状态
        }
        break;

      case key_state_3:                 // 等待按键释放状态,此状态只返回无按键事件
        if (key_press) key_state = key_state_0; //按键已释放,转换到按键初始态
        break;
    }
    return key_return;
}

/*=============
中间层按键处理函数,调用低层函数一次,处理双击事件的判断,返回上层正确的无键、单键、双键、长键4个按键事件。
本函数由上层循环调用,间隔10ms
===============*/

unsigned char key_read(void)
{
    static unsigned char key_m = key_state_0, key_time_1 = 0;
    unsigned char key_return = N_key,key_temp;
     
    key_temp = key_driver();
     
    switch(key_m)
    {
        case key_state_0:
            if (key_temp == S_key )
            {
                 key_time_1 = 0;               // 第1次单击,不返回,到下个状态判断后面是否出现双击
                 key_m = key_state_1;
            }
            else
                 key_return = key_temp;        // 对于无键、长键,返回原事件
            break;

        case key_state_1:
            if (key_temp == S_key)             // 又一次单击(间隔肯定<500ms)
         
                                                
            {   if( key_temp == S_key)
               
                                                         {
                 key_time_1 = 0;               // 第1次单击,不返回,到下个状态判断后面是否出现双击
                 key_m = key_state_4;
               }
                                                        
                                                         else
                                                                          { key_return = D_key;           // 返回双击键事件,回初始状态
                     key_m = key_state_0; }
                                                                 
            }
            else                                
            {                                  // 这里500ms内肯定读到的都是无键事件,因为长键>1000ms,在1s前低层返回的都是无键
                 if(++key_time_1 >= 50)
                 {
                      key_return = S_key;      // 500ms内没有再次出现单键事件,返回上一次的单键事件
                      key_m = key_state_0;     // 返回初始状态
                 }
             }
             break;
       case key_state_4:
            if (key_temp == S_key)             // 又一次单击(间隔肯定<500ms)
         
                                                
            {   if( key_temp == S_key)
               
                                                         {
                 key_time_1 = 0;               // 第1次单击,不返回,到下个状态判断后面是否出现双击
                 key_m = key_state_5;
               }
                                                        
                                                         else
                                                                          { key_return = C_key;           // 返回双击键事件,回初始状态
                     key_m = key_state_0; }
                                                                 
            }
            else                                
            {                                  // 这里500ms内肯定读到的都是无键事件,因为长键>1000ms,在1s前低层返回的都是无键
                 if(++key_time_1 >= 50)
                 {
                      key_return = D_key;      // 500ms内没有再次出现单键事件,返回上一次的单键事件
                      key_m = key_state_0;     // 返回初始状态
                 }
             }
             break;
                        case key_state_5:
            if (key_temp == S_key)             // 又一次单击(间隔肯定<500ms)
            {
                 key_return = E_key;           // 返回四击键事件,回初始状态
                 key_m = key_state_0;
            }
            else                                
            {                                  // 这里500ms内肯定读到的都是无键事件,因为长键>1000ms,在1s前低层返回的都是无键
                 if(++key_time_1 >= 50)
                 {
                      key_return = C_key;      // 500ms内没有再次出现单键事件,返回上一次的单键事件
                      key_m = key_state_0;     // 返回初始状态
                 }
             }
             break;



     }
               
                return key_return;
}     

void main()
{
        P0 = 0xff;        //IO口初始化
        P2 = 0xff;
                        //定时器的初始化                                                   
        TMOD = 0x01;                           //选择定时器的工作模式:定时器0,方式1
        TH0 = (65535 - 10000)/256; //定时器的初值
        TL0 = (65535 - 10000)%256;
        EA = 1;                                           //开打总中断使能
        ET0 = 1;                                   //打开定时器0 的使能
        TR0 = 1;                                   //打开定时器0 ,开始工作

        while(1)
        {
                if(time_10ms_ok)          //time_10ms_ok = 1,表示计时到了10MS。(10MS扫描一次按键)
                {
                        time_10ms_ok = 0; //清除计时10MS标志
                        key = key_read(); //调用扫描按键程序,返回一个键值

                        if (key == L_key) //长按:点亮P1口上的8个LED灯。(低电平点亮)
                        {
                                P0 = 0x00;
                        }  
            else if(key == D_key)//双击:点亮P1口上第二个LED灯。(低电平点亮)  
            {
                                P0 = 0xfd;
                        }  
            else if(key == S_key)//单击:点亮P1口上第一个LED灯。(低电平点亮)  
            {
                                P0 = 0xfe;
                        }
                              else if(key == C_key)//单击:点亮P1口上第1个和第二个LED灯。(低电平点亮)  
            {
                                P0 = 0xfc;//11111100
                        }
                              else if(key == E_key)//单击:点亮P1口上第1个和第二个LED灯。(低电平点亮)  
            {
                                P0 = 0xf0;//11110000
                        }
                }
        }
}

void timer0(void) interrupt 1        //用的是定时器0, 这个“interrupt 1”中的“1”代表1号中断即是定时器0中断。如果是“0”就是外部中断0;“2“=外部中断1;”3“定时器1中断;”4“=串行口中断
{
        TH0 = (65535 - 10000)/256;
        TL0 = (65535 - 10000)%256; //定时器0的方式1,得在中断程序中重复初值。
        time_10ms_ok = 1;  //定时10MS 的标志
}

在原代码基础上修改      原代码链接:https://blog.csdn.net/Sun19910114/article/details/53810110
回复

使用道具 举报

9#
ID:451718 发表于 2021-4-8 12:04 | 只看该作者
本帖最后由 robinsonlin 于 2021-4-8 13:54 编辑

1,在1ms定时器中断里面建一个 unsigned long SysTick++;
2,当检测到按键响应时,变量TKey = SysTick,采集此时的系统时钟,同时KeyCount变量+1;
3,在SysTick - Tkey<=1500时,如果还有按键响应,就KeyCount++;
4,当SysTick - TKey>1500时,执行switch(KeyCount), break后KeyCount变量清零。
附长短周期判断,长短周期判断,实际是检测按键的连续性, 需要再加一个10ms的定时器做按键判断。在10ms定时器中,设定按键检测标志,并记录SysTick,如按下时,TFlag = 1; KeyDelay = SysTick;这个操作记得加锁,只执行一次。 当检测到按键弹起,TFlag = 0;  然后判断 SysTick - KeyDelay是否大于500,如果大于就是长按键,KeyCount += 10;如果SysTick - KeyDelay小于500ms,就是短按键,KeyCount ++ ;  KeyCount 十位就是长按次数,个位是短按次数。   SysTick - Tkey > 1500时,记得清理keyCount。

评分

参与人数 1黑币 +20 收起 理由
admin + 20 回帖助人的奖励!

查看全部评分

回复

使用道具 举报

10#
ID:47286 发表于 2021-4-8 12:32 | 只看该作者
用一个变量当计数器 每按一下按键 计数器+一次 然后判断变量值 运行对应的部分 大致框架如下 细节还需要补上 比如防抖什么的

if(key==1)
{
keynum++;
if(keynum==10)
{
keynum=0;//假设10种流水方式
}
}

if(keynum==0)
{
停止流水或关灯
}
else if(keynum==1)
{
流水1
}
else if(........)
{

}

评分

参与人数 1黑币 +20 收起 理由
admin + 20 回帖助人的奖励!

查看全部评分

回复

使用道具 举报

11#
ID:895128 发表于 2021-4-8 13:13 | 只看该作者
这个问题解决的,现在新的问题是通过按键

比如按键中间间隔延时实现两长一短,或者两短一长这样的判断。

暂时思路是通过延时来实现:
检测按键是否按下,按下后等待500ms又按下后在等待1000ms后按下就是两短一长。两长一短同理,但是实现起来有一个代码我有点蒙蔽。

评分

参与人数 1黑币 +20 收起 理由
admin + 20 回帖助人的奖励!

查看全部评分

回复

使用道具 举报

12#
ID:895128 发表于 2021-4-8 13:13 | 只看该作者
dzbj 发表于 2021-4-8 12:32
用一个变量当计数器 每按一下按键 计数器+一次 然后判断变量值 运行对应的部分 大致框架如下 细节还需要补 ...

这个实现的不是连续按键,是每一次按键对应的事件吧。
回复

使用道具 举报

13#
ID:895128 发表于 2021-4-8 14:31 | 只看该作者
突然想,能不能用按键中间时间间隔实现莫尔斯电码的输入。
回复

使用道具 举报

14#
ID:895128 发表于 2021-4-8 14:51 | 只看该作者
#include<reg52.h>
#define N_key    0              //无键
#define S_key    1              //单键
#define D_key    2              //双键
#define L_key    3              //长键
#define C_key    4              //三击键
#define E_key    5              //四击键
#define F_key    6              //两短一长击建
#define key_state_0 0
#define key_state_1 1
#define key_state_2 2
#define key_state_3 3
#define key_state_4 4
#define key_state_5 5
sbit key_input = P2^0;    // 按键输入口
unsigned char time_10ms_ok = 0;
unsigned char key = 0;

unsigned char key_driver(void)
{
    static unsigned char key_state = key_state_0, key_time = 0;
    unsigned char  key_return = N_key;
        bit key_press;

    key_press = key_input;                    // 读按键I/O电平

    switch (key_state)
    {
      case key_state_0:                              // 按键初始态
        if (key_press) key_state = key_state_1;      // 键被按下,状态转换到按键消抖和确认状态
        break;
      
      case key_state_1:                      // 按键消抖与确认态
        if (key_press)
        {
             key_time = 0;                   //  
             key_state = key_state_2;   // 按键仍然处于按下,消抖完成,状态转换到按下键时间的计时状态,但返回的还是无键事件
        }
        else
             key_state = key_state_0;   // 按键已抬起,转换到按键初始态。此处完成和实现软件消抖,其实按键的按下和释放都在此消抖的。
        break;
      
      case key_state_2:
        if(!key_press)
        {
             key_return = S_key;        // 此时按键释放,说明是产生一次短操作,回送S_key
             key_state = key_state_0;   // 转换到按键初始态
        }
        else if (++key_time >= 100)     // 继续按下,计时加10ms(10ms为本函数循环执行间隔)
        {
             key_return = L_key;        // 按下时间>1000ms,此按键为长按操作,返回长键事件
             key_state = key_state_3;   // 转换到等待按键释放状态
        }
        break;

      case key_state_3:                 // 等待按键释放状态,此状态只返回无按键事件
        if (!key_press) key_state = key_state_0; //按键已释放,转换到按键初始态
        break;
    }
    return key_return;
}

/*=============
中间层按键处理函数,调用低层函数一次,处理双击事件的判断,返回上层正确的无键、单键、双键、长键4个按键事件。
本函数由上层循环调用,间隔10ms
===============*/

unsigned char key_read(void)
{
    static unsigned char key_m = key_state_0, key_time_1 = 0;
    unsigned char key_return = N_key,key_temp;
     
    key_temp = key_driver();
     
    switch(key_m)
    {
         case key_state_0:
            if (key_temp == S_key )
            {
                 key_time_1 = 0;               // 第1次单击,不返回,到下个状态判断后面是否出现双击
                 key_m = key_state_1;
            }
            else
                 key_return = key_temp;        // 对于无键、长键,返回原事件
            break;

          case key_state_1:
            if (key_temp == S_key)             // 又一次单击(间隔肯定<500ms)
            {
                 key_return = D_key;           // 返回双击键事件,回初始状态
                 key_m = key_state_0;
            }
            else                                
            {                                  // 这里500ms内肯定读到的都是无键事件,因为长键>1000ms,在1s前低层返回的都是无键
                 if(++key_time_1 >= 50&&++key_time_1 <= 75)
                 {
                      key_return = E_key;      // 500ms内没有再次出现单键事件,返回上一次的单键事件
                      key_m = key_state_0;     // 返回初始状态
                 }
                 else
                                                                         if(++key_time_1 >= 75)

                 {
                      key_return = S_key;      // 500ms内没有再次出现单键事件,返回上一次的单键事件
                      key_m = key_state_0;     // 返回初始状态
                 }

            }
                                                 
                                                 
                                                 
             break;
               
                return key_return;
}     

void main()
{
        P0 = 0xff;        //IO口初始化
        P2 = 0x00;  //定时器的初始化                                                  
        TMOD = 0x01;                           //选择定时器的工作模式:定时器0,方式1
        TH0 = (65535 - 10000)/256; //定时器的初值
        TL0 = (65535 - 10000)%256;
        EA = 1;                                           //开打总中断使能
        ET0 = 1;                                   //打开定时器0 的使能
        TR0 = 1;                                   //打开定时器0 ,开始工作

        while(1)
        {
                if(time_10ms_ok)          //time_10ms_ok = 1,表示计时到了10MS。(10MS扫描一次按键)
                {
                        time_10ms_ok = 0; //清除计时10MS标志
                        key = key_read(); //调用扫描按键程序,返回一个键值

                        if (key == L_key) //长按:点亮P1口上的8个LED灯。(低电平点亮)
                        {
                                P0 = 0x00;
                        }  
            else if(key == D_key)//双击:点亮P1口上第二个LED灯。(低电平点亮)  
            {
                                P0 = 0xfd;
                        }  
            else if(key == S_key)//单击:点亮P1口上第一个LED灯。(低电平点亮)  
            {
                                P0 = 0xfe;
                        }
                              else if(key == C_key)//单击:点亮P1口上第1个和第二个LED灯。(低电平点亮)  
            {
                                P0 = 0xfc;//11111100
                        }
                              else if(key == E_key)//单击:点亮P1口上第1个和第二个LED灯。(低电平点亮)  
            {
                                P0 = 0xf0;//11110000
                        }
                               else if(key == F_key)//单击:点亮P1口上第1个和第二个LED灯。(低电平点亮)  
            {
                                P0 = 0x70;//11110000
                        }
                }
        }
}

void timer0(void) interrupt 1        //用的是定时器0, 这个“interrupt 1”中的“1”代表1号中断即是定时器0中断。如果是“0”就是外部中断0;“2“=外部中断1;”3“定时器1中断;”4“=串行口中断
{
        TH0 = (65535 - 10000)/256;
        TL0 = (65535 - 10000)%256; //定时器0的方式1,得在中断程序中重复初值。
        time_10ms_ok = 1;  //定时10MS 的标志
}


led.c(117): error C141: syntax error near 'void'

报错,求助。
回复

使用道具 举报

15#
ID:584814 发表于 2021-4-20 08:23 | 只看该作者
unsigned char key_read(void) 尾巴上少了个 }
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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