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);
再举一个例子,比如我们要初始化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最最基本的内容。掌握了这些,就可以试着写一个跑马灯一类的实验了。