STM32 嵌入式学习入门(2)——STM32的GPIO介绍

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/FelikZhang/article/details/79243222

STM32的GPIO介绍


 GPIO:General Purpose Input Output ,即通用输入/输出,简称为GPIO。

GPIO应该是学习单片机、学习嵌入式、学习STM32的第一个知识点了。在介绍GPIO相关的内容前,这里先总得说一下自己对GPIO的理解。对于初学者,可以把GPIO的作用想象成C语言里面的做输入输出的函数(scanf(); printf(); gets(); puts();等等),在C语言里面scanf()和printf()这两个函数是做输入输出的,对于几乎所有的C语言程序,都可以看成是用输入函数读取了一些输入,然后进行程序的逻辑处理,最后通过输出函数把程序最后执行的结果显示出来的过程。

同样地,这可以类比到嵌入式系统上面。比如循迹小车、避障小车、平衡小车、四旋翼等等,我们其实都可以看成这样的结构:

1)首先通过外部模块(这里主要是指各种传感器)读取系统的相关参数。比如循迹小车,就是利用红外模块去探测小车当前的行驶状态,并实时地将小车的行驶状态发送给主控制器(STM32)。而这种数据的发送就是将红外模块的特定引脚与STM32的GPIO相连,以高低电平的形式向STM32传递信息的。

2)主控制器对接收到的数据进行判断。接着上面循迹小车的例子,在主控制器的程序中,每过一定的时间(比如10ms)就会扫描并一下接收传感器信号的那几个引脚(IO口)上的高低电平的信息。然后对这些信息进行分析判断,判断的结果是小车当前是否还沿着预定轨迹行驶,如果偏离了预定的轨迹,应该怎么调整。

3)主控制器将判断结果通过IO口传出。上面第2点,最后的判断结果会通过IO口将调整信息发出到电机驱动部分,电机驱动部分会根据主控模块发来的数据对电机转速进行调整,从而达到让小车循迹行驶的目的。

总结一下,上面这三点主要是想让大家了解GPIO的作用,在一个项目、一个系统中的作用。说简单了就是输出高低电平和读取高低电平输入的。


下面进入正文,介绍GPIO的初始化和使用方法。


首先从最简单的角度介绍GPIO是什么东西。


首先GPIO最基本、最简单的作用是我们可以通过编程的方式让它作输入或者输出,而输入/输出的形式为高低电平(通常0V为低电平,3.3V为高电平)。要让GPIO作输入或者输出,首先就需要对IO口相关的寄存器进行配置。先介绍一下什么是寄存器,寄存器是中央处理器内的组成部分,寄存器是有限存贮容量的高速存贮部件,它们可用来暂存指令、数据和地址。因此对IO口的初始化就是向相关寄存器里面写不同的值,从而确定使用哪一个IO口(IO口标号)、以及IO口工作模式(输入还是输出)、输出速度等参数。在经过初始化之后就可以正常使用IO口了,比如如果IO口设置成了某个输入模式,就可以通过调用相关函数或者直接操作相关寄存器去得到IO口的电平是高电平还是低电平。

下面从库函数的层面来说明如何初始化IO口。


typedef struct
{
	uint16_t GPIO_Pin;             /*!< Specifies the GPIO pins to be configured.
									  This parameter can be any value of @ref GPIO_pins_define */

	GPIOSpeed_TypeDef GPIO_Speed;  /*!< Specifies the speed for the selected pins.
									  This parameter can be a value of @ref GPIOSpeed_TypeDef */

	GPIOMode_TypeDef GPIO_Mode;    /*!< Specifies the operating mode for the selected pins.
									  This parameter can be a value of @ref GPIOMode_TypeDef */
}GPIO_InitTypeDef;

首先看这个结构体的定义,里面有三个变量,首先这三个变量的类型是通过类型重命名得到的,具体如下:

typedef unsigned short     int uint16_t;
typedef enum
{ 
  GPIO_Speed_10MHz = 1,
  GPIO_Speed_2MHz, 
  GPIO_Speed_50MHz
}GPIOSpeed_TypeDef;
typedef enum
{ GPIO_Mode_AIN = 0x0,			//模拟输入
  GPIO_Mode_IN_FLOATING = 0x04,		//浮空输入
  GPIO_Mode_IPD = 0x28,			//下拉输入
  GPIO_Mode_IPU = 0x48,			//上拉输入
  GPIO_Mode_Out_OD = 0x14,		//开漏输出
  GPIO_Mode_Out_PP = 0x10,		//推挽输出
  GPIO_Mode_AF_OD = 0x1C,		//复用开漏输出
  GPIO_Mode_AF_PP = 0x18		//复用推挽输出
}GPIOMode_TypeDef;

所以实际上,GPIO_InitTypeDef这个结构体第一个变量类型为一种无符号的整形,变量名为GPIO_Pin,即为确定是哪一个IO口。GPIO_InitTypeDef的第二个变量类型是用枚举定义的,根据变量名很容易知道它是确定IO口的输入或输出的速度的。GPIO_InitTypeDef的第三变量同样是枚举定义的,是确定IO口作输入还是输出,当然这里输入和输出又可以各自细分为好几种,所以这里看到这个结构体中八种模式(mode)。这八种模式中,最常用的也是应该掌握的有三种:推挽输出、开漏输出、上拉输入。

推挽输出:可以输出高、低电平,连接数字器件推挽结构一般是指两个三极管分别受两互补信号的控制,总是在一个三极管导通的时候另一个截止。高低电平由IC的电源低定。

开漏输出:输出端相当于三极管的集电极.要得到高电平状态需要上拉电阻才行.适合于做电流型的驱动,其吸收电流的能力相对强(一般20ma以内) 。

上拉输入:IO口内部输入时由上拉电阻上拉。

具体八种IO口工作模式的解释,可以参考这个帖子:http://www.openedv.com/posts/list/21980.htm

比如说我们需要将PB5这个IO口设置为推挽输出,输入速度为50MHz,实现的代码如下:

 GPIO_InitTypeDef  GPIO_InitStructure;
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	 		//使能PB端口时钟		①
	
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;				//PB.5 端口配置
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 		//推挽输出
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;			//IO口速度为50MHz
	
 GPIO_Init(GPIOB, &GPIO_InitStructure);					//根据设定参数初始化GPIOB.5	
//void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);

这里可能不懂的地方是我用圆圈标出来的两个地方。首先第①行,对于STM32而言,外设和IO口是很多的,默认状态下这些外设和IO口的状态都是关闭的(未使能的),这样可以节约CPU的资源,在使用的时候就要使能(开启)相关时钟。②的位置是调用IO口初始化函数,这是一个库函数,是官方提供的,初学者没有必要了解函数实现的细节。但需要掌握该函数调用时的入口参数。我们的例子是初始化PB5,所以第一个确定IO分组的参数应该写GPIOB,第二个参数就是传入刚才配置好的变量的地址。

再举一个例子,比如我们要初始化PA8这个IO口为下拉输入,输入速度为50MHz,实现的代码如下:,代码如下:

 GPIO_InitTypeDef  GPIO_InitStructure;
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	 		//使能PA端口时钟
	
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;				//PA.8 端口配置
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;	 			//下拉输入
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;			//IO口速度为50MHz
	
 GPIO_Init(GPIOA, &GPIO_InitStructure);					//根据设定参数初始化GPIOA.8


通过上面的操作,就对IO口完成初始化了。下面我们可以开始对IO口进行相关操作了,比如读取IO口的电平(当IO口设置为某个输入模式的时候),或者通过IO口输出高低电平(当IO口设置为某个输出模式的时候)。这里介绍这么三个库函数:

uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
第一个函数用于读取相关IO口的电平的高低,比如我要读 GPIOA.5 的电平状态, 那么方法是:GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_5);返回值是 1(Bit_SET)或者 0(Bit_RESET)。

后面两个函数用于设置IO口的电平的高低,GPIO_SetBits()用于设置IO口电平为高,GPIO_ResetBits();用于设置IO口电平为低。

比如我们要设置 GPIOB.5 输出 1,那么方法为:GPIO_SetBits(GPIOB, GPIO_Pin_5);反之如果要设置GPIOB.5 输出位 0,方法为:GPIO_ResetBits (GPIOB, GPIO_Pin_5);

以上的介绍就是GPIO最最基本的内容。掌握了这些,就可以试着写一个跑马灯一类的实验了。


猜你喜欢

转载自blog.csdn.net/FelikZhang/article/details/79243222