配合正点原子视频(工程新建与路径加入省略)
按键输入硬件连接:
【KEY0 和 KEY1 是低电平有效的,而 KEY_UP 是高电平有效的】
1)读取IO口输入电平调用库函数为:
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
2)读取IO口输入电平操作寄存器为: GPIOx_IDR:端口输入寄存器
3)使用位带操作读取IO口输入电平:
PEin(4) -读取GPIOE.4口电平
PEin(n) -读取GPIOE.n口电平
外部都没有上下拉电阻,所以,需要在 STM32F1 内部设置上下拉。
C语言关键字 :static
int getValue(void)
{
int flag=0;
for(int i=0;i<=3;i++)
{
flag++;
}
return flag;
}
//函数执行两次后 值为3
int getValue(void)
{
static int flag=0;
for(int i=0;i<=3;i++)
{
flag++;
}
return flag;
}
//函数执行两次后 值为6 (第二次执行函数时,flag值为3继续执行++)
实验思想
按键扫描(支持连续按)的一般思路
【如果我要实现:按键按下,没有松开,只能算按下一次,这个函数无法实现。】
u8 KEY_Scan(void)
{
if(KEY按下)
{
delay_ms(10);//延时10-20ms,防抖。
if(KEY确实按下)
{
return KEY_Value;
}
return 无效值;
}
}
按键扫描(不支持连续按)的一般思路
【不支持连续按:就是说,按键按下了,没有松开,只能算一次。】
u8 KEY_Scan(void)
{
static u8 key_up=1;
if(key_up && KEY按下)
{
delay_ms(10);//延时,防抖
key_up=0;//标记这次key已经按下
if(KEY确实按下)
{
return KEY_VALUE;
}
}else if(KEY没有按下) key_up=1;
return 没有按下
}
按键扫描(两种模式合二为一)的一般思路
u8 KEY_Scan(u8 mode)
{
static u8 key_up=1;
if(mode==1) key_up=1;//支持连续按
if(key_up && KEY按下)
{
delay_ms(10);//延时,防抖
key_up=0;//标记这次key已经按下
if(KEY确实按下)
{
return KEY_VALUE;
}
}else if(KEY没有按下) key_up=1;
return 没有按下
}
一.库函数版本
1.按键输入实验步骤
在实验一跑马灯的基础上实现按键输入(直观看出是否有按下操作,按下灯亮)
1)使能IO口时钟。调用函数 RCC_APB2PeriphClockCmd();
2)初始化IO模式:上拉/下拉输入。 调用函数GPIO_Init();
3)扫描IO口电平。
key.h
#if ndef __KEY_H
#define __KEY_H
#include "sys.h"
#define KEY0 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4) //读取按键 0
#define KEY1 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3) // 读取按键 1
#define WK_UP GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0) // 读取按键 WK_UP
#define KEY0_PRES 1 //KEY0 按下
#define KEY1_PRES 2 //KEY1 按下
#define WKUP_PRES 3 //WK_UP 按下
void KEY_Init(void); //IO 初始化
u8 KEY_Scan(u8); 按键扫描函数
#endif
KEY0------PE4 (GPIO_Pin_4)
因此 #define KEY0 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4) //读取按键 0
key.c
#include "key.
#include "sys.h"
#include "delay.h"
//按键初始化函数
void KEY_Init(void) //IO 初始化
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOE,ENABLE);
//使能PORTA,PORTE 时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4;// GPIO.E 3 ~4
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入
GPIO_Init(GPIOE, &GPIO_InitStructure); //初始化 GPIOE3,4
GPIO_InitStr ucture.GPIO_Pin = GPIO_Pin_0; //初始化 WK_UP ---->GPIOA.0
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0 设置成输入,下拉
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA.0
}
//按键处理函数
//返回按键值
//mode: 0不支持连续按 ; 1支持连续按
//0 ,没有任何按键按下
//1 KEY0 按下
//2 KEY1 按下
//3 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;// 无按键按下
}
KEY_Init() :是用来初始化按键输入的 IO 口的。
KEY_Scan() : 是用来扫描这 3 个 IO 口是否有按键按下。
当mode 为 0 的时候, KEY_Scan 函数将不支持连续按, 扫描某个按键,该按键按下之后
必须要松开,才能第二次触发,否则不会再响应这个按键,这样的好处就是可以防止按一次多
次触发,而坏处就是在需要长按的时候比较不合适。
当mode 为 1 的时候, KEY_Scan 函数将支持连续按,如 果某个按键一直按下,则会一直
返回这个按键的键值,这样可以方便的实现长按检测。
main.c
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "beep.h"
//按键输入实验
int main(void)
{
u8 key;
delay_init(); 延时函数初始化
LED_Init(); //LED 端口初始化
KEY_Init(); 初始化与按键连接的硬件接口
BEEP_Init(); 初始化蜂鸣器端口
LED0=0; 先点亮红灯
while(1)
{
key =KEY_Scan(0) // 得到键值
if(key)
{
switch(key)
{
case WKUP_PRES: // 控制蜂鸣器
BEEP=!BEEP;
break;
case KEY1_PRES: // 控制 LED1 翻转
LED1=!LED1;
break;
case KEY0_PRES: // 同时控制 LED0,LED1 翻转
LED0=!LED0;
LED1=!LED1;
break;
}
}
else
delay_ms(10);
}
}
二.寄存器版本
key.h
#ifndef __KEY_H
#define __KEY_H
#include "sys.h"
#define KEY0 PEin(4) //PE4
#define KEY1 PEin(3) //PE3
#define WK_UP PAin(0) //PA0
#define KEY0_PRES 1 //KEY0按下
#define KEY1_PRES 2 //KEY1按下
#define WKUP_PRES 3 //WKUP按下
void KEY_Init(void); //IO初始化
u8 KEY_Scan(u8); //按键扫描函数
#endif
宏定义段也有另一种写法,上述写法简单明了,但是下面的写法更易移植
#define KEY0 (1<<4) //KEY0 PE4
#define KEY1 (1<<3) //KEY1 PE3
#define WK_UP (1<<0) //KEY_UP PA0
#define KEY0_GET() ((GPIOE-->IDR&(KEY0))?1 0) //读取按键 KEY0
KEY0 #define KEY1_GET() ((GPIOE-->IDR&(KEY1))?1 0) //读取按键 KEY1
KEY1 #define WK_UP _GET() ((GPIOA-->IDR&( WK_UP))?1 0) //读取按键 WK_UP
key.c
#include "key.h"
#include "delay.h"
//按键初始化函数
void KEY_Init(void)
{
RCC->APB2ENR|=1<<2; //使能PORTA时钟
RCC->APB2ENR|=1<<6; //使能PORTE时钟
GPIOA->CRL&=0xFFFFFFF0; //PA0设置成输入,默认下拉
GPIOA->CRL|=0x00000008;
GPIOE->CRL&=0xFFF00FFF; //PE3/4设置成输入
GPIOE->CRL|=0x00088000;
GPIOE->ODR|=3<<3; //PE3/4 上拉
}
//按键处理函数
//返回按键值
//mode:0,不支持连续按 ;1,支持连续按 ;
//0,没有任何按键按下
//1 KEY0按下
//2 KEY1按下
//3 KEY_UP按下 即 WK_UP //注意此函数有响应优先级 ,KEY0>KEY1>KEY_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 1;
else if(KEY1==0)
return 2;
else if(WK_UP==1)
return 3;
}
else if(KEY0==1&&KEY1==1&&WK_UP==0)
key_up=1;
return 0; //无按键按下
}
main.c
#include "sys.h"
#include "led.h"
#include "beep.h"
#include "delay.h"
#include "key.h"
int main (void)
{
u8 key;
Stm32_Clock_Init(9); //系统时钟设置
delay_init(72); //延时初始化
LED_Init();
BEEP_Init();
KEY_Init();
LED0=0; //先点亮红灯
while(1)
{
key=KEY_Scan(0); //得到键值
if(key)
{
switch(key)
{
case WKUP_PRES:
//BEEP=!BEEP;
break;
case KEY1_PRES:
LED1=!LED1;
break;
case KEY0_PRES:
LED1=!LED1;
LED0=!LED0;
break;
}
}
else
delay_ms(10);
}
}