GPIO基础知识
GPIO配置是所有外设驱动的基础。
STM32H7 的 GPIO 特性如下:
① 输出状态:开漏/推挽 + 上拉/下拉电阻。
② 通过输出数据寄存器(GPIOx_ODR)或者外设(GPIO 设置为复用模式时)输出数据。
③ GPIO 速度等级设置。
④ 输入状态:浮空,上拉/下拉,模拟。
⑤ 通过输入数据寄存器(GPIOx_IDR)或者外设(GPIO 设置为复用模式)输入数据。
⑥ 通过寄存器 GPIOx_BSRR 实现对寄存器 GPIOx_ODR 的位操作。
⑦ 通过配置寄存器 GPIOx_LCKR 的锁机制,实现冻结 IO 口配置。
⑧ 每两个时钟周期就可以翻转一次 IO。
⑨ 高度灵活的引脚复用功能,允许 IO 引脚既可以做 GPIO 也可以做功能复用。
STM32H7 的 GPIO 端口可以配置为如下的 8 种模式: ① 输入浮空
② 输入上拉
③ 输入下拉
④ 模拟功能
⑤ 具有上拉或下拉功能的开漏输出
⑥ 具有上拉或下拉功能的推挽输出
⑦ 具有上拉或下拉功能的复用功能推挽
⑧ 具有上拉或下拉功能的复用功能开漏
由于上拉和下拉是可选配置,对应的 HAL 库配置使用下面 6 种就可以表示:
① GPIO_MODE_INPUT 输入模式
② GPIO_MODE_OUTPUT_PP 推挽输出
③ GPIO_MODE_OUTPUT_OD 开漏输出
④ GPIO_MODE_AF_PP 复用推挽
⑤ GPIO_MODE_AF_OD 复用开漏
⑥ GPIO_MODE_ANALOG 模拟模式
开漏输出模式输出高电平的驱动能力完全由外接上拉电阻决定,但是其输出低电平的驱动能力很强。
相对于开漏输出模式,推挽输出最大优势是输出高电平时,上升时间快,电压驱动能力强。
IO 补偿单元用于控制 I/O 通信压摆率(tfall / trise)以此来降低 I/O 噪声。当前 STM32H7 的速度等级可以配置为以下四种:
#define GPIO_SPEED_FREQ_LOW ((uint32_t)0x00000000U) /*!< Low speed */
#define GPIO_SPEED_FREQ_MEDIUM ((uint32_t)0x00000001U) /*!< Medium speed */
#define GPIO_SPEED_FREQ_HIGH ((uint32_t)0x00000002U) /*!< Fast speed */
#define GPIO_SPEED_FREQ_VERY_HIGH ((uint32_t)0x00000003U) /*!< High speed */
使用后两种速度等级的话,最好使能 IO 补偿单元。
从功耗和防干扰考虑,不使用的引脚设置为模拟模式,悬空即可。
硬件连接
将 LED,独立按键五项摇杆按键的跳线(KEY JMP)接好。(其实已经默认用跳帽连接好了,如图所示)
我们使用的是LED1,其对应的IO端口是PB6;使用CTR(五项摇杆中间)按键,其对应的IO端口是PI11。
本实验的操作与现象是:按下 CTR(五项摇杆中间)按键 LED1会熄灭,松开 LED1会亮。
实验步骤与代码详解
本实验是用MDK-ARM调试,基于HAL库的,开发板为STM32H743ⅡT6。
需要包括的头文件 | |
---|---|
main.c | main.h、gpio.h |
main.h | stm32h7xx_hal.h |
gpio.c | gpio.h |
gpio.h | main.h |
① GPIO初始化
gpio.h中定义GPIO初始化函数:
void MX_GPIO_Init(void);
main.h中对所要用的引脚与端口进行宏定义:
#define KEY_Pin GPIO_PIN_11
#define KEY_GPIO_Port GPIOI
#define LED_Pin GPIO_PIN_6
#define LED_GPIO_Port GPIOB
GPIO_PIN_11、GPIOI、GPIO_PIN_6、GPIOB已在stm32h7xx_hal.h中定义过,故main.h要包括该头文件,而在main.h中进行宏定义,是为了方便移植,其作用相当于别名。
gpio.c中实现GPIO初始化函数:
(1)定义GPIO_InitTypeDef类型的结构体GPIO_InitStruct
GPIO_InitTypeDef GPIO_InitStruct = {0};
具体查看GPIO_InitTypeDef的结构体,在stm32h7xx_hal_gpio.h中进行了定义,其结构体元素包括Pin、Mode、Pull、Speed、Alternate
typedef struct
{
uint32_t Pin; //指定要配置的GPIO引脚
uint32_t Mode; //指定选定接点的工作模式
uint32_t Pull; //指定选定接点的上拉或下拉激活
uint32_t Speed; //指定选定接点的速度
uint32_t Alternate; //要连接到所选引脚的外围设备
}GPIO_InitTypeDef;
(2)使能GPIOI、GPIOH、GPIOB的时钟
/* GPIO Ports Clock Enable 使能GPIO端口时钟 */
__HAL_RCC_GPIOI_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
(3)配置GPIO引脚输出电平
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
(4)选择结构体GPIO_InitStruct的元素参数,并调用HAL库函数HAL_GPIO.Init初始化
/*配置CTR即PI11 */
GPIO_InitStruct.Pin = KEY_Pin; //选择引脚KEY
GPIO_InitStruct.Mode = GPIO_MODE_INPUT; //输入模式
GPIO_InitStruct.Pull = GPIO_PULLUP; //上拉
HAL_GPIO_Init(KEY_GPIO_Port, &GPIO_InitStruct);
/*配置LED1即PB6 */
GPIO_InitStruct.Pin = LED_Pin; //选择引脚LED
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; //推挽输出
GPIO_InitStruct.Pull = GPIO_PULLUP; //上拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; //高速
HAL_GPIO_Init(LED_GPIO_Port, &GPIO_InitStruct);
其中,库函数HAL_GPIO_Init的定义(位于stm32h7xx_hal_gpio.h中)如下,入口参数为指针类型的GPIO端口与GPIO结构体
void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init)
② main.c中实现实验现象
while (1) //死循环
{
/* 按下 CTR(五项摇杆中间)按键 LED1会亮,松开 LED1会熄灭 */
#if 0
if(HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_RESET){
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
}else{
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
}
HAL_Delay(100);
#endif
/* 按下 CTR(五项摇杆中间)按键 LED1会熄灭,松开 LED1会亮 */
#if 1
if(HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_SET){
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
}else{
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
}
HAL_Delay(100);
#endif
}
附上代码:
main.c
#include "main.h"
#include "gpio.h"
void SystemClock_Config(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
while (1)
{
/* 按下 CTR(五项摇杆中间)按键 LED1会熄灭,松开 LED1会亮 */
#if 1
if(HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_SET){
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
}else{
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
}
HAL_Delay(100);
#endif
}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
while ((PWR->D3CR & (PWR_D3CR_VOSRDY)) != PWR_D3CR_VOSRDY)
{
}
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 4;
RCC_OscInitStruct.PLL.PLLN = 400;
RCC_OscInitStruct.PLL.PLLP = 2;
RCC_OscInitStruct.PLL.PLLQ = 2;
RCC_OscInitStruct.PLL.PLLR = 2;
RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_1;
RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
RCC_OscInitStruct.PLL.PLLFRACN = 0;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
|RCC_CLOCKTYPE_D3PCLK1|RCC_CLOCKTYPE_D1PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;
RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
void Error_Handler(void)
{
}
main.h
#ifndef __MAIN_H
#define __MAIN_H
#ifdef __cplusplus
extern "C" {
#endif
#include "stm32h7xx_hal.h"
void Error_Handler(void);
#define KEY_Pin GPIO_PIN_11
#define KEY_GPIO_Port GPIOI
#define LED_Pin GPIO_PIN_6
#define LED_GPIO_Port GPIOB
#ifdef __cplusplus
}
#endif
#endif
gpio.h
#ifndef __gpio_H
#define __gpio_H
#ifdef __cplusplus
extern "C" {
#endif
#include "main.h"
void MX_GPIO_Init(void);
#ifdef __cplusplus
}
#endif
#endif
gpio.c
#include "gpio.h"
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable 使能GPIO端口时钟 */
__HAL_RCC_GPIOI_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level 配置GPIO引脚输出电平 */
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = KEY_Pin; //选择引脚KEY
GPIO_InitStruct.Mode = GPIO_MODE_INPUT; //输入模式
GPIO_InitStruct.Pull = GPIO_PULLUP; //上拉
HAL_GPIO_Init(KEY_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = LED_Pin; //选择引脚LED
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; //推挽输出
GPIO_InitStruct.Pull = GPIO_PULLUP; //上拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; //高速
HAL_GPIO_Init(LED_GPIO_Port, &GPIO_InitStruct);
}