精华内容
下载资源
问答
  • 此程序为MCS51型单片机按键驱动程序,用C语言写成,
  • 前言:下面是自己写的一个简易的单片机按键程序,主要采用了物理键值和逻辑键值查表映射的方法。此外还添加了长按,短按以及连续按键的功能。 1. 键值映射数据结构表   不管是直接采用MCU的引脚做矩阵键盘处理...

    前言:下面是自己写的一个简易的单片机按键程序,主要采用了物理键值和逻辑键值查表映射的方法。此外还添加了长按,短按以及连续按键的功能。


    1. 键值映射数据结构表

      不管是直接采用MCU的引脚做矩阵键盘处理还是采用一些特殊的按键芯片获取键值,获取的物理键值总是和电气特性息息相关。对于程序来说,为了屏蔽物理层的特殊性,可以采用物理键值和逻辑键值查表映射的方法获取逻辑键值。

    1.1 物理键值和逻辑键值

      对于表中的数据结构,功能按键必须有其对应的物理按键和逻辑按键键值,即使两者或许都一样,这是为了程序的扩展性考虑。

    1.2 长按和短按

      一个功能按键在设计时,可以通过短按和长按区分不同功能,在按键个数有限的情况下,实现更多的功能。比如:一个按键短按功能为开始,长按功能为修改参数。此时需要一个数据表示短按或者长按,更方便的方法是,使用一个数据来表示按键键值的获取时间,下面例子中:1表示1个时间片获取键值;500表示500个时间片获取键值。

    1.3 连续按键送数

      连续按键功能一般是都是连续加或者连续减按键用的比较多。当+/-键按下时,数值可以实现连续加减功能,且按下时间越快,数字变化越快。

      下面是针对上述功能,设计的按键数据结构及其键值映射表。

    #define PHYSI_KEY_TYPE   uint8_t  //1个公共端1个字节,2个公共端拼成两个字节大小,以此类推
    #define KEYNUM            6   //按键数量(包括单个按键和多个按键)
    
    typedef struct {
      PHYSI_KEY_TYPE physicalV;   //物理键值
      uint8_t getTim;      //送键值时间 区分长按短按
      uint8_t logicalV;    //逻辑键值
      uint8_t isConti;     //是否出现连按按键
    }KEY_VAL;  
    
    /*---------逻辑键值定义-----------*/
    #define Key_Start            10
    #define Key_CAL              11
    #define Key_Find             12
    #define Key_Plus             13
    #define Key_Minus            14
    #define Key_Pra              15
    
    /*--物理键值和逻辑键值对应----物理键值---送键值时间(长按/短按)---逻辑键值---连按标志-*/
    const KEY_VAL KeyVal_Tab[KEYNUM]= 
    {
      {0x01,1,Key_Plus,1},
      {0x02,1,Key_Minus,1},
      {0x04,1,Key_Start,0},
      {0x08,500,Key_Find,0},
      {0x10,1,Key_Pra,0},
      {0x40,1,Key_CAL,0},
    };

    2. 机械抖动处理

      除了用特殊的按键驱动芯片,否则一般的按键驱动比如GPIO矩阵按键等都会出现抖动电平,持续时间一般为50ms,所以在分情况进行按键抖动处理必不可少。一般的做法是,在这50ms或者更长的时间里面,多次采样,当出现持续稳定数据时,认为去抖动完成。

    3. 按键计时

      人一般一次按键的时间大约是500ms,对单片机来说,这可能是很多个时间片。在大多数情况下,我们可能只需要一次按键键值获取,也就是说除了第一次按键有效外,后面的按键按下时间片很多时候都是人体反应缓慢导致的信号,所以后面时间片的信号大多是需要过滤的。而另外的连续按键情况下,又需要连续不断送键值,因此对于每个按键都需要一个计时器来记下键值出现的时间

    3.1 按键按下计时

      当出现稳定键值时(去抖处理完成后),计时器开始计。按键短按时,第一个时间片就获取键值给主程序(即计时器数值为1时);长按按键时,直到计时器数等于指定时间片数时,才获取键值。

    3.2 连续按键计时

      当检查到该键值的连续按键标志被置位时,连续按键计时启动。现采取方案为:

    • 按键按下1S时间内,每500ms送一次键值
    • 按键按下1~2S时间内,每200ms送一次键值
    • 按键按下2~3S时间内,每100ms送一次键值
    • 按键按下3S以上时间,都以20ms时间送一次键值

      下面是对该函数的细节设计,该函数的调用时间片为20ms:

    /*-------定时器相关数据结构--------*/
    typedef struct{
    uint8_t keyDownTim;   //按键按下计时器
      uint8_t keyDownContiTim;  //连续按键计时器
      uint8_t ContiF;           //连续按键标识
      uint8_t getTim;          //送数时间
    }KEY_TIM;
    
    /*------------------按键获取函数-------------------
    * 入口参数 : 多个公共端拼成的缓冲区物理键值 , 定义的按键数量
    * 返回 :逻辑键值
    -------------------------------------------------*/
    uint8_t SYS_readKeys(PHYSI_KEY_TYPE keyBufUnion,uint8_t keyNum)
    { 
      static PHYSI_KEY_TYPE  KeyBuf1 = 0;
      static PHYSI_KEY_TYPE  KeyBuf2 = 0;
      static PHYSI_KEY_TYPE  KeyBuf3 = 0;
      
      static KEY_TIM Tim;
      
      uint8_t keyValue = 0;
      
      /*--------按键消抖----- 20 x 3 = 60ms -----*/
        KeyBuf3 = KeyBuf2;
        KeyBuf2 = KeyBuf1;
        KeyBuf1 = keyBufUnion;
     
        if((KeyBuf1 != KeyBuf2) || (KeyBuf1 != KeyBuf2) || (KeyBuf1 != KeyBuf2))
          return keyValue;
        
      /*---------------扫描键值表-----------*/
      for(uint8_t i = 0; i < keyNum; i++)
      {
        if(keyBufUnion == KeyVal_Tab[i].physicalV) 
        {
          keyValue   = KeyVal_Tab[i].logicalV;
          Tim.ContiF = KeyVal_Tab[i].isConti;  //连续按键标识
          Tim.getTim = KeyVal_Tab[i].getTim;   //送数时间
        }
      }
      
      /*---------按键时间检测----------*/
      if(keyValue == 0)
      {
        Tim.keyDownTim = 0;
        Tim.keyDownContiTim = 0;
        return keyValue;
      }
      else if(keyValue > 0)
      {
        if(Tim.keyDownTim < 0xFF)     Tim.keyDownTim++;
        
        if(Tim.keyDownTim == Tim.getTim)
        {
          return keyValue;  //短按或长按
        }
        
        if(Tim.ContiF == 1)  //连续按键
        {
          if(Tim.keyDownContiTim < 0xFF)     Tim.keyDownContiTim++;
          if(Tim.keyDownContiTim < 1000/20)   //1档 500ms送数
          {
            if(Tim.keyDownTim >= 500/20)  Tim.keyDownTim = 0;
          }
          else if(Tim.keyDownContiTim < 2000/20)  //2档 200ms送数
          {
            if(Tim.keyDownTim >= 200/20)  Tim.keyDownTim = 0;
          }
          else if(Tim.keyDownContiTim < 3000/20)    //3档 100ms送数
          {
            if(Tim.keyDownTim >= 100/20)  Tim.keyDownTim = 0;
          }
          else                                     //4档 20ms送数
          {
            if(Tim.keyDownTim >= 20/20)  Tim.keyDownTim = 0;
          }
        }  
      } 
      return 0;
    }
    展开全文
  • 51单片机_5X5按键驱动20210915100123
  • 单片机按键驱动小技巧 平时在开发简单的工程应用时,会应用引脚资源少一些的MCU,这时要做稍复杂一些的用户交互功能选项时,就需要单个按键实现更多的功能,这时就需要按键实现短按、长按、连按功能,以下说明这些...

    单片机按键驱动小技巧

    平时在开发简单的工程应用时,会应用引脚资源少一些的MCU,这时要做稍复杂一些的用户交互功能选项时,就需要单个按键实现更多的功能,这时就需要按键实现短按、长按、连按功能,以下说明这些功能的实现方法,应用场合在按键资源不足无法使用矩阵键盘的情况下。

    业务逻辑说明(异步非阻塞方式实现)

    在这里插入图片描述

    实现代码(异步非阻塞方式实现)

    给出的示例代码以STC15F系单片机实现,硬件环境为触摸IC输出信号,实际可以应用到轻触按键等任何需要消抖的按键需求;
    硬件配置

    sbit touchPad_1 = P1^6;
    sbit touchPad_2 = P1^7;
    sbit touchPad_3 = P1^5;
    

    单次扫描:

    u8 touchPadScan_oneShoot(void){	
    
    	u8 valKey_Temp = 0;
    	
    	if(!touchPad_1)valKey_Temp |= 0x01;
    	if(!touchPad_2)valKey_Temp |= 0x02;
    	if(!touchPad_3)valKey_Temp |= 0x04;
    	
    	return valKey_Temp;
    }
    

    实践步骤-1):首先初始化定时器用于对按键按下时间以及连按间隔时间进行计时

    定义按键按下计时值以及连按间隔时间进行计时值

    u16	xdata touchPadActCounter	= 0;  //触摸计时
    u16	xdata touchPadContinueCnt	= 0;  //连按间隔计时
    

    定时器初始化

    void appTimer0_Init(void){	//50us 中断@24.000M
    
    	AUXR |= 0x80;		
    	TMOD &= 0xF0;		
    	TL0   = 0x50;		
    	TH0   = 0xFB;	
    	TF0   = 0;	
    	ET0	  = 1;	//开中断
    	PT0   = 0;	//高优先级中断
    	
    	TR0   = 1;		
    }
    

    计时运行

    void timer0_Rountine (void) interrupt TIMER0_VECTOR{
    	
    	u8 code period_1ms 		= 20;
    	static u8 counter_1ms 	= 0; 
    	
    	//****************1ms专用**********************************************/
    	if(counter_1ms < period_1ms)counter_1ms ++;
    	else{
    	
    		/*触摸动作时间计时*/
    		if(touchPadActCounter)touchPadActCounter --;
    		
    		/*连按间隔时间计时*/
    		if(touchPadContinueCnt)touchPadContinueCnt --;
    	}
    }
    

    实践步骤-2):具体业务逻辑实现,本代码段为异步进行,即在主程序大循环内一直调用即可.
    相关宏及数据结构

    #define	timeDef_touchPressContinue	350		//连按间隔时间设置,单位:ms
    
    typedef enum{
    
    	press_Null = 1, //无按键
    	press_Short, //短按
    	press_ShortCnt, //连按
    	press_LongA, //长按A
    	press_LongB, //长按B
    }keyCfrm_Type;
    

    普通长短按触发逻辑封装

    void touchPad_functionTrigNormal(u8 statusPad, keyCfrm_Type statusCfm){ //普通长短按触发
    
    	switch(statusCfm){
    	
    		case press_Short:{
    			
    #if(DEBUG_LOGOUT_EN == 1)				
    			{ //用后注释,否则占用大量代码空间
    				u8 xdata log_buf[64];
    				
    				sprintf(log_buf, "touchPad:%02X, shortPress.\n", (int)statusPad);
    				PrintString1_logOut(log_buf);
    			}
    #endif	
    			switch(statusPad){
    				
    				case 1:
    				case 2:
    				case 4:{
    					
    					swCommand_fromUsr.actMethod = relay_flip;
    					swCommand_fromUsr.objRelay = statusPad;
    					EACHCTRL_realesFLG = statusPad; //互控
    					devActionPush_IF.push_IF = 1; //推送使能
    					
    				}break;
    					
    				default:{}break;
    			}
    			
    		}break;
    		
    		case press_ShortCnt:{
    			
    #if(DEBUG_LOGOUT_EN == 1)				
    			{ //用后注释,否则占用大量代码空间
    				u8 xdata log_buf[64];
    				
    				sprintf(log_buf, "touchPad:%02X, cntPress.\n", (int)statusPad);
    				PrintString1_logOut(log_buf);
    			}
    #endif	
    			
    			switch(statusPad){
    				
    				case 1:
    				case 2:
    				case 4:{
    					
    					swCommand_fromUsr.actMethod = relay_flip;
    					swCommand_fromUsr.objRelay = statusPad;
    					
    				}break;
    					
    				default:{}break;
    			}
    			
    		}break;
    		
    		case press_LongA:{
    			
    #if(DEBUG_LOGOUT_EN == 1)				
    			{ //用后注释,否则占用大量代码空间
    				u8 xdata log_buf[64];
    				
    				sprintf(log_buf, "touchPad:%02X, longPress_A.\n", (int)statusPad);
    				PrintString1_logOut(log_buf);
    			}
    #endif	
    		
    			switch(statusPad){
    			
    				case 1:{
    					
    				
    				}break;
    					
    				case 2:{}break;
    					
    				case 4:{
    				
    					devStatusChangeTo_devHold(1); //设备网络挂起
    				
    				}break;
    					
    				default:{}break;
    			}
    			
    		}break;
    			
    		case press_LongB:{
    			
    #if(DEBUG_LOGOUT_EN == 1)				
    			{ //用后注释,否则占用大量代码空间
    				u8 xdata log_buf[64];
    				
    				sprintf(log_buf, "touchPad:%02X, longPress_B.\n", (int)statusPad);
    				PrintString1_logOut(log_buf);
    			}
    #endif	
    		
    			switch(statusPad){
    			
    				case 1:{}break;
    					
    				case 2:{}break;
    					
    				case 4:{}break;
    					
    				default:{}break;
    			}
    			
    		}break;
    			
    		default:{}break;
    	}
    }
    

    连按触发逻辑封装

    void touchPad_functionTrigContinue(u8 statusPad, u8 loopCount){	//连按触发
    	
    	EACHCTRL_realesFLG = statusPad; //最后一次按下触发互控同步
    	devActionPush_IF.push_IF = 1; //最后一次按下触发推送使能
    	
    #if(DEBUG_LOGOUT_EN == 1)				
    	{ //用后注释,否则占用大量代码空间
    		u8 xdata log_buf[64];
    		
    		sprintf(log_buf, "touchPad:%02X, %02Xtime pressOver.\n", (int)statusPad, (int)loopCount);
    		PrintString1_logOut(log_buf);
    	}
    #endif	
    
    	switch(statusPad){
    	
    		case 1:{
    		
    			switch(loopCount){
    			
    				case 3:{
    				
    				}break;
    				
    				case 4:{
    				
    					usrZigbNwkOpen(); //开放网络
    					
    				}break;
    					
    				default:{}break;
    			}
    			
    		}break;
    			
    		case 2:{
    		
    			switch(loopCount){
    			
    				case 3:{}break;
    					
    				default:{}break;
    			}
    			
    		}break;
    			
    		case 4:{
    		
    			switch(loopCount){
    			
    				case 3:{}break;
    					
    				case 4:{
    				
    					devHoldStop_makeInAdvance(); //设备网络挂起提前结束
    				
    				}break;
    					
    				default:{}break;
    			}
    			
    		}break;
    			
    		default:{}break;
    	}
    }
    

    具体业务逻辑

    void touchPad_Scan(void){
    
    	static u8 touchPad_temp = 0;
    	static bit keyPress_FLG = 0;
    	
    	static bit funTrigFLG_LongA = 0;
    	static bit funTrigFLG_LongB = 0;
    	
    	code	u16	touchCfrmLoop_Short 	= timeDef_touchPressCfm,	//消抖时间,即短按确认时间,单位:ms
    				touchCfrmLoop_LongA 	= timeDef_touchPressLongA,	//长按A确认时间,单位:ms
    				touchCfrmLoop_LongB 	= timeDef_touchPressLongB,	//长按B确认时间,单位:ms
    				touchCfrmLoop_MAX	 	= 60000;//计时封顶
    	
    	static u8 pressContinueGet = 0;
    	       u8 pressContinueCfm = 0;
    	
    	u16 conterTemp = 0; //时差计算缓存,所有计时都为倒计时,初值为touchCfrmLoop_MAX,所以确认时间时,需要进行减法运算
    	
    	if(touchPadScan_oneShoot()){
    		
    		if(!keyPress_FLG){
    		
    			keyPress_FLG = 1;
    			touchPadActCounter = touchCfrmLoop_MAX;
    			touchPadContinueCnt = timeDef_touchPressContinue;  //连按间隔判断时间更新
    			touchPad_temp = touchPadScan_oneShoot();
    		}
    		else{
    			
    			if(touchPad_temp == touchPadScan_oneShoot()){
    				
    				conterTemp = touchCfrmLoop_MAX - touchPadActCounter;	
    				if(conterTemp > touchCfrmLoop_LongA && conterTemp <= touchCfrmLoop_LongB){
    				
    					if(!funTrigFLG_LongA){
    					
    						funTrigFLG_LongA = 1;
    						touchPad_functionTrigNormal(touchPad_temp, press_LongA);
    					}
    				}
    				if(conterTemp > touchCfrmLoop_LongB && conterTemp <= touchCfrmLoop_MAX){
    				
    					if(!funTrigFLG_LongB){
    					
    						funTrigFLG_LongB = 1;
    						touchPad_functionTrigNormal(touchPad_temp, press_LongB);
    					}
    				}
    			}
    		}
    	}else{
    		
    		if(keyPress_FLG){
    		
    			conterTemp = touchCfrmLoop_MAX - touchPadActCounter;
    			if(conterTemp > touchCfrmLoop_Short && conterTemp <= touchCfrmLoop_LongA){
    			
    				if(touchPadContinueCnt)pressContinueGet ++;
    				if(pressContinueGet <= 1)touchPad_functionTrigNormal(touchPad_temp, press_Short); 
    				else touchPad_functionTrigNormal(touchPad_temp, press_ShortCnt);
    				beeps_usrActive(3, 50, 3);
    			}
    		}
    	
    		if(!touchPadContinueCnt && pressContinueGet){
    		
    			pressContinueCfm = pressContinueGet;
    			pressContinueGet = 0;
    			
    			if(pressContinueCfm >= 2){
    				touchPad_functionTrigContinue(touchPad_temp, pressContinueCfm);
    				pressContinueCfm = 0;
    			}
    			
    			touchPad_temp = 0;
    		}
    
    		funTrigFLG_LongA = 0;
    		funTrigFLG_LongB = 0;
    			
    		touchPadActCounter = 0;
    		keyPress_FLG = 0;
    	}
    }
    

    至此,业务逻辑程序代码实现结束,以上代码针对按键功能对原工程进行剥离整理,若有需要,可查看博主原工程更为详细完整代码:https://github.com/Nepenthes/LB_ZIGB_devNode_sw_3bit/blob/master/Proj_sw_devZigbNode/Sensor/usrKin.c
    同时,以上示例中的打印输出为串口引脚印射输出,与实际通信串口内部串口寄存器共用,这样做可以节省一点内存,具体实现也看查看原工程.

    以上完成后便可以将驱动在前台大循环进行调用:
    在这里插入图片描述
    功能效果(图片):
    在这里插入图片描述
    功能效果(视频):
    https://www.bilibili.com/video/av33750150?from=search&seid=12846664617436496314

    展开全文
  • 51单片机复用驱动4x6按键、16个数码管 带protues仿真
  • 基于飞思卡尔单片机XS128的按键驱动程序,改程序调试通过,只需修改IO即可移植和使用。
  • 功能 :判断按键是否按下 参数 :<unsigned int>函数 readButtonDown 的返回值<unsigned char>从0开始的位置表示 结果 :按下时为真 没按下时为假 */ #define BUTTON_DOWN_IF(ButtonDownState, positio

    原理图

    矩阵按键原理图

    头文件

    #ifndef __BUTTON_MATRIX_H__
    #define __BUTTON_MATRIX_H__
    
    #include <reg52.h>
    
    /*
    功能	:判断按键是否按下
    参数	:<unsigned int>函数 readButtonDown 的返回值<unsigned char>从0开始的位置表示
    结果	:按下时为真 没按下时为假
    */
    #define BUTTON_DOWN_IF(ButtonDownState, position) (((ButtonDownState) & (0x0001 << (position))) != 0)
    
    /*
    功能	:读取按下的按键
    参数	:<void>
    返回值	:<unsigned int>每位和按键一一对应
    		> 0为没按下 1为按下
    */
    extern unsigned int readButtonDown()#endif
    
    

    源文件

    #include "buttonMatrix.h"
    
    unsigned int readButtonDown() {
    	unsigned int state = 0xffff;
    	unsigned char scanPort;
    	P3 = 0xf0;
    	if (P3 != 0xf0) {
    		for (scanPort = 0; scanPort < 4; scanPort++) {
    			P3 = ~(0x08 >> scanPort);
    			state = (state << 4) | (P3 >> 4);
    		}
    	}
    	return ~state;
    }
    
    
    展开全文
  • 51单片机 CH452驱动数码管,按键按键控制,简单的按键控制程序
  • 通过第一篇的驱动解析,我们大概能知道普通io口按键一般的分类以及工程中的实际原理图,第二个就是知道怎么利用芯片本身自带的定时器以及主循环中断来检测按键按下以及通过按下的时间以及松手的检测来判断。...

            通过第一篇的驱动解析,我们大概能知道普通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) 
                       {
    
                         
    
    
    
    
    
    
    
                       }
    
    
    
    
    
                }     
    
    
    
    
    
    
    
    
    
    
    }
    
    
    
    

     

    展开全文
  • 单片机按键FIFO

    千次阅读 2020-05-29 18:47:27
    一般的单片机系统,按键作为人机交互工具是必不可少的,但是普通的按键需要消抖处理,极大的增加了程序开销,降低系统实时性。 安富莱的FIFO按键,无需延时处理消抖,可以记录按键按下、弹起、长按、组合按,并且...
  • 独立按键单片机中很重要的一个器件,在这篇文章里,通过这个用独立按键控制LED灯的小程序来介绍独立按键开关的使用。1.按键分类与输入原理 按键按照结构原理科分为两类,一类是触点式开关按键,如机械式开关、导电...
  • 4*4矩阵长短按键扫描程序,定时器中断软件消抖处理 ,开发平台51单片机,程序共77行,C语言编程。可以检测长短按键状态,不检测组合按键
  • 单片机按键实验

    2014-08-14 18:58:29
    很详细的按键扫描实验,分两个程序,按键数码管显示和按键液晶显示,同时包含液晶驱动
  • STC单片机按键扫描程序

    千次阅读 2018-10-17 11:07:08
    STC单片机按键扫描程序 最近在做一个电子秤相关项目,使用STC系列单片机作为主控芯片,项目第一阶段直接使用IAP15W4K58S4驱动两个矩阵键盘,一切调试顺利,在项目即将结束时老板要求使用另一块单片机驱动矩阵键盘,...
  • 51按键驱动模块

    2014-01-23 16:46:34
    51单片机万能按键驱动
  • 独立按键原理是这样的:按键没按下的时候,相应端口是高电平状态,而当按键按下的时候,相应的端口则是低电平。所以可以根据这个现象,实现相应的功能。 还有一点应该注意的是:按键在闭合和断开时,触点会存在抖动...
  • 51单片机按键控制步进电机加减速及正反转

    万次阅读 多人点赞 2018-11-02 11:33:59
    之前尝试用单片机控制42步进... 所用硬件:步进电机及驱动器、STC89C52单片机、直流电源 1、硬件连接图 注意:上图为共阳极接法,实际连接参考总体线路连接。 驱动器信号端定义: PUL+:脉冲信号输入正。( C...
  • 如果是刚入行的单片机工程师来说,写一个完整的具有实用性和稳定性和扩展性的按键驱动还是比较困难的,这篇博客主要是结合自己的实际经验之谈,解析一下普通的Io按键驱动如何写和实现,如果有大神看出有错误的东西,...
  • 单片机系统驱动

    2020-04-13 15:37:25
    对于一些单片机而言,系统的资源大多数情况下是有限的。为了充分的利用资源(节省成本),工程设计的时候往往会对资源进行不断的压榨。 对于单片机而言它要做的事情大抵...为了更好的驱动整个单片机程序,我们有...
  • 前面我们学习了《8051单片机实战分析(以STC89C52RC为例) | 07 - 独立按键驱动》,但是在单片机系统中,若使用按键较多时如电子密码锁、电话机键盘等一般都至少有12到16个按键,通常采用矩阵式按键,即矩阵键盘。...
  • 本设计采用51单片机,控制42/57步进电机启动,并且使用双按键进行加减速控制,数码管显示速度,使用专门的驱动驱动电机,可以细分。工作的源码。
  • 4 条输入线接到单片机的 IO 口上,当按键 K3 按下时,K3两边的线路将会导通,P32这个单片机IO口直接接到GND,此时P32这个引脚就是低电平了。当松开按键后,当松开按键后,线路断开,就不会有电流通过,那么P32引脚就...
  • 状态机按键驱动

    2015-10-08 12:02:22
    针对传统的按键驱动中采用的延时去抖动而做的优化,不采用延时这种占用单片机资源的方式,采用状态机的思路进行优化。
  • SPI通讯数码管与按键驱动代码,可以节省单片机IO,驱动简单方便
  • C语言实现,用于嵌入式尤其单片机系统,小巧简单易用的事件驱动型按键驱动模块,可无限量扩展按键,按键事件的回调异步处理方式可以简化你的程序结构,按键业务逻辑更清晰。c语言源代码;修订了原版的bug,更易用。...
  • 以经典的51内核单片机设计一款产品,功能如下:1、采用宏晶的STC15L2K32S2-LQFP32,2k SRAM,32k ROM;目的:选用STC15系列1T的经典51内核单片机,资源丰富。2、设计1个运行指示灯,工作时,间隔1s闪烁;目的:学习...
  • 接触过的按键驱动中,大部分是使用延时函数消抖,或者是在扫描到按键状态之后,直接就在按键扫描程序内部对事件进行处理。一直在思考一个不使用延时函数同时兼容性和移植性较强的按键驱动程序。之前用了半天时间总算...
  • #include "REG52.H" ... * 调整抖动时间阀值的大小,可以更改按键的触发灵敏度。 * 去抖动的时间本质上等于累计定时中断次数的时间。 */ #define const_key_time_short120//短按的按键去抖动延时的时间 #d...
  • 基于单片机的C源码;单片机驱动74HC164芯片,用于数码管显示使用; 项目使用代码,可以直接移植使用;

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 7,234
精华内容 2,893
关键字:

单片机按键驱动