以检测按键输入为例:
如图一共有三个按键,WK-UP按键被按下会被拉到高电平,其它两个被按下会被拉到低电平。下面例子中如果key0被按下那就让led0状态反转,如果是key1被按下那就让led1状态反转,如果是wk-up被按下那就让蜂鸣器状态反转。
首先要说明的是按键有两种模式,一种就像是快进键,持续被按下就持续快进,就像是可重复触发的单稳态触发器,另外一种就是像关机一样的键,就算被持续按下也只是被判定为一次触发,就像是不可重复触发的单稳态触发器。在程序中用key_mode这个参数配置模式。
读取IO输入的库函数是:
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
在程序中直接使用了宏定义绕过库函数的使用达到操作寄存器的目的
1.key.h
#ifndef __key_h_
#define __key_h_
#include "stm32f10x.h"
#include "sys.h"
#include "delay.h"
#define wk_up PAin(0) //位操作参数宏,用多级宏映射寄存器
#define key0 PEin(4)
#define key1 PEin(3)
#define beep PBout(8)
#define led0 PBout(5)
#define led1 PEout(5)
u8 key_scan(u8 key_mode);
void key_init(void);
#endif
2.key.c
#include "key.h"
void key_init()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOE,ENABLE);//使能PORTA,PORTE时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_3;//KEY0-KEY1
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入
GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化GPIOE4,3
//初始化 WK_UP-->GPIOA.0 下拉输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0设置成输入,默认下拉
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.0
}
u8 key_scan(u8 key_mode)
{
static u8 key_up = 1;//按键没有被按下
if(key_mode) key_up = 1;//key_mode为1时持续按下被判定为多次按下,key_mode为0时持续按下被判定为一次按下
if(key_up&&((wk_up==1)||(key0==0)||(key1==0)))
{
delay_ms(10);//延时,防止按键抖动带来误判
key_up = 0;
if(wk_up==1)
{
return 1;//返回1代表wk_up被按下
}
else if(key0==0)
{
return 2;//key0被按下返回2
}
else if(key1==0)
{
return 3;//key1 被按下返回3
}
}
else if(key1==1&&key0==1&&wk_up==0)key_up=1;//按键没被判定按下
return 0;
}
在这里有必要解释下key_scan这个函数:
(1.)
static u8 key_up = 1;
if(key_mode) key_up = 1;
这两行代码可能会让人感到疑惑,会有人认为key_up重复被赋值为1,如果是在第一次调用这个函数,并且key_mode为1时确实是重复赋值,然而可能重复被赋值的次数只有这一次,因为static u8 key_up = 1;仅仅在第一次调用这个函数时执行。
如果按键处于持续按下被判定为多次按下模式时,当按键被按下key_up = 0;这段代码一定会执行,但是当下次再调用key_scan函数时key_up又被赋值为1,这样就能多次判定被按下。
如果是持续按下被判定为一次按下模式,当持续按下按键时,第一次监测到按键被按下后key_up 被赋值为0,下次再检测时虽然按键被按下,但是无法进入if(key_up&&((wk_up==1)||(key0==0)||(key1==0)))
语句块,也就不会被判定为按下。
(2.)
else if(key1==1&&key0==1&&wk_up==0)key_up=1;
//该语句不能放入if(key_up&&((wk_up==1)||(key0==0)||(key1==0)))语句块之内,
//虽然该语句放在if(key_up&&((wk_up==1)||(key0==0)||(key1==0)))看似合理,
//但却会造成问题:当有按键被判定为按下后key_up被赋值为0,在下次检测
//时虽然按键已经被弹起了,但是却无法进入
//if(key_up&&((wk_up==1)||(key0==0)||(key1==0)))语句块,这样一来
//key_up就永远为0,之后任何按键按下去都不会被判定为按下。
3.led.h
void led_init(void);
4.led.c
#include "stm32f10x.h"
#include "led.h"
#include "sys.h"
void led_init(void)
{
GPIO_InitTypeDef gpio_init_structure;
//1.时钟使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
//2.初始化
gpio_init_structure.GPIO_Mode=GPIO_Mode_Out_PP;
gpio_init_structure.GPIO_Pin=GPIO_Pin_5;
gpio_init_structure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&gpio_init_structure);
PBout(5)=1;//3.设置初始电平高,不亮
gpio_init_structure.GPIO_Mode=GPIO_Mode_Out_PP;
gpio_init_structure.GPIO_Pin=GPIO_Pin_5;
gpio_init_structure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOE,&gpio_init_structure);
PEout(5)=1;//3.设置初始电平高,不亮
}
5.beep.h
void beep_init(void);
6.beep.c
#include<beep.h>
#include<stm32f10x.h>
void beep_init()
{
GPIO_InitTypeDef GPIOSetting_structure; //设置IO口模式,速度等信息
GPIOSetting_structure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIOSetting_structure.GPIO_Pin=GPIO_Pin_8;
GPIOSetting_structure.GPIO_Speed=GPIO_Speed_50MHz;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//使相应的时钟
GPIO_Init(GPIOB,&GPIOSetting_structure);//初始化相应的IO
GPIO_ResetBits(GPIOB,GPIO_Pin_8);//默认推挽输出低
}
7.main.c
![](/qrcode.jpg)
#include "key.h"
#include "led.h"
#include "beep.h"
int main(void)
{
u8 key = 0;//在mdk5里面,变量的声明要放在任何可执行语句之前,否则会报错
delay_init();
beep_init();
led_init();
key_init();
while(1){
key = key_scan(0);//传入1表示按键持续按下会被多次判定有效,传入0时即使按键持续被按下也只判定为被按下一次
if(key)
{
switch(key)
{
case 1: beep = !beep;break;
case 2: PBout(5) = !PBout(5);break;
case 3: PEout(5) = !PEout(5);break;
}
}
delay_ms(10);//10ms扫描一次按键
}
}