微控制器按鍵驅動小技巧

平時在開發簡單的工程應用時,會應用引腳資源少一些的MCU,這時要做稍複雜一些的使用者互動功能選項時,就需要單個按鍵實現更多的功能,這時就需要按鍵實現短按、長按、連按功能,以下說明這些功能的實現方法,應用場合在按鍵資源不足無法使用矩陣鍵盤的情況下。

業務邏輯說明

Created with Raphaël 2.2.0呼叫開始按鍵單次掃描.是否有按鍵按下?按鍵按下標誌是否置位?按鍵掃描值是否為上一次確認值?按鍵按下計時值是否大於長按且按鍵長按觸發標誌為0?長按業務邏輯觸發,按鍵長按觸發標誌置位.呼叫結束,等待下次呼叫開始按鍵按下標誌置位;按鍵按下計時(倒計時)值賦初值;按鍵連按計時(倒計時)值賦初值;將按鍵掃描值賦於按鍵確認值.按鍵按下標誌是否置位?按鍵按下計時值是否大於短按(即消抖值)且小於長按?按鍵連按計時是否小於連按間隔?短按業務邏輯觸發.連按次數自增.按鍵連按次數是否大於1?連按業務邏輯觸發.按鍵按下標誌復位;按鍵按下計時(倒計時)值清零;按鍵連按計時(倒計時)值清零;按鍵確認值清零;按鍵長按觸發標誌復位.連按次數清零.yesnoyesnoyesnoyesnoyesnoyesnoyesnoyesno

實現程式碼

給出的示例程式碼以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 --;
	}
}

實踐步驟-1-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;
	}
}