STM32外设驱动编程:GPIO控制实战
发布时间: 2024-05-02 00:28:33 阅读量: 8 订阅数: 18
# 1. STM32 GPIO 外设概述
STM32 微控制器中的通用输入/输出 (GPIO) 外设负责管理与外部设备的通信。它提供了一种灵活的方式来配置和操作引脚,允许用户控制外部设备并读取来自外部设备的输入。GPIO 外设在各种应用中至关重要,包括:
- LED 控制
- 按键检测
- 外部中断触发
- 模拟输入
- PWM 输出
# 2. GPIO 寄存器配置与操作
### 2.1 GPIO 寄存器结构
STM32 的 GPIO 寄存器组位于外设总线地址空间中,每个 GPIO 端口都有一个独立的寄存器组。GPIO 寄存器组由以下寄存器组成:
| 寄存器名称 | 描述 |
|---|---|
| GPIOx_MODER | 模式寄存器,用于配置引脚的模式(输入、输出、模拟、复用功能) |
| GPIOx_OTYPER | 输出类型寄存器,用于配置引脚的输出类型(推挽输出、开漏输出) |
| GPIOx_OSPEEDR | 输出速度寄存器,用于配置引脚的输出速度(低速、中速、高速、极高速) |
| GPIOx_PUPDR | 上拉/下拉寄存器,用于配置引脚的上拉/下拉电阻(无、上拉、下拉) |
| GPIOx_IDR | 输入数据寄存器,用于读取引脚的输入电平 |
| GPIOx_ODR | 输出数据寄存器,用于设置引脚的输出电平 |
| GPIOx_BSRR | 置位/复位寄存器,用于同时置位或复位引脚 |
| GPIOx_LCKR | 锁定寄存器,用于锁定/解锁 GPIO 配置寄存器 |
| GPIOx_AFRL/AFRH | 复用功能寄存器,用于配置引脚的复用功能 |
### 2.2 GPIO 模式和引脚配置
GPIO 引脚可以配置为以下模式:
- 输入模式:引脚作为输入,可以读取外部电平。
- 输出模式:引脚作为输出,可以输出高电平或低电平。
- 模拟模式:引脚作为模拟输入,可以连接到模拟外设。
- 复用功能模式:引脚作为特定外设的复用功能,例如 USART、SPI、I2C 等。
引脚模式通过 GPIOx_MODER 寄存器配置,每个引脚有 2 个模式位,如下所示:
```
GPIOx_MODER[1:0] = 00: 输入模式
GPIOx_MODER[1:0] = 01: 输出模式
GPIOx_MODER[1:0] = 10: 模拟模式
GPIOx_MODER[1:0] = 11: 复用功能模式
```
### 2.3 GPIO 输入输出操作
GPIO 引脚的输入输出操作可以通过 GPIOx_IDR 和 GPIOx_ODR 寄存器进行。
- 输入操作:读取 GPIOx_IDR 寄存器可以获取引脚的输入电平。
- 输出操作:设置 GPIOx_ODR 寄存器可以设置引脚的输出电平。
```c
// 设置 GPIOA 引脚 5 为输出,输出高电平
GPIOA->MODER = (GPIOA->MODER & ~(3 << (5 * 2))) | (1 << (5 * 2));
GPIOA->ODR |= (1 << 5);
```
```c
// 读取 GPIOB 引脚 7 的输入电平
uint8_t input_level = (GPIOB->IDR >> 7) & 0x01;
```
# 3.1 GPIO 中断机制
GPIO 中断是一种由 GPIO 引脚状态变化触发的事件。当连接到 GPIO 引脚的外设或信号发生变化时,将触发中断,从而允许微控制器对该事件做出响应。
STM32 GPIO 中断机制基于嵌套向量中断控制器 (NVIC)。NVIC 负责管理所有中断源,包括 GPIO 中断。每个 GPIO 端口都有一个专用的中断向量,用于处理该端口的所有中断。
GPIO 中断可以配置为以下四种触发方式:
- **上升沿触发:**当 GPIO 引脚从低电平变为高电平时触发中断。
- **下降沿触发:**当 GPIO 引脚从高电平变为低电平时触发中断。
- **任何沿触发:**当 GPIO 引脚发生任何电平变化时触发中断。
- **电平触发:**当 GPIO 引脚保持在特定电平时触发中断。
### 3.2 中断配置和处理
要配置 GPIO 中断,需要执行以下步骤:
1. **配置 GPIO 引脚:**设置 GPIO 引脚为输入或输出模式,并选择所需的触发方式。
2. **使能中断:**使用 `NVIC_EnableIRQ()` 函数使能 GPIO 中断向量。
3. **编写中断服务程序 (ISR):**编写一个 ISR 来处理 GPIO 中断。ISR 应读取 GPIO 输入寄存器以确定触发中断的引脚,并执行适当的操作。
以下代码示例演示了如何配置 GPIO 中断:
```c
// 使能 GPIOA 时钟
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
// 配置 PA0 为输入,上升沿触发中断
GPIOA->MODER &= ~GPIO_MODER_MODER0;
GPIOA->MODER |= GPIO_MODER_MODER0_0;
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR0;
GPIOA->PUPDR |= GPIO_PUPDR_PUPDR0_0;
GPIOA->OSPEEDR |= GPIO_OSPEEDR_OSPEEDR0;
GPIOA->ODR |= GPIO_ODR_ODR0;
// 使能 GPIOA 中断
NVIC_EnableIRQ(EXTI0_IRQn);
// 中断服务程序
void EXTI0_IRQHandler(void)
{
// 读取 GPIOA 输入寄存器
uint32_t gpioa_input = GPIOA->IDR;
// 检查 PA0 是否触发中断
if (gpioa_input & GPIO_IDR_IDR0)
{
// 执行操作...
}
// 清除中断标志
EXTI->PR |= EXTI_PR_PR0;
}
```
### 3.3 中断优先级和嵌套
STM32 NVIC 支持中断优先级和嵌套。中断优先级决定了中断的处理顺序,优先级较高的中断将优先处理。中断嵌套允许高优先级中断在处理低优先级中断时被中断。
中断优先级和嵌套可以通过以下寄存器进行配置:
- **NVIC_IPRx:**设置中断 x 的优先级。
- **NVIC_ISPRx:**设置中断 x 是否可被嵌套。
以下代码示例演示了如何配置中断优先级和嵌套:
```c
// 设置 EXTI0 中断优先级为 1
NVIC_SetPriority(EXTI0_IRQn, 1);
// 使能 EXTI0 中断嵌套
NVIC_EnableIRQ(EXTI0_IRQn);
```
# 4. GPIO 实战应用
### 4.1 LED 控制
#### 4.1.1 LED 硬件连接
将 LED 的正极连接到 STM32 的 GPIO 引脚,负极连接到地线。
#### 4.1.2 LED 软件控制
```c
// 设置 GPIO 引脚为输出模式
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = GPIO_PIN_13;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
// 打开 LED
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
// 关闭 LED
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
```
### 4.2 按键检测
#### 4.2.1 按键硬件连接
将按键的一端连接到 STM32 的 GPIO 引脚,另一端连接到地线。
#### 4.2.2 按键软件检测
```c
// 设置 GPIO 引脚为输入模式
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 检测按键按下
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET) {
// 按键按下,执行操作
}
```
### 4.3 外部中断触发
#### 4.3.1 外部中断硬件连接
将外部中断源连接到 STM32 的 GPIO 引脚。
#### 4.3.2 外部中断软件配置
```c
// 设置 GPIO 引脚为外部中断模式
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 配置外部中断
HAL_NVIC_SetPriority(EXTI2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI2_IRQn);
// 外部中断处理函数
void EXTI2_IRQHandler(void) {
// 清除中断标志位
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2);
// 执行中断处理操作
}
```
# 5. GPIO 高级应用
### 5.1 GPIO 复用功能
STM32 的 GPIO 引脚具有复用功能,可以连接到不同的外设模块。复用功能通过 GPIO 寄存器中的 **AFR**(Alternate Function Register)进行配置。
**AFR 寄存器结构:**
```c
typedef struct {
uint32_t AFRL; // 低 16 位复用功能寄存器
uint32_t AFRH; // 高 16 位复用功能寄存器
} GPIO_TypeDef;
```
**AFR 寄存器的每 4 位对应一个 GPIO 引脚的复用功能选择。** 例如,对于 GPIOA 引脚 0,其复用功能选择位于 AFRL 寄存器的第 0-3 位。
**复用功能配置步骤:**
1. 设置 GPIO 模式为复用模式(**GPIO_Mode_AF**)。
2. 根据外设模块,配置 AFR 寄存器中的对应位。
### 5.2 GPIO 模拟输入
STM32 的 GPIO 引脚可以配置为模拟输入,用于读取模拟信号。模拟输入功能通过 GPIO 寄存器中的 **ADC**(Analog-to-Digital Converter)配置。
**ADC 寄存器结构:**
```c
typedef struct {
uint32_t CR1; // 控制寄存器 1
uint32_t CR2; // 控制寄存器 2
uint32_t SMPR1; // 采样时间寄存器 1
uint32_t SMPR2; // 采样时间寄存器 2
uint32_t JOFR1; // 偏移寄存器 1
uint32_t JOFR2; // 偏移寄存器 2
uint32_t HTR; // 定时器寄存器
uint32_t LTR; // 定时器寄存器
uint32_t SQR1; // 规则序列寄存器 1
uint32_t SQR2; // 规则序列寄存器 2
uint32_t SQR3; // 规则序列寄存器 3
uint32_t JSQR; // 注入序列寄存器
uint32_t JDR1; // 数据寄存器 1
uint32_t JDR2; // 数据寄存器 2
uint32_t JDR3; // 数据寄存器 3
uint32_t JDR4; // 数据寄存器 4
uint32_t DR; // 数据寄存器
} ADC_TypeDef;
```
**模拟输入配置步骤:**
1. 设置 GPIO 模式为模拟输入模式(**GPIO_Mode_AIN**)。
2. 配置 ADC 寄存器,设置采样时间、转换模式等参数。
3. 启动 ADC 转换。
### 5.3 GPIO PWM 输出
STM32 的 GPIO 引脚可以配置为 PWM(脉宽调制)输出,用于生成周期性脉冲信号。PWM 输出功能通过 GPIO 寄存器中的 **TIM**(Timer)配置。
**TIM 寄存器结构:**
```c
typedef struct {
uint32_t CR1; // 控制寄存器 1
uint32_t CR2; // 控制寄存器 2
uint32_t SMCR; // 状态机控制寄存器
uint32_t DIER; // 中断和事件使能寄存器
uint32_t SR; // 状态寄存器
uint32_t EGR; // 事件生成寄存器
uint32_t CCMR1; // 捕获/比较模式寄存器 1
uint32_t CCMR2; // 捕获/比较模式寄存器 2
uint32_t CCER; // 捕获/比较使能寄存器
uint32_t CNT; // 计数器寄存器
uint32_t PSC; // 预分频器寄存器
uint32_t ARR; // 自动重装载寄存器
uint32_t RCR; // 重复计数器寄存器
uint32_t CCR1; // 捕获/比较寄存器 1
uint32_t CCR2; // 捕获/比较寄存器 2
uint32_t CCR3; // 捕获/比较寄存器 3
uint32_t CCR4; // 捕获/比较寄存器 4
uint32_t BDTR; // 失真控制寄存器
uint32_t DCR; // DMA 控制寄存器
uint32_t DMAR; // DMA 请求寄存器
} TIM_TypeDef;
```
**PWM 输出配置步骤:**
1. 设置 GPIO 模式为复用模式(**GPIO_Mode_AF**),连接到 TIM 外设。
2. 配置 TIM 寄存器,设置时钟源、分频比、脉冲宽度等参数。
3. 启动 TIM 定时器。
# 6. GPIO 驱动开发实战
### 6.1 GPIO 驱动框架设计
GPIO 驱动框架设计的主要目标是提供一个可重用、可扩展的接口,用于访问和控制 GPIO 外设。框架应遵循以下原则:
- **模块化:**驱动应由独立的模块组成,每个模块负责特定功能。
- **可重用:**模块应设计为可重用,以便可以在不同的应用程序中使用。
- **可扩展:**框架应易于扩展,以支持新的 GPIO 外设或功能。
基于这些原则,GPIO 驱动框架可以设计为以下模块:
- **GPIO 初始化模块:**负责初始化 GPIO 外设和配置引脚模式。
- **GPIO 读写模块:**提供读写 GPIO 引脚值的功能。
- **GPIO 中断模块:**处理 GPIO 中断事件并提供中断处理程序。
- **GPIO 高级功能模块:**提供对 GPIO 复用功能、模拟输入和 PWM 输出等高级功能的访问。
### 6.2 GPIO 驱动实现
基于设计的框架,可以实现 GPIO 驱动。以下是一个示例代码片段,演示如何使用 GPIO 驱动框架初始化 GPIO 引脚:
```c
#include "gpio_driver.h"
// 初始化 GPIOA 的引脚 5 为输出模式
gpio_init(GPIOA, GPIO_PIN_5, GPIO_MODE_OUTPUT);
```
### 6.3 驱动测试与验证
GPIO 驱动开发完成后,需要进行测试和验证以确保其正确性和可靠性。测试应包括以下方面:
- **功能测试:**验证驱动程序是否可以正确初始化 GPIO 外设、读写引脚值和处理中断。
- **性能测试:**测量驱动程序的性能,例如引脚读写速度和中断响应时间。
- **可靠性测试:**对驱动程序进行压力测试和异常情况测试,以确保其在各种条件下都能正常工作。
0
0