找回密码
 立即注册

QQ登录

只需一步,快速开始

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

5个按键分别接在MCU单片机P1 P3 P4 IO口上,如何用状态机检测?

  [复制链接]
跳转到指定楼层
楼主
/*******************************************************************************
* 文件名:单片机MCU按键函数
* 描  述:
* 功  能:
* 参  数:无
*******************************************************************************/                                                                       
#define PD 0xe0|(P1<<4&0x10)|(P4&0x0c)|(P3&0x03)   //P1.0/P3.0 p3.1/P4.2 p4.3组合5个按键为1个字节数据
//   1110 0000 |0000 0001   |0000 1100 |0000 0011
//   移位后    |0001 0000   |0000 1100 |0000 0011  

#define S0 0   //状态0
#define S1 1   //状态1
#define S2 2   //状态2
#define S3 3   //状态3
                                          
void key_scan()     //放在10ms的定时器中扫描
{
        static u8 state=S0,key_time;
        u8 key;
        key=PD&0xff;                                
       switch(state)                                 
            {
         case S0:                                     //状态0
                  if(key!= 0xff) state = S1; break;        //判断输入是否为0,为0转入状态1
               
        case S1:                                    //状态1
                if(key==0xff) state = S0;                //判断输入是否为1,为1返回状态0
                else                                     //否则,转入状态2,执行按键程序
                 {
                         state=S2;  
                switch(key)
                 {
                                 case 0xfe:
                                  //执行任务1; break;                                                                                          
                                
                                                                                                        
                                 case 0xef:
                                        //执行任务2; break;        
                                       
                                 case 0xfd:
                                        //执行任务3; break;        
                                                        
                                 case 0xfb:
                                //执行任务4; break;        
                                                
                                 case 0xf7:
                                //执行任务5; break;                                                                        
                                 default:break;                                                   
                     }
                     break;

       case S2:                                                  //状态2
               if(key==0xff) state = S0;                              //判断输入是否为1,为1返回状态0
               else if(++key_time==60) {key_time=0;state=S3;}        //否则开始计时,计时结束转入状态3
                 break;

        case S3:                                                  //状态3
                    if(key==0xff)  state=S0;                               //判断输入是否为1,为1返回状态0
                    else if(++key_time==5)                                 //否则开始计时,计时结束按键连击
                             {
                                key_time=0;
                                switch(key)
                                        {
                                        case 0xfe:                                                                                                                                       
                                                //执行任务6; break;               
                                                
                                        case 0xfd:
                                                //执行任务1; break;                                                                                                                                                                                                                                                                                                
                                      }
                         }
                   break;
  }
}                                                                                                                                                                                                                    如果5个按键都接同一组IO口,没问题,但分别接在不同组IO上(如分别接在P1 P3 P4某个IO口上),目前重定义把5个按键组成为1个字节数据,再判断就不能识别了,我也明白switch(key)中的key值不能识别是P1 P3 P4上某个真正的IO口导致的,但我不知道怎么还原到P1 P3 P4上具体识别出哪个IO为有效。
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏4 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:213173 发表于 2022-8-20 21:17 | 只看该作者
换一种写法可能比较好理解

  1. #define PD 0x80   | (P1<<3&0x60)|(P2>>1&0x18)|(P3&0x07)//P1.2~3/P2.4~5/P3.0~2组合7个按键为1个字节数据
  2. //      1000 0000    0000 1100    0011 0000   0000 0111
  3. //
  4. /*******************************************************************************
  5. * 文件名:按键函数
  6. * 描  述:
  7. * 功  能:
  8. * 参  数:无
  9. *************************************************/
  10. void key_scan()     //放在10ms的定时器中扫描
  11. {
  12.         static u8  key_time=0;
  13.         static bit key_sign=0;
  14.         u8 key;
  15.         key=PD&0xff;
  16.         if(key!=0xff)//有键按下
  17.         {
  18.                 if(++key_time>=2 && !key_sign)//
  19.                 {
  20.                         key_sign=1;
  21.                         switch(key)
  22.                         {
  23.                                 case 0xfe:/*执行任务1;*/ break;
  24.                                 case 0xfd:/*执行任务2;*/ break;
  25.                                 case 0xfb:/*执行任务3;*/ break;
  26.                                 case 0xf7:/*执行任务4;*/ break;
  27.                                 case 0xef:/*执行任务5;*/ break;
  28.                                 //case 0xdf:/*执行任务6;*/ break;
  29.                                 //case 0xbf:/*执行任务7;*/ break;
  30.                                   //case 0x7f:/************/ break;
  31.                                 default:break;
  32.                         }
  33.                 }
  34.         }
  35.         else                 //松手
  36.         {
  37.                 key_time=0;
  38.                 key_sign=0;
  39.         }
  40. }

复制代码
回复

使用道具 举报

板凳
ID:146878 发表于 2022-8-20 22:59 | 只看该作者
sbit P_KEY1= P1^2;
sbit P_KEY2= P1^3;
sbit P_KEY3......
sbit P_KEY4......
sbit P_KEY5......
void key_scan()     //放在10ms的定时器中扫描
{
        static u8 state=S0,key_time;
        u8 key=0;
        if(P_KEY1)  key|=0x01;
        if(P_KEY2)  key|=0x02;
        if(P_KEY3)  key|=0x04;
        if(P_KEY4)  key|=0x08;
        if(P_KEY5)  key|=0x10;                             
       switch(state)   
       ......
       .......
       .......
}
回复

使用道具 举报

地板
ID:146878 发表于 2022-8-20 23:01 | 只看该作者
这点弯都转不过来的话,那你路还很长啊,小伙子。。
回复

使用道具 举报

5#
ID:686513 发表于 2022-8-21 08:24 | 只看该作者
本帖最后由 zhth1979 于 2022-8-21 09:05 编辑
wulin 发表于 2022-8-20 21:17
换一种写法可能比较好理解

上面5个按键为:P1.0/P3.0 p3.1/P4.2 p4.3  现在改过来了。 #define PD 0xe0|(P1<<4&0x10)|(P4&0x0c)|(P3&0x03)   //P1.0/P3.0 p3.1/P4.2 p4.3组合5个按键为1个字节数据 //   1110 0000 |0000 0001   |0000 1100 |0000 0011
//   移位后    |0001 0000   |0000 1100 |0000 0011  
switch(key)中key判断是PD的值,怎么能说明case 0xfe(1111 1110)中0xfe代表是P3.2口?而不是P1或P2中的某个IO口。  
回复

使用道具 举报

6#
ID:686513 发表于 2022-8-21 08:38 | 只看该作者
本帖最后由 zhth1979 于 2022-8-21 09:05 编辑
pdwdzz 发表于 2022-8-20 22:59
sbit P_KEY1= P1^2;
sbit P_KEY2= P1^3;
sbit P_KEY3......

上面5个按键为:P1.0/P3.0 p3.1/P4.2 p4.3  现在改过来了。 #define PD 0xe0|(P1<<4&0x10)|(P4&0x0c)|(P3&0x03)   //P1.0/P3.0 p3.1/P4.2 p4.3组合5个按键为1个字节数据 //   1110 0000 |0000 0001   |0000 1100 |0000 0011
//   移位后    |0001 0000   |0000 1100 |0000 0011  
switch(key)中key判断是PD的值,怎么能说明case 0xfe(1111 1110)中0xfe代表是P3.2口?而不是P1或P2中的某个IO口。  
回复

使用道具 举报

7#
ID:686513 发表于 2022-8-21 08:38 | 只看该作者
本帖最后由 zhth1979 于 2022-8-21 09:04 编辑
pdwdzz 发表于 2022-8-20 23:01
这点弯都转不过来的话,那你路还很长啊,小伙子。。
上面5个按键为:P1.0/P3.0 p3.1/P4.2 p4.3  现在改过来了。 #define PD 0xe0|(P1<<4&0x10)|(P4&0x0c)|(P3&0x03)   //P1.0/P3.0 p3.1/P4.2 p4.3组合5个按键为1个字节数据 //   1110 0000 |0000 0001   |0000 1100 |0000 0011
//   移位后    |0001 0000   |0000 1100 |0000 0011  
switch(key)中key判断是PD的值,怎么能说明case 0xfe(1111 1110)中0xfe代表是P3.2口?而不是P1或P2中的某个IO口。      
回复

使用道具 举报

8#
ID:213173 发表于 2022-8-21 18:16 | 只看该作者
zhth1979 发表于 2022-8-21 08:24
上面5个按键为:P1.0/P3.0 p3.1/P4.2 p4.3  现在改过来了。 #define PD 0xe0|(P1


单片机源程序如下:

  1. #include <reg51.H>
  2. sfr P4   =   0xC0;   //1111,1111 端口4
  3. sfr AUXR =   0x8E;   //0000,0000 辅助寄存器
  4. #define u8 unsigned int
  5. #define u16 unsigned char
  6. #define PD (P1<<4&0x10)|(P4&0x0c)|(P3&0x03)

  7. void Timer0Init()                //10毫秒@12.000MHz
  8. {
  9.         AUXR &= 0x7F;                //定时器时钟12T模式
  10.         TMOD &= 0xF0;                //设置定时器模式
  11.         TMOD |= 0x01;                //设置定时器模式
  12.         TL0 = 0xF0;                //设置定时初始值
  13.         TH0 = 0xD8;                //设置定时初始值
  14.         TF0 = 0;                //清除TF0标志
  15.         TR0 = 1;                //定时器0开始计时
  16.         EA  = 1;
  17.         ET0 = 1;

  18. }

  19. /***********************************************
  20. * 文件名:按键函数
  21. * 描  述:
  22. * 功  能:
  23. * 参  数:无
  24. *************************************************/
  25. void key_scan()     //放在10ms的定时器中扫描
  26. {
  27.         static u8  key_time=0;
  28.         static bit key_sign=0;
  29.         u8 key;
  30.         key=PD&0x1f;
  31.         if(key!=0x1f)//有键按下
  32.         {
  33.                 if(++key_time>=2 && !key_sign)//
  34.                 {
  35.                         key_sign=1;
  36.                         switch(key)
  37.                         {
  38.                                 case 0x1e:P2=0xfe; break;
  39.                                 case 0x1d:P2=0xfd; break;
  40.                                 case 0x1b:P2=0xfb; break;
  41.                                 case 0x17:P2=0xf7; break;
  42.                                 case 0x0f:P2=0xef; break;
  43.                                 default:break;
  44.                         }
  45.                 }
  46.         }
  47.         else                 //松手
  48.         {
  49.                 key_time=0;
  50.                 key_sign=0;
  51.         }
  52. }


  53. void main()
  54. {
  55.         Timer0Init();
  56.         while(1)
  57.         {
  58.         
  59.         }
  60. }

  61. void Timer0Interrupt(void) interrupt 1
  62. {
  63.         TH0 = 0xD8;                //设置定时初始值
  64.         TL0 = 0xF0;                //设置定时初始值
  65.         key_scan();
  66. }
复制代码
回复

使用道具 举报

9#
ID:161164 发表于 2022-8-22 09:42 | 只看该作者
  1. u8 bdata GPIO = 0xff;
  2. sbit Key0 = GPIO^0;
  3. sbit Key1 = GPIO^1;
  4. sbit Key2 = GPIO^2;
  5. sbit Key3 = GPIO^3;
  6. sbit Key4 = GPIO^4;
  7. u8 PD()
  8. {
  9.         Key4 = (bit)(P0 & 0x01);
  10.         Key3 = (bit)(P4 & 0x08);
  11.         Key2 = (bit)(P4 & 0x04);
  12.         Key1 = (bit)(P3 & 0x02);
  13.         Key0 = (bit)(P3 & 0x01);
  14.         return GPIO;
  15. }
复制代码
回复

使用道具 举报

10#
ID:686513 发表于 2022-8-22 10:14 | 只看该作者
本帖最后由 zhth1979 于 2022-8-22 12:39 编辑

找到原因了,这个单机的P4有ADC输入,不能做普通的双向口接按键了。把P4去除掉就好了。
回复

使用道具 举报

11#
ID:1034262 发表于 2022-8-22 10:36 | 只看该作者
按键都是按时隙读取,我常用32ms,即每隔32ms读一次按键,值需要花费1~2us。
回复

使用道具 举报

12#
ID:686513 发表于 2022-8-22 12:41 | 只看该作者
coody_sz 发表于 2022-8-22 10:36
按键都是按时隙读取,我常用32ms,即每隔32ms读一次按键,值需要花费1~2us。

它的速度不是很快吗?10ms检测,刚好同时也做按键的消抖了。
回复

使用道具 举报

13#
ID:509408 发表于 2022-8-22 14:21 | 只看该作者
zhth1979 发表于 2022-8-21 08:38
上面5个按键为:P1.0/P3.0 p3.1/P4.2 p4.3  现在改过来了。 #define PD 0xe0|(P1

key读到case :0xfe 把它换成二进制1111 1110 对应到你的PD宏定义,不就是P3口的P3.0=0?即P30被按下?
说白了还是对位操作不熟练 转不过弯来
回复

使用道具 举报

14#
ID:123289 发表于 2022-8-22 14:43 | 只看该作者
1、一位一位读出来判断。
2、各个IO口读出来,按位分析。
你认为哪个方法适合你,就用哪个方法,都行。
回复

使用道具 举报

15#
ID:509408 发表于 2022-8-22 14:43 | 只看该作者
tzs233 发表于 2022-8-22 14:21
key读到case :0xfe 把它换成二进制1111 1110 对应到你的PD宏定义,不就是P3口的P3.0=0?即P30被按下?
说 ...

先掌握好二进制和16进制转换关系。我再问你如果KEY= 0xf9,是哪几个按键被按下了 你想得清楚吗? 代码精简了是好事,但牺牲了可读性。在你不熟练或思路不清晰的时候,还是按楼上的做法封装成一个函数。通过变量或返回值读出来。精简代码那是最后的事情。调试的时候也时如此的,哪能一步到位。
回复

使用道具 举报

16#
ID:190577 发表于 2022-8-24 09:05 | 只看该作者

// 按键
#define key_state_0 0
#define key_state_1 1
#define key_state_2 2
#define key_state_3 3
#define key_no 0
#define key_click 1
#define key_double 2
#define key_long 3
sbit KEY = P0^4;
sbit key1=P5^0;// 红光
sbit key2=P3^0;//绿光开关
sbit key3=P0^2;        //蓝
sbit key4=P4^0;        //白
sbit key5=P0^3; //加湿度
sbit key6=P5^2;//自动

static unsigned char key_read(void)
{
        static unsigned char key_state_buffer1 = key_state_0;
        static unsigned char key_timer_cnt1 = 0;
                static         unsigned char temp=0;
        unsigned char key_return = key_no;
       // unsigned char key;

        //key = KEY;  //read the I/O states

        switch(key_state_buffer1)
        {
                case key_state_0:
                        if((KEY == 0)||(key1 == 0)||(key2==0)||(key3==0)||(key4==0)||(key5==0)||(key6==0))
                         {  key_state_buffer1 = key_state_1; temp=0;
                                                         return 0;
                                                 }
                                //按键被按下,状态转换到按键消抖和确认状态//
                        break;
                case key_state_1:
                        if(KEY== 0)
                        {
                                temp=1;
                                                            key_timer_cnt1 = 0;
                                key_state_buffer1 = key_state_2;
                                //按键仍然处于按下状态
                                //消抖完成,key_timer开始准备计时
                                //状态切换到按下时间计时状态
                        }
                                                else if(key1==0)
                                                {
                                                           temp=2;
                                                           // key_timer_cnt1 = 0;
                                key_state_buffer1 = key_state_2;
                                                }
                                                else if(key2==0)
                                                {
                                                            temp=4;
                                                           // key_timer_cnt1 = 0;
                                key_state_buffer1 = key_state_2;
                                                }
                                                else if(key3==0)
                                                {
                                                            temp=5;
                                                           // key_timer_cnt1 = 0;
                                key_state_buffer1 = key_state_2;
                                                }
                                                else if(key4==0)
                                                {
                                                            temp=6;
                                                           // key_timer_cnt1 = 0;
                                key_state_buffer1 = key_state_2;
                                                }
                                                else if(key5==0)
                                                {
                                                            temp=7;
                                                           // key_timer_cnt1 = 0;
                                key_state_buffer1 = key_state_2;
                                                }
                                                else if(key6==0)
                                                {
                                                            temp=8;
                                                           // key_timer_cnt1 = 0;
                                key_state_buffer1 = key_state_2;
                                                }
                        else
                                                {
                                key_state_buffer1 = key_state_0; temp=0;
                                                }
                                //按键已经抬起,回到按键初始状态
                        break;  //完成软件消抖
                case key_state_2:
                                                 if((KEY == 0)||(key1 == 0)||(key2==0)||(key3==0)||(key4==0)||(key5==0)||(key6==0))
                                                {
                                                         key_state_buffer1 = key_state_2;
                                                         if(temp==1)
                                                         {
                                                                 if(++key_timer_cnt1 >= 100)  //按键继续按下,计时超过1000ms
                                                                 {
                                                                    
                                        key_return = key_long;  //送回长按事件
                                        key_state_buffer1 = key_state_3;  //转换到等待按键释放状态
                                                                 }
                                                          }
                                                }
                                                else
                                                {
                                                         key_return = temp;  //按键抬起,产生一次click操作
                             key_state_buffer1 = key_state_0;  //转换到按键初始状态
                                                }
                       /* if(KEY == 1)
                        {
                                key_return = key_click;  //按键抬起,产生一次click操作
                                key_state_buffer1 = key_state_0;  //转换到按键初始状态
                        }
                        else if(++key_timer_cnt1 >= 100)  //按键继续按下,计时超过1000ms
                        {
                                key_return = key_long;  //送回长按事件
                                key_state_buffer1 = key_state_3;  //转换到等待按键释放状态
                        } */
                        break;
                case key_state_3:  //等待按键释放
                        if((KEY == 0)||(key1 == 0)||(key2==0)||(key3==0)||(key4==0)||(key5==0)||(key6==0)) //按键释放
                                key_state_buffer1 = key_state_3;  //切回按键初始状态
                                                 else //按键释放
                                key_state_buffer1 = key_state_0;  //切回按键初始状态
                        break;
        }
        return key_return;
}
回复

使用道具 举报

17#
ID:686513 发表于 2022-8-24 10:24 | 只看该作者
tzs233 发表于 2022-8-22 14:43
先掌握好二进制和16进制转换关系。我再问你如果KEY= 0xf9,是哪几个按键被按下了 你想得清楚吗? 代码精简 ...

KEY= 0xf9      1111 1001  对应我上面的是控制P3.1和P4.2对吗?不过现在已经可以了。
回复

使用道具 举报

18#
ID:624769 发表于 2022-8-24 16:09 来自手机 | 只看该作者
恕我才疏学浅,       key=PD & 0xff;    这种写法的意义何在?  为什么不写成  key=PD | 0x00;    或者  key=PD % 0x100;    ?
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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