GPIO英文全称General-Purpose Input /Output Ports,中文意思是通用I/O端口。由于MCU的通信外设接口众多,不可能每一外设固定一组GPIO,在MCU中,可通过软件运行期间能够动态配置和控制的引脚的状态,所以每个 GPIO 口除了通用输入输出功能外,还可能有其它复用功能。
在MM32L0产品中,每个 GPIO 端口有两个 32 位配置寄存器(GPIOx_CRL, GPIOx_CRH),两个 32 位数据寄存器(GPIOx_IDR 和 GPIOx_ODR),一个 32 位置位/复位寄存器(GPIOx_BSRR),一个 16 位复位寄存器(GPIOx_BRR)、一个 32 位锁定寄存器(GPIOx_LCKR)和两个复用功能选择寄存器(GPIOx_AFRH)和(GPIOx_AFRL)。
GPIO 端口的每个位可以由软件分别配置成多种模式。
• 输入浮空
• 输入上拉
• 输入下拉
• 模拟输入
• 开漏输出
• 推挽式输出
• 推挽式复用功能
• 开漏复用功能
每个 I/O 端口可以自由编程,然而必须按照 32 位字访问 I/O 端口寄存器(不允许半字或字节访问)。GPIOx_BSRR 和 GPIOx_BRR 寄存器允许对任何 GPIO 寄存器进行读/更改的独立访问;这样,在读更改访问之间产生 IRQ 不会发生危险。
图1. I/O端口位的基本结构
1、输入浮空详解:
浮空(floating)就是逻辑器件的输入引脚既不接高电平,也不接低电平。一般实际运用时,引脚不建议悬空,易受干扰。
2、输入上拉\下拉详解:
上拉就是把电位拉高,比如拉到Vcc。上拉就是将不确定的信号通过一个电阻拉在高电平!弱强只是上拉电阻的阻值不同,没有什么严格区分。在MM32L0xxx中该上拉电阻位40k欧姆。
3、模拟输入详解:
模拟输入是指模拟信号的输入。配置模拟输入时,所有的上拉、下拉电阻和施密特触发器,均处于禁止状态,因此 “输入数据寄存器”将不能反映端口上的电平状态,也就是说,模拟输入配置下,CPU不能在“输入数据寄存器”上读到有效的数据。
4、开漏输出详解:
输出端相当于三极管的集电极,对输入数据寄存器的访问可得到 I/O 状态。可以做不同电压信号转换;多个同时级联还可以实现线与逻辑)。
5、推挽式输出详解:
可以输出高,低电平,连接数字器件; 推挽结构一般是指两个三极管分别受两互补信号的控制,总是在一个三极管导通的时候另一个截止。高低电平由IC的电源决定。
6、复用功能详解:
可以理解为GPIO口被用作第二功能时的配置情况(即并非作为通用IO口使用)。端口必须配置成复用功能输出模式(推挽或开漏)。
在MM32L0产品UM的8.1.11 章节外设的 GPIO 配置,用户可以查到需要使用相关功能时各GPIO的状态配置的表格。
下面将根据库函数对MM32L0xx的GPIO配置,首先来看一下GPIO_Init函数的原型void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)。这个函数的实现是在HAL_gpio.h文件中,若要使用该函数在相应的应用程序的前面包含HAL_gpio.h头文件。
该函数的第一个参数为GPIO_ TypeDef,它是一个结构体类型,该类型在MM32L0xx.h中被定义。定义的原型为:
typedef struct
{
__IO uint32_t CRL;
__IO uint32_t CRH;
__IO uint32_t IDR;
__IO uint32_t ODR;
__IO uint32_t BSRR;
__IO uint32_t BRR;
__IO uint32_t LCKR;
__IO uint32_t RESERVED0;
__IO uint32_t AFRL;
__IO uint32_t AFRH;
} GPIO_TypeDef;
该函数的第二个参数为GPIO_ InitTypeDef,它也是一个结构体类型,该类型在HAL_gpio.h中被定义,第一个参数只找到配置的目标寄存器,第二个参数就是对相应端口如何配置的数据参数。这些参数存储在指向GPIO_InitTypeDef变量的首地址处。定义的原型为:
typedef struct
{
uint16_t GPIO_Pin;
GPIOSpeed_TypeDef GPIO_Speed;
GPIOMode_TypeDef GPIO_Mode;
}GPIO_InitTypeDef;
GPIA是固件库中定义的一个宏,在编译的时候会宏展开,先列出与GPIOD端口地址映射有关的宏定义如下:
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
#define GPIOA_BASE (0x48000000)
如在配置MCU的PA15控制一个LED灯的亮和灭:
void GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); //使能GPIOA时钟
//GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_1);//此处只是控制闪灯,如使用到相关外设一定要配置GPIO复用功能。
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15; //配置GPIOA.15
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//50MHz
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
GPIO_InitTypeDef的第一个变量为GPIO_Pin是一个16位的无符号数,该数只有16位,每一位代表一个引脚,若要配置某一个端口的某一个引脚只需要把相应的位设置为1就可以了。在MM32的固件库中有如下引脚号定义:
#define GPIO_Pin_0 ((uint16_t)0x0001) /* Pin 0 selected */
#define GPIO_Pin_1 ((uint16_t)0x0002) /* Pin 1 selected */
#define GPIO_Pin_2 ((uint16_t)0x0004) /* Pin 2 selected */
#define GPIO_Pin_3 ((uint16_t)0x0008) /* Pin 3 selected */
#define GPIO_Pin_4 ((uint16_t)0x0010) /* Pin 4 selected */
#define GPIO_Pin_5 ((uint16_t)0x0020) /* Pin 5 selected */
#define GPIO_Pin_6 ((uint16_t)0x0040) /* Pin 6 selected */
#define GPIO_Pin_7 ((uint16_t)0x0080) /* Pin 7 selected */
#define GPIO_Pin_8 ((uint16_t)0x0100) /* Pin 8 selected */
#define GPIO_Pin_9 ((uint16_t)0x0200) /* Pin 9 selected */
#define GPIO_Pin_10 ((uint16_t)0x0400) /* Pin 10 selected */
#define GPIO_Pin_11 ((uint16_t)0x0800) /* Pin 11 selected */
#define GPIO_Pin_12 ((uint16_t)0x1000) /* Pin 12 selected */
#define GPIO_Pin_13 ((uint16_t)0x2000) /* Pin 13 selected */
#define GPIO_Pin_14 ((uint16_t)0x4000) /* Pin 14 selected */
#define GPIO_Pin_15 ((uint16_t)0x8000) /* Pin 15 selected */
#define GPIO_Pin_All ((uint16_t)0xFFFF) /* All pins selected */
使用这些定义好的宏就方便很多,要配置某几个引脚只需要把相应的引脚相或就可以了。若你要多某一个端口的所有位进行配置,那么只需要使用一个宏GPIO_Pin_All 。
GPIOSpeed_TypeDef是一个枚举类型,它用于定义GPIO速度的参数,它的格式如下:
typedef enum
{
GPIO_Speed_10MHz = 1,
GPIO_Speed_2MHz,
GPIO_Speed_50MHz
}GPIOSpeed_TypeDef;
通过定义可以知道,GPIOSpeed_TypeDef有三种取值,那么GPIO的速度有三种。
GPIOMode_TypeDef也是一个枚举类型,它用于定义GPIO工作的模式,它的定义如下:
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;
这两位数据用来存储到某一个引脚的模式控制位MODEx[1:0] ,而高四位用来标志某一些标志。
MM32的GPIO库函数:
1、void GPIO_DeInit(GPIO_TypeDef* GPIOx);
函数解释:将外设 GPIOx 寄存器重设为缺省值,该函数的作用是把GPIO相关的寄存器配置成上电复位后的默认状态,在第一次初始化前或者不再使用某一个接口后可以调用该函数。
函数参数说明:GPIOx:GPIO的分组,如GPIOA 、GPIOB等的宏定义。
2、void GPIO_AFIODeInit(void);
函数解释:将复用功能(重映射事件控制和 EXTI 设置)重设为缺省值。
3、void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
函数解释:GPIO的初始化函数,该函数的作用是对IO进行初始化。
函数参数说明:
(1)GPIOx:GPIO的分组,如GPIOA 、GPIOB等的宏定义。
(2)GPIO_InitStruct:GPIO的初始化相关结构体。该结构体里面的成员变量决定了我们具体的初始化参数。以下进行说明:
l GPIO_Pin:指定具体的IO脚,如GPIO_Pin_0 GPIO_Pin_1这样的宏定义,这些宏由厂家写好,我们直接使用即可。
l GPIO_Mode:指定GPIO的模式。
4、void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct);
函数解释:把 GPIO_InitStruct 中的每一个参数按缺省值填入,GPIO结构体的初始化。对GPIO_InitStruct结构体进行默认配置
函数参数说明:GPIO_InitStruct,直接传入该结构体的指针,在该函数内会对结构体进行初始化。
5、uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
函数解释:读取指定端口管脚输入。
函数参数说明:GPIOx:GPIO的分组(如GPIOA,GPIOB等)。GPIO_Pin:具体的gpio管脚(如GPIO_Pin_0 、GPIO_Pin_1这样的宏定义)
6、uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
函数解释:读取指定的GPIO端口输入。
函数参数说明:GPIOx:gpio的分组/gpio端口;GPIO_Pin:具体的gpio管脚
函数返回值说明:输入管脚的值Bit_SET(高电平) Bit_RESET(低电平)
7、uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
函数解释:读取指定端口管脚输出。
函数参数说明:GPIOx:GPIO的分组/ GPIO端口;GPIO_Pin:具体的gpio管脚
函数返回值说明:输出管脚的值Bit_SET(高电平) Bit_RESET(低电平)
8、uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
函数解释:读取输出IO分组/端口的值
函数参数说明:GPIOx:GPIO的分组/ GPIO端口
函数返回值说明:一个io端口的所有数据 (输出状态)
9、void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
函数解释:对IO管脚进行置位(输出高电平)。这个函数使用GPIOx_BSRR寄存器来实现原子读或者修改操作。在这种情况下,在读和修改访问时发生一个IRQ中断是没有危险的。
函数参数说明:GPIOx:GPIO的分组/ GPIO端口;GPIO_Pin:具体的gpio管脚或者是io管脚的组合
10、void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
函数解释:清除指定的数据端口位,对IO管脚进行复位(输出低电平)。这个函数使用GPIOx_BSRR寄存器来实现读或者修改操作。在这种情况下,在读和修改访问时发生一个IRQ中断是没有危险的。
函数参数说明:GPIOx:GPIO的分组/ GPIO端口;GPIO_Pin:具体的GPIO管脚或者是IO管脚的组合
11、void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
函数解释:对某一位进行写入操作。
函数参数说明:GPIOx:GPIO的分组/ GPIO端口;GPIO_Pin:具体的GPIO管脚;BitVal:写入高电平或者低电平(Bit_RESET:写入低电平 Bit_SET:写入高电平
12、void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
函数解释:对GPIO端口进行写入操作,适用于对统一端口的多个管脚的写入
函数参数说明:GPIOx:GPIO的分组/ GPIO端口; BitVal:写入高电平或者低电平(Bit_RESET:写入低电平 Bit_SET:写入高电平)
13、void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
函数解释:锁定GPIO的寄存器,锁定的寄存器是GPIOx_MODER,GPIOx_OTYPER, GPIOx_OSPEEDR,GPIOx_PUPDR, GPIOx_AFRL and GPIOx_AFRH。在下一次复位前,被锁定的管脚不能被修改。
函数参数说明:GPIOx:GPIO的分组(如GPIOA,GPIOB等)。GPIO_Pin:具体的gpio管脚(如GPIO_Pin_0 、GPIO_Pin_1这样的宏定义)。
14、void GPIO_PinAFConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_PinSource, uint8_t GPIO_AF);
函数解释:改变指定管脚的映射关系,即配置指定管脚的复用功能。
函数参数说明:GPIOx:GPIO的分组/ GPIO端口;GPIO_PinSource:具体要配置成复用功能的管脚(如GPIO_Pin_0 GPIO_Pin_1这样的宏定义);GPIO_AF:选择该管脚要使用的复用功能。
例:配置UART1的GPIO程序
void UART_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); //使能GPIOA时钟
GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_1);//GPIO复用功能设置
GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_1);
//UART1_TX GPIOA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);// 初始化GPIOA.9
//UART1_RX GPIOA.10
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);// 初始化GPIOA.10
}
转自: 灵动微电MMCU