STM32第七节:GPIO输入——按键检测(包含带参宏)

news2024/7/4 5:41:58

目录

前言

STM32第七节:GPIO输入——按键检测(包含带参宏)

带参宏

代码替换展示

定义带参宏

GPIO输入——按键检测

硬件部分

端口输入数据寄存器(GPIOx_IDR)

编写程序

配置以及编写bsp_key文件

main函数编程

bsp_led.c以及bsp_led.h文件函数编程

使用固件库控制io口

直接操作寄存器的方法控制IO

小结


前言

        上节课我们学习了GPIO输出——使用固件库点亮LED,包含LED以及GPIO的讲解,以及具体代码的编写。那么我们节本课就接着上节课讲讲带参宏以及GPIO输入——按键检测。

        创作不易,点个三连霸!


STM32第七节:GPIO输入——按键检测(包含带参宏)

带参宏

代码替换展示

        我们在编写程序的时候,在其他代码里见到过带参宏的定义;例如LED_G(ON/OFF);这种定义,那么带参宏是纯粹的C语言知识,我们看以下的代码,这里就在上一节的基础上相当于替换掉了GPIO口操作的两行代码,换成了带参宏。

	//GPIO_SetBits(LED_G_GPIO_PORT,LED_G_GPIO_PIN);
	LED_G(OFF);
	Delay(0xFFFFF);
	//GPIO_ResetBits(LED_G_GPIO_PORT,LED_G_GPIO_PIN);
	LED_G(ON);
	Delay(0xFFFFF);

        那么,我们该如何定义带参宏呢?

定义带参宏

        那么我们打开bsp_led.h,再次定义两个宏ON/OFF:

#define ON     1
#define OFF    0

#define LED_G(a)  if(a) \
						GPIO_ResetBits(LED_G_GPIO_PORT,LED_G_GPIO_PIN);\
				  else  GPIO_SetBits(LED_G_GPIO_PORT,LED_G_GPIO_PIN);

        这里我们其实是写了一个宏定义函数,我们设置了ON为1,OFF为0;在下面的宏函数中,定义LED_G(a)中的参量是否为1或0;这样我们就可以控制输出的具体代码,使得main.c文件更加简洁明了,可读性更强。

GPIO输入——按键检测

        上节课讲了GPIO口的输出,这节课我们来讲讲输入。我们可以通过一个按键,来改变外部的这个电平的状态,让io口来读取电平的状态。

硬件部分

        在我们的指南者板子上,只有两个按键K1,K2。 我们看右边的高电平为3V3,但是我们的GPIO对于这个是有限制的,所以我们在前面接了一个限流电阻(R4,R5,R7,R11),当按键没有按下的时候,默认接地,为低电平;按键按下之后,就变成了高电平。因为PA0有自动唤醒的功能wakeup,而wakeup一定要是上升沿才能唤醒的,为了统一风格,所以是上升沿输入。

        电路图中的电容又有什么用呢?之前在学51单片机的时候,我们采取的消抖方式为软件消抖,我们这个是机械按键,需要延时20ms(消抖是前后都要消抖),要不然就会像交流电一样不断接通3.3V,如果我们接了这个电容的话,就会一直给电容充放电,直到稳定。之后无论是按下还是抬起,电容也在不断的充放电,对我们的电路没有影响。所以我们就不需要软件消抖。如果等于高电平,我们就确认按键按下了,如果等于低电平,我们就抬起了按键,进行相应的动作。

端口输入数据寄存器(GPIOx_IDR)

         很显然,这个寄存器还是配置低位的寄存器,不做更改时为0,若配置某位为1,即接通3.3V,变为高电平。

编写程序

        我们现在还没讲中断,等以后我们还会写中断函数(类似51单片机)

配置以及编写bsp_key文件

        我们先打开bsp_key.h文件,定义KEY1和KEY2的宏定义,包括打开时钟,宏定义接口以及设定Pin的值为0和13。

#define    KEY1_GPIO_CLK     RCC_APB2Periph_GPIOA
#define    KEY1_GPIO_PORT    GPIOA			   
#define    KEY1_GPIO_PIN	 GPIO_Pin_0

#define    KEY2_GPIO_CLK     RCC_APB2Periph_GPIOC
#define    KEY2_GPIO_PORT    GPIOC		   
#define    KEY2_GPIO_PIN	 GPIO_Pin_13

        定义好之后,类似bsp_led.c中,我们打开bsp_key.c,然后创建一个函数LED_KEY_Config(void),然后再该函数中定义结构体类型,打开APB2上的时钟,配置模式以及初始化GPIO。

void LED_KEY_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	
	RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK,ENABLE);
	
	GPIO_InitStruct.GPIO_Pin = KEY1_GPIO_PIN;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	
	GPIO_Init(KEY1_GPIO_PORT,&GPIO_InitStruct);  //&地址即可
}

        然后我们要创建一个按键检测的函数。刚刚讲过我们的按键是硬件消抖,所以我们这里就不再需要进行delay函数的消抖。我们宏定义按键按下为KEY_ON,释放按键为KEY_OFF;

#define KEY_ON     1
#define KEY_OFF    0

        紧接着我们编写函数,由于有返回值,我们使用uint8_t写函数,先使用if检测是否有按键按下,如果没有就是OFF。然后在按下之后,我们需要检测是否松手,也就是按键的释放。使用while关键字检测是否一直为按下状态。(这里使用了GPIO_ReadInputDataBit函数,用来读取按键的状态):

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;
}

        这样我们就编写完成函数,记得要声明一下:

void Key_GPIO_Config(void);
uint8_t Key_Scan(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin);

        到这里我们的bsp_key的函数就全部编写完成了,接下来编写其他函数。

main函数编程

        我们在这里首先引用Key_GPIO_ConfiG();来初始化函数。然后在while循环中写一个if语句,如果按键检测结果为KEY_ON,则使LED1翻转,即LED1_TOGGLE;复制这段代码,拷贝一份到下面,检测按键2的状态。这就是主函数中的代码,接下来我们配置TOGGLE函数以及bsp_led中的函数及代码。

#include "stm32f10x.h"   // 相当于51单片机中的  #include <reg51.h>
#include "bsp_led.h"
#include "bsp_key.h"

void Delay(uint32_t count)
{
	for(;count!=0;count--);
}

int main(void)
{	
	LED_GPIO_Config();
	Key_GPIO_Config();

	while(1)                            
	{	   
		if( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON  )
		{
			LED1(ON);
		}
		if( Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON  )
		{
			LED2_TOGGLE;
		}	
	}
}

bsp_led.c以及bsp_led.h文件函数编程

        我们先编写宏定义LED连接的GPIO端口RGB。

/* 定义LED连接的GPIO端口, 用户只需要修改下面的代码即可改变控制的LED引脚 */
// R-红色
#define LED1_GPIO_PORT    	GPIOB			            /* GPIO端口 */
#define LED1_GPIO_CLK 	    RCC_APB2Periph_GPIOB		/* GPIO端口时钟 */
#define LED1_GPIO_PIN		GPIO_Pin_5		        	/* 连接到SCL时钟线的GPIO */

// G-绿色
#define LED2_GPIO_PORT    	GPIOB
#define LED2_GPIO_CLK 	    RCC_APB2Periph_GPIOB
#define LED2_GPIO_PIN		GPIO_Pin_0

// B-蓝色
#define LED3_GPIO_PORT    	GPIOB
#define LED3_GPIO_CLK 	    RCC_APB2Periph_GPIOB
#define LED3_GPIO_PIN		GPIO_Pin_1

#define ON     1
#define OFF    0

        如果我们想实现翻转LED灯,可以通过控制寄存器的方法,也可以通过控制标准的固件库的方法来控制io口。

使用固件库控制io口

        本节课刚开始就介绍了带参宏的定义,我们可以通过这个办法来控制:

/* 使用标准的固件库控制IO*/
#define LED1(a)	if (a)	\
					GPIO_SetBits(LED1_GPIO_PORT,LED1_GPIO_PIN);\
					else		\
					GPIO_ResetBits(LED1_GPIO_PORT,LED1_GPIO_PIN)

#define LED2(a)	if (a)	\
					GPIO_SetBits(LED2_GPIO_PORT,LED2_GPIO_PIN);\
					else		\
					GPIO_ResetBits(LED2_GPIO_PORT,LED2_GPIO_PIN)

#define LED3(a)	if (a)	\
					GPIO_SetBits(LED3_GPIO_PORT,LED3_GPIO_PIN);\
					else		\
					GPIO_ResetBits(LED3_GPIO_PORT,LED3_GPIO_PIN)
直接操作寄存器的方法控制IO

        在使用这个方法之前,我们先介绍一下C语言中的异或二进制运算符^。0^1为1,1^1为0;而0^0为0,1^0为1,然后我们就可以控制io口。我们既需要操作BSRR和BRR寄存器,也需要操作ODR寄存器,分别输出高电平,低电平以及输出反转状态。

/* 直接操作寄存器的方法控制IO */
#define	digitalHi(p,i)		   {p->BSRR=i;}	   //输出为高电平		
#define digitalLo(p,i)		   {p->BRR=i;}	   //输出低电平
#define digitalToggle(p,i)     {p->ODR ^=i;}   //输出反转状态

/* 定义控制IO的宏 */
#define LED1_TOGGLE		   digitalToggle(LED1_GPIO_PORT,LED1_GPIO_PIN)
#define LED1_OFF		   digitalHi(LED1_GPIO_PORT,LED1_GPIO_PIN)
#define LED1_ON			   digitalLo(LED1_GPIO_PORT,LED1_GPIO_PIN)

#define LED2_TOGGLE		   digitalToggle(LED2_GPIO_PORT,LED2_GPIO_PIN)
#define LED2_OFF		   digitalHi(LED2_GPIO_PORT,LED2_GPIO_PIN)
#define LED2_ON			   digitalLo(LED2_GPIO_PORT,LED2_GPIO_PIN)

#define LED3_TOGGLE		   digitalToggle(LED3_GPIO_PORT,LED3_GPIO_PIN)
#define LED3_OFF		   digitalHi(LED3_GPIO_PORT,LED3_GPIO_PIN)
#define LED3_ON			   digitalLo(LED3_GPIO_PORT,LED3_GPIO_PIN)

        然后我们就可以使用三原色来进行混色:(基本混色)

/* 基本混色,后面高级用法使用PWM可混出全彩颜色,且效果更好 */
//红
#define LED_RED  \
					LED1_ON;\
					LED2_OFF\
					LED3_OFF

//绿
#define LED_GREEN		\
					LED1_OFF;\
					LED2_ON\
					LED3_OFF

//蓝
#define LED_BLUE	\
					LED1_OFF;\
					LED2_OFF\
					LED3_ON
					
//黄(红+绿)					
#define LED_YELLOW	\
					LED1_ON;\
					LED2_ON\
					LED3_OFF
					
//紫(红+蓝)
#define LED_PURPLE	\
					LED1_ON;\
					LED2_OFF\
					LED3_ON

//青(绿+蓝)
#define LED_CYAN \
					LED1_OFF;\
					LED2_ON\
					LED3_ON
					
//白(红+绿+蓝)
#define LED_WHITE	\
					LED1_ON;\
					LED2_ON\
					LED3_ON
					
//黑(全部关闭)
#define LED_RGBOFF	\
					LED1_OFF;\
					LED2_OFF\
					LED3_OFF

         在bsp_led.c文件中,我们需要配置初始化结构体,然后打开时钟(有选择性);然后紧接着设置模式以及速度。(都是前几节课熟知的,不在多讲)然后就是通过控制3个Pin的值然后初始化GPIO口;并附带关闭所有LED灯的代码:

void LED_GPIO_Config(void)
{
		GPIO_InitTypeDef GPIO_InitStructure;

		RCC_APB2PeriphClockCmd( LED1_GPIO_CLK | LED2_GPIO_CLK | LED3_GPIO_CLK, ENABLE);
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	
		GPIO_InitStructure.GPIO_Pin = LED1_GPIO_PIN;
		GPIO_Init(LED1_GPIO_PORT, &GPIO_InitStructure);	 //&地址即可
		
		GPIO_InitStructure.GPIO_Pin = LED2_GPIO_PIN;
		GPIO_Init(LED2_GPIO_PORT, &GPIO_InitStructure);
		
		GPIO_InitStructure.GPIO_Pin = LED3_GPIO_PIN;
		GPIO_Init(LED3_GPIO_PORT, &GPIO_InitStructure);

		/* 关闭所有led灯	*/
		GPIO_SetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);
		GPIO_SetBits(LED2_GPIO_PORT, LED2_GPIO_PIN);	 
		GPIO_SetBits(LED3_GPIO_PORT, LED3_GPIO_PIN);
}

        如果断言错误,我们执行如下代码:

void assert_failed(uint8_t* file, uint32_t line)
{
	// 断言错误时执行的代码
	LED1_ON;
}

小结

        到这里我们就写完了所有代码,以及代码的讲解(两种方式控制io口)。下节课我们学习位带操作。

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

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

相关文章

EasyCode 插件的具体使用

前言 EasyCode 是基于IntelliJ IDEA Ultimate版开发的一个代码生成插件&#xff0c;主要通过自定义模板&#xff08;基于velocity&#xff09;来生成各种你想要的代码。通常用于生成Entity、Dao、Service、Controller。如果你动手能力强还可以用于生成HTML、JS、PHP等代码。理…

Spring Cloud项目整合Sentinel及简单使用

说明&#xff1a;Sentinel是阿里巴巴开发的微服务治理中间件&#xff0c;可用于微服之间请求的流量管控、权限控制、熔断降级等场景。本文介绍如何在Spring Cloud项目中整合Sentinel&#xff0c;以及Sentinel的简单使用。 环境 首先搭建一个简单的微服务环境&#xff0c;有以…

关于分布式微服务数据源加密配置以及取巧方案(含自定义加密配置)

文章目录 前言Spring Cloud 第一代1、创建config server项目并加入加解密key2、启动项目&#xff0c;进行数据加密3、实际项目中的测试server Spring Cloud Alibaba低版本架构不支持&#xff0c;取巧实现无加密配置&#xff0c;联调环境问题加密数据源配置原理探究自定义加密解…

Django框架的全面指南:从入门到高级【第128篇—Django框架】

Django框架的全面指南&#xff1a;从入门到高级 Django是一个高效、功能强大的Python Web框架&#xff0c;它被广泛用于构建各种规模的Web应用程序。无论是初学者还是有经验的开发人员&#xff0c;都可以从入门到掌握Django的高级技巧。在本指南中&#xff0c;我们将带你逐步了…

C++Qt学习——添加资源文件

目录 1、创建好了文件之后&#xff0c;在左边空白处按下CtrlN&#xff0c;创建Qt 以及Qt Resource File 2、写入名称&#xff0c;点击下一步 3、可以发现已经创建好啦。 4、点击Add Prefix 5、写上前缀&#xff0c;最好加上斜杠 6、选择提前放好的图片或者icon 7、发…

(C语言)strcmp函数详解与模拟实现与strncmp详解

目录 1. strcmp函数详解 2. strcmp模拟实现 3. strncmp函数 3.1 特殊情况分析 1. strcmp函数详解 头文件<string.h> 返回值是int类型&#xff0c;函数是将str1与str2这两个数组进行比较&#xff0c; 若str1>str2返回大于0的值 若str1<str2返回小于0的值 若s…

【linux本地安装tinycudann包教程】

【linux本地安装tinycudann包教程】 tiny-cuda-nn官网链接 如果你是windows 10系统的,想要安装tiny-cuda-nn可以参考我的文章——windows 10安装tiny-cuda-n包 根据官网要求:C++要求对应14,其实这样就已经告诉我们linux系统中的gcc版本不能高于9,同时下面又告诉我们gcc版…

如何在 Linux ubuntu 系统上搭建 Java web 程序的运行环境

如何在 Linux ubuntu 系统上搭建 Java web 程序的运行环境 基于包管理器进行安装 Linux 会把一些软件包放到对应的服务器上&#xff0c;通过包管理器这样的程序&#xff0c;来把这些软件包给下载安装 ubuntu系统上的包管理器是 apt centos系统上的包管理器 yum 注&#xff1a;…

Pytorch学习 day14(模型的验证步骤)

如何利用已经训练好的模型&#xff0c;验证它的结果&#xff0c;步骤如下&#xff1a; 步骤一&#xff1a;加载测试输入并更改为合适尺寸 保存图片到指定文件夹下&#xff0c;注意是否为同级目录注意&#xff1a;返回上一级目录为“…/xxx"有时&#xff0c;我们自己的输…

【QT+QGIS跨平台编译】之七十三:【QGIS_Analysis跨平台编译】—【错误处理:字符串错误】

文章目录 一、字符串错误二、处理方法三、涉及到的文件一、字符串错误 常量中有换行符错误:(也有const char * 到 LPCWSTR 转换的错误) 二、处理方法 需要把对应的文档用记事本打开,另存为 “带有BOM的UTF-8” 三、涉及到的文件 涉及到的文件有: src\analysis\processin…

中国城市统计年鉴、中国县域统计年鉴、中国财政统计年鉴、中国税务统计年鉴、中国科技统计年鉴、中国卫生统计年鉴​

统计年鉴是指以统计图表和分析说明为主&#xff0c;通过高度密集的统计数据来全面、系统、连续地记录年度经济、社会等各方面发展情况的大型工具书来获取统计数据资料。 统计年鉴是进行各项经济、社会研究的必要前提。而借助于统计年鉴&#xff0c;则是研究者常用的途径。目前国…

论坛管理系统|基于Spring Boot+ Mysql+Java+B/S架构的论坛管理系统设计与实现(可运行源码+数据库+设计文档+部署说明+视频演示)

推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 目录 目录 前台功能效果图 管理员功能登录前台功能效果图 用户功能模块 系统功能设计 数据库E-R图设计 l…

数字逻辑-时序逻辑电路一

一、实验目的 &#xff08;1&#xff09;熟悉触发器的逻辑功能及特性。 &#xff08;2&#xff09;掌握集成D和JK触发器的应用。 &#xff08;3&#xff09;掌握时序逻辑电路的分析和设计方法。 二、实验仪器及材料 三、实验内容及步骤 1、用D触发器&#xff08;74LS74&am…

网络学习:9个计算机的“网络层”知识点

目录 一、IP 地址 1.1 分类表示法&#xff1a; 1.1.1 分类表示地址的其他说明 1.2 无分类编址 CIDR 二、IP 数据报文格式 Q: IP 报文里有什么&#xff1f;可以不按顺序或者字节来讲一讲 三、 路由概念 3.1 路由表 3.2 路由网络匹配 3.3 ARP 解析 3.4 RARP 逆地址解析…

Python 协程-asyncio、async/await

看到吐血 (ཀ」 ∠) 协程(Coroutine)本质上是一个函数&#xff0c;特点是在代码块中可以将执行权交给其他协程众所周知&#xff0c;子程序&#xff08;函数&#xff09;都是层级调用的&#xff0c;如果在A中调用了B&#xff0c;那么B执行完毕返回后A才能执行完毕。协程与子程序…

STM32第十课:串口发送

一、usart串口 1.1 USART串口协议 串口通讯(Serial Communication) 是一种设备间非常常用的串行通讯方式&#xff0c;因为它简单便捷&#xff0c;因此大部分电子设备都支持该通讯方式&#xff0c;电子工程师在调试设备时也经常使用该通讯方式输出调试信息。在计算机科学里&…

FPGA - 单总线协议(one-wire)

1&#xff0c;简介 单总线&#xff08;one-wire&#xff09;是美国 DALLAS 公司推出的外围串行扩展总线技术&#xff0c;与 SPI、I2C 等串行数据通信方式不同&#xff0c;它采用单根信号线&#xff0c;既传输时钟又传输数据&#xff0c;而且数据传输是双向的。它具有节省 I/O口…

idea Springboot 在线考试管理系统开发mysql数据库web结构java编程计算机网页

一、源码特点 springboot 在线考试管理系统是一套完善的完整信息系统&#xff0c;结合mvc框架和bootstrap完成本系统springboot spring mybatis &#xff0c;对理解JSP java编程开发语言有帮助系统采用springboot框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统具有…

如何理解数据经济,数据要素,数据资源,数据资产入表、数据交易,数据经纪

引言 在数字经济时代&#xff0c;数据逐渐成为了新的财富。这不仅体现在企业层面&#xff0c;政府和个人也在积极地生成、收集和分析数据&#xff0c;以寻找商业价值和社会效益。数据财政模型的兴起&#xff0c;标志着从传统的以土地资源为核心的经济模型向以数据资产为核心的…

双链表(上)

1.结构 1.带头双向循环链表 注意&#xff1a;这里的“带头”跟前面我们说的“头节点”是两个概念&#xff0c;实际前面的在单链表阶段称呼不严 谨&#xff0c;但是为了同学们更好的理解就直接称为单链表的头节点。 带头链表里的头节点&#xff0c;实际为“哨兵位”&#xff0c…