STM32F103标准外设库——GPIO 输入、输出 (五)

个人名片:

🦁作者简介:一名喜欢分享和记录学习的在校大学生

🐯个人主页:妄北y

🐧个人QQ:2061314755

🐻个人邮箱:2061314755@qq.com

🦉个人WeChat:Vir2021GKBS

🐼本文由妄北y原创,首发CSDN 🎊🎊🎊

🐨座右铭:大多数人想要改造这个世界,但却罕有人想改造自己。

专栏导航:

妄北y系列专栏导航:

C/C++的基础算法:C/C++是一种常用的编程语言,可以用于实现各种算法,这里我们对一些基础算法进行了详细的介绍与分享。🎇🎇🎇

C/C++刷题库:分享一些关于编程的练习基础题,也会后续加入一系列的算法题,分享自己的解题思路和方法。🥰🥰🥰

计算机网络:对计算机网络的基础知识框架有一个简单的学习与认识,对计算机网络中常见的题型进行一个总结与归纳。🍾🍾🍾

QT基础入门学习:对QT的基础图形化页面设计进行了一个简单的学习与认识,利用QT的基础知识进行了翻金币小游戏的制作🤹🤹🤹

Linux基础编程:初步认识什么是Linux,为什么学Linux,安装环境,进行基础命令的学习,入门级的shell编程。🍻🍻🍻

Linux的系统编程+网络编程:IO编程、进程、线程、进程间通讯(包括管道、信号、信号量、共享内存等)网络编程主要就是socket,poll,epoll,以及对TCP/IP的理解,同时要学会高并发式服务器的编写。🙌🙌🙌

Linux应用开发基础开发:分享Linux的基本概念、命令行操作、文件系统、用户和权限管理等,网络编程相关知识,TCP/IP 协议、套接字(Socket)编程等,可以实现网络通信功能。💐💐💐

Linux项目开发:Linux基础知识的实践,做项目是最锻炼能力的一个学习方法,这里我们会学习到一些简单基础的项目开发与应用,而且都是毕业设计级别的哦。🤸🤸🤸

非常期待和您一起在这个小小的互联网世界里共同探索、学习和成长。💝💝💝 ✨✨ 欢迎订阅本专栏 ✨✨

文章介绍:

🎉 本篇文章对STM32学习的相关知识进行分享!🥳🥳🥳

LED 灯的控制使用到 GPIO 外设的基本输出功能,按键检测使用到 GPIO 外设的基本输入功能。

如果您觉得文章不错,期待你的一键三连哦,你的鼓励是我创作动力的源泉,让我们一起加油,一起奔跑,让我们顶峰相见!!!💪💪💪

🎁感谢大家点赞👍收藏⭐评论✍️

目录:

[一、GPIO 输出---点亮LED](#一、GPIO 输出—点亮LED)

1、硬件设计

2、软件设计

1.编程要点

2.代码分析

[(1)LED 灯引脚宏定义](#(1)LED 灯引脚宏定义)

[(2)控制 LED 灯亮灭状态的宏定义](#(2)控制 LED 灯亮灭状态的宏定义)

[(3)LED GPIO 初始化函数](#(3)LED GPIO 初始化函数)

(4)主函数

[二、GPIO 输入---按键检测](#二、GPIO 输入—按键检测)

1、硬件设计

2、软件设计

1.编程要点

2.代码分析

(1)按键引脚宏定义

[(2)按键 GPIO 初始化函数](#(2)按键 GPIO 初始化函数)

(3)检测按键的状态

(4)主函数


一、GPIO 输出---点亮LED

1、硬件设计

这是一个 RGB 灯,里面由红蓝绿三个小灯构成,使用 PWM 控制时可以混合成 256 不同的颜色。

这些 LED 灯的阴极都是连接到 STM32 的 GPIO 引脚,只要我们控制 GPIO 引脚的电平输出状态, 即可控制 LED 灯的亮灭。若您使用的实验板 LED 灯的连接方式或引脚不一样,只需根据我们的 工程修改引脚即可,程序的控制原理相同。

IO端口位的基本结构

2、软件设计

为了使工程更加有条理,我们把 LED 灯控制相关的代码独立分开存储,方便以后移植。在"工 程模板"之上新建"bsp_led.c "及"bsp_led.h "文件,其中的"bsp"即 Board Support Packet 的缩写 (板级支持包),这些文件也可根据您的喜好命名,这些文件不属于STM32 标准库的内容,是由我们自己根据应用需要编写的。

1.编程要点

(1). 使能 GPIO 端口时钟;

(2). 初始化 GPIO 目标引脚为推挽输出模式;

(3). 编写简单测试程序,控制 GPIO 引脚输出高、低电平。

2.代码分析

(1)LED 灯引脚宏定义

在编写应用程序的过程中,要考虑更改硬件环境的情况,例如 LED 灯的控制引脚与当前的不一 样,我们希望程序只需要做最小的修改即可在新的环境正常运行。这个时候一般把硬件相关的部分使用宏来封装,若更改了硬件环境,只修改这些硬件相关的宏即可,这些定义一般存储在头文 件。

cpp 复制代码
/* 定义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			              /*  GPIO端口 */
#define LED2_GPIO_CLK 	    RCC_APB2Periph_GPIOB		/* GPIO端口时钟*/
#define LED2_GPIO_PIN		GPIO_Pin_0			        /* 连接到SCL时钟线的GPIO */

// B-蓝色
#define LED3_GPIO_PORT    	GPIOB			              /*  GPIO端口 */
#define LED3_GPIO_CLK 	    RCC_APB2Periph_GPIOB		/* GPIO端口时钟 */
#define LED3_GPIO_PIN		GPIO_Pin_1			        /*连接到SCL时钟线的GPIO */

以上代码分别把控制 LED 灯的 GPIO 端口、GPIO 引脚号以及 GPIO 端口时钟封装起来了。在实际控制的时候我们就直接用这些宏,以达到应用代码硬件无关的效果。

(2)控制 LED 灯亮灭状态的宏定义

为了方便控制 LED 灯,我们把 LED 灯常用的亮、灭及状态反转的控制也直接定义成宏。

cpp 复制代码
/*直接操作寄存器的方法控制 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

这部分宏控制 LED 亮灭的操作是直接向 BSRR、BRR 和 ODR 这三个寄存器写入控制指令来实现的,对 BSRR 写 1 输出高电平,对 BRR 写 1 输出低电平,对 ODR 寄存器某位进行异或操作可反转位的状态。

RGB 彩灯可以实现混色,如最后一段代码我们控制红灯和绿灯亮而蓝灯灭,可混出黄色效果。

代码中的"\"是 C 语言中的续行符语法,表示续行符的下一行与续行符所在的代码是同一行。代码中因为宏定义关键字"#define"只是对当前行有效,所以我们使用续行符来连接起来,以下的 代码是等效的:

cpp 复制代码
#define LED_YELLOW LED1_ON; LED2_ON; LED3_OFF

(3)LED GPIO 初始化函数

利用上面的宏,编写 LED 灯的初始化函数

cpp 复制代码
void LED_GPIO_Config(void)
{		
		/*定义一个GPIO_InitTypeDef类型的结构体*/
		GPIO_InitTypeDef GPIO_InitStructure;

		/*开启LED相关的GPIO外设时钟*/
		RCC_APB2PeriphClockCmd( LED1_GPIO_CLK | LED2_GPIO_CLK | LED3_GPIO_CLK, ENABLE);
		/*选择要控制的GPIO引脚*/
		GPIO_InitStructure.GPIO_Pin = LED1_GPIO_PIN;	

		/*设置引脚模式为通用推挽输出*/
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   

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

		/*调用库函数,初始化GPIO*/
		GPIO_Init(LED1_GPIO_PORT, &GPIO_InitStructure);	
		
		/*选择要控制的GPIO引脚*/
		GPIO_InitStructure.GPIO_Pin = LED2_GPIO_PIN;

		/*调用库函数,初始化GPIO*/
		GPIO_Init(LED2_GPIO_PORT, &GPIO_InitStructure);
		
		/*选择要控制的GPIO引脚*/
		GPIO_InitStructure.GPIO_Pin = LED3_GPIO_PIN;

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

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

(1) 使用 GPIO_InitTypeDef 定义 GPIO 初始化结构体变量,以便下面用于存储 GPIO 配置

(2) 调用库函数 RCC_APB2PeriphClockCmd 来使能 LED 灯的 GPIO 端口时钟,在前面的章节中 我们是直接向 RCC 寄存器赋值来使能时钟的,不如这样直观。该函数有两个输入参数,第 一个参数用于指示要配置的时钟,如本例中的"RCC_APB2Periph_GPIOB",应用时我们使 用"|"操作同时配置 3 个 LED 灯的时钟;函数的第二个参数用于设置状态,可输入"Disable" 关闭或"Enable"使能时钟。

(3) 向 GPIO 初始化结构体赋值,把引脚初始化成推挽输出模式,其中的 GPIO_Pin 使用宏 "LEDx_GPIO_PIN"来赋值,使函数的实现方便移植。

(4) 使用以上初始化结构体的配置,调用 GPIO_Init 函数向寄存器写入参数,完成 GPIO 的初始 化,这里的 GPIO 端口使用"LEDx_GPIO_PORT"宏来赋值,也是为了程序移植方便。

(5) 使用同样的初始化结构体,只修改控制的引脚和端口,初始化其它 LED 灯使用的 GPIO 引脚。

(6) 使用宏控制 RGB 灯默认关闭。

(4)主函数

编写完 LED 灯的控制函数后,就可以在 main 函数中测试了,在 main 函数中,调用我们前面定义的 LED_GPIO_Config 初始化好 LED 的控制引脚,然后直接调 用各种控制 LED 灯亮灭的宏来实现 LED 灯的控制。

cpp 复制代码
#include "stm32f10x.h"
#include "bsp_led.h"

#define SOFT_DELAY Delay(0x0FFFFF);

void Delay(__IO u32 nCount); 

/**
  * @brief  主函数
  * @param  无  
  * @retval 无
  */
int main(void)
{	
	/* LED 端口初始化 */
	LED_GPIO_Config();	 

	while (1)
	{
		LED1_ON;			  // 亮
		SOFT_DELAY;
		LED1_OFF;		   // 灭

		LED2_ON;			 // 亮
		SOFT_DELAY;
		LED2_OFF;		   // 灭

		LED3_ON;			 // 亮
		SOFT_DELAY;
		LED3_OFF;		   // 灭	 

		/*轮流显示 红绿蓝黄紫青白 颜色*/
		LED_RED;
		SOFT_DELAY;
		
		LED_GREEN;
		SOFT_DELAY;
		
		LED_BLUE;
		SOFT_DELAY;
		
		LED_YELLOW;
		SOFT_DELAY;
		
		LED_PURPLE;
		SOFT_DELAY;
				
		LED_CYAN;
		SOFT_DELAY;
		
		LED_WHITE;
		SOFT_DELAY;
		
		LED_RGBOFF;
		SOFT_DELAY;		
	}
}

void Delay(__IO uint32_t nCount)	 //简单的延时函数
{
	for(; nCount != 0; nCount--);
}

二、GPIO 输入---按键检测

1、硬件设计

按键机械触点断开、闭合时,由于触点的弹性作用,按键开关不会马上稳定接通或一下子断开,使用按键时会产生图按键抖动说明图中的带波纹信号,需要用软件消抖处理滤波,不方便输入检测。本实验板连接的按键带硬件消抖功能,它利用电容充放电的延时,消除了波纹,从而简化软件的处理,软件只需要直接检测引脚的电平即可。

从按键的原理图可知,这些按键在没有被按下的时候,GPIO 引脚的输入状态为低电平 (按键所在的电路不通,引脚接地),当按键按下时,GPIO 引脚的输入状态为高电平 (按键所在的电路导通, 引脚接到电源)。只要我们检测引脚的输入电平,即可判断按键是否被按下。

2、软件设计

同 LED 的工程,为了使工程更加有条理,我们把按键相关的代码独立分开存储,方便以后移植。 在"工程模板"之上新建"bsp_key.c"及"bsp_key.h"文件,这些文件也可根据您的喜好命名,这 些文件不属于 STM32 标准库的内容,是由我们自己根据应用需要编写的。

1.编程要点

(1). 使能 GPIO 端口时钟;

(2). 初始化 GPIO 目标引脚为输入模式 (浮空输入);

(3). 编写简单测试程序,检测按键的状态,实现按键控制 LED 灯。

2.代码分析

(1)按键引脚宏定义

同样,在编写按键驱动时,也要考虑更改硬件环境的情况。我们把按键检测引脚相关的宏定义到 "bsp_key.h"文件中

cpp 复制代码
//  引脚定义
#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

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

以上代码根据按键的硬件连接,把检测按键输入的 GPIO 端口、GPIO 引脚号以及 GPIO 端口时钟 封装起来了。

(2)按键 GPIO 初始化函数

利用上面的宏,编写按键的初始化函数。

cpp 复制代码
void Key_GPIO_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	
	/*开启按键端口的时钟*/
	RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK|KEY2_GPIO_CLK,ENABLE);
	
	//选择按键的引脚
	GPIO_InitStructure.GPIO_Pin = KEY1_GPIO_PIN; 
	// 设置按键的引脚为浮空输入
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; 
	//使用结构体初始化按键
	GPIO_Init(KEY1_GPIO_PORT, &GPIO_InitStructure);
	
	//选择按键的引脚
	GPIO_InitStructure.GPIO_Pin = KEY2_GPIO_PIN; 
	//设置按键的引脚为浮空输入
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; 
	//使用结构体初始化按键
	GPIO_Init(KEY2_GPIO_PORT, &GPIO_InitStructure);	
}

同为 GPIO 的初始化函数,初始化的流程与"LED GPIO 初始化函数"章节中的类似,主要区别 是引脚的模式。函数执行流程如下:

(1) 使用 GPIO_InitTypeDef 定义 GPIO 初始化结构体变量,以便下面用于存储 GPIO 配置。

(2) 调用库函数 RCC_APB2PeriphClockCmd 来使能按键的 GPIO 端口时钟,调用时我们使用"|" 操作同时配置两个按键的时钟。

(3) 向 GPIO 初始化结构体赋值,把引脚初始化成浮空输入模式,其中的 GPIO_Pin 使用宏 "KEYx_GPIO_PIN"来赋值,使函数的实现方便移植。由于引脚的默认电平受按键电路影 响,所以设置成浮空输入。

(4) 使用以上初始化结构体的配置,调用 GPIO_Init 函数向寄存器写入参数,完成 GPIO 的初始 化,这里的 GPIO 端口使用"KEYx_GPIO_PORT"宏来赋值,也是为了程序移植方便。

(5) 使用同样的初始化结构体,只修改控制的引脚和端口,初始化其它按键检测时使用的 GPIO 引脚。

(3)检测按键的状态

初始化按键后,就可以通过检测对应引脚的电平来判断按键状态了。

cpp 复制代码
 /*
 * 函数名:Key_Scan
 * 描述  :检测是否有按键按下
 * 输入  :GPIOx:x 可以是 A,B,C,D或者 E
 *		     GPIO_Pin:待读取的端口位 	
 * 输出  :KEY_OFF(没按下按键)、KEY_ON(按下按键)
 */
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;
}

(4)主函数

接下来我们使用主函数编写按键检测流程,代码中初始化 LED 灯及按键后,在 while 函数里不断调用 Key_Scan 函数,并判断其返回值,若返回值表示按键按下,则反转 LED 灯的状态。

cpp 复制代码
#include "stm32f10x.h"
#include "bsp_led.h"  
#include "bsp_key.h" 

/**
  * @brief  主函数
  * @param  无
  * @retval 无
  */ 
int main(void)
{	
	/* LED端口初始化 */
	LED_GPIO_Config();
	LED1_ON;

	/* 按键端口初始化 */
	Key_GPIO_Config();
	
	/* 轮询按键状态,若按键按下则反转LED */
	while(1)                            
	{	   
		if( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON  )
		{
			/*LED1反转*/
			LED1_TOGGLE;
		} 

		if( Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON  )
		{
			/*LED2反转*/
			LED2_TOGGLE;
		}		
	}
}

大佬觉得有用的话点个赞 👍🏻 呗。

❤️❤️❤️本人水平有限,如有纰漏,欢迎各位大佬评论批评指正!😄😄😄

💘💘💘如果觉得这篇文对你有帮助的话,也请给个点赞、收藏下吧,非常感谢!👍 👍 👍

🔥🔥🔥任务在无形中完成,价值在无形中升华,让我们一起加油吧!🌙🌙🌙

相关推荐
Whappy00133 分钟前
4.STM32之通信接口《精讲》之IIC通信---软件实现IIC《深入浅出》面试必备!
stm32·单片机·嵌入式硬件
文弱书生6562 小时前
TIM输入捕获
stm32·单片机·嵌入式硬件
飞凌嵌入式5 小时前
飞凌嵌入式旗下教育品牌ElfBoard与西安科技大学共建「科教融合基地」
嵌入式硬件·学习·嵌入式·飞凌嵌入式
weixin_452600696 小时前
【青牛科技】电流模式PWM控制器系列--D4870
科技·单片机·嵌入式硬件·音视频·智能电表·白色家电电源·机顶盒电源
嵌新程9 小时前
day06(单片机高级)PCB设计
单片机·嵌入式硬件·pcb
stm 学习ing10 小时前
FPGA 第十讲 避免latch的产生
c语言·开发语言·单片机·嵌入式硬件·fpga开发·fpga
LateBloomer77713 小时前
FreeRTOS——信号量
笔记·stm32·学习·freertos
wenchm13 小时前
细说STM32单片机DMA中断收发RTC实时时间并改善其鲁棒性的另一种方法
stm32·单片机·嵌入式硬件
编码追梦人15 小时前
如何实现单片机的安全启动和安全固件更新
单片机
电子工程师UP学堂15 小时前
电子应用设计方案-16:智能闹钟系统方案设计
单片机·嵌入式硬件