硬件介绍
当按键置空时,IO接地
按键按下之后,IO口接通3.3V高电压,电流比较大,为了避免损坏IO,这里需要加装一个限流电阻。可以看到IO口是默认低电平,按键按下后产生一个上升沿,和平常的电路设计不太一样。这是因为PA0还具有一种自动唤醒的功能叫Weakup,Weakup必须要是上升沿才能唤醒的。
电容C6以及C14是用来做硬件消抖的。其原理就是,机械按键在按下的过程大约有20ms,在这个过程中,IO与3.3V电源不断地接通,相当于形成了一个交流电,这时有电容的话,这个过程就相当于是在给这个电容不断地充放电,当按键稳定之后,电容里的电荷又会通过接地线释放。
原理介绍
我们的STM32单片机,GPIO输出可以是ODR,BSRR,BRR寄存器来控制,其输入则是通过ODR寄存器来控制实现。
ODR寄存器也是低16位有效,如果对应的位读出来是0,表示检测到的是低电平,如果是1则是检测到高电平3.3V,即按键被按下。
这里的按键检测主要采用查询的方式,后续大家会学习到中断的按键检测。
实际代码操作
bsp_key.h
本节的工程代码,直接在之前固件库点亮LED的基础上继续加工。
首先我们在原有工程的基础上,在USER文件夹中新建一个key文件夹,用来放我们的驱动函数以及头文件bsp_key.c和bsp_key.h。
打开工程,将上面的C文件添加到我们的工程,同时在仙女棒里指定一下头文件的路径。然后再bsp_key.c中将我们的.h文件包含进来。
在驱动函数中,我们首先对GPIO进行一个初始化,这个过程跟之前初始LED的GPIO是类似的,需要修改的地方就是Pin,Mode以及RCC。
/***********************bsp_key.h******************/
//1.避免重复调用头文件报错
#ifndef _KEY_H
#define _KEY_H
//2.调用总头文件
#include "stm32f10x.h"
//3.对硬件部分的宏定义
#define KEY1_GPIO_PIN GPIO_Pin_0
#define KEY1_GPIO_PART GPIOA
#define KEY1_GPIO_CLK RCC_APB2Periph_GPIOA
#define KEY2_GPIO_PIN GPIO_Pin_13
#define KEY2_GPIO_PART GPIOC
#define KEY2_GPIO_CLK RCC_APB2Periph_GPIOC
//4.串口初始化函数以及串口状态扫描函数
void KEY_GPIO_Config(void);
uint8_t KEY_Scan(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin);
//5.按键按下标志宏
/** 若按键按下为高电平,设置KEY_ON = 1,KEY_OFF = 0
* 若按键按下为低电平,设置KEY_ON = 0,KEY_OFF = 1
*/
#define KEY_ON 1
#define KEY_OFF 0
#endif /*_KEY_H*/
驱动函数bsp_key.c
1.初始化操作
这部分和之前点亮LED灯是一样的。
/*********************bsp_key.c**********************/
//按键端口初始化函数
void KEY_GPIO_Config()
{
//首先定义一个结构体类型
GPIO_InitTypeDef GPIO_InitStruct;
//时钟使能
RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK|KEY2_GPIO_CLK, ENABLE);
//选择按键的引脚进行配置
GPIO_InitStruct.GPIO_Pin = KEY1_GPIO_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(KEY1_GPIO_PART, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = KEY2_GPIO_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(KEY2_GPIO_PART, &GPIO_InitStruct);
}
2.按键检测
我们写一个函数void Key_Scan(),根据我们的需求可以知道,他可以传入两个形参,端口以及具体的引脚号,通过KEY_Scan()就可以扫描按键的状态。
GPIO引脚的输入电平可通过读取IDR寄存器对应的数据位来感知,而STM32标准库中就有相关函数,我们可以在stm32f10x_gpio.h的末尾所有函数的声明中找到我们需要的这个函数GPIO_ReadInputDataBit(),然后右键点击查看他的具体函数结构。该函数输入GPIO端口及引脚号,返回该引脚的电平状态,高电平返回1,低电平返回0 。
那么这里就可以设计我们的KEY_Scan()了:
a、首先我们已经明确了函数的功能是检测是否有按键按下,那么这就是一个具有返回值的函数,对于具有返回值的函数,我们就不用void来声明了,相对应的我们根据其返回值类型对其进行声明,同时我们也知道他的两个输入参数是端口和引脚。
这一部分的C语言知识,大家可以看这个教程连接:C函数教程
uint8_t KEY_Scan(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin)
{
}
b、然后就是函数的具体内容,首先我们为了检测按键是否被按下,我们要将GPIO_ReadInputDataBit()函数读取到的结果与我们定义好的宏KEY_ON进行比较,那么如果检测到了按键被按下,我们就直接返回KEY_ON是否可以呢?答案当然是不行的,因为我们在一次按键操作的过程中,按键被按下是程序会一直扫描他的状态,每一次扫描程序都会认为我们完成了一次按键操作,这就相当于我们其实只按了一次,但是单片机认为我们按了无数次。
为了避免这个问题,我们使用while循环持续监测案件状态,直到按键被释放,我们才返回一个KEY_ON的值,如果没有按键按下,那么就直接返回KEY_OFF。
/**
*@brief 检测是否有按键按下
*@param GPIOx:具体的端口,x可以是(A_G)
*@param GPIO_PIN:具体的端口位,可以是GPIO_Pin_x(0-15)
*@retval 按键的状态
* @arg KEY_ON:按键按下
* @arg KEY_OFF:按键没按下
*/
uint8_t KEY_Scan(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin)
{
if(GPIO_ReadInputDataBit(GPIOx, GPIO_Pin) == KEY_ON)
{
while(GPIO_ReadInputDataBit(GPIOx, GPIO_Pin) == KEY_ON);
return KEY_ON;
}else
return KEY_OFF;
}
c、消除抖动:因为之前的讲解中已经说过,本电路因为有硬件消抖,所以不需要做消抖处理,如果硬件没有设计这部分的内容,那么就需要我们自己在KEY_Scan()这个函数中做软件滤波,防止波纹抖动引起的误触发。
到这里bsp_key.c文件中的内容也就完成了。下一步就是mian函数了。
int mian()
{
/*端口初始化*/
LED_GPIO_Config();
KEY_GPIO_Config();
/*查询按键状态,若按键按下,开启或关闭LED*/
while(1)
{
if(KEY_Scan(KEY1_GPIO_PART,KEY1_GPIO_PIN)==KEY_ON){
GPIO_SetBits(LED_G_GPIO_PART,LED_G_GPIO_PIN);
}
if(KEY_Scan(KEY2_GPIO_PART,KEY2_GPIO_PIN)==KEY_ON){
GPIO_ResetBits(LED_G_GPIO_PART,LED_G_GPIO_PIN);
}
}
}