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

 个人名片:

作者简介:一名喜欢分享和记录学习的在校大学生
个人主页:妄北y

个人QQ:2061314755

个人邮箱:[email protected]
个人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

1、硬件设计

2、软件设计

1.编程要点

2.代码分析

(1)LED 灯引脚宏定义

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

 (3)LED GPIO 初始化函数

(4)主函数 

二、GPIO 输入—按键检测 

 1、硬件设计

2、软件设计

1.编程要点

2.代码分析

(1)按键引脚宏定义

(2)按键 GPIO 初始化函数

(3)检测按键的状态 

 (4)主函数


一、GPIO 输出—点亮LED

1、硬件设计

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

STM32F103标准外设库——GPIO 输入、输出 (五)_第1张图片

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

IO端口位的基本结构

STM32F103标准外设库——GPIO 输入、输出 (五)_第2张图片

2、软件设计

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

1.编程要点

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

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

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

2.代码分析

(1)LED 灯引脚宏定义

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

/* 定义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 灯常用的亮、灭及状态反转的控制也直接定义成宏。

/*直接操作寄存器的方法控制 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”只是对当前行有效,所以我们使用续行符来连接起来,以下的 代码是等效的:

#define LED_YELLOW LED1_ON; LED2_ON; LED3_OFF

 (3)LED GPIO 初始化函数

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

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 灯的控制。 

#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、硬件设计

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

STM32F103标准外设库——GPIO 输入、输出 (五)_第3张图片

STM32F103标准外设库——GPIO 输入、输出 (五)_第4张图片         从按键的原理图可知,这些按键在没有被按下的时候,GPIO 引脚的输入状态为低电平 (按键所在的电路不通,引脚接地),当按键按下时,GPIO 引脚的输入状态为高电平 (按键所在的电路导通, 引脚接到电源)。只要我们检测引脚的输入电平,即可判断按键是否被按下。

2、软件设计

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

1.编程要点

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

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

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

2.代码分析

(1)按键引脚宏定义

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

//  引脚定义
#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 初始化函数

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

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)检测按键的状态 

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

 /*
 * 函数名: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 灯的状态。

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

 

大佬觉得有用的话点个赞 呗。
❤️❤️❤️本人水平有限,如有纰漏,欢迎各位大佬评论批评指正!

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

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

你可能感兴趣的:(stm32,嵌入式硬件,单片机,GPIO输入,推挽输出,浮空输入)