G
N
I
D
A
O
L

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;
		}
	}
}
posted @ 2021-04-17 21:37  漫舞八月(Mount256)  阅读(1180)  评论(0编辑  收藏  举报