第15章.GPIO输入-按键检测

目录

0. 《STM32单片机自学教程》专栏

15.1.硬件设计

15.1.1 硬件设计示意图

15.1.2 机械按键问题

15.2 软件设计 

15.2.1 工程文件创建

15.2.2 LED相关程序设计

15.2.3 按键检测程序设计

15.2.4 主函数设计

15.3 触类旁通


0. 《STM32单片机自学教程》专栏

        本文作为专栏《STM32单片机自学教程》专栏其中的一部分,返回专栏总纲,阅读所有文章,点击Link:  

STM32单片机自学教程-[目录总纲]_stm32 学习-CSDN博客     

        本节我们学习一下GPIO的输入检测,按键检测是最常用最简单的输入检测场景。掌握了按键输入检测之后,其他的不同设备外设检测都是大同小异,触类旁通,我们更重要的是要学习这些工程开发思想和技巧。

15.1.硬件设计

15.1.1 硬件设计示意图

图15.1-1 硬件连接示意图 

        我们的硬件连接非常简单,PA1口接一个按键到GND,PB1控制LED灯的亮灭。我们要实现的程序现象就是,按下按键后LED灯状态进行反转,实现亮-灭控制。 

15.1.2 机械按键问题

        按键是机械的,在按下和松手的瞬间会伴随有一定时间的抖动,按键开关不会马上稳定接通或一下断开,使用按键时会产生图 15.1-2 中所示波浪信号,我们写程序的时候需要用软件消抖处理滤波。

图15.1-2 按键抖动说明

        当然也可以设计硬件进行消抖,如果有硬件消抖,软件就不用再设计消抖处理。硬件消抖一般是利用RC电路的电容充放电特性来对抖动产生的电压毛刺进行滤波,简单示意图如图15.1-3.

图15.1-3 硬件消抖

15.2 软件设计 

15.2.1 工程文件创建

        目前设计的软件,要更加注重的是可移植性,所以很多软件设计里有很多封装,有很多“层层套娃”等,目的是为了软件的可移植性可重用性。一些硬件的改动,仅仅需要软件配置上做一定适配,软件就能用,减少开发周期和成本。

        我们利用以前的工程模版,再新建2个文件夹,“BSP”(即板级支持包)和“SrcLibrary”,分别存放板子的驱动函数和工程服务函数(这个可以按自己习惯来,文件夹命名不同人有不同的风格),工程里也对应新建2个组。如图15.2-1。

图15.2-1 工程创建示意图 

        我们一个开发板可能有很多外设,一般每个外设我们都需要写驱动服务函数,即2个文件,.h和.C。.h一般是.C文件需要被外部调用的变量和函数的声明,以及软件和硬件配置相关内容;.C文件主要是一些和外设相关的过程实现函数。如上图,我们本次需要用到LED和按键Key两个外设,在BSP中我们对应建立了2个外设对应的.c和.h文件。工程文件的建立过程前面多次讲过,这里就略过。

        “SrcLibrary”文件夹里我们放了“Delay”函数,因为本次编程中用到延时相关的内容,这里我们直接拿过来用即可。后面我们还会介绍。这里也提一下,我们开发时要善于用别人已经开发好的函数,这样可以大大提高开发速度,有些函数我们知道怎么去构建,但有时候确实非常耗费时间。也有些函数我们也没必要去深究,我一直秉承一个思想,当今世界知识爆炸,需要你去学的东西太多了,每一项你都要去深究学习,是不对的,要有的放矢,该放下的就放下,“学海无涯,人生苦短,点到即止”。当然对于你从事的专业工作,那肯定还是要深入研究,这个不要太死板。

15.2.2 LED相关程序设计

        为方便以后移植,我们建立LED.h文件,对LED使用的端口,是高电平点亮还是低电平点亮等方式进行配置。这样即便后续硬件有更改,那么我们只用修改.h文件里的配置信息即可。当项目非常大时,这些差异化的配置,我们也可以统一放到一个.h文件中,如建立一个user_config.h,把所有需要特殊配置的内容都放在这里面。现在开发,这些配置都是可以通过工具自动生成的,都无需人工代码,现在项目开发有完备的工具链,你只需要在开发界面里选择好你的设置内容,代码自动生成。

        LED.h的代码如下: 

#ifndef __LED_H
#define	__LED_H

#include "stm32f10x.h"

/* LED连接的GPIO端口配置, 用户只需要修改下面的代码即可改变控制的LED引脚 */
#define LED_GPIO_PORT GPIOB			              /* GPIO端口 */
#define LED_GPIO_CLK RCC_APB2Periph_GPIOB		/* GPIO端口时钟 */
#define LED_GPIO_PIN GPIO_Pin_1			        /* GPIO -pin*/

/** 定义LED亮灭对应的高低电平,根据硬件设计可修改 
  * 1 - off
  * 0 - on
  */
#define LED_ON  0
#define LED_OFF 1


void LED_Init(void);//LED端口初始化
void LED_Toggle(void);//LED灯反转

#endif 

        代码中对LED的亮灭也进行了定义,这样做有两个好处:一是更加形象,编程的时候一看便知是什么意思;二是方便程序移植,如果其他硬件配置的高低电平点亮方式和软件设计的不同,那么我们只需要修改一下这个配置即可,无需再修改主代码。这段代码除了LED相关的硬件配置,还声明了2个函数,分别是LED端口的初始化函数和LED灯反转的函数,这2个函数需要在LED.c文件中实现。LED.c文件代码如下:

#include "stm32f10x.h"                  // Device header
#include "LED.h"

//LED端口初始化函数
void LED_Init(void)
{		
	/*定义一个GPIO_InitTypeDef类型的结构体*/
	GPIO_InitTypeDef GPIO_InitStructure;
	/*开启LED相关的GPIO外设时钟*/

	RCC_APB2PeriphClockCmd(LED_GPIO_CLK, ENABLE);
	/*选择要控制的GPIO引脚*/
	GPIO_InitStructure.GPIO_Pin = LED_GPIO_PIN;	
	/*设置引脚模式为通用推挽输出*/

	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  // 推挽输出

	/*设置引脚速率为50MHz */   
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 

	/*调用库函数,初始化GPIO*/
	GPIO_Init(LED_GPIO_PORT, &GPIO_InitStructure);		

}

//LED端口反转函数
void LED_Toggle(void)
{
	if (GPIO_ReadOutputDataBit(LED_GPIO_PORT, LED_GPIO_PIN) == LED_ON)
	{
		GPIO_SetBits(LED_GPIO_PORT, LED_GPIO_PIN);
	}
	else
	{
		GPIO_ResetBits(LED_GPIO_PORT, LED_GPIO_PIN);
	}
	
}
/*注:LED的端口反转函数,还可以这样写,就是靠操作端口输出寄存器ODR的值,异或位运算^=可以反转IO口
void LED_Toggle(void)
{
	LED_GPIO_PORT->ODR ^=LED_GPIO_PIN;//输出反转状态	
}
*/

        初始化函数我们就不讲了,步骤都是固定的,开启时钟,配置模式和速率,这里模式我们选择推挽输出 。

        LED的反转函数LED_Toggle,我们用到了标准库函数GPIO_ReadOutputDataBit,这个函数是读取端口指定pin的输出状态。不清楚的可以再翻看前面《第14章GPIO简介》中库函数的介绍。我们采用的实现方式是最直观的好理解的一种方式,同时我们也提供了另外一个方式,就是通过对GPIO数据输出寄存器ODR的指定位进行异或操作,将对应PIn反转:

        LED_GPIO_PORT->ODR ^=LED_GPIO_PIN

        这个在《第2章.STM32开发C语言常用知识点》中有介绍,有需要的可以再翻看一下。

15.2.3 按键检测程序设计

        和前面LED类似,在.h文件中进行硬件相关配置和实现函数的声明。

#ifndef __KEY_H
#define	__KEY_H


#include "stm32f10x.h"

//  引脚定义
#define    KEY_GPIO_CLK RCC_APB2Periph_GPIOA
#define    KEY_GPIO_PORT GPIOA			   
#define    KEY_GPIO_PIN GPIO_Pin_1

 /** 按键按下标置宏
	*  按键按下为高电平,设置 KEY_ON=1, KEY_OFF=0
	*  若按键按下为低电平,把宏设置成KEY_ON=0 ,KEY_OFF=1 
	*/
#define KEY_ON	0
#define KEY_OFF	1

void Key_Init(void);
uint8_t Key_State(void);

#endif 

        Key.c需要中需要设计2个函数,分别是初始化函数和按键是否按下的检测函数,代码如下:

#include "stm32f10x.h"                  // Device header
#include "Key.h"
#include "Delay.h"

//按键端口初始化
void Key_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(KEY_GPIO_CLK, ENABLE);
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
	GPIO_InitStructure.GPIO_Pin = KEY_GPIO_PIN;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(KEY_GPIO_PORT, &GPIO_InitStructure);
}

//获取按键的状态,是否被按下
uint8_t Key_State(void)
{
	/*检测是否有按键按下 */
	if(GPIO_ReadInputDataBit(KEY_GPIO_PORT,KEY_GPIO_PIN) == KEY_ON )  
	{	 
		Delay_ms(20);//消抖
		/*等待按键释放 */
		while(GPIO_ReadInputDataBit(KEY_GPIO_PORT,KEY_GPIO_PIN) == KEY_ON);   
		Delay_ms(20);//消抖
		return 	KEY_ON;	 
	}
	else
	{
		return KEY_OFF;
	}
		
}

         初始化函数我们这里设置为上拉输入模式。按键检测函数Key_State函数中用到了对GPIO数据输入寄存器IDR指定pin口的数据读取函数GPIO_ReadInputDataBit,用法和前面得GPIO_ReadOutputDataBit一样,有2个参数,分别是端口GPIOX和对应pin口。按键检测中需要Delay函数延时20ms进行消抖处理,两处消抖对应我们上节画的那个示意图。

15.2.4 主函数设计

        主函数main.c的设计就非常简单了,首先是对硬件LED和KEY对应GPIO口初始化,然后就是检测按键是否按下,如果按下就反转LED灯的状态,代码如下:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "LED.h"
#include "Key.h"

int main(void)
{	
	/* LED端口初始化 */
	LED_Init();
	/* 按键端口初始化 */
	Key_Init();
	
	/* 轮询按键状态,若按键按下则反转LED */
	while(1)                            
	{	   
		if( Key_State()== KEY_ON)
		{
			/*LED反转*/
			LED_Toggle();
		} 	
	}
}

资源已经上传:

https://download.csdn.net/download/weixin_42109443/89623713

15.3 触类旁通

        前面是最简单的一个输入检测和输出控制的实例,掌握了这个实例,我们用STM32可做的事就非常多了,无非是输入的硬件和输出控制的硬件的变化,但操作方式是一模一样的。比如输入硬件可以换成光敏元器件,根据亮度的不同会反馈高低电平,我们可以根据这个高低电平控制灯的亮灭,例如我们常见的日控灯。也可以将输入器件换成烟雾检测器件,输出变成蜂鸣器,检测到烟雾检测器件输入的信号,就给蜂鸣器高低电平实现鸣叫。在后面时钟章节,我们可以做一个类似的实验。

参考资料:
        【1】哔站江协科技STM32入门教程

        【2】《STM32单片机原理与项目实战》刘龙、高照玲、田华著

        【3】《ARM Cortex-M3嵌入式原理及应用》黄可亚著

        【4】《STM32嵌入式微控制器快速上手》陈志旺著

        【5】《STM32单片机应用与全案例实践》沈红卫等著

        【6】《野火STM32开发指南》

        【7】《正点原子STM32开发指南》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

村里大明白

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值