1. 程式人生 > >STM32 嵌入式學習入門(3)——STM32F103 按鍵輸入控制LED燈

STM32 嵌入式學習入門(3)——STM32F103 按鍵輸入控制LED燈

STM32 嵌入式學習入門(3)—— STM32F103 按鍵輸入控制LED燈
按鍵是微控制器上一個很重要的輸入裝置,也很容易掌握,只要明白了IO口最基本的使用,就可以操作按鍵了。 我們的目的是控制開發板上板載的三個按鍵來操作開發板上板載的兩個LED燈實現亮或滅(按鍵第一次按下時燈亮,再按下時燈滅,以此類推)。 博主所用的開發板是正點原子的mini板(STM32F103RCT6)和戰艦板(STM32F103ZET6),因此下面的內容的例子以這兩款開發板為例,但是相關的原理對任何開發板來說都是一樣的,只要自己的開發板上板載了按鍵和LED燈(這兩個資源應該是所有開發板上都有的資源吧),然後檢視自己開發板的資料手冊和硬體電路圖、原理圖,找到各個按鍵對應的IO口就好了。

在開發板上,板載硬體資源(比如我們這裡用到的按鍵和LED燈)都是和確定的GPIO相連的,要使用這些硬體資源,實際上可以理解成對這些IO口的操作。

下面先大致說一下整個流程。 1.首先要使用這些硬體資源,就要對其進行初始化。初始化包括使能IO口時鐘和初始化IO口引數兩個部分,這兩點在上一篇博文中有提到了。 2.初始化完成後就可以操作IO口了,就是寫相關的邏輯程式碼。這裡是按鍵控制LED燈,所以我們首先要掃描與按鍵相連的IO口的電平。 如果IO電平發生變化,那麼說明按鍵被按下了,我們就要讓燈的狀態發生反轉。如果IO電平沒有發生變化,那麼說明按鍵沒有被按下,我們可以過一個很小的時間間隔再去掃描與按鍵相連的IO口看是否發生變化,這樣形成了一個死迴圈。

下面上一段程式碼,是正點原子Mini STM32F103RCT6開發板(mini板)按鍵實驗的主函式部分:
#define LED0 PAout(8)	// PA8
#define LED1 PDout(2)	// PD2

#define KEY0_PRES	1	//KEY0  
#define KEY1_PRES	2	//KEY1 
#define WKUP_PRES	3	//WK_UP
 
int main(void)
{	
	u8 t=0;	  
 	
	delay_init();	    	//延時函式初始化	  
	LED_Init();		//初始化與LED連線的硬體介面
	KEY_Init();          	//初始化與按鍵連線的硬體介面
	
	LED0=0;					//點亮LED0
	
	while(1){
		t=KEY_Scan(0);		//得到鍵值	KEY_Scan(0);即不支援連續有效。如果需要支援連續按,這裡引數設定為1.	
		switch(t){
			case KEY0_PRES:		
LED0=!LED0; break; case KEY1_PRES: LED1=!LED1; break; case WKUP_PRES: LED0=!LED0; LED1=!LED1; break; default: delay_ms(10); } }//while(1) }

程式碼比較長,但整體邏輯還是很容易理解的,首先解釋一下前兩行的巨集定義,這裡是 通過位操作,實現了對PA8/PD2的電平輸出的操作的,這一行程式碼其實挺複雜的 ,準確說是博主也講不出原理 (如果你去檢視stm32的官方庫函式,會發現這裡的PAout(n)是通過很多層巨集定義得到的),但是我會呼叫。總之,這樣定義了以後,下面的程式碼你對LED0賦1對應著設定IO口為高電平,賦0就是設定IO口為低電平。 下面的三個巨集定義是為下面switch-case服務的,增強了程式碼的可讀性。 接下來就是主函數了,首先是呼叫三個初始化函式,這個等下後面具體說。初始化完成後,然後LED0=0;這一句,點亮LED0。接著就是while(1)迴圈了,死迴圈裡面先呼叫KEY_Scan(0);函式對按鍵進行檢測,該函式的返回值有四種情況,即KEY0_PRES、KEY1_PRESWKUP_PRES、0。前三個返回值表明相應的按鍵被按下了,返回0表明當前沒有按鍵按下。然後是switch-case結構了,如果有按鍵按下,執行相應分支,對LED上的電平進行反轉,實現按一次亮,再按一次滅,以此類推,這樣的效果。如果沒有按鍵按下,那麼延時10ms,然後繼續檢測有沒有按鍵按下。整體程式的執行過程就是這樣。
下面的問題就是三個初始化函式和KEY_Scan(0);函式是怎麼實現的了。 1.延時函式 這裡用到了delay_init();和delay_ms(10);兩個函式。延時函式要想完全搞清楚其中的原理需要了解STM32核心中定時器的知識,不是三兩句可以解釋清楚的,以後有時間再寫一篇詳細介紹一下延時函式,寫完後貼到這裡來。對於剛剛接觸STM32的同學來說,我建議剛開始就會呼叫這個函式就好了,不要深究其實現原理,因為涉及其它內容比較多,零基礎剛開始接觸STM32,去看那些,如果看不懂挺打擊人積極性的。這兩個函式 的呼叫方法: delay_ms(10);呼叫時候傳遞一個整形引數n,表示要延時n毫秒,如果程式中用到了delay_ms(n);函式,那麼在呼叫延時函式前要呼叫延時初始化函式 delay_init();就是這樣無參呼叫就好了。另外,delay_ms(n);函式呼叫時候n的值也不能過大,n的值是有一個上限的,這就像int所能表示的最大值也是有上限的一樣。具體的這個值是多少,是和時鐘頻率有關的,對時鐘頻率為72M的條件下(這算是個預設條件了),n<=1864。  上面說的兩個延時函式都不是自己寫的,正點原子的每個實驗程式碼的SYSTEM資料夾的delay.c檔案中都有這個程式碼,所以博主就拿來主義了,很方便。 如果是別的開發板,可以查查手冊看怎樣能實現延時的功能,實在不行,下面的程式碼也能實現延時……
void Delay(u32 count)
{
  u32 i=0;
  for(;i<count;i++);

}


2.LED_Init();和KEY_Init();函式 這兩個函式是自己寫的,就是對IO口的相關引數進行配置,先上程式碼:
void LED_Init(void)
{
 
	GPIO_InitTypeDef  GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOD, ENABLE);	 //使能PA,PD埠時鐘
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;				 //LED0-->PA.8 埠配置
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推輓輸出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度為50MHz
	GPIO_Init(GPIOA, &GPIO_InitStructure);					 //根據設定引數初始化GPIOA.8
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;	    		 //LED1-->PD.2 埠配置, 推輓輸出
	GPIO_Init(GPIOD, &GPIO_InitStructure);	  				 //推輓輸出 ,IO口速度為50MHz
}

void KEY_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC,ENABLE);//使能PORTA,PORTC時鐘
	
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_15;		//PA15
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; 		//設定成上拉輸入
 	GPIO_Init(GPIOA, &GPIO_InitStructure);			//初始化GPIOA15
	
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_5;		//PC5
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; 		//設定成上拉輸入
 	GPIO_Init(GPIOC, &GPIO_InitStructure);			//初始化GPIOC5
 
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0;//PA0
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0設定成輸入,預設下拉	  
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.0	
}

關於GPIO口的初始化,可以參考博主的上一篇文章, STM32 嵌入式學習入門(2)——STM32的GPIO介紹 至於這裡配置為什麼是這些引數,是根據開發板硬體連線確定的,上面的程式碼是正點原子MiniSTM32F103RCT6開發板的程式碼,這款開發板的原理圖如下圖:




從這裡可以看出來按鍵對應的IO口以及它們是與高電平相連還是低電平相連。比如兩個LED,硬體上,它們都是與高電平相連的,所以你把另一端設定為低電平的時候它們就被點亮,如果另一端設定為高電平,那麼它們就滅。另外IO口的模式也是從硬體連線上確定的。

3. KEY_Scan(0);函式的實現 這個函式的實現其實不難,和STM32的知識沒多大關係,就是C語言的邏輯問題,程式碼如下:
//按鍵處理函式
//返回按鍵值
//mode:0,不支援連續按;1,支援連續按;
//返回值:
//0,沒有任何按鍵按下
//KEY0_PRES,KEY0按下
//KEY1_PRES,KEY1按下
//WKUP_PRES,WK_UP按下 
//注意此函式有響應優先順序,KEY0>KEY1>WK_UP!!
u8 KEY_Scan(u8 mode)
{	 
	static u8 key_up=1;//按鍵按鬆開標誌
	if(mode)key_up=1;  //支援連按		  
	if(key_up&&(KEY0==0||KEY1==0||WK_UP==1))
	{
		delay_ms(10);//去抖動 
		key_up=0;
		if(KEY0==0)
			return KEY0_PRES;
		else 
			if(KEY1==0)
				return KEY1_PRES;
		else
			if(WK_UP==1)
				return WKUP_PRES;
	}else 
	if(KEY0==1&&KEY1==1&&WK_UP==0)
		key_up=1;
	return 0;// 無按鍵按下
}