stm32_GPIO
GPIO
1. 概念与基本原理
GPIO,全称General Purpose Input/Output,即通用输入/输出端口。它是微控制器(MCU)、单片机、树莓派、ESP32等嵌入式系统中非常基础且重要的功能模块。
基本原理: GPIO引脚可以被软件配置为输入或输出。
- 输入模式 (Input Mode): 当配置为输入时,GPIO引脚可以读取外部设备的电平状态(高电平或低电平),从而感知外部信号或传感器数据。
- 输出模式 (Output Mode): 当配置为输出时,GPIO引脚可以输出高电平或低电平,从而控制外部设备,如点亮LED、驱动继电器、控制电机等。
核心思想: 通过软件对硬件引脚进行直接操作,实现微控制器与外部世界的交互。
2. GPIO引脚结构(可图示)
想象一个GPIO引脚的内部结构,它通常包含以下几个关键部分:
外部引脚|V
+---------------------+
| |
| 保护二极管(ESD保护)|
| |
+---------------------+|V
+---------------------+
| |
| 输入缓冲器/施密特触发器 | <-- 确保输入信号的稳定性
| |
+---------------------+|V
+---------------------+
| |
| 输入数据寄存器 | <-- 存储当前引脚的电平状态
| |
+---------------------+|V
+---------------------+
| |
| 输出数据寄存器 | <-- 存储要输出的电平状态
| |
+---------------------+|V
+---------------------+
| |
| 输出驱动器(推挽/开漏)| <-- 提供电流驱动外部设备
| |
+---------------------+|V
+---------------------+
| |
| 方向控制寄存器(输入/输出)| <-- 配置引脚是输入还是输出
| |
+---------------------+|V
+---------------------+
| |
| 上拉/下拉电阻配置 | <-- 控制引脚默认电平(防止浮空)
| |
+---------------------+
关键部件解释:
-
保护二极管(ESD保护): 防止静电放电(ESD)损坏芯片。
-
输入缓冲器/施密特触发器: 提高输入信号的抗噪声能力,确保输入信号的可靠性。
-
输入数据寄存器: 存储当前引脚的逻辑电平(0或1)。
-
输出数据寄存器: 存储要输出到引脚的逻辑电平。
-
输出驱动器:
根据输出数据寄存器的值驱动引脚电平。常见的驱动类型有:
- 推挽输出 (Push-Pull): 可以输出高电平(连接到VCC)或低电平(连接到GND),驱动能力强,常用于驱动LED、电机等。
- 开漏输出 (Open-Drain/Open-Collector): 只能输出低电平(连接到GND)或高阻态(浮空)。通常需要外接一个上拉电阻才能输出高电平。常用于I2C总线、电平转换等。
-
方向控制寄存器: 配置GPIO引脚是作为输入还是输出。
-
上拉/下拉电阻配置:
- 上拉电阻 (Pull-up): 将引脚默认拉高到高电平。常用于按键输入,当按键按下时,引脚被拉低。
- 下拉电阻 (Pull-down): 将引脚默认拉低到低电平。当按键按下时,引脚被拉高。
- 无(浮空): 不使用内部上拉或下拉电阻。此时引脚电平容易受外部干扰影响,通常需要外部上拉/下拉电阻。
3. GPIO工作模式(可图示)
GPIO的工作模式主要包括以下几个方面:
+-------------------------------------------------------+
| GPIO工作模式 |
+-------------------------------------------------------+
| |
| 1. 方向配置 (Direction Configuration) |
| +-----------------------------------------+ |
| | | |
| | 输入模式 (Input Mode) | |
| | - 读取外部电平状态 | |
| | - 配合上拉/下拉电阻使用 | |
| | | |
| | 输出模式 (Output Mode) | |
| | - 输出高/低电平 | |
| | - 控制外部设备 | |
| +-----------------------------------------+ |
| |
| 2. 输出类型配置 (Output Type Configuration) |
| +-----------------------------------------+ |
| | | |
| | 推挽输出 (Push-Pull) | |
| | - 可以输出高/低电平 | |
| | - 驱动能力强 | |
| | | |
| | 开漏输出 (Open-Drain) | |
| | - 只能输出低电平或高阻态 | |
| | - 需要外部上拉电阻 | |
| +-----------------------------------------+ |
| |
| 3. 上拉/下拉电阻配置 (Pull-up/Pull-down Configuration)|
| +-----------------------------------------+ |
| | | |
| | 内部上拉 (Internal Pull-up) | |
| | 内部下拉 (Internal Pull-down) | |
| | 无上拉/下拉 (No Pull-up/Pull-down) | |
| +-----------------------------------------+ |
| |
| 4. 中断配置 (Interrupt Configuration) |
| +-----------------------------------------+ |
| | | |
| | 边沿触发 (Edge Triggered) | |
| | - 上升沿触发 | |
| | - 下降沿触发 | |
| | - 双边沿触发 | |
| | | |
| | 电平触发 (Level Triggered) | |
| | - 高电平触发 | |
| | - 低电平触发 | |
| +-----------------------------------------+ |
+-------------------------------------------------------+
详细说明:
- 方向配置: 这是最基本的配置,决定了引脚是输入还是输出。
- 输出类型配置: 仅当引脚配置为输出时才需要考虑。
- 上拉/下拉电阻配置: 无论输入还是输出模式,都可以配置,但主要对输入模式有意义,用于确定引脚在未被外部驱动时的默认电平。
- 中断配置 (Interrupt): 允许GPIO引脚在电平变化(上升沿、下降沿、高电平、低电平)时触发微控制器中断,从而无需持续轮询引脚状态,提高系统效率和响应速度。
4. GPIO操作流程(可图示)
GPIO的操作通常遵循以下流程:
+-------------------+
| 开始 |
+-------------------+|V
+-------------------+
| 1. 初始化GPIO外设 |
| - 使能GPIO时钟 |
+-------------------+|V
+-------------------+
| 2. 配置GPIO引脚 |
| - 选择引脚号 |
| - 配置方向(输入/输出)|
| - 配置输出类型(推挽/开漏)|
| - 配置上拉/下拉电阻 |
| - (可选)配置复用功能 |
+-------------------+|V
+-------------------+
| 3. 进行GPIO操作 |
| - 如果是输出: |
| - 设置高/低电平 |
| - 如果是输入: |
| - 读取引脚电平 |
+-------------------+|V
+-------------------+
| 4. (可选)中断处理 |
| - 配置中断触发条件 |
| - 编写中断服务函数 |
+-------------------+|V
+-------------------+
| 结束 |
+-------------------+
流程详解:
- 初始化GPIO外设:
- 使能GPIO时钟: 大多数微控制器为了节省功耗,外设的时钟是默认关闭的。使用GPIO前需要先使能对应GPIO端口的时钟。
- 配置GPIO引脚:
- 选择引脚号: 明确要操作的GPIO引脚(例如PA0, PB5等)。
- 配置方向: 设置为输入或输出。
- 配置输出类型: 如果是输出,选择推挽或开漏。
- 配置上拉/下拉电阻: 根据应用需求配置内部上拉、下拉或无。
- (可选)配置复用功能: GPIO引脚往往不止一种功能,它们可以被复用为其他外设(如UART, SPI, I2C等)的接口。当用作GPIO时,需要确保其没有被配置为其他复用功能。
- 进行GPIO操作:
- 输出: 通过写入GPIO数据寄存器来设置引脚的电平(高或低)。
- 输入: 通过读取GPIO数据寄存器来获取引脚的当前电平。
- (可选)中断处理:
- 配置中断触发条件: 设置在什么条件下触发中断(上升沿、下降沿等)。
- 编写中断服务函数 (ISR): 当中断发生时,CPU会跳转到预先定义好的中断服务函数中执行相应的处理逻辑。
5. 完整流程代码案例 (以STM32为例)
这里以STM32微控制器为例,使用HAL库(硬件抽象层)来演示一个完整的GPIO代码案例。我们将实现一个简单的功能:控制一个LED灯亮灭,并通过一个按键控制LED的亮灭状态。
硬件连接:
- LED: 连接到STM32F407开发板上的GPIOD的第13个引脚 (PD13)。LED的一端接PD13,另一端通过限流电阻接地。
- 按键: 连接到STM32F407开发板上的GPIOA的第0个引脚 (PA0)。按键一端接PA0,另一端接地。PA0将配置为带内部上拉电阻的输入模式,这样按键未按下时,PA0为高电平;按键按下时,PA0为低电平。
开发环境: Keil MDK, STM32CubeMX (用于生成初始化代码)
5.1 STM32CubeMX配置步骤
- 新建项目并选择芯片。
- 配置GPIO:
- PD13 (LED):
- 设置为
GPIO_Output
- 用户标签:
LED_PIN
- GPIO output level:
Low
(默认低电平) - GPIO mode:
Output Push Pull
- No pull-up and pull-down
- Maximum output speed:
Low
- 设置为
- PA0 (Button):
- 设置为
GPIO_Input
- 用户标签:
BUTTON_PIN
- GPIO mode:
Input mode
- GPIO pull-up/pull-down:
Pull-up
- (可选:如果需要中断)选择
External Interrupt Mode with Rising/Falling Edge trigger detection
,这里我们用轮询方式,所以不需要中断。
- 设置为
- PD13 (LED):
- 时钟配置: 保持默认即可,确保GPIO端口的时钟被使能。
- 生成代码。
5.2 Keil MDK代码实现
在STM32CubeMX生成的基础代码上,我们主要修改 main.c
文件。
/* USER CODE BEGIN Includes */
#include "main.h" // 包含CubeMX生成的头文件
/* USER CODE END Includes *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*//* USER CODE END PFP *//* USER CODE BEGIN 0 */
/* USER CODE END 0 *//*** @brief The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init(); // CubeMX生成的GPIO初始化函数/* USER CODE BEGIN 2 *//* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */// 读取按键状态// HAL_GPIO_ReadPin(GPIOx, GPIO_Pin) 函数用于读取GPIO引脚的电平// 如果按键按下 (PA0被拉低),HAL_GPIO_ReadPin 返回 GPIO_PIN_RESET (低电平)if (HAL_GPIO_ReadPin(BUTTON_GPIO_Port, BUTTON_Pin) == GPIO_PIN_RESET){// 按键被按下HAL_Delay(10); // 简单的消抖延时,防止按键抖动误判if (HAL_GPIO_ReadPin(BUTTON_GPIO_Port, BUTTON_Pin) == GPIO_PIN_RESET){// 再次确认按键按下,执行操作// 翻转LED状态// HAL_GPIO_TogglePin(GPIOx, GPIO_Pin) 函数用于翻转GPIO引脚的电平HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);// 等待按键释放,防止一次按键触发多次翻转while(HAL_GPIO_ReadPin(BUTTON_GPIO_Port, BUTTON_Pin) == GPIO_PIN_RESET);}}// 可以添加其他任务或延时// HAL_Delay(100); // 如果没有按键操作,可以适当延时以降低CPU占用}/* USER CODE END 3 */
}/*** @brief System Clock Configuration* @retval None*/
void SystemClock_Config(void)
{/* ... (CubeMX生成的时钟配置代码) ... */
}/* USER CODE BEGIN 4 *//* USER CODE END 4 *//*** @brief This function is executed in case of error occurrence.* @retval None*/
void Error_Handler(void)
{/* USER CODE BEGIN Error_Handler_Debug *//* User can add his own implementation to report the HAL error return state */while(1){}/* USER CODE END Error_Handler_Debug */
}#ifdef USE_FULL_ASSERT
/*** @brief Reports the name of the source file and the source line number* where the assert_param error has occurred.* @param file: pointer to the source file name* @param line: assert_param error line source number* @retval None*/
void assert_failed(uint8_t *file, uint32_t line)
{ /* USER CODE BEGIN 6 *//* User can add his own implementation to report the file name and line number,tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT *//************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
5.3 代码解释
-
#include "main.h"
: 包含CubeMX生成的main.h
,其中定义了所有GPIO引脚和端口的宏(例如LED_GPIO_Port
,LED_Pin
,BUTTON_GPIO_Port
,BUTTON_Pin
)。 -
MX_GPIO_Init();
:这是STM32CubeMX生成的GPIO初始化函数。它会在程序启动时,根据我们在CubeMX中的配置,自动完成:
- 使能相应GPIO端口的时钟。
- 配置GPIO引脚的方向(输入/输出)。
- 配置GPIO引脚的输出类型(推挽/开漏)。
- 配置GPIO引脚的上拉/下拉电阻。
- 设置初始电平。
-
HAL_GPIO_ReadPin(BUTTON_GPIO_Port, BUTTON_Pin)
:HAL_GPIO_ReadPin
是HAL库提供的读取GPIO引脚电平的函数。BUTTON_GPIO_Port
(GPIOA) 是按键连接的GPIO端口。BUTTON_Pin
(GPIO_PIN_0) 是按键连接的GPIO引脚。- 当按键按下时,PA0被拉低,函数返回
GPIO_PIN_RESET
(逻辑0)。
-
HAL_Delay(10);
: 一个简单的软件消抖,防止按键按下时由于机械抖动产生多个高低电平跳变,导致LED多次翻转。 -
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
:HAL_GPIO_TogglePin
是HAL库提供的翻转GPIO引脚电平的函数。LED_GPIO_Port
(GPIOD) 是LED连接的GPIO端口。LED_Pin
(GPIO_PIN_13) 是LED连接的GPIO引脚。- 每次调用都会将LED的状态从亮变为灭,或从灭变为亮。
-
while(HAL_GPIO_ReadPin(BUTTON_GPIO_Port, BUTTON_Pin) == GPIO_PIN_RESET);
: 这是一个等待按键释放的循环。它确保只有当按键完全释放后,程序才会继续向下执行,从而避免在按键持续按下的情况下LED持续闪烁。