我在高职教STM32——GPIO入门之按键输入(1)

大家好,我是老耿,高职青椒一枚,一直从事单片机、嵌入式、物联网等课程的教学。 对于高职的学生层次,同行应该都懂的,老师在课堂上教学几乎是没什么成就感的。正因如此,才有了借助 CSDN 平台寻求认同感和成就感 的想法。在这里,我准备陆续把自己花了很多心思的教学设计分享出来,主要面向广大师生朋友,单片机老鸟就略过吧。欢迎点赞+关注,各位的支持是本人持续输出的动力,多谢多谢!

前面,我们介绍了STM32的IO口作为输出的使用,这一章,我们将向大家介绍如何使用IO口作为输入。在本章中,我们将利用开发板上的按键来控制LED的亮灭。通过本章的学习,我们将明白按键的电路原理,了解按键消抖是怎么回事,巩固GPIO的初始化配置,学习GPIO端口输入函数等知识。

【学习目标】

  1. 了解按键防抖、锁存的方法
  2. 巩固GPIO初始化的过程,独立完成代码编写
  3. 理解按键单击、双击、长按的程序算法

**按键是初学嵌入式的第一类输入器件,****入门不难,但是一旦按法多样化(单击/双击/长按),或是结合其他被控器件,就需要用上中断、定时器、状态机等知识,难度也就上来了。**本章还是基于GPIO输入电平的传统方法来按键,计划分两个部分,本文是第一部分。

一、认识按键开关

1.1 按键开关的形态和结构

按键开关主要是指轻触式按键开关,广泛用于各种电器和电子消费品中,有各种各样不同的形态,如图1所示,用圈标注的是我们开发板使用的类型。不管何种形态,它都是一种电子开关,使用时以满足操作力的条件向开关操作方向施压,开关可以闭合接通,当撤销压力时开关即断开,其内部结构是靠金属弹片受力变化来实现通断的。
图1 不同形态的按键开关

如图2所示,我们的开发板上一共有4个按键开关,用KEYx来表示。这种开关虽然有四个引脚,但我们来看图3,内部①脚和②脚是常闭的,③脚和④脚也是常闭的,这样其实相当于只有两个引脚了。
图2 开发板上的四个按键
图3 四脚按键开关的内部结构示意

1.2 按键的抖动和消抖

按键机械触点断开或闭合时,由于触点的弹性作用,按键开关不会马上稳定接通或一下子断开,使用按键时会产生带波纹信号,如图4所示,所以需要对波纹信号进行消抖处理,否则可能会引起误判。
图4 按键接通或断开瞬间的抖动纹波

显然上图中的纹波是我们不希望的,因此就需要想办法消抖。消抖的办法有两种:硬件消抖和软件消抖,硬件消抖是在按键两端并联一个0.1uF的电容,利用电容充放电的延时,消除波纹。硬件消抖很少采用,更普遍的做法是通过软件消抖,通过延时10~20ms的方式避开抖动,也可以采用连续多次检测电平的方式避开抖动。

二、按键控制LED编程实践

2.1 任务描述

本实验的任务是用一个按键实现对一个LED的控制,每按一次,LED的状态就改变一次。在控制方式上,使用了无锁存和有锁存两种方法,分别实现了按下有效和松开有效。

2.2 硬件电路

图5是开发板上四个按键与STM32连接的电路原理图,连接方式都是一样的,这里就以SW1(KEY1)为例进行介绍。当SW1按下时,KEY1端(PC13)与GND接通,为低电平;当SW1松开时,KEY1端电平被上拉电阻R48拉高。这样,我们通过检测PC13的输入电平就知道KEY1是按下还是松开了。同理,KEY2、KEY3、KEY4的状态需要分别检测PC11、PC12、PD2的输入电平。
图5 四个按键的原理图

2.3 工程文件清单

本实验的工程文件清单如图6所示,在HARDWARE目录中添加了一对按键的驱动文件 key.ckey.h。由于用到了LED,因此之前的 led.cled.h 需要保留。
图6 按键工程文件清单

2.4 工程代码剖析

1. key.h文件源码

头文件里依然是与IO有关的宏和函数声明,如代码清单1所示。

cpp 复制代码
//-------------------------------------------------------
// 代码清单1:key.h
//-------------------------------------------------------

#ifndef  _KEY_H_
#define  _KEY_H_

#include "stm32f10x.h"

//-------------------------------------------------------
// 必要的宏定义
//-------------------------------------------------------
#define  KEY1_PIN     GPIO_Pin_13
#define  KEY2_PIN     GPIO_Pin_11
#define  KEY3_PIN     GPIO_Pin_12
#define  KEY4_PIN     GPIO_Pin_2

//-------------------------------------------------------
// 库函数操作宏定义
//-------------------------------------------------------
#define  READ_KEY1	GPIO_ReadInputDataBit(GPIOC, KEY1_PIN)
#define  READ_KEY2	GPIO_ReadInputDataBit(GPIOC, KEY2_PIN)
#define  READ_KEY3	GPIO_ReadInputDataBit(GPIOC, KEY3_PIN)
#define  READ_KEY4	GPIO_ReadInputDataBit(GPIOD, KEY4_PIN)

//--------------------------------------------------------
// 函数声明
//--------------------------------------------------------
void Key_Init(void);	//按键初始化函数

#endif

这里,我们用 GPIO_ReadInputDataBit() 这个库函数来读取一个IO口的电平,函数名虽然长了点,但确实见名就能知意。它有两个参数:GPIOx和GPIO_Pin_x,返回值就是读到的电平(1或0),确实也很直观。

2. key.c文件源码

该文件里就一个函数 Key_Init(),用来初始化按键的IO口,如代码清单2所示。

cpp 复制代码
/*
 ************************************************************************
 * 代码清单2:key.c
 * 描    述:按键的初始化、驱动
 * 平    台:麒麟座V3.2
 * 作    者:老耿
 * 日    期:yyyy-mm-dd
 * 固 件 库:ST3.5.0
 * 版    本:V1.0
 * 修改记录:无
 ************************************************************************
*/

//必要的头文件
#include "key.h"
#include "delay.h"

/**
 ************************************************************************
 * 函 数 名:Key_Init
 * 功    能:按键端口初始化
 * 入口参数:无
 * 出口参数:无
 * 说    明:将按键端口设置成输入模式
 ************************************************************************
**/
void Key_Init(void)
{
	//定义一个GPIO初始化对象(结构体)
	GPIO_InitTypeDef  gpio_initstruct;
	
	//打开必要的外设时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);
	
	//填充初始化结构体,上拉输入模式,并执行生效
	gpio_initstruct.GPIO_Pin = KEY1_PIN | KEY2_PIN | KEY3_PIN;
	gpio_initstruct.GPIO_Mode = GPIO_Mode_IPU;
	//输入模式不用配置Speed参数
	//gpio_initstruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOC, &gpio_initstruct);
	
	//KEY4与KEY1/2/3不是一组端口,单独再初始化
	gpio_initstruct.GPIO_Pin = KEY4_PIN;
	GPIO_Init(GPIOD, &gpio_initstruct);
}

虽然本实验只使用到了KEY1,但在代码中,我们将KEY2、KEY3、KEY4的IO口也一并进行了初始化。需要注意的是,GPIO要配置为输入模式。由于按键松开时对应的引脚为高电平,所以我们将其配置为上拉输入模式。

那为什么没有配置为下拉输入模式呢?那是因为下拉模式,引脚默认为低电平,这相当于按键被按下的状态。这样的话,程序将无法检测按键是否被按下,因为无论按键是否被按下,引脚电平都为低电平了。当然,配置成浮空输入模式当然也是可以的。

还有一点大家是否发现,上面代码中,没有配置引脚的速度,那是因为引脚处于输入模式时,并不需要配置引脚速度。引脚速度仅是指单片机向引脚刷新电平的频率,所以只有在引脚处于输出模式时才有效。

3. main.c文件源码

主程序里编写了无锁存和有锁存两种按键控灯的方法,如代码清单3所示,大家测试的时候请保留一种方式的代码,注释掉另一种,来体会这两种方式的差异。

cpp 复制代码
/**
 ******************************************************
 * 代码清单3:main.c
 * 项    目:按键控制LED
 * 任务描述:按一次KEY1,变一次LED
 * 实验平台:OneNET STM32开发板V3.2
 * 作    者:老耿
 * 日    期:yyyy/mm/dd
 ******************************************************
**/
 
//-----------------------------------------------------
// 必要的头文件
//-----------------------------------------------------
#include "delay.h"
#include "led.h"
#include "key.h"


//-----------------------------------------------------
// 主函数
//-----------------------------------------------------
int main()
{
	delay_init();	//延时初始化
	Led_Init();		//LED初始化
	Key_Init();		//按键初始化
	
    while(1)    //以下两种方式保留一种,注释掉另一种
    {
        /*-------------- 无锁存方式 -------------*/
//      if(!READ_KEY1)  //按住红灯亮
//          RED_ON;
//      else
//          RED_OFF;    //松开红灯灭

        /*-------------- 有锁存方式 -------------*/
        if(!READ_KEY1)      //按下KEY1
        {
            delay_ms(10);   //延时消抖
            if(!READ_KEY1)  //确认按下
            {
                while(!READ_KEY1);  //等待松开
                RED_TOG;    //红灯变化
            }
        }
    }
}

2.5 验证与测试

我们对两种方式分别下载和测试后,大家应该可以发现,无锁存方式下需要按住不放,LED才亮着,一旦松开,LED就灭了。而有锁存的方式下,需要完成按下和松开这一组动作(即单击)才能改变LED的状态,实现这个效果的就是代码清单3中的第36行,当按键被按下时,while的条件将永远成立,这样将导致程序一直停留在此处,只有按键松开时,后续代码才可以得到执行,也就达到了通过按键锁存程序状态的目的。

此外,在有锁存方式中,当按键被按下后,并不是马上进行后续动作,而是延时了10ms,再次判断按键是否被按下,这样就避免了按键因电平抖动造成被误判的可能。大家也可以尝试去掉消抖这条语句,看每次按键按下的操作是否都能有效。

(第一部分完,共两部分)

相关推荐
cjy_Somnr2 小时前
keil5报错显示stm32的SWDIO未连接不能烧录
stm32·单片机·嵌入式硬件
Lay_鑫辰3 小时前
西门子诊断-状态和错误位(“轴”工艺对象 V1...3)
服务器·网络·单片机·嵌入式硬件·自动化
无垠的广袤5 小时前
【工业树莓派 CM0 NANO 单板计算机】本地部署 EMQX
linux·python·嵌入式硬件·物联网·树莓派·emqx·工业物联网
雲烟7 小时前
嵌入式设备EMC安规检测参考
网络·单片机·嵌入式硬件
泽虞7 小时前
《STM32单片机开发》p7
笔记·stm32·单片机·嵌入式硬件
田甲8 小时前
【STM32】 数码管驱动
stm32·单片机·嵌入式硬件
up向上up8 小时前
基于51单片机垃圾箱自动分类加料机快递物流分拣器系统设计
单片机·嵌入式硬件·51单片机
纳祥科技17 小时前
Switch快充方案,内置GaN,集成了多个独立芯片
单片机
单片机日志18 小时前
【单片机毕业设计】【mcugc-mcu826】基于单片机的智能风扇系统设计
stm32·单片机·嵌入式硬件·毕业设计·智能家居·课程设计·电子信息
松涛和鸣19 小时前
从零开始理解 C 语言函数指针与回调机制
linux·c语言·开发语言·嵌入式硬件·排序算法