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

news2024/12/25 13:45:06

        大家好,我是老耿,高职青椒一枚,一直从事单片机、嵌入式、物联网等课程的教学。对于高职的学生层次,同行应该都懂的,老师在课堂上教学几乎是没什么成就感的。正因如此,才有了借助 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所示。

 

//-------------------------------------------------------
// 代码清单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所示。

/*
 ************************************************************************
 * 代码清单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所示,大家测试的时候请保留一种方式的代码,注释掉另一种,来体会这两种方式的差异。

/**
 ******************************************************
 * 代码清单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,再次判断按键是否被按下,这样就避免了按键因电平抖动造成被误判的可能。大家也可以尝试去掉消抖这条语句,看每次按键按下的操作是否都能有效。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1868667.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

android 通过gradle去除aar的重复资源图片

背景:项目中引入了aar包,结果导致资源出问题了,于是需要对下面aar包进行重复资源去除操作 操作具体如下: 目录:app/build.gradle 末尾配置 apply from: "${project.rootDir}/scripts/excludewidgetAar.gradle&qu…

【文件夹加密】文件夹怎么加密码,快来试试这4个方法

文件夹怎么加密?在日常工作中,为了保护我们的文件数据、隐私信息不被泄露,因此文件夹加密非常有必要。文件夹加密技术是通过特定的算法改变文件夹中文件的信息数据,使未授权的用户即使获得了已加密的文件或文件夹,也因…

已解决javax.security.auth.DestroyFailedException:在尝试销毁某个对象时失败的正确解决方法,亲测有效!!!

已解决javax.security.auth.DestroyFailedException:在尝试销毁某个对象时失败的正确解决方法,亲测有效!!! 目录 问题分析 报错原因 解决思路 解决方法 检查对象状态 确认权限设置 检查资源锁定情况 修正实现…

Windows环境下安装MySQL数据库的步骤

说明: 由于环境的不同,安装过程中可能会遇到各种各样的问题,不用慌,先根据错误提示搜索,多试一下。 安装前,请先认真看一下,有可能会遇到的几个问题: 1、证书链问题,一般…

【PyScript】PyScript 基础入门

【PyScript】PyScript 基础入门 PyScript 是一个为了支持 Python 运行在浏览器的开源平台。 1.PyScript 应用程序的创建 PyScript 程序需要以下三个内容 一个提供给浏览器的 index.html 文件。PyScript 的运行环境描述,通常是一个 pyscript.json 或 pyscript.to…

7km远距离WiFi实时图传模块,无人机海上无线传输方案,飞睿智能WiFi MESH自组网技术

在浩瀚无垠的海洋上,无人机正在开启一场前所未有的技术创新。它们不再只是天空的舞者,更是海洋的守望者,为我们带来前所未有的视野和数据。而这一切的背后,都离不开一项创新性的技术——飞睿智能远距离WiFi实时图传模块与无线Mesh…

热门开源Text2SQL框架

Chat2DB 项目地址:https://github.com/chat2db/Chat2DB简介:Chat2DB是一个通用的SQL客户端和数据分析工具,能够辅助生成SQL,同时支持对话式的数据分析功能,提供了网页、客户端2种使用方式,它支持几乎所有流…

[图解]SysML和EA建模住宅安全系统-01-包图

1 00:00:01,400 --> 00:00:04,870 得到这个之后,我们就来画我们的包图了 2 00:00:05,350 --> 00:00:07,940 我们来看包图的内容 3 00:00:09,750 --> 00:00:12,430 名字是这个,模型组织 4 00:00:13,820 --> 00:00:20,570 然后上面&#xf…

Xilinx FPGA:vivado用串口控制数码管

一、项目要求 要求输入从千位到个位数字给4位数码管,要求分别输入的数字都能显示 二、关键信号流向 三、程序设计 顶层: timescale 1ns / 1ps module TOP(input sys_clk ,input rst_n ,input rx_…

基于LangChain构建RAG应用

前言 Hello,大家好,我是GISer Liu😁,一名热爱AI技术的GIS开发者,上一篇文章中我们详细介绍了RAG的核心思想以及搭建向量数据库的完整过程;😲 本文将基于上一篇文章的结果进行开发,主…

@RequestParam注解的使用及源码解析

前言 RequestParam 注解是我们进行JavaEE开发,最常见的几个注解之一,这篇博文我们以案例和源码相结合,帮助大家更好的了解RequestParam 注解 使用案例 1.获取 URL 上的值 GetMapping("/simple") public String simple(RequestP…

MySQL自学教程:1. MySQL简介与安装

MySQL简介与安装 一、MySQL简介二、MySQL安装(一)Windows系统上的安装(二)Linux系统上的安装(以Ubuntu为例)(三)Mac OS系统上的安装三、安装后的基本配置四、总结一、MySQL简介 MySQL是一个流行的开源关系型数据库管理系统(RDBMS),广泛应用于各种业务场景,从小型个…

2024肥晨赠书活动第三期:《前端工程化:基于Vue.js 3.0的设计与实践》

文章目录 内容简介作者简介关于《前端工程化:基于Vue.js 3.0的设计与实践》文章目录文章简介《前端工程化:基于Vue.js 3.0的设计与实践》全书速览结束语 内容简介 本书以Vue.js的3.0版本为核心技术栈,围绕“前端工程化”和TypeScript的知识点…

保姆级本地部署Qwen2

重点:Qwen2提供了CPU与GPU两种运行方式 运行成功效果图: 前提说明:如果需要用GPU,那么请在物理机安装ubuntu系统,不然显卡驱动很难安装,不建议新手部署。训练微调模型需要用到GPU。本文仅以ubuntu系统演示…

vue3+ts <script setup lang=“ts“> element-plus的el-date-picker设置默认日期

效果图(单个日期): utils.ts: /*** 格式化时间戳* param {number} timestamp 时间戳* param {string} format 格式* returns {string}*/ export const formatTimeStamp (timestamp: number, format: string) > {if (!timesta…

Python魔法参数:深入解析*args和**kwargs的强大用途

目录 引言 基础概念解析 *args:处理位置参数 **kwargs:处理关键字参数 *args和**kwargs的实际应用场景 1. 函数装饰器中使用*args和**kwargs 2. 类构造函数中使用*args和**kwargs 3. API调用中使用**kwargs 与其他参数类型的结合使用 结合默认参数 位置参数与关键…

利用powershell开展网络钓鱼

要确保人们打开我们的恶意文件并执行它们,我们只需让微软努力工作多年来赢得人们的信任,然后将一些危险的宏插入到幻灯片中。 本博文将介绍如何通过屏幕顶部的一个友好的警告提示,在用户启用宏后立即运行您的宏。 首先,我们需要打…

pytest-yaml-sanmu(五):跳过执行和预期失败

除了手动注册标记之外,pytest 还内置了一些标记可直接使用,每种内置标记都会用例带来不同的特殊效果,本文先介绍 3 种。 1. skip skip 标记通常用于忽略暂时无法执行,或不需要执行的用例。 pytest 在执行用例时,如果…

手持小风扇哪个品牌好耐用?手持小风扇品牌排行榜揭晓分享

炎炎夏日,手持小风扇、USB小风扇,成为人手一台的“网红”。这些小风扇造型小巧,可以装进包里,夏日出街或者挤公交地铁都可以拿出来吹一吹。那么这些小风扇性价比高不高呢?真的好用吗?耐用吗?根据…

00. 这里整理了最全的爬虫框架(Java + Python)

目录 1、前言 2、什么是网络爬虫 3、常见的爬虫框架 3.1、java框架 3.1.1、WebMagic 3.1.2、Jsoup 3.1.3、HttpClient 3.1.4、Crawler4j 3.1.5、HtmlUnit 3.1.6、Selenium 3.2、Python框架 3.2.1、Scrapy 3.2.2、BeautifulSoup Requests 3.2.3、Selenium 3.2.4…