STM32學習筆記(2)——按鍵輸入實驗


零、按鍵基本認識

1、防抖

按鍵機械觸點斷開、閉合的時候,由於觸點的彈性作用,按鍵開關不會馬上穩定接通或一下子斷開,而是會產生一些波紋信號,這些波紋信號會干擾高低電平的判斷。如下圖所示,在按鍵按下的前后均有信號抖動:

為了解決這個問題,有一些電路自帶消抖功能,利用電容充放電的延時,消除了干擾波紋,從而簡化了軟件的處理,軟件只需檢測引腳的高低電平即可。這叫硬件消抖。

然而,我們的實驗板卻沒有硬件消抖功能,因此,當用戶按下按鍵時,軟件需要延時一會兒(一般為10ms左右),待引腳的輸入電平穩定后再判斷高低電平。這叫軟件消抖。

大致的思路如下:

uint8_t KEY_Scan(void)
{
    if(KEY按下)
    {
        delay_ms(10);//延時10-20ms,防抖。
        if(KEY確實按下)
        {
            return KEY_Value;
        }
        return 無效值;
    }
}

返回的結果只有兩種:要么按鍵確實被按下了,要么按鍵確實沒被按下。

2、支持連續按

支持連續按的意思是,用戶一直按着按鍵,相應的外設就會一直工作,直到用戶松手,相應的外設才不工作。簡單來說就是用戶按一次不松手算很多次按下。其實,上面的程序就實現了這個功能,思路跟上面是一樣的。

我們重復寫一遍。大致的思路如下:

uint8_t KEY_Scan(void)
{
    if(KEY按下)
    {
        delay_ms(10);//延時,防抖。
        if(KEY確實按下)
        {
            return KEY_Value;
        }
        return 無效值;
    }
}

3、不支持連續按

不支持連續按的意思是,即使用戶一直按着按鍵,但相應的外設不會一直工作,響一下就不響了。簡單來說就是用戶按一次不松手只能算一次按下。

大致的思路如下:

uint8_t KEY_Scan(void)
{
    static uint8_t iskey = 0;  //記錄之前按鍵有無被按過
    //初始化iskey為0,表明按鍵之前沒被按過
    if(!iskey &&  KEY按下) //如果按鍵之前沒被按過且現在按鍵被按下了
    {
        delay_ms(10);//延時,防抖
        iskey = 1;  //標記按鍵已經被按下
        if(KEY確實按下)
        {
           return KEY_VALUE;
        }
    }else if(KEY沒有按下)
        iskey = 0; //如果按鍵沒被按下(即松開),則標記按鍵沒被按過
    return 沒有按下
}

4、STM32F103精英上按鍵的電路圖

電路圖如下如所示:

從電路圖我們可以看到,精英版為我們提供了兩種按鍵,WK_UP接VCC電源,而KEY0和KEY1接地。因此,WK_UP連接的端口為下拉輸入模式,KEY0和KEY1連接的端口為上拉輸入模式。

當單片機讀到的電平為低電平時,說明KEY0和KEY1被按下,而WK_UP沒有被按下;

當單片機讀到的電平為高電平時,說明KEY0和KEY1沒有被按下,而WK_UP被按下。

最后,WK_UP接的是PA0端口,KEY0接的是PE4端口,KEY1接的是PE3端口。

一、按鍵實驗初體驗

1.支持連續按

本程序實現功能:按下按鍵,相應外設會工作,若按鍵不松手則外設一直工作。換言之,外設工作與否取決於按鍵是否被按下。

程序如下:

/* =====key.h=====*/
#ifndef __KEY_H
#define __KEY_H
#include "sys.h"

/* 檢測按鍵電平情況的函數 */
#define KEY0 GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_4)
#define KEY1 GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_3)
#define KEY_UP GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)

/* 由上到下分別是:上拉輸入式按下、下拉輸入式按下 */
#define IPU_ON 0
#define IPD_ON 1

/* 由上到下均是不同按鍵對應的編號:全部未按下為0、key0為1、key1為2、wk_up為3 */
#define ALL_KEY_UNPRS 0
#define KEY0_PRS 1
#define KEY1_PRS 2
#define KEY_UP_PRS 3

void KEY_Init(void);
u8 KEY_Scan(void);

#endif


/* =====key.c===== */
#include "stm32f10x.h"
#include "key.h"
#include "delay.h"

void KEY_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOE, ENABLE);
	
	/* KEY0 & KEY1 -- Ground */
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4; /* PinE3 -- KEY1, PinE4 -- KEY0 */
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; /* 上拉輸入 */
	GPIO_Init(GPIOE, &GPIO_InitStructure);
	
	/* KEY_UP(WK_UP) -- VCC */
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; /* PinA0 -- KEY_UP */
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; /* 下拉輸入 */
	GPIO_Init(GPIOA, &GPIO_InitStructure);
}

u8 KEY_Scan(void)
{
	if(KEY0 == IPU_ON || KEY1 == IPU_ON || KEY_UP == IPD_ON)
	{
		delay_ms(10);
		if(KEY0 == IPU_ON)
			return KEY0_PRS;
		else if(KEY1 == IPU_ON)
			return KEY1_PRS;
		else if(KEY_UP == IPD_ON)
			return KEY_UP_PRS;
	}
	return ALL_KEY_UNPRS; 
}


/* =====main.c===== */
#include "stm32f10x.h"
#include "sys.h"
#include "beep.h"
#include "key.h"
#include "led.h"
#include "delay.h"

int main()
{
	u8 key = 0;
	delay_init();
	KEY_Init();
	LED_Init();
	Beep_Init();
	while(1)
	{
		key = KEY_Scan();
		switch(key)
		{
			case KEY0_PRS:
				LED0_ON;
				while(KEY_Scan() != ALL_KEY_UNPRS); /* 松手檢測,這里的作用是:
				/* 如果用戶不松手,程序將會卡在這個地方,相應的外設也會持續工作 */
				break;
			case KEY1_PRS:
				LED1_ON;
				while(KEY_Scan() != ALL_KEY_UNPRS);
				break;
			case KEY_UP_PRS:
				BEEP_ON;
				while(KEY_Scan() != ALL_KEY_UNPRS);
				break;
			default:
				LED0_OFF;
				LED1_OFF;
				BEEP_OFF;
				break;
		}
	}
}

2.不支持連續按

本程序實現功能:按下按鍵,相應外設會工作,且經過300ms后不工作。這就是說若按鍵不松手,外設不會一直工作。

將以上部分程序修改如下:

/* =====key.c===== */
/* key_Scan函數修改如下,其他部分不變 */
u8 KEY_Scan(void)
{
	static int isPrsBefore = 0;
	if((isPrsBefore == 0) && (KEY0 == IPU_ON || KEY1 == IPU_ON || KEY_UP == IPD_ON))
	{
		delay_ms(10);
		isPrsBefore = 1;
		if(KEY0 == IPU_ON)
			return KEY0_PRS;
		else if(KEY1 == IPU_ON)
			return KEY1_PRS;
		else if(KEY_UP == IPD_ON)
			return KEY_UP_PRS;
	}else if (KEY0 == !IPU_ON && KEY1 == !IPU_ON && KEY_UP == !IPD_ON)
		isPrsBefore = 0;
	return ALL_KEY_UNPRS; 
}


/* =====main.c===== */
#include "stm32f10x.h"
#include "sys.h"
#include "beep.h"
#include "key.h"
#include "led.h"
#include "delay.h"

int main()
{
	u8 key = 0;
	delay_init();
	KEY_Init();
	LED_Init();
	Beep_Init();
	while(1)
	{
		key = KEY_Scan();
		switch(key)
		{
			case KEY0_PRS:
				LED0_ON;
				delay_ms(300);
				break;
			case KEY1_PRS:
				LED1_ON;
				delay_ms(300);
				break;
			case KEY_UP_PRS:
				BEEP_ON;
				delay_ms(300);
				break;
			default:
				LED0_OFF;
				LED1_OFF;
				BEEP_OFF;
				break;
		}
	}
}

二、綜合實驗

以下我們實現一個功能:按下按鍵,相應外設工作狀態將反轉。按鍵不松手,工作狀態不會改變(即不支持連續按)。

為實現工作狀態反轉功能,本程序使用位帶操作讀取IO口輸入電平和輸出電平。這種辦法實現了與51類似的IO口控制功能,比如51里是這樣用的:sbit LED0 = P2^6;
在STM32里是這樣用的:#define LED0 PEin(5),意思是讀取GPIOE.5口的輸入電平;#define LED0 PEout(5),意思是讀取GPIOE.5口的輸出電平。

完整代碼如下(頭文件sys.h為正點原子資料盤自帶文件):

/* =====led.h===== */
#ifndef __LED_H
#define __LED_H

#define LED0_OFF GPIO_SetBits(GPIOB, GPIO_Pin_5)
#define LED0_ON GPIO_ResetBits(GPIOB, GPIO_Pin_5)
#define LED1_OFF GPIO_SetBits(GPIOE, GPIO_Pin_5)
#define LED1_ON GPIO_ResetBits(GPIOE, GPIO_Pin_5)

#define LED0 PBout(5) //位帶操作
#define LED1 PEout(5)

void LED_Init(void);

#endif


/* =====led.c===== */
#include "stm32f10x.h"
#include "led.h"

void LED_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOE, ENABLE);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; /* 推挽輸出 */
	
	/* Definition: void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct) */
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	GPIO_Init(GPIOE, &GPIO_InitStructure);
	
	LED0_OFF;
	LED1_OFF;
}


/* =====beep.h===== */
#ifndef __BEEP_H
#define __BEEP_H

#define BEEP_ON GPIO_SetBits(GPIOB, GPIO_Pin_8)
#define BEEP_OFF GPIO_ResetBits(GPIOB, GPIO_Pin_8)

#define BEEP PBout(8)

void Beep_Init(void);

#endif


/* =====beep.c===== */
#include "stm32f10x.h"
#include "beep.h"

void Beep_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	BEEP_OFF;
}


/* =====key.h===== */
#ifndef __KEY_H
#define __KEY_H
#include "sys.h"

/* Definition: uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) */
#define KEY0 GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_4)
#define KEY1 GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_3)
#define KEY_UP GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)

#define IPU_ON 0
#define IPD_ON 1

#define ALL_KEY_UNPRS 0
#define KEY0_PRS 1
#define KEY1_PRS 2
#define KEY_UP_PRS 3

void KEY_Init(void);
u8 KEY_Scan(void);

#endif


/* =====key.c===== */
#include "stm32f10x.h"
#include "key.h"
#include "delay.h"

void KEY_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOE, ENABLE);
	
	/* KEY0 & KEY1 -- Ground */
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4; /* PinE3 -- KEY1, PinE4 -- KEY0 */
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; /* 上拉輸入 */
	GPIO_Init(GPIOE, &GPIO_InitStructure);
	
	/* KEY_UP(WK_UP) -- VCC */
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; /* PinA0 -- KEY_UP */
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; /* 下拉輸入 */
	GPIO_Init(GPIOA, &GPIO_InitStructure);
}

u8 KEY_Scan(void)
{
	static int isPrsBefore = 0;
	if((isPrsBefore == 0) && (KEY0 == IPU_ON || KEY1 == IPU_ON || KEY_UP == IPD_ON))
	{
		delay_ms(10);
		isPrsBefore = 1;
		if(KEY0 == IPU_ON)
			return KEY0_PRS;
		else if(KEY1 == IPU_ON)
			return KEY1_PRS;
		else if(KEY_UP == IPD_ON)
			return KEY_UP_PRS;
	}else if (KEY0 == !IPU_ON && KEY1 == !IPU_ON && KEY_UP == !IPD_ON)
		isPrsBefore = 0;
	return ALL_KEY_UNPRS; 
}


/* =====main.c===== */
#include "stm32f10x.h"
#include "sys.h"
#include "beep.h"
#include "key.h"
#include "led.h"
#include "delay.h"

int main()
{
	u8 key = 0;
	delay_init();
	KEY_Init();
	LED_Init();
	Beep_Init();
	while(1)
	{
		key = KEY_Scan();
		switch(key)
		{
			case KEY0_PRS:
				LED0 = !LED0; //工作狀態實現反轉
				break;
			case KEY1_PRS:
				LED1 = !LED1;
				break;
			case KEY_UP_PRS:
				BEEP = !BEEP;
				break;
			default:
				break;
		}
	}
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM