【STM32Cube开发记录】8-GPIO功能

几乎所有的MCU芯片都提供有不同数量的引脚,这些引脚可以用作GPIO。这些GPIO也是和外部世界进行通信的方式,例如最简单的LED,还有UART、USB、SPI等。再了解GPIO的使用方法之前,可以先了解下STM32外设如何映射到逻辑地址以及它们在HAL库中的表示方式。

1 STM32外设映射

每个STM32外设都通过几个总线连接到MCU。

system bus连接了Cortex-M Core到总线矩阵(Bus Matrix)。Bus Matrix负责管理Cortex-M Core和DMA之间的仲裁。Cortex-M Core和DMA都充当主设备。

DMA bus连接了DMA的Advanced High-performance Bus(AHB)主接口到Bus Matrix,Bus Matrix管理CPU和DMA对SRAM、闪存存储器和外设的访问。

Bus Matrix管理Cortex-M Cored的system bus和DMA bus之间的访问仲裁,采用了循环轮询算法。总线矩阵由两个主设备(CPU、DMA)和四个从设备(闪存存储接口、SRAM、具有AHB到高级外设总线(APB)桥接的AHB1和AHB2)组成。AHB外设通过总线矩阵连接到系统总线,以允许DMA访问。AHB到APB桥提供了AHB总线和APB总线之间的全同步连接,其中大多数外设连接到APB总线。

【STM32Cube开发记录】5-寄存器和存储器映射中提到的外设映射到一个4GB的地址。从0x4000 0000到最高0x5FFF FFFF。这个区域被进一步划分几个区域。

这种分配方式,是特定于给定的STM32微控制器的。例如,在STM32F072微控制器中,AHB2总线映射到地址范围从0x48000000到0x480017FF。这意味着这个区域宽度为6144字节。这个区域进一步分为多个子区域,每个子区域对应一个特定的外设。以前面的例子为例,GPIOA外设从0x48000000到0x480003FF映射,这意味着它占用了1KB的地址映射外设存储空间。

这个内存映射空间的分配方式取决于具体的外设。下表显示了GPIO外设的内存布局。这种内存映射结构使MCU能够直接访问外设的寄存器和功能,从而实现与外部世界的通信和控制。每个外设都有自己的地址范围,其中包含了与该外设相关的寄存器和功能。这使得软件能够轻松地与外设进行交互和配置。

 外设就是通过修改和读取这些映射区域的每个寄存器来进行控制。举个例子,以GPIOA外设为例,要将PA4引脚配置为输出引脚,我们需要配置MODER寄存器,使位[9:8]配置为01(对应于通用输出模式)。

接下来,为了将引脚拉高,我们需要设置Output Data Register(ODR)中相应的位[4],该寄存器映射到GPIOA + 0x14的内存位置,即0x48000000 + 0x14。以下是一个示例,展示了如何使用指针来访问STM32F72微控制器中GPIOA外设的内存映射:

volatile uint32_t *GPIOA_MODER = 0x0, *GPIOA_ODR = 0x0; 

GPIOA_MODER = (uint32_t*)0x48000000; // GPIOA->MODER 寄存器地址 
GPIOA_ODR = (uint32_t*)(0x48000000 + 0x14); // GPIOA->ODR 寄存器地址

*GPIOA_MODER = *GPIOA_MODER | 0x100; // Sets MODER[9:8] = 0x1 
*GPIOA_ODR = *GPIOA_ODR | 0x10; // Sets ODR[4] = 0x1, PA4输出高电平

HAL的作用就是将上面这种具体的外设映射进行抽象化,通过为每个外设定义多个处理程序来实现这一点。处理程序实际上就是一个C结构体,其引用用于指向实际的外设地址。我们通常可以看到下面配置GPIOA4的例子

GPIO_InitStruct.Pin = GPIO_PIN_4; 
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; 
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

这里面的GPIOA变量是一个指向GPIO_TypeDef类型的指针,它的定义如下

typedef struct { 
volatile uint32_t MODER; 
volatile uint32_t OTYPER; 
volatile uint32_t OSPEEDR; 
volatile uint32_t PUPDR; 
volatile uint32_t IDR; 
volatile uint32_t ODR; 
volatile uint32_t BSRR; 
volatile uint32_t LCKR; 
volatile uint32_t AFR[2]; 
volatile uint32_t BRR; 
} GPIO_TypeDef;

GPIOA指向的地址就是:

GPIO_TypeDef *GPIOA = 0x48000000; 

GPIOA->MODER |= 0x100; 
GPIOA->ODR |= 0x10;

2 GPIO的配置

HAL提供了一些接口给我们去初始化GPIO,不需要使用者去知道如何配置它的寄存器。我们使用HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init)函数配置GPIO。其中GPIO_InitTypeDef是一个C的结构体,定义如下:

typedef struct { 
uint32_t Pin; 
uint32_t Mode; 
uint32_t Pull; 
uint32_t Speed; 
uint32_t Alternate; 
} GPIO_InitTypeDef;

Pin(引脚):从0开始的引脚数字,比如,GPIO_PIN_4。可以一次性配置多个引脚,(GPIO_PIN_1 | GPIO_PIN_5 | GPIO_PIN_6)。

Mode(模式):引脚模式。可配置以下选项:

  • GPIO_MODE_INPUT:浮空输入模式
  • GPIO_MODE_OUTPUT_PP:推挽输出模式
  • GPIO_MODE_OUTPUT_OD:开漏输出模式
  • GPIO_MODE_AF_PP:复用推挽输出模式
  • GPIO_MODE_AF_OD:复用开漏输出模式
  • GPIO_MODE_ANALOG:模拟输入模式
  • GPIO_MODE_IT_RISING:外部中断模式,上升沿触发检测
  • GPIO_MODE_IT_FALLING:外部中断模式,下降沿触发检测
  • GPIO_MODE_IT_RISING_FALLING:外部中断模式,上升沿和下降沿触发检测
  • GPIO_MODE_EVT_RISING:外部事件模式,上升沿触发检测
  • GPIO_MODE_EVT_FALLING:外部事件模式,下降沿触发检测
  • GPIO_MODE_EVT_RISING_FALLING:外部事件模式,上升沿和下降沿触发检测

Pull(上拉):配置上拉下拉。如果处于输出模式,一般配置为NOPULL,当处于输入模式,根据默认输入值来进行配置。上拉就是将不确定的信号通过一个电阻钳位在高电平,电阻同时起到限流作用;而下拉就是将不确定的信号通过一个电阻钳位在低电平,电阻同时起到限流作用(开漏输出假如不连接外部的上拉电阻,则只能输出低电平)。

  • GPIO_NOPULL:无上拉或下拉
  • GPIO_PULLUP :上拉
  • GPIO_PULLDOWN : 下拉

Speed(速度) :定义Pin引脚的开关速度。这个速度是几个常量值,MCU的GPIO都有最大的开关频率。

  • GPIO_SPEED_FREQ_LOW
  • GPIO_SPEED_FREQ_MEDIUM
  • GPIO_SPEED_FREQ_HIGH       
  • GPIO_SPEED_FREQ_VERY_HIGH   

Alternate(复用):复用功能,选择你需要连接的外设。

2.1 GPIO模式

STM32的IO口硬件结构如下,通过GPIO_InitTypeDef的配置,MCU的IO会变更以下工作方式。

(GPIO模式GPIO_MODE_EVT_*与睡眠模式有关。当 I/O 配置为在这些模式之一下工作时,如果触发了相应的 I/O,CPU 将被唤醒(当使用 WFE 指令置于睡眠模式时),而不会产生相应的中断。GPIO模式GPIO_MODE_IT_*模式与中断管理有关。这可能由 STM32 系列不一样而异,尤其是对于低功耗系列。必要时务必参考 MCU 的参考手册)
这种设计也可以优化PCB的设计,比如你外部需要使用上拉电阻,无需在单独配置专用的电阻,相应的GPIO可以进行配置,节省成本和空间:

  • GPIO_InitTypeDef.Mode = GPIO_MODE_OUTPUT_PP
  • GPIO_InitTypeDef.Pull = GPIO_PULLUP

2.2 GPIO的复用功能

大多数GPIO都具有复用功能,它们可以用作至少一个内部外设的I/O引脚。但是,一个 I/O 一次只能关联到一个外设。如果想知道哪些IO口可以复用哪些功能,可以查手册,也可以在CubeMX的IO配置直接查看PIN脚。之后会自动生成代码,给出一个示例:

​​​​​​​GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
GPIO_InitStruct.Alternate = GPIO_AF1_USART2;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

2.3 GPIO几个关键函数

CubeHAL提供了一些针对GPIO日常高频率使用的函数。

1. 读IO口的函数

GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
当 I/O是低电平时,返回GPIO_PIN_RESET,如果I/O是高电平时,返回GPIO_PIN_SET。

2. 改变IO口状态函数

void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)

3. 翻转IO口状态函数

void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)

4. 锁定IO口状态
任何更改其配置的后续尝试都将失败,直到发生reset

HAL_StatusTypeDef HAL_GPIO_LockPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)

2.4 取消初始化GPIO

可以将 GPIO 引脚设置为其默认重置状态,即输入浮空模式。

void HAL_GPIO_DeInit(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin)

这个函数在对资源或功耗进行管理的情况下非常有用:

  • 当不再需要特定的外设时。
  • 在 CPU 进入休眠模式时,以避免浪费功耗。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值