单片机普通io口按键驱动解析2

本文深入探讨了按键驱动的设计与实现细节,包括按键分类、利用定时器和主循环中断进行按键检测,以及如何通过按键状态执行不同操作。通过实例讲解了如何搭建多个按键的结构,实现了单个按键的识别与响应,涵盖了键值获取、状态判断和执行逻辑,适用于嵌入式系统开发。

        通过第一篇的驱动解析,我们大概能知道普通io口按键一般的分类以及工程中的实际原理图,第二个就是知道怎么利用芯片本身自带的定时器以及主循环中断来检测按键按下以及通过按下的时间以及松手的检测来判断。通过第一篇博文的分析,我们其实已经可以取得键值以及键值的状态了。接下来就是利用键值和状态去执行不同的操作。比如说短按放手后去操作,短按不放手执行什么动作,长按放手,长按不放手等状态,这些都会在实际的产品中应用到。

    第二篇按键驱动博文就准备精简一点了,该写的原理已经在第一篇博文里面体现出来了。这篇主要看看多个按键多个io口怎么搭一个可行的结构出来,暂时做一个只能识别单个按键的程序,不能同时检测多个按键的程序。其实大多的工程需求的话就是只能同时触发并检测到一个按键,并不会同时检测两个按键。(一些特殊点的工程会有这种需求,比如说双按键进入测试模式等)。

      需求:6个io按键,全部低推按键,假如对应的芯片io口为PB0,PB1,PB2,PA0,PA1,PA2这六个io口。PB0口按键长按开关机,短按转换模式,其他口的按键短按各自点亮一个LED灯。这边的说明会在整个架构的C代码中说明注释。不再过多地文字描述。不过重点的还是会标注清楚的。

//首先是通过合理的宏给各个Io口定义(有人说有个比较好的做法,就是直接读取整个io的8位,然后异或,就可以知道是否按下并且取得键值(其实我并不苟同这样的做法,因为如果一个芯片有B端口,A端口,C端口,D端口,有多个按键但是每个按键都是取不同端口中的某一个位,难道所有的端口寄存器都全部读出来?))


//建立一个key.h
//六个按键的io口定义
#define KEY_1_PORT   PB0
#define KEY_2_PORT   PB1
#define KEY_3_PORT   PB2
#define KEY_4_PORT   PA0
#define KEY_5_PORT   PA1
#define KEY_6_PORT   PA2
//按键键值的定义
#define   KEY_NO_VALUE    0x00
#define   KEY_1           0x01
#define   KEY_2           0x04  
#define   KEY_3           0x08
#define   KEY_4           0x10
#define   KEY_5           0x20
#define   KEY_6           0x40

//延续博客一
//宏定义
//具体需要的时间以后直接改变宏的参数就ok了!
#define KEY_SHORT_CNT  15  //短按标准
#define KEY_LONG_CNT      1000
#define KEY_HOLD_CNT      1300
#define KEY_RELEASE_CNT    30  //松手


//定义按键按下去的状态
#define KEY_SHORT_UP  0x01//短按松手放开
#define KEY_SHORT     0x05 //执行一次短按之后松开手之后不需要呼应
#define KEY_LONG_UP   0x02//长按松手后执行消息
#define KEY_HOLD      0x03//长按不放,需要持续性消息
#define KEY_LONG      0x04 //长按
#define KEY_NO_STATE     0xff




#if 0

/*如果需要精致一点,上面的按键状态定义和按键键值定义还可以做成一个独立的枚举变量,将键值或者键值的状态圈在一个范围内,这样就会让代码更严谨,同时会更好看一点*/
//比如
type enum
{
      KEY_SHORT=0x01,
      KEY_SHORT,
      KEY_SHORT_UP,
      KEY_LONG,
      KEY_LONG_UP,
      KEY_HOLD,
      KEY_NO_STATE=0xff,                                                 
   


}My_KEY_STATE_ENUM


My_KEY_STATE_ENUM  my_key_state;//这时候my_key_state就会限定在某个领域值之内了。


#endif 


//暂时简单一点,后面升级后再补充
u8 gu8_key_value;//存储实时的键值

//延续博客按键驱动一的函数,下面会有所升级,请注意对比

void  Get_key_value(void);












    接下来就是key.c的操作了(一般具有一定C的基础都会看得懂),不懂的去补习一下C的基础

key.c





//取得键值的函数  (一次性读出Io口状态的,代码比较难理解,通用性不强,运用于特定的工程里面,可以节省代码)
//延续博客一的按键驱动一
//此次是PB2,PB1,PB0  PA2,PA1,PA0 六个普通的Io按键
//取值函数需要扫描,越快越好,放主循环是比较好的选择
    /*如果遇到io口是连续的话,比如PB2,PB1,PB0这种,对于8位的单片机来说
      一般有8个bit位,比如PB7-PB0  //现在是低推状态,也就是说PB2,PB1,PB0没有按下去的时候是
     高电平才对。也就是说需要读一下电平是否有按下,PA2,PA1,PA0也是一样的道理//这样会节省一下扫描的速度以及优化代码执行的效率*/

u8 Get_key_value(void)
{
         u8 key_value; //6个按键,可以用6个不同的bit来表示   00 PB2,PB1,PB0 PA2,PA1,PA0
       
        if( PORTB&0x07 !=0x07 || PORTA&0x07!=0x07 )//说明有io被按下
          {
               key_value= ((PORTB&0x07<<3) |(PORTA&0x07));//一次性读出所有io的状态
             

           }
          else
          {

                key_value=KEY_NO_VALUE;//直接无键值

          }  
          
         return key_value;

}


/*第二种,顺序读按键值法,理解简单,代码比较多*/

u8 Get_key_value(void)
{
         u8 key_value; //
         
         if(!KEY_1_PORT)//第一键按下的话
        {
           key_value |=0x01;          
        }  
        if(!KEY_2_PORT)  
        {

            key_value |=0x02;  
        }   
       if(!KEY_3_PORT) 
        {

            key_value |=0x04;  


        }    
             if(!KEY_4_PORT) 
        {

            key_value |=0x08;  


        }    
             if(!KEY_5_PORT) 
        {

            key_value |=0x10;  


        }    
               if(!KEY_6_PORT) 
        {

            key_value |=0x20;  


        }    

         
    

        
        
         return key_value;//最后返回的就是这6个io的电平情况 //这种如果没有value ,则为0x00;

}


//原来的防抖函数升级成防抖加上执行动作的按键主函数

//多个按键升级版
void key_value_filter(u8 key_value)
{

        static u16 key_press_cnt;//按下去的检测
        static u8  key_release_cnt;//松手的检测 
        static u8  back_key;//存储按下去的键值  
        u8 key_state=KEY_NO_STATE;//初始化键值的状态 

          if(key_value!=KEY_NO)//此时的值已经从主循环扫描而得到了
        {
          
            if( back_key!=key_value)//快速变换按键的情况下,以最新的按键为主
              {
                   back_key=key_value;
                   key_press_cnt=0;

              }
             key_press_cnt++;
             key_release_cnt=0;//松手计数清零
             if(key_press_cnt==KEY_SHORT_CNT )//有些按键的作用只有短按,实际工作中具体分析
              {
                   key_state=KEY_SHORT;

              }
              else if(key_press_cnt==KEY_LONG_CNT)//到达长按的阈值,1s
              {

                 key_state=KEY_LONG;
              }
              else if(key_press_cnt>= KEY_HOLD_CNT)//到达了长按不放的阈值,1.3s
              {

                     key_press_cnt=KEY_LOMG_CNT;//防止超过参数的最大值,同时防止长按继续触发
                      key_state=KEY_HOLD;


              }
             


        }
       else
        {
               key_release_cnt++;//检测到松手电平了,不要马上判断说是松手了
               if(key_release_cnt>=KEY_RELEASE_CNT)//约30ms稳定的松手电平之后就判断为松手,
                {
                     key_release_cnt=0;
                     if(key_press_cnt>=KEY_SHORT_CNT && key_press_cnt<KEY_LONG_CNT)//大于15ms小于1s判断为短按
                        {
                               //待会继续补充 
                                 key_state=KEY_SHORT_UP;

                        }   
                      else if(key_press_cnt>=KEY_LONG_CNT)
                        {
                                  key_state=KEY_LONG_UP;

                        }
                       key_press_cnt=0;//只有真正松手了才会清掉这个计数             
                }
               
              

        }
      

        key_to_deal_work(back_key,key_state);//处理按键值及状态

}


//如果芯片有空间的话,还可以对每个按键的每个状态处理的消息做成一个二维数组,这样就会更清晰地去判断每个按键的作用了。

void key_to_deal_work(u8 key_value,u8 key_state)
{

           if(key_value!=KEY_NO_VALUE && key_state!=KEY_NO_STATE)
            {
               switch(key_value) 
                   {

                     







                   }





            }     










}



 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值