STM32F4的GPIO区别与F1的GPIO配置,每个 I/O 端口位均可自由编程,但 I/O 端口寄存器必须按 32 位字、半字或字节进行访问。 GPIOx_BSRR 寄存器旨在实现对 GPIO ODR 寄存器进行原子读取/修改访问。这样便可确保 在读取和修改访问之间发生中断请求也不会有问题。
GPIO端口包括 4 个 32 位配置寄存器(GPIOx_MODER、GPIOx_OTYPER、GPIOx_OSPEEDR 和 GPIOx_PUPDR)、2 个 32 位数据寄存器(GPIOx_IDR 和 GPIOx_ODR)、1 个 32 位置位/复位寄存器 (GPIOx_BSRR)、1 个 32 位锁定寄存器 (GPIOx_LCKR) 和 2 个 32 位复用功能选择寄存器(GPIOx_AFRH 和 GPIOx_AFRL)。
一、 GPIO寄存器(端口控制 数据输入输出 置位/复位 锁定 复用)
I/O 端口控制寄存器
GPIO 有 4 个 32 位存储器映射的控制寄存器(GPIOx_MODER、GPIOx_OTYPER、 GPIOx_OSPEEDR、GPIOx_PUPDR),可配置多达 16 个 I/O。
GPIOx_MODER 寄存器用于 选择 I/O 方向(输入、输出、AF、模拟)。
MODERy[1:0]:端口 x 配置位 (Port x configuration bits) (y = 0..15)
这些位通过软件写入,用于配置 I/O 方向模式。
00:输入(复位状态)
01:通用输出模式
10:复用功能模式
11:模拟模式
GPIOx_OTYPER 和 GPIOx_OSPEEDR 寄存器分 别用于选择输出类型(推挽或开漏)和速度 (无论采用哪种 I/O 方向,都会直接将 I/O 速度引 脚连接到相应的 GPIOx_OSPEEDR 寄存器位)。无论采用哪种 I/O 方向,GPIOx_PUPDR 寄 存器都用于选择上拉/下拉。
输出类型配置寄存器OTy[1:0]:端口 x 配置位 (Port x configuration bits) (y = 0..15)
这些位通过软件写入,用于配置 I/O 端口的输出类型。
0:输出推挽(复位状态)
1:输出开漏
输出速度配置寄存器OSPEEDRy[1:0]:端口 x 配置位 (Port x configuration bits) (y = 0..15)
这些位通过软件写入,用于配置 I/O 输出速度。
00:2 MHz(低速)
01:25 MHz(中速)
10:50 MHz(快速)
11:30 pF 时为 100 MHz(高速)(15 pF 时为 80 MHz 输出(最大速度))
端口上拉/下拉寄存器PUPDRy[1:0]:端口 x 配置位 (Port x configuration bits) (y = 0..15)
这些位通过软件写入,用于配置 I/O 上拉或下拉。
00:无上拉或下拉
01:上拉
10:下拉
11:保留
I/O 端口数据寄存器
每个 GPIO 都具有 2 个 16 位数据寄存器:输入和输出数据寄存器(GPIOx_IDR 和 GPIOx_ODR)。GPIOx_ODR 用于存储待输出数据,可对其进行读/写访问。通过 I/O 输入的数据存储到输入数据寄存器 (GPIOx_IDR) 中,它是一个只读寄存器。
端口输入寄存器IDRy[15:0]:端口输入数据 (Port input data) (y = 0..15)
这些位为只读形式,只能在字模式下访问。它们包含相应 I/O 端口的输入值。
端口输出寄存器IODRy[15:0]:端口输出数据 (Port output data) (y = 0..15)
这些位可通过软件读取和写入。
注意: 对于原子置位 /复位,通过写入 GPIOx_BSRR 寄存器,可分别对 ODR 位进行置位和复位 (x = A..I/)。
I/O 数据位操作
端口置位/复位寄存器(GPIOx_BSRR)
BRy:端口 x 复位位 y (Port x reset bit y) (y = 0..15)
这些位为只写形式,只能在字、半字或字节模式下访问。读取这些位可返回值 0x0000。
0:不会对相应的 ODRx 位执行任何操作
1:对相应的 ODRx 位进行复位
注意: 如果同时对 BSx 和 BRx 置位,则 BSx 的优先级更高。
位 15:0 BSy:端口 x 置位位 y (Port x set bit y) (y= 0..15)
这些位为只写形式,只能在字、半字或字节模式下访问。读取这些位可返回值 0x0000。
0:不会对相应的 ODRx 位执行任何操作
1:对相应的 ODRx 位进行置位
I/O锁定机制
GPIO 端口配置锁定寄存器 (GPIOx_LCKR) (x = A..I)
GPIO port configuration lock register
当正确的写序列应用到第 16 位 (LCKK) 时,此寄存器将用于锁定端口位的配置。位 [15:0] 的 值用于锁定 GPIO 的配置。在写序列期间,不能更改 LCKR[15:0] 的值。将 LOCK 序列应用 到某个端口位后,在执行下一次复位之前,将无法对该端口位的值进行修改。
注意:可使用特定的写序列对 GPIOx_LCKR 寄存器执行写操作。在此写序列期间只允许使用字访问 (32 位长)。
每个锁定位冻结一个特定的配置寄存器(控制寄存器和复用功能寄存器)。
LCKK[16]:锁定键 (Lock key)
可随时读取此位。可使用锁定键写序列对其进行修改。
0:端口配置锁定键未激活。
1:端口配置锁定键已激活。直到 MCU 复位时,才锁定 GPIOx_LCKR 寄存器。
锁定键写序列:
WR LCKR[16] = ‘1’ + LCKR[15:0]
WR LCKR[16] = ‘0’ + LCKR[15:0]
WR LCKR[16] = ‘1’ + LCKR[15:0]
RD LCKR
RD LCKR[16] = ‘1’(此读操作为可选操作,但它可确认锁定已激活)
注意: 在锁定键写序列期间,不能更改 LCK[15:0] 的值。
锁定序列中的任何错误都将中止锁定操作。
在任一端口位上的第一个锁定序列之后,对 LCKK 位的任何读访问都将返回“1”,直
到下一次 CPU 复位为止。
位 15:0 LCKy:端口 x 锁定位 y (Port x lock bit y) (y= 0..15)
这些位都是读/写位,但只能在 LCKK 位等于“0”时执行写操作。
0:端口配置未锁定
1:端口配置已锁定
GPIO 复用功能低位寄存器 (GPIOx_AFRL) (x = A..I) GPIO 复用功能高位寄存器 (GPIOx_AFRH) (x = A..I)
AFRHy:端口 x 位 y 的复用功能选择 (Alternate function selection for port x bit y) (y = 8.0.15)
这些位通过软件写入,用于配置复用功能 I/O。
AFRHy 选择:
0000:AF0 0001:AF1 0010:AF2 0011:AF3
0100:AF4 0101:AF5 0110:AF6 0111:AF7
1000:AF8 1001:AF9 1010:AF10 1011:AF11
1100:AF12 1101:AF13 1110:AF14 1111:AF15
二、GPIO框图解析
通过GPIO 硬件结构框图,就可以从整体上深入了解GPIO 外设及它的各种应用模式。该图从最右端看起,最右端就是代表STM32 芯片引出的GPIO 引脚,其余部件都位于芯片内部。
1. 保护二极管及上、下拉电阻
引脚的两保护个二级管可以防止引脚外部过高或过低的电压输入,当引脚电压高于VDD_FT 时,上方的二极管导通,当引脚电压低于VSS 时,下方的二极管导通,防止不正常电压引入芯片导致芯片烧毁。尽管有这样的保护,并不意味着STM32 的引脚能直接外接大功率驱动器件,如直接驱动电机,强制驱动要么电机不转,要么导致芯片烧坏,必须要加大功率及隔离电路驱动。具体电压、电流范围可查阅《STM32F4xx 规格书》。
上拉、下拉电阻,从它的结构我们可以看出,通过上、下拉对应的开关配置,我们可以控制引脚默认状态的电压,开启上拉的时候引脚电压为高电平,开启下拉的时候引脚电压为低电平,这样可以消除引脚不定状态的影响。如引脚外部没有外接器件,或者外部的器件不干扰该引脚电压时,STM32 的引脚都会有这个默认状态。 也可以设置“既不上拉也不下拉模式”,我们也把这种状态称为浮空模式,配置成这个模式时,直接用电压表测量其引脚电压为1 点几伏,这是个不确定值。所以一般来说我们都会选择给引脚设置“上拉模式”或“下拉模式”使它有默认状态。
STM32 的内部上拉是“弱上拉”,即通过此上拉输出的电流是很弱的,如要求大电流还是需要外部上拉。
通过“上拉/下拉寄存器GPIOx_PUPDR”控制引脚的上、下拉以及浮空模式。
2. P-MOS 管和N-MOS 管
GPIO 引脚线路经过上、下拉电阻结构后,向上流向“输入模式”结构,向下流向“输出模式”结构。先看输出模式部分,线路经过一个由P-MOS 和N-MOS 管组成的单元电路。这个结构使GPIO 具有了“推挽输出”和“开漏输出”两种模式。
所谓的推挽输出模式,是根据这两个MOS 管的工作方式来命名的。在该结构中输入高电平时,上方的P-MOS 导通,下方的N-MOS 关闭,对外输出高电平;而在该结构中输入低电平时,N-MOS 管导通,P-MOS 关闭,对外输出低电平。当引脚高低电平切换时,两个管子轮流导通,一个负责灌电流,一个负责拉电流,使其负载能力和开关速度都比普通的方式有很大的提高。推挽输出的低电平为0 伏,高电平为3.3 伏,参考图 7-2 左侧,它是推挽输出模式时的等效电路。
而在开漏输出模式时,上方的P-MOS 管完全不工作。如果我们控制输出为0,低电平,则P-MOS 管关闭,N-MOS 管导通,使输出接地,若控制输出为1 (它无法直接输出高电平)时,则P-MOS 管和N-MOS 管都关闭,所以引脚既不输出高电平,也不输出低电平,为高阻态。为正常使用时必须接上拉电阻(可用STM32 的内部上拉,但建议在STM32 外部再接一个上拉电阻),参考图 7-2 中的右侧等效电路。它具“线与”特性,也就是说,若有很多个开漏模式引脚连接到一起时,只有当所有引脚都输出高阻态,才由上拉电阻提供高电平,此高电平的电压为外部上拉电阻所接的电源的电压。若其中一个引脚为低电平,那线路就
相当于短路接地,使得整条线路都为低电平,0 伏。推挽输出模式一般应用在输出电平为0 和3.3 伏而且需要高速切换开关状态的场合。
在STM32 的应用中,除了必须用开漏模式的场合,我们都习惯使用推挽输出模式。 开漏输出一般应用在I2C、SMBUS 通讯等需要“线与”功能的总线电路中。除此之外,还用在电平不匹配的场合,如需要输出5 伏的高电平,就可以在外部接一个上拉电阻,上拉电源为5 伏,并且把GPIO 设置为开漏模式,当输出高阻态时,由上拉电阻和电源向外输出5 伏的电平。
通过 “输出类型寄存器GPIOx_OTYPER”可以控制GPIO 端口是推挽模式还是开漏模式。
3. 输出数据寄存器
前面提到的双MOS 管结构电路的输入信号,是由GPIO“输出数据寄存器GPIOx_ODR”提供的,因此我们通过修改输出数据寄存器的值就可以修改GPIO 引脚的输出电平。而“置位/复位寄存器GPIOx_BSRR”可以通过修改输出数据寄存器的值从而影响电路的输出。
4. 复用功能输出
“复用功能输出”中的“复用”是指STM32 的其它片上外设对GPIO 引脚进行控制,此时GPIO 引脚用作该外设功能的一部分,算是第二用途。从其它外设引出来的“复用功能输出信号”与GPIO 本身的数据据寄存器都连接到双MOS 管结构的输入中,通过图中的梯形结构作为开关切换选择。
例如我们使用USART 串口通讯时,需要用到某个GPIO 引脚作为通讯发送引脚,这个时候就可以把该GPIO 引脚配置成USART 串口复用功能,由串口外设控制该引脚,发送数据。
5. 输入数据寄存器
看GPIO 结构框图的上半部分,它是GPIO 引脚经过上、下拉电阻后引入的,它连接到施密特触发器,信号经过触发器后,模拟信号转化为0、1 的数字信号,然后存储在“输入数据寄存器GPIOx_IDR”中,通过读取该寄存器就可以了解GPIO 引脚的电平状态。
6. 复用功能输入
与“复用功能输出”模式类似,在“复用功能输出模式”时,GPIO 引脚的信号传输到STM32 其它片上外设,由该外设读取引脚状态。
同样,如我们使用USART 串口通讯时,需要用到某个GPIO 引脚作为通讯接收引脚,这个时候就可以把该GPIO 引脚配置成USART 串口复用功能,使USART 可以通过该通讯引脚的接收远端数据。
7. 模拟输入输出
当GPIO 引脚用于ADC 采集电压的输入通道时,用作“模拟输入”功能,此时信号是不经过施密特触发器的,因为经过施密特触发器后信号只有0、1 两种状态,所以ADC 外设要采集到原始的模拟信号,信号源输入必须在施密特触发器之前。类似地,当GPIO 引脚用于DAC 作为模拟电压输出通道时,此时作为“模拟输出”功能,DAC 的模拟信号输出就不经过双MOS 管结构了,在GPIO 结构框图的右下角处,模拟信号直接输出到引脚。同时,当GPIO 用于模拟功能时(包括输入输出),引脚的上、下拉电阻是不起作用的,这个时候即使在寄存器配置了上拉或下拉模式,也不会影响到模拟信号的输入输出。
三、GPIO工作模式
1. 输入模式(上拉/下拉/浮空)
在输入模式时,施密特触发器打开,输出被禁止。数据寄存器每隔1 个AHB1 时钟周期更新一次,可通过输入数据寄存器GPIOx_IDR 读取I/O 状态。其中AHB1 的时钟如按默认配置一般为180MHz。
用于输入模式时,可设置为上拉、下拉或浮空模式。
2. 输出模式(推挽/开漏,上拉/下拉)
在输出模式中,输出使能,推挽模式时双MOS 管以方式工作,输出数据寄存器GPIOx_ODR 可控制I/O 输出高低电平。开漏模式时,只有N-MOS 管工作,输出数据寄存器可控制I/O 输出高阻态或低电平。输出速度可配置,有2MHz\25MHz\50MHz\100MHz 的选项。此处的输出速度即I/O 支持的高低电平状态最高切换频率,支持的频率越高,功耗越大,如果功耗要求不严格,把速度设置成最大即可。
此时施密特触发器是打开的,即输入可用,通过输入数据寄存器GPIOx_IDR 可读取I/O 的实际状态。
用于输出模式时,可使用上拉、下拉模式或浮空模式。但此时由于输出模式时引脚电平会受到ODR 寄存器影响,而ODR 寄存器对应引脚的位为0,即引脚初始化后默认输出低电平,所以在这种情况下,上拉只起到小幅提高输出电流能力,但不会影响引脚的默认状态(弱上拉)。
3. 复用功能(推挽/开漏,上拉/下拉)
复用功能模式中,输出使能,输出速度可配置,可工作在开漏及推挽模式,但是输出信号源于其它外设,输出数据寄存器GPIOx_ODR 无效;输入可用,通过输入数据寄存器可获取I/O 实际状态,但一般直接用外设的寄存器来获取该数据信号。
用于复用功能时,可使用上拉、下拉模式或浮空模式。同输出模式,在这种情况下,初始化后引脚默认输出低电平,上拉只起到小幅提高输出电流能力,但不会影响引脚的默认状态(弱上拉)。
4. 模拟输入输出
模拟输入输出模式中,双MOS 管结构被关闭,施密特触发器停用,上/下拉也被禁止。其它外设通过模拟通道进行输入输出。
通过对GPIO 寄存器写入不同的参数,就可以改变GPIO 的应用模式,再强调一下,要了解具体寄存器时一定要查阅《STM32F4xx 参考手册》中对应外设的寄存器说明。在GPIO 外设中,通过设置“模式寄存器GPIOx_MODER”可配置GPIO 的输入/输出/复用/模拟模式,“输出类型寄存器GPIOx_OTYPER”配置推挽/开漏模式,配置“输出速度寄存器GPIOx_OSPEEDR”可选2/25/50/100MHz 输出速度,“上/下拉寄存器GPIOx_PUPDR”可配置上拉/下拉/浮空模式,各寄存器的具体参数值见表 7-1。
注意:在模拟配置中,I/O 引脚不能为 5 V 容忍。
图 23 说明了 I/O 端口位的高阻态模拟输入配置。
四、HAL库GPIO配置
/**
* @brief GPIO 初始化结构体类型定义
*/
typedef struct
{
uint32_t Pin; /*!< 指定要配置的GPIO引脚 */
uint32_t Mode; /*!< 指定所选引脚的工作模式 */
uint32_t Pull; /*!< 指定所选引脚的上拉或下拉激活 */
uint32_t Speed; /*!< 指定所选引脚的速度 */
uint32_t Alternate; /*!< 要连接到所选引脚的外设 */
}GPIO_InitTypeDef;
00163 /**
00164 * @brief Initializes the GPIOx peripheral according to the specified parameters in the GPIO_Init.
00165 * @param GPIOx where x can be (A..K) to select the GPIO peripheral for STM32F429X device or
00166 * x can be (A..I) to select the GPIO peripheral for STM32F40XX and STM32F427X devices.
00167 * @param GPIO_Init pointer to a GPIO_InitTypeDef structure that contains
00168 * the configuration information for the specified GPIO peripheral.
00169 * @retval None
00170 */
00171 void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init)
00172 {
00173 uint32_t position;
00174 uint32_t ioposition = 0x00U;
00175 uint32_t iocurrent = 0x00U;
00176 uint32_t temp = 0x00U;
00177
00178 /* Check the parameters 断言检测输入参数 */
00179 assert_param(IS_GPIO_ALL_INSTANCE(GPIOx));
00180 assert_param(IS_GPIO_PIN(GPIO_Init->Pin));
00181 assert_param(IS_GPIO_MODE(GPIO_Init->Mode));
00182 assert_param(IS_GPIO_PULL(GPIO_Init->Pull));
00183
00184 /* Configure the port pins 配置GPIO引脚 */
00185 for(position = 0U; position < GPIO_NUMBER; position++)
00186 {
00187 /*以下运算是为了通过 GPIO_InitStruct->GPIO_Pin 算出引脚号0-15*/
/*经过运算后pos的pinpos位为1,其余为0,与GPIO_Pin_x宏对应。pinpos变量每次循环加1,*/
00188 ioposition = 0x01U << position;
00189 /* pos与GPIO_InitStruct->Pin做 & 运算,若运算结果currentpin == pos,
则表示GPIO_InitStruct->Pin的pinpos位也为1,
从而可知pinpos就是GPIO_InitStruct->Pin对应的引脚号:0-15*/
00190 iocurrent = (uint32_t)(GPIO_Init->Pin) & ioposition;
00191
00192 if(iocurrent == ioposition)
00193 {
00194 /*--------------------- GPIO Mode Configuration ------------------------*/
00195 /* In case of Alternate function mode selection */
/* 在复用功能模式选择的情况下 */
00196 if((GPIO_Init->Mode == GPIO_MODE_AF_PP) || (GPIO_Init->Mode == GPIO_MODE_AF_OD))
00197 {
00198 /* Check the Alternate function parameter */
00199 assert_param(IS_GPIO_AF(GPIO_Init->Alternate));
00200 /* Configure Alternate function mapped with the current IO */
/* 配置与当前IO映射的备用功能 */
00201 temp = GPIOx->AFR[position >> 3U];
00202 temp &= ~(0xFU << ((uint32_t)(position & 0x07U) * 4U)) ;
00203 temp |= ((uint32_t)(GPIO_Init->Alternate) << (((uint32_t)position & 0x07U) * 4U));
00204 GPIOx->AFR[position >> 3U] = temp;
00205 }
00206
00207 /* Configure IO Direction mode (Input, Output, Alternate or Analog) */
/* 配置IO方向模式(输入,输出,复用或模拟) */
00208 temp = GPIOx->MODER;
00209 temp &= ~(GPIO_MODER_MODER0 << (position * 2U));
00210 temp |= ((GPIO_Init->Mode & GPIO_MODE) << (position * 2U));
00211 GPIOx->MODER = temp;
00212
00213 /* In case of Output or Alternate function mode selection */
/* 在输出或复用功能模式选择的情况下 */
00214 if((GPIO_Init->Mode == GPIO_MODE_OUTPUT_PP) || (GPIO_Init->Mode == GPIO_MODE_AF_PP) ||
00215 (GPIO_Init->Mode == GPIO_MODE_OUTPUT_OD) || (GPIO_Init->Mode == GPIO_MODE_AF_OD))
00216 {
00217 /* Check the Speed parameter */
00218 assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
00219 /* Configure the IO Speed */
/* 配置速度参数 */
00220 temp = GPIOx->OSPEEDR;
00221 temp &= ~(GPIO_OSPEEDER_OSPEEDR0 << (position * 2U));
00222 temp |= (GPIO_Init->Speed << (position * 2U));
00223 GPIOx->OSPEEDR = temp;
00224
00225 /* Configure the IO Output Type */
/* 配置IO输出类型 */
00226 temp = GPIOx->OTYPER;
00227 temp &= ~(GPIO_OTYPER_OT_0 << position) ;
00228 temp |= (((GPIO_Init->Mode & GPIO_OUTPUT_TYPE) >> 4U) << position);
00229 GPIOx->OTYPER = temp;
00230 }
00231
00232 /* Activate the Pull-up or Pull down resistor for the current IO */
/* 激活当前IO的上拉或下拉电阻 */
00233 temp = GPIOx->PUPDR;
00234 temp &= ~(GPIO_PUPDR_PUPDR0 << (position * 2U));
00235 temp |= ((GPIO_Init->Pull) << (position * 2U));
00236 GPIOx->PUPDR = temp;
00237
00238 /*--------------------- EXTI Mode Configuration ------------------------*/
00239 /* Configure the External Interrupt or event for the current IO */
/* 配置外部中断或事件为IO触发源 */
00240 if((GPIO_Init->Mode & EXTI_MODE) == EXTI_MODE)
00241 {
00242 /* Enable SYSCFG Clock */
00243 __HAL_RCC_SYSCFG_CLK_ENABLE();
00244
00245 temp = SYSCFG->EXTICR[position >> 2U];
00246 temp &= ~(0x0FU << (4U * (position & 0x03U)));
00247 temp |= ((uint32_t)(GPIO_GET_INDEX(GPIOx)) << (4U * (position & 0x03U)));
00248 SYSCFG->EXTICR[position >> 2U] = temp;
00249
00250 /* Clear EXTI line configuration */
/* 清除中断线配置 */
00251 temp = EXTI->IMR;
00252 temp &= ~((uint32_t)iocurrent);
00253 if((GPIO_Init->Mode & GPIO_MODE_IT) == GPIO_MODE_IT)
00254 {
00255 temp |= iocurrent;
00256 }
00257 EXTI->IMR = temp;
00258
00259 temp = EXTI->EMR;
00260 temp &= ~((uint32_t)iocurrent);
00261 if((GPIO_Init->Mode & GPIO_MODE_EVT) == GPIO_MODE_EVT)
00262 {
00263 temp |= iocurrent;
00264 }
00265 EXTI->EMR = temp;
00266
00267 /* Clear Rising Falling edge configuration */
/* 清除上升下降沿配置 */
00268 temp = EXTI->RTSR;
00269 temp &= ~((uint32_t)iocurrent);
00270 if((GPIO_Init->Mode & RISING_EDGE) == RISING_EDGE)
00271 {
00272 temp |= iocurrent;
00273 }
00274 EXTI->RTSR = temp;
00275
00276 temp = EXTI->FTSR;
00277 temp &= ~((uint32_t)iocurrent);
00278 if((GPIO_Init->Mode & FALLING_EDGE) == FALLING_EDGE)
00279 {
00280 temp |= iocurrent;
00281 }
00282 EXTI->FTSR = temp;
00283 }
00284 }
00285 }
00286 }
00287