一、GPIO概述
GPIO是通用输入/输出(General Purpose I/O)的简称,主要用于工业现场需要用到数字量输入/输出的场合,例如:
- 输出功能:继电器,LED,蜂鸣器等的控制
- 输入功能:传感器状态、高低电平等信息的读取
- 复用功能:片内外设的对外接口
- 时序模拟:模拟SPI、I2C和UART等常用接口的时序
基本概念
端口(PORT):独立的外设子模块,包含多个引脚,通过多个硬件寄存器控制引脚
GPIO模块由端口GPIOA、GPIOB、GPIOC等多个独立的子模块构成
**引脚(PIN)😗*对应微控制器的一个管脚,归属于端口,由端口寄存器的对应位控制
一个端口默认包含16个引脚,但是不同型号的STM32微控制器所包含的端口数量及各端口包含的引脚数量各不相同,具体信息可查询芯片的数据手册。
二、GPIO模块的电路结构与功能描述
工作模式分类
输入模式
根据上拉/下拉电阻的使能情况分为浮空输入、上拉输入、下拉输入三种输入模式。
输入配置
- 输出驱动器关闭
- 施密特触发器打开,获取引脚状态
- 通过PUPDR寄存器值使能上拉/下拉电阻,选择输入模式
- 输入数据寄存器每隔一个AHB时钟周期对引脚上的数据进行一次采样
- 通过读取输入数据寄存器的值获取引脚电平状态
输出模式
根据两个MOS管的导通情况分为推挽输出和开漏输出。
输出配置
- 输出驱动器打开
开漏模式:输出寄存器中的“0”可激活N-MOS,从而输出寄存器中 的“1” 会使端口保持高阻态(P-MOS始终不激活)
推挽模式:输出寄存器中的“0”激活N-MOS,“1”激活P-MOS。 - 施密特触发器输入被打开
- 通过PUPDR寄存器值使能上拉/下拉电阻
- 输入数据寄存器每隔一个AHB时钟周期对引脚上的数据进行一次采样
- 通过读取输入数据寄存器的值获取引脚电平状态
- 对输出数据寄存器的读访问可获取最后写入值
推挽输出
P-MOS管的小圆圈表示还要进行一次反相,因此当同相端输出“1”时,反相后,P-MOS管的门级输入低电平,P-MOS管导通。
- 当输出数据寄存器对应位为“1”时,同相端输出“1”,反相端输出“0”.此时P-MOS管导通,N-MOS管截止,引脚输出高电平
- 当输出数据寄存器对应位为“0”时,同相端输出“0”,反相端输出“1”.此时P-MOS管截止,N-MOS管导通,引脚输出低电平
开漏输出
开漏输出时,P-MOS管关闭,只有N-MOS管工作,此时只能输出低电平。要输出高电平必须外加上拉电阻,主要用于I2C总线。
- 当输出数据寄存器对应位为“1”时,反相端输出“0”。此时N-MOS管截止,由外加的上拉电阻提供高电平
- 当输出数据寄存器对应位为“0”时,反相端输出“1”。此时N-MOS管导通,引脚输出低电平
模拟模式
单纯的引脚模拟状态或引脚作为片内模拟外设的复用引脚
模拟状态:表示引脚功能选择为模拟模式,但不作为任何片内模拟外设的复用引脚,只是为了减少系统功耗
模拟外设复用引脚:表示引脚作为片内模拟外设(A/D转换模块、D/A转换模块、模拟比较器等)的复用引脚,用于完成相应的功能操作
模拟配置
- 输出驱动器关闭
- 施密特触发器输入停用,I/O引脚的每个模拟值的功耗值都为0;输出强制为常量0
- 弱上拉/下拉电阻被硬件禁用
- 通过读取输入数据寄存器的值为0
复用模式
引脚电平状态由片内外设控制,一般情况下,处于复用模式的引脚的参数可借助CubeMX自动配置。
复用配置
对 I/O 端口进行编程作为复用功能时:
- 可将输出缓冲器配置为推挽或开漏模式
- 输出缓冲器由外设信号驱动(发送器使能和数据)
- 施密特触发器输入激活
- 根据PUPDR寄存器值决定弱上拉/下拉电阻使能状态
- 输入数据寄存器每隔一个AHB时钟周期对引脚上的数据进行一次采样
- 通过读取输入数据寄存器的值获取引脚电平状态
用法总结
输入模式
- 浮空输入:按键识别
- 上拉输入:IO内部上拉电阻输入
- 下拉输入:IO内部下拉电阻输入
模拟模式
- 作为片内模拟外设的对外引脚
- 单纯作为低功耗使用
输出模式
- 推挽输出:可以输出高/低电平,主要用于连接数字器件,如指示灯和继电器等模块
- 开漏输出:只能输出低电平,适合于电流型驱动,也可作为电平转换
复用模式
- 复用推挽:片内外设功能(UART的Tx,Rx;SPI的MOSI,MISO,SCK,SS)
- 复用开漏:片内外设功能(I2C的SCL,SDA)
三、GPIO模块的常用寄存器介绍
每个端口包含10个寄存器,其中x表示端口号,取值从A开始。分别介绍如下。
GPIO端口模式寄存器(GPIOx_MODER)
偏移地址:0x00
复位值:每款芯片略有差异,具体查阅参考手册
GPIO端口输出类型寄存器(GPIOx_OTYPER)
偏移地址:0x04
复位值:每款芯片略有差异,具体查阅参考手册
GPIO端口输出速度寄存器(GPIOx_OSPEEDR)
偏移地址:0x08
复位值:每款芯片略有差异,具体查阅参考手册
速度的划分参考设备数据手册和电源及负载状态,
GPIO端口上拉/下拉寄存器(GPIOx_PUPDR)
偏移地址:0x0C
复位值:每款芯片略有差异,具体查阅参考手册
GPIO端口输入数据寄存器(GPIOx_IDR)
偏移地址:0x10
复位值:每款芯片略有差异,具体查阅参考手册
这些位为只读形式,只能在字模式下访问,它们包含相应IO端口的输入值。
GPIO端口输出数据寄存器(GPIOx_ODR)
偏移地址:0x14
复位值:每款芯片略有差异,具体查阅参考手册
对于原子置位复位,可通过写BSRR寄存器进行设置。
GPIO端口置位/复位寄存器(GPIOx_BSRR)
偏移地址:0x18
复位值:每款芯片略有差异,具体查阅参考手册
这些位为只写形式,只能在字、半字或字节模式下访问。读取这些位可返回0x0000,如果BR和BS同时写入1,BS优先级高,优先输出高电平。
四、基于寄存器方式控制GPIO
1.利用指针访问单个寄存器
通过查询芯片的参考手册,可查找到各端口寄存器映射到的地址,根据寄存器起始地址及偏移量可访问指定寄存器。
地址转换方法
- 对于C语言的编译器而言,寄存器的地址值只代表一个有符号常数,无法代表地址;
- 在C语言中,利用指针类型来存放变量的地址,利用指针类型定义的变量称为指针变量;
- 利用强制类型转换可以将常数转为指针变量;
- 指针的基类型表示指针所指向变量的类型,它决定了从该地址开始可以访问的地址范围;
- 指针的解引用表示从指针所指向的地址中取出存放的数据。
unsigned int *p //表示定义一个指向无符号整型的指针
p = 0x4002000UL // 表示为指针变量P赋值,指向地址单元0x40020000
//*p即指针的解引用,表示访问从地址单元0x4002000开始的4个地址单元的内容
寄存器宏定义
#define GPIOA_MODER *(volatile unsigned int *)(0x40020000UL)
注
寄存器地址值不具备符号特性,寄存器为32位,volatile避免编译器优化
例程开发
1)新建MDK工程
- 在合适位置新建STM32_Register文件夹,表示此文件夹中存放的为寄存器操作工程
- 打开MDK软件,选择Project菜单新建工程,找到上一步创建的文件夹,创建名为LED的工程
- 选择目标MCU
- 在工具栏上点开Manager RTE,勾选CMSIS下CORE和DECVICE下Startup组件
2)新建源文件并编写用户代码
- 在工程窗口中选择Source Group1文件夹右键单击选择添加新项到该文件夹
- 选择C File类型文件,命名为main.c
- 将该文件中添加如下代码
3)编译下载
首先在工程设置的Debug标签页中,选择仿真器并在仿真器设置中勾选Reset and Run;然后编译下载。
2.利用结构体指针访问寄存器组
实际开发中我们需要访问多个寄存器,通过手册我们可看出,端口寄存器地址是连续的,那么可利用结构体来实现对连续的多个寄存器进行定义。
寄存器组宏定义
#define GPIOA ((GPIO_TypeDef *)0x40020000UL)
注
将常数转换为了结构体指针,通过结构体指针加内部成员变量的形式,即可访问硬件寄存器。
GPIOA->MODER;
芯片头文件
上述结构体方式已经被ST公司以头文件的方式提供,ST公司将STM32微控制器片内所有外设的寄存器都采用上述的方法进行了定义。
用户在使用时,只需包含该头文件,就可以通过外设结构体指针访问外设的相关寄存器。
不同型号微控制器头文件各不相同,如STM32F411系列微控制器的头文件为stm32f411xe.h。
例程开发
四、基于HAL库方式控制GPIO
使用Cube MX建立工程的流程已在HAL库介绍中介绍过,这里直接以GPIO模块为例展开实践。
1. 目标选择
- 启动CubeMX软件,选择“ACESS TO MCU SELECTOR”,进入MCU筛选器
- 在芯片搜索框中输入要使用的芯片型号系列
- 在芯片列表框中双击具体的芯片型号,启动芯片配置
2. 基础配置
1)选择时钟源
根据需要,在RCC选项卡中选择所需时钟源
2)配置调试接口
我们使用的ST-Link调试器,所以在SYS页面的Debug选项中选择Serial Wire
3.引脚分配
单击所要配置的引脚,在下拉选项中选择所要使用的模式,如控制LED,就选择GPIO_Output。可自定义引脚名称,便于识别,提高程序代码可读性。
4.外设配置
在GPIO选项卡中,在出现的引脚清单中,单击要配置的引脚,进行引脚的初始化配置:初始电平,引脚模式,上下拉电阻,频率,用户标签等。
5.时钟配置
- 修改时钟源频率
- 选择锁相环输入时钟
- 选择系统时钟源
- 设置HCLK频率,并回车
6.工程配置
设置工程名称和路径,选择IDE;在代码生成选项卡中,根据需要勾选一些选项,一般选择如下
7.生成工程
最后点击右上角的GENERATE CODE,生成对应工程初始化代码,生成完成后可点击OPEN PROJECT在IDE中打开工程代码
8.程序编写
在MDK软件中
main.h
文件中对端口和引脚取了别名,即上面步骤中定义的用户标签main.c
文件中添加用户代码,代码添加在USER CODE BEGIN 和USER CODE END之间。
上面生成的GPIO初始化代码在gpio.c中
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = LED_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(LED_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pins : PBPin PBPin */
GPIO_InitStruct.Pin = KEY1_Pin|KEY2_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}