藍橋杯嵌入式——一文搞定按鍵配置

藍橋杯嵌入式——一文搞定按鍵配置

作者:閆永成  QQ:793805481

  藍橋杯的板子上,一共有五個按鍵,其中RESET(重置)按鍵已經配置好了,我們只需要配置其他四個按鍵供我們使用就可以了。

按鍵的原理圖:

在這裏插入圖片描述
在這裏插入圖片描述
  可以看到,K1,K2,K3,K4分別與PA0,PA8,PA1,PB2相連,如果我們把IO口的模式設置爲上拉輸入,按鍵沒有按下時,IO口就爲高電平;按鍵按下後,IO口處就爲低電平。
  然後,像LED配置時一樣,新建key.c和key.h文件並添加到工程中,然後就可以編寫程序了。

編寫程序:

key.h

#ifndef _KEY_H
#define _KEY_H

#include "stm32f10x.h"	   //包含頭文件

//宏定義
#define B1 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)
#define B2 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_8)
#define B3 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)
#define B4 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_2)

void KEY_Init(void);  //初始化函數
void KEY_Scan(void);  //按鍵掃描函數
#endif

宏定義部分:GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)的作用是讀取PA0引腳的值,當按鍵沒有按下時爲1,按鍵按下後被拉低爲0。之所以這樣宏定義,是因爲這樣就可以用B1,B2,B3,B4表示相應引腳的電平,在後續的使用中較爲方便、簡潔。
  注意:宏定義一定不要多加一個“;”,寫成:
#define B1 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0);
  這樣系統編譯是不會報錯的很難發現。

key.c

void KEY_Init(void)//初始化函數
{  
   GPIO_InitTypeDef GPIO_InitStruct;
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);

   GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_8;
   GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;//設置爲上拉輸入模式
   GPIO_InitStruct.GPIO_Speed=GPIO_Speed_10MHz;
   GPIO_Init(GPIOA,&GPIO_InitStruct);

   GPIO_InitStruct.GPIO_Pin=GPIO_Pin_1|GPIO_Pin_2;
   GPIO_Init(GPIOB,&GPIO_InitStruct);

}

  按鍵的初始化函數較爲簡單,只需要記得把GPIO的模式設爲上拉輸入模式即可。重點在於按鍵掃描函數。

  之前學過按鍵的應該知道,我們使用按鍵時要進行消抖,因爲按鍵可能會由於按下和鬆開過程中自身抖動而引起誤差。一般我們採用延時50ms再次判斷按鍵是否按下來實現。考慮到在程序的執行過程中,我們要不斷執行按鍵掃描函數來判斷按鍵狀態,所以我們直接通過如下方式,即每50ms執行一次按鍵掃描函數來實現消抖。

在這裏插入圖片描述
  這一點有不太理解的,請參考藍橋杯嵌入式——滴答定時器

然後看按鍵掃描函數的代碼:

void KEY_Scan(void)	
{
  	static u16 key1_sum=0,key2_sum=0,key3_sum=0,key4_sum=0;//靜態變量
	if(B1==0)//B1按下
	{
	  key1_sum++;
	  if(key1_sum==1)//短按有效  且作用一次
	  {
	    LED_Init();
		LED_Control(Ledall,0);//功能
	  }
	}
	else {key1_sum=0;}//B1鬆開  即使key1_sum重新爲0
	if(B2==0)
	{
	  key2_sum++;
	  if(key2_sum==1)
	  {
	    LED_Init();
		LED_Control(Ledall,1);
	  }
	}
	else {key2_sum=0;}
	if(B3==0)
	{
	  key3_sum++;
	  if(key3_sum==1)
	  {
	    STM3210B_LCD_Init();
		LCD_SetTextColor(Red);
		LCD_DisplayStringLine(Line1,(u8 *)"54188");
	  }
	}
	else {key3_sum=0;}
	if(B4==0)
	{
	  key4_sum++;
	  if(key4_sum==1)
	  {
	    STM3210B_LCD_Init();
		LCD_SetTextColor(Blue);
		LCD_DisplayStringLine(Line2,(u8 *)"54188");
	  }
	}
	else {key4_sum=0;}
 }

  按鍵按下時,有短按有效,長按有效,而作用效果又分爲只作用一次和一直作用。舉例說明:短按有效,即按鍵按下就產生作用,當按下後沒有鬆開時可一直起作用(即一直執行相關操作),如電腦鍵盤上的按鍵,短按有效,且按下後就一直起作用,也可以就按下後那一瞬間後產生一次作用,之後不再執行相關操作,如電視遙控器上的暫停按鈕。而長按有效,是指按下一段時間後才起作用,比如筆記本電腦開關機鍵長按10s後纔會強制關機。

  上邊的掃描函數,定義static變量的目的就是使按鍵按下後只起一次作用,即key1_sum=1時,執行一次。若要實現按鍵按下後一直起作用只需要清除靜態變量即可。如:

if(B1==0)//B1按下
	{
	 i++;//i不斷++
	}

  上邊的掃描函數,也是短按有效,即按鍵按下後key1_sum由0變爲1後(50ms)執行操作。若要實現長按有效,只需更改key1_sum相應的值。如:

……
static u16 key1_sum=0,key2_sum=0,key3_sum=0,key4_sum=0;//靜態變量
	if(B1==0)//B1按下
	{
	  key1_sum++;
	  if(key1_sum==20)//長按有效  且作用一次
	  {
	    LED_Init();
		LED_Control(Ledall,0);//功能
	  }
	}
	else {key1_sum=0;}//B1鬆開  即使key1_sum重新爲0
……

  由前邊我們可以知道,每50ms執行一次掃描函數,所以當長按1s有效時,也就是key1_sum=20時,執行操作。
  若要長按產生作用後一直有效只需:

static u16 key1_sum=0,key2_sum=0,key3_sum=0,key4_sum=0;//靜態變量
	if(B1==0)//B1按下
	{
	  key1_sum++;
	  if(key1_sum==20)//長按有效  且作用一次
	  {
	    i++;
	  }
	  key1_sum=19//執行操作後 更改
	}
	else {key1_sum=0;}//B1鬆開  即使key1_sum重新爲0

  關於按鍵的基本配置就這些。按鍵的使用在藍橋杯比賽中屬於重點內容,需要理解後,熟悉掌握。


補充:

  使用按鍵執行某些操作時,一些簡單的操作可以直接在按鍵掃描函數中直接寫出,但一切複雜的操作需要通過一些標誌變量進行標誌後,在其他地方實現,在其他複雜程序中我們會見到。




========再更

  關於按鍵的長短按控制,還有另一種實現方式。先看程序:

1.按鍵初始化函數,同上。

void KEY_Init(void)
{  
   GPIO_InitTypeDef GPIO_InitStruct;
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);

   GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_8;
   GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;
   GPIO_InitStruct.GPIO_Speed=GPIO_Speed_10MHz;
   GPIO_Init(GPIOA,&GPIO_InitStruct);

   GPIO_InitStruct.GPIO_Pin=GPIO_Pin_1|GPIO_Pin_2;
   GPIO_Init(GPIOB,&GPIO_InitStruct);
}

2.按鍵掃描函數

extern void Delay_Ms(u32 nTime);//聲明外部函數
u8 KEY_Scan(void)	
{
  u8 state=0;
  if((B1==0)||(B2==0)||(B3)==0||(B4==0))//有按鍵按下
  {
  Delay_Ms(10);//消抖
    
  if(B1==0)state=1;
  else if(B2==0) state=2;
       else if(B3==0) state=3;
	        else if(B4==0) state=4;
  }
  return state;//返回值
}

  這裏,直接採用延時函數進行消抖處理。延時函數直接調用之前定義過的延時函數即可,無需再定義。
  這個掃描函數較爲簡單,只是實時返回按鍵狀態情況。返回值爲1、2、3、4時分別對應按鍵1、2、3、4按下;沒有按鍵按下時,返回值始終爲0。

3.按鍵功能部分

extern u32 KEY_MS;//外部變量  在滴答定時器中斷函數中 自++
u32 KEY_Time_Ms=0;//獲取長按時KEY_MS的值 用於之後比較
u8 KEY_Long=0;//長按鍵判斷
void KEY_Proc(void)
{
   u8 key_val;

   key_val=KEY_Scan();

   if(key_val!=KEY_Long)
   {
     KEY_Long=key_val;
	 KEY_Time_Ms=KEY_MS;
   }
   else 
     key_val=0;

  //短按判斷key_val的值
   if(key_val==2)
   {
     LED_Control(Ledall,0);
   }
   
   //長按判斷KEY_Long的值
   if(KEY_Long==1)
   {
     if(KEY_MS-KEY_Time_Ms>1000)//長按1s
	 {
	   LED_Control(Ledall,1);
	   LED_Flight_Water();
	 }
   }
}

注:此處長短按功能只是簡單舉個例子,其他複雜操作只需按上述形式添加即可。
4.在while循環中調用KEY_Proc()這個函數即可實時實現按鍵掃描和按鍵功能。

u32 TimingDelay = 0;
u32 KEY_MS=0;

void Delay_Ms(u32 nTime);
//Main Body
int main(void)
{
	SysTick_Config(SystemCoreClock/1000);
	LED_Init();
	KEY_Init();
	
	while(1)
	{
	  KEY_Proc();
	  }
}

5.最不要忘記了滴答定時器中斷函數裏邊添加KEY_MS++

extern u32 KEY_MS;
void SysTick_Handler(void)
{

	TimingDelay--;
	KEY_MS++;
}

總結:這種按鍵的配置方式是官方給出的例程的配置,其邏輯較強,新手不易理解,但其代碼較爲簡單,代碼風格好,值得學習。這裏給出官方代碼,帶有詳細註釋,大家可以自取後仔細理解食用。
鏈接:https://pan.baidu.com/s/1D7oqATSBbnotGIzAdagVcQ
提取碼:865g 。









發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章