STM32微控制器GPIO库函数
目录
概述
通用输入/输出(GPIO)模块是STM32微控制器中最基本且最重要的外设之一。通过GPIO库函数,开发者可以方便地配置和操作GPIO引脚,实现与外部设备的数字信号交互,如控制LED、读取按钮状态、进行串行通信等。STM32提供了两种主要的外设库:HAL(硬件抽象层)库和标准外设库,每种库都有其独特的API和使用方法。
GPIO库函数基础
HAL库与标准外设库
STM32微控制器支持两种主要的外设库:
- HAL库(Hardware Abstraction Layer):提供高级别的抽象,简化了外设的配置和操作,适合快速开发和跨系列移植。
- 标准外设库:提供对底层寄存器的直接访问,适合需要精细控制和优化的应用。
本文主要介绍HAL库中的GPIO函数,并简要提及标准外设库的相关函数。
GPIO库函数分类
GPIO库函数主要分为以下几类:
- 初始化函数:用于配置GPIO引脚的模式、速度、输出类型和上拉/下拉电阻。
- 引脚操作函数:用于设置、重置和切换GPIO引脚的状态。
- 读取函数:用于读取GPIO引脚的当前状态。
- 中断配置函数:用于配置GPIO引脚的外部中断。
- 复用功能配置函数:用于配置GPIO引脚的复用功能,如USART、SPI等外设。
GPIO数学基础
理解GPIO(通用输入/输出)模块的数学基础对于有效配置和使用GPIO引脚至关重要。以下是一些关键的数学概念和公式,它们在GPIO应用中经常用到。
电阻分压公式
在配置上拉或下拉电阻时,电阻分压公式用于计算引脚电压。这对于确保引脚在不同状态下有正确的电压水平非常重要。
V p i n = V C C × R 2 R 1 + R 2 V_{pin} = V_{CC} \times \frac{R_2}{R_1 + R_2} Vpin=VCC×R1+R2R2
- V p i n V_{pin} Vpin:引脚电压
- V C C V_{CC} VCC:供电电压
- R 1 R_1 R1:上拉电阻
- R 2 R_2 R2:下拉电阻
应用实例:假设 V C C = 3.3 V V_{CC} = 3.3V VCC=3.3V, R 1 = 10 k Ω R_1 = 10k\Omega R1=10kΩ, R 2 = 10 k Ω R_2 = 10k\Omega R2=10kΩ
V p i n = 3.3 V × 10 k Ω 10 k Ω + 10 k Ω = 1.65 V V_{pin} = 3.3V \times \frac{10k\Omega}{10k\Omega + 10k\Omega} = 1.65V Vpin=3.3V×10kΩ+10kΩ10kΩ=1.65V
输入电流计算
在输入模式下,引脚的输入电流可以通过电阻和电压计算。这有助于确保电流在安全范围内,避免损坏微控制器。
I i n = V C C R p u l l I_{in} = \frac{V_{CC}}{R_{pull}} Iin=RpullVCC
- I i n I_{in} Iin:输入电流
- R p u l l R_{pull} Rpull:上拉或下拉电阻
示例: V C C = 3.3 V V_{CC} = 3.3V VCC=3.3V, R p u l l = 10 k Ω R_{pull} = 10k\Omega Rpull=10kΩ
I i n = 3.3 V 10 k Ω = 0.33 m A I_{in} = \frac{3.3V}{10k\Omega} = 0.33mA Iin=10kΩ3.3V=0.33mA
输出驱动能力
输出引脚的驱动能力决定了其能驱动的负载电流。确保不超过微控制器的规格限制,以防止损坏引脚。
I o u t = V O H − V O L R l o a d I_{out} = \frac{V_{OH} - V_{OL}}{R_{load}} Iout=RloadVOH−VOL
- V O H V_{OH} VOH:高电平输出电压
- V O L V_{OL} VOL:低电平输出电压
- R l o a d R_{load} Rload:负载电阻
功率计算
GPIO引脚的功率消耗可以通过以下公式计算:
P G P I O = V G P I O × I G P I O P_{GPIO} = V_{GPIO} \times I_{GPIO} PGPIO=VGPIO×IGPIO
- P G P I O P_{GPIO} PGPIO:GPIO引脚功率
- V G P I O V_{GPIO} VGPIO:GPIO引脚电压
- I G P I O I_{GPIO} IGPIO:GPIO引脚电流
RC时间常数
GPIO引脚连接电容性负载时,RC时间常数决定了信号的上升和下降时间。信号的稳定性和响应速度依赖于RC时间常数的合理配置。
τ = R × C \tau = R \times C τ=R×C
- τ \tau τ:时间常数
- R R R:电阻
- C C C:电容
应用实例:若 R = 1 k Ω R = 1k\Omega R=1kΩ, C = 100 n F C = 100nF C=100nF
τ = 1 k Ω × 100 n F = 0.1 s \tau = 1k\Omega \times 100nF = 0.1s τ=1kΩ×100nF=0.1s
GPIO应用实例
实际应用中,GPIO库函数配合数学公式可以实现各种功能。以下是几个常见的GPIO应用实例,详细介绍了电路设计、数学计算和软件实现。
LED控制
通过GPIO引脚控制LED的开关,实现指示灯功能。
电路设计:
- 将LED通过限流电阻连接到GPIO输出引脚。
- 负极连接到地。
数学计算:
限流电阻的计算确保LED在安全电流下工作,防止烧毁LED或损坏微控制器引脚。
R = V C C − V L E D I L E D R = \frac{V_{CC} - V_{LED}}{I_{LED}} R=ILEDVCC−VLED
- V C C V_{CC} VCC:供电电压(如3.3V)
- V L E D V_{LED} VLED:LED正向电压(如2V)
- I L E D I_{LED} ILED:LED工作电流(如20mA)
R = 3.3 V − 2 V 0.02 A = 65 Ω R = \frac{3.3V - 2V}{0.02A} = 65\Omega R=0.02A3.3V−2V=65Ω
选择标准电阻值为68Ω,以确保安全。
按钮输入与中断
使用GPIO引脚读取按钮状态,并通过中断响应按钮按下事件,提高系统响应速度和效率。
电路设计:
- 按钮一端连接到GPIO输入引脚。
- 另一端连接到地。
- 在
PUPDR
寄存器中启用上拉电阻。
工作原理:
- 按钮未按下时,引脚通过上拉电阻维持高电平。
- 按钮按下时,引脚被拉低,触发中断。
串行通信
通过GPIO引脚实现串行通信,如USART、SPI、I2C等,实现与其他设备的数据交换。
配置步骤:
- 将相关引脚配置为复用功能模式。
- 在
AFR
寄存器中选择对应的复用功能。 - 使用相应的通信协议初始化和管理数据传输。
PWM信号生成
通过GPIO引脚生成PWM(脉宽调制)信号,用于控制电机速度、LED亮度等,实现模拟信号控制。
配置步骤:
- 配置相关引脚为复用功能模式。
- 配置定时器以PWM模式工作。
- 设置PWM频率和占空比。
外部中断触发
通过GPIO引脚触发外部中断,实现对外部事件的快速响应,如传感器信号变化、紧急停止等。
配置步骤:
- 配置GPIO引脚为输入模式,并启用上拉或下拉电阻。
- 配置
EXTI
寄存器以连接到特定的引脚。 - 配置中断触发类型(上升沿、下降沿、双边沿)。
- 使能中断并配置中断优先级。
- 编写中断服务程序(ISR)。
常见问题与解决方法
在使用STM32 GPIO库函数时,开发者可能会遇到各种问题。以下列出了一些常见问题及其解决方法,帮助您快速排除故障。
GPIO引脚无法正确读取输入状态
-
原因:
- 未配置上拉/下拉电阻。
- 外部电路连接问题,如按钮未正确接地。
- 引脚损坏或未正确配置模式。
-
解决方法:
- 检查并确保在
GPIO_InitTypeDef
结构体中正确配置Pull
参数(GPIO_PULLUP
或GPIO_PULLDOWN
)。 - 确认外部电路连接是否正确,按钮连接到地或VCC。
- 使用示波器或万用表检查引脚电压,确保信号稳定。
- 重新初始化GPIO引脚,确保配置正确。
- 检查并确保在
输出引脚无法驱动负载
-
原因:
- 负载电流超过GPIO引脚的最大承载能力。
- 连接电路错误,如短路或缺少限流电阻。
- 引脚未正确配置为输出模式。
-
解决方法:
- 检查负载电流是否在GPIO引脚规格范围内(通常不超过20mA)。
- 确保在LED等负载前使用限流电阻。
- 检查电路连接,避免短路。
- 确认通过
GPIO_InitTypeDef
结构体正确配置引脚模式为输出。
复用功能引脚功能异常
-
原因:
- 复用功能配置错误,未选择正确的复用功能编号。
AFR
寄存器设置不当。- 相关外设未正确初始化。
-
解决方法:
- 核对引脚的复用功能编号,确保选择正确的
Alternate
值。 - 检查
AFR
寄存器配置,确保每个引脚的复用功能位正确设置。 - 确保相关外设(如USART、SPI)的初始化代码正确无误。
- 核对引脚的复用功能编号,确保选择正确的
中断无法触发
-
原因:
- 中断未正确配置。
- 中断未使能或优先级设置错误。
- 外部信号变化未达到触发条件。
-
解决方法:
- 检查中断配置步骤,确保
EXTI
寄存器正确配置。 - 确认NVIC中断使能及优先级设置。
- 使用调试工具验证外部信号是否达到中断触发条件。
- 确保中断服务程序(ISR)正确实现。
- 检查中断配置步骤,确保
信号抖动或不稳定
-
原因:
- 输入信号未去抖动,尤其是机械按钮输入。
- 电源噪声影响,引脚电压不稳定。
-
解决方法:
- 在硬件电路中添加去抖电路,如RC滤波器或使用电容。
- 在软件中实现去抖逻辑,通过延时和状态确认减少抖动影响。
- 增加电源滤波电容,改善电源质量,减少噪声干扰。
功耗过高
-
原因:
- 未使用的GPIO引脚未配置为低功耗模式。
- 输出引脚持续高电流,导致微控制器功耗增加。
-
解决方法:
- 配置未使用的GPIO引脚为模拟输入模式,以减少漏电流。
- 检查输出引脚的负载,确保没有短路或过高电流。
- 使用低功耗模式配置,减少GPIO引脚的电流消耗。
GPIO库函数基础
了解STM32的GPIO库函数是高效开发的关键。STM32提供了两种主要的外设库:HAL(硬件抽象层)库和标准外设库。本文将重点介绍HAL库中的GPIO函数,并简要提及标准外设库的相关函数。
HAL库与标准外设库
-
HAL库(Hardware Abstraction Layer):
- 提供高级别的抽象,简化了外设的配置和操作。
- 适合快速开发和跨系列移植。
- 采用结构化编程风格,易于理解和使用。
-
标准外设库:
- 提供对底层寄存器的直接访问。
- 适合需要精细控制和优化的应用。
- 编程风格更接近底层硬件,适合有一定经验的开发者。
GPIO库函数分类
GPIO库函数主要分为以下几类:
- 初始化函数:用于配置GPIO引脚的模式、速度、输出类型和上拉/下拉电阻。
- 引脚操作函数:用于设置、重置和切换GPIO引脚的状态。
- 读取函数:用于读取GPIO引脚的当前状态。
- 中断配置函数:用于配置GPIO引脚的外部中断。
- 复用功能配置函数:用于配置GPIO引脚的复用功能,如USART、SPI等外设。
GPIO初始化
GPIO初始化是使用GPIO库函数的第一步,涉及配置GPIO引脚的工作模式、速度、输出类型和上拉/下拉电阻等参数。
GPIO_InitTypeDef结构体
在HAL库中,GPIO的初始化依赖于GPIO_InitTypeDef
结构体,该结构体包含了配置GPIO引脚所需的所有参数:
typedef struct
{
uint32_t Pin; /*!< Specifies the GPIO pins to be configured.
This parameter can be any value of @ref GPIO_pins_define */
uint32_t Mode; /*!< Specifies the operating mode for the selected pins.
This parameter can be a value of @ref GPIO_mode_define */
uint32_t Pull; /*!< Specifies the Pull-up or Pull-Down activation for the selected pins.
This parameter can be a value of @ref GPIO_pull_define */
uint32_t Speed; /*!< Specifies the speed for the selected pins.
This parameter can be a value of @ref GPIO_speed_define */
uint32_t Alternate; /*!< Peripheral to be connected to the selected pins.
This parameter can be a value of @ref GPIO_Alternate_function_selection */
} GPIO_InitTypeDef;
HAL_GPIO_Init函数
HAL_GPIO_Init函数用于根据GPIO_InitTypeDef结构体的配置,初始化指定的GPIO端口:
HAL_StatusTypeDef HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init);
- 参数:
GPIOx:指向GPIO端口的指针(如GPIOA、GPIOB)。
GPIO_Init:指向GPIO_InitTypeDef结构体的指针,包含引脚的配置参数。 - 返回值:函数执行状态(成功或失败)。
标准外设库的GPIO初始化
在标准外设库中,GPIO初始化使用GPIO_Init函数和GPIO_InitTypeDef结构体:
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
- 参数:
GPIOx:指向GPIO端口的指针。
GPIO_InitStruct:指向GPIO_InitTypeDef结构体的指针,包含引脚的配置参数。 - 功能:根据结构体中的参数配置GPIO引脚。
GPIO引脚操作
在GPIO初始化完成后,可以使用GPIO库函数对引脚进行操作,包括设置引脚状态、切换引脚状态以及读取引脚状态。
设置引脚状态
HAL_GPIO_WritePin函数
HAL_GPIO_WritePin函数用于设置GPIO引脚的输出状态(高或低)。
void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState);
- 参数:
GPIOx:指向GPIO端口的指针。
GPIO_Pin:指定要设置的引脚(如GPIO_PIN_0)。
PinState:引脚状态,GPIO_PIN_SET或GPIO_PIN_RESET。
使用示例:
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET); // 设置PA5为高电平
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); // 设置PA5为低电平
HAL_GPIO_TogglePin函数
HAL_GPIO_TogglePin函数用于切换GPIO引脚的当前状态。
void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
- 参数:
GPIOx:指向GPIO端口的指针。
GPIO_Pin:指定要切换的引脚。
使用示例:
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); // 切换PA5的电平状态
读取引脚状态
HAL_GPIO_ReadPin函数
HAL_GPIO_ReadPin函数用于读取GPIO引脚的当前输入状态。
GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
- 参数:
GPIOx:指向GPIO端口的指针。
GPIO_Pin:指定要读取的引脚。 - 返回值:引脚状态,GPIO_PIN_SET或GPIO_PIN_RESET。
使用示例:
GPIO_PinState pinState = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13);
if(pinState == GPIO_PIN_SET) {
// 引脚为高电平
} else {
// 引脚为低电平
}
GPIO高级功能
除了基本的引脚操作外,STM32 GPIO库函数还支持高级功能,如中断配置和复用功能配置,这些功能极大地扩展了GPIO的应用范围。
中断配置
GPIO引脚可以配置为外部中断源,响应外部信号的变化(上升沿、下降沿或双边沿),实现实时响应。
HAL_GPIO_EXTI_Callback函数
HAL_GPIO_EXTI_Callback是中断回调函数,当指定的GPIO引脚触发中断时会调用该函数。开发者需要在此函数中编写中断处理代码。
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin);
- 参数:
GPIO_Pin:触发中断的引脚编号。
使用示例:
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
if(GPIO_Pin == GPIO_PIN_13) {
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); // 切换LED状态
}
}
HAL_GPIO_EXTI_IRQHandler函数
HAL_GPIO_EXTI_IRQHandler用于处理中断事件,并调用回调函数。
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin);
- 参数:
GPIO_Pin:触发中断的引脚编号。
使用示例:
在中断服务程序(ISR)中调用:
void EXTI15_10_IRQHandler(void) {
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13);
}
复用功能配置
复用功能允许GPIO引脚连接到其他外设,如USART、SPI、I2C等,实现多功能的引脚使用。
Alternate Function设置
在GPIO_InitTypeDef结构体中,通过设置Alternate成员来指定复用功能编号。
使用示例:配置PA9为USART1_TX,PA10为USART1_RX
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 配置PA9为USART1_TX
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 复用推挽输出
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 配置PA10为USART1_RX
GPIO_InitStruct.Pin = GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
注意事项:
- 每个引脚的复用功能编号(AF)需根据具体外设手册选择。
- 确保相关外设(如USART)已经正确初始化。
代码示例及解读
以下是多个代码示例,展示如何使用STM32的GPIO库函数进行不同的操作,包括LED控制、按钮输入与中断、串行通信、PWM信号生成及外部中断触发。所有代码均使用HAL库实现,旨在帮助您理解和应用GPIO库函数。
示例代码1:LED控制
通过GPIO引脚控制LED的开关,实现指示灯功能。
#include "stm32f4xx_hal.h"
// LED引脚定义
#define LED_PIN GPIO_PIN_5
#define LED_GPIO_PORT GPIOA
// GPIO初始化函数
void GPIO_Init(void) {
__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = LED_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出模式
GPIO_InitStruct.Pull = GPIO_NOPULL; // 无上拉/下拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速
HAL_GPIO_Init(LED_GPIO_PORT, &GPIO_InitStruct);
}
// 延时函数
void delay(uint32_t time) {
HAL_Delay(time);
}
int main(void) {
HAL_Init(); // 初始化HAL库
GPIO_Init(); // 初始化GPIO
while (1) {
HAL_GPIO_TogglePin(LED_GPIO_PORT, LED_PIN); // 切换LED状态
delay(500); // 延时500ms
}
}
简要解读:
- GPIO初始化:
- 启用GPIOA时钟。
- 配置PA5为推挽输出模式,高速,无上拉/下拉电阻。
- 主循环:
- 使用HAL_GPIO_TogglePin函数切换PA5引脚的电平,实现LED的闪烁。
- 调用HAL_Delay函数实现500ms的延时控制闪烁频率。
示例代码2:按钮输入与中断
使用GPIO引脚读取按钮状态,并通过中断响应按钮按下事件,实现实时响应。
#include "stm32f4xx_hal.h"
// LED引脚定义
#define LED_PIN GPIO_PIN_5
#define LED_GPIO_PORT GPIOA
// 按钮引脚定义
#define BUTTON_PIN GPIO_PIN_13
#define BUTTON_GPIO_PORT GPIOC
// GPIO初始化函数
void GPIO_Init(void) {
__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟
__HAL_RCC_GPIOC_CLK_ENABLE(); // 使能GPIOC时钟
__HAL_RCC_SYSCFG_CLK_ENABLE(); // 使能SYSCFG时钟
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 配置PA5为输出模式
GPIO_InitStruct.Pin = LED_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(LED_GPIO_PORT, &GPIO_InitStruct);
// 配置PC13为输入模式,并设置为中断触发(下降沿)
GPIO_InitStruct.Pin = BUTTON_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING; // 下降沿触发中断
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(BUTTON_GPIO_PORT, &GPIO_InitStruct);
}
// 中断初始化函数
void EXTI_Init(void) {
HAL_NVIC_SetPriority(EXTI15_10_IRQn, 2, 0); // 设置中断优先级
HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); // 使能EXTI15_10中断
}
// 中断回调函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
if(GPIO_Pin == BUTTON_PIN) {
HAL_GPIO_TogglePin(LED_GPIO_PORT, LED_PIN); // 切换LED状态
}
}
int main(void) {
HAL_Init(); // 初始化HAL库
GPIO_Init(); // 初始化GPIO
EXTI_Init(); // 初始化外部中断
while (1) {
// 主循环可以执行其他任务
}
}
// 中断服务程序
void EXTI15_10_IRQHandler(void) {
HAL_GPIO_EXTI_IRQHandler(BUTTON_PIN);
}
简要解读:
- GPIO初始化:
- 配置PA5为输出模式,用于控制LED。
- 配置PC13为输入模式,并设置为下降沿触发中断,启用上拉电阻。
- 中断初始化:
- 设置EXTI15_10中断的优先级并使能中断。
- 中断回调:
- 当按钮按下(PC13引脚检测到下降沿)时,回调函数HAL_GPIO_EXTI_Callback被调用,切换LED状态。
- 中断服务程序:
- 调用HAL_GPIO_EXTI_IRQHandler处理中断,并触发回调函数。
示例代码3:串行通信(USART1)
通过GPIO引脚实现串行通信,实现与其他设备的数据交换。
#include "stm32f4xx_hal.h"
// USART1引脚定义
#define USART1_TX_PIN GPIO_PIN_9
#define USART1_RX_PIN GPIO_PIN_10
#define USART1_GPIO_PORT GPIOA
#define USART1_AF GPIO_AF7_USART1
// USART1句柄
UART_HandleTypeDef huart1;
// GPIO初始化函数
void GPIO_Init(void) {
__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 配置PA9为USART1_TX
GPIO_InitStruct.Pin = USART1_TX_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 复用推挽输出
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = USART1_AF;
HAL_GPIO_Init(USART1_GPIO_PORT, &GPIO_InitStruct);
// 配置PA10为USART1_RX
GPIO_InitStruct.Pin = USART1_RX_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Alternate = USART1_AF;
HAL_GPIO_Init(USART1_GPIO_PORT, &GPIO_InitStruct);
}
// USART1初始化函数
void USART1_Init(void) {
__HAL_RCC_USART1_CLK_ENABLE(); // 使能USART1时钟
huart1.Instance = USART1;
huart1.Init.BaudRate = 9600;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
HAL_UART_Init(&huart1);
}
// USART1发送字符函数
void USART1_SendChar(char c) {
HAL_UART_Transmit(&huart1, (uint8_t*)&c, 1, HAL_MAX_DELAY);
}
// USART1接收字符函数
char USART1_ReceiveChar(void) {
uint8_t c;
HAL_UART_Receive(&huart1, &c, 1, HAL_MAX_DELAY);
return c;
}
int main(void) {
HAL_Init(); // 初始化HAL库
GPIO_Init(); // 初始化GPIO
USART1_Init(); // 初始化USART1
while (1) {
char received = USART1_ReceiveChar(); // 接收字符
USART1_SendChar(received); // 发送回显
}
}
简要解读:
- GPIO初始化:
配置PA9为USART1_TX,PA10为USART1_RX的复用功能模式。 - USART1初始化:
设置波特率为9600,数据位为8位,无校验,1停止位,启用发送和接收功能。 - 发送与接收函数:
- USART1_SendChar用于发送一个字符。
- USART1_ReceiveChar用于接收一个字符。
- 主循环:
实现简单的回显功能,接收到的字符将被发送回去。
示例代码4:PWM信号生成(TIM3_CH1在PA6上)
通过GPIO引脚生成PWM(脉宽调制)信号,用于控制电机速度、LED亮度等。
#include "stm32f4xx_hal.h"
// PWM引脚定义
#define PWM_PIN GPIO_PIN_6
#define PWM_GPIO_PORT GPIOA
#define PWM_AF GPIO_AF2_TIM3
// 定时器句柄
TIM_HandleTypeDef htim3;
// GPIO初始化函数
void GPIO_Init(void) {
__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 配置PA6为TIM3_CH1复用功能
GPIO_InitStruct.Pin = PWM_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 复用推挽输出
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = PWM_AF;
HAL_GPIO_Init(PWM_GPIO_PORT, &GPIO_InitStruct);
}
// TIM3初始化函数
void TIM3_PWM_Init(void) {
__HAL_RCC_TIM3_CLK_ENABLE(); // 使能TIM3时钟
htim3.Instance = TIM3;
htim3.Init.Prescaler = 1600 - 1; // 预分频器
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 10000 - 1; // 自动重装载值
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_PWM_Init(&htim3);
TIM_OC_InitTypeDef sConfigOC = {0};
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 2500; // 比较寄存器值,占空比25%
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1); // 启动PWM
}
int main(void) {
HAL_Init(); // 初始化HAL库
GPIO_Init(); // 初始化GPIO
TIM3_PWM_Init(); // 初始化TIM3 PWM
while (1) {
// 主循环可以进行占空比调整等操作
}
}
简要解读:
- GPIO初始化:
配置PA6为TIM3_CH1的复用功能模式。 - TIM3初始化:
- 设置预分频器为1600,自动重装载值为10000,确定PWM频率。
- 配置TIM3通道1为PWM模式1,占空比25%。
- 启动TIM3的PWM输出。
- 主循环:
主循环保持空闲,可用于动态调整PWM占空比,实现如LED亮度调节或电机速度控制。
示例代码5:外部中断触发(PC13)
通过GPIO引脚触发外部中断,实现对外部事件的快速响应。
#include "stm32f4xx_hal.h"
// LED引脚定义
#define LED_PIN GPIO_PIN_5
#define LED_GPIO_PORT GPIOA
// 按钮引脚定义
#define BUTTON_PIN GPIO_PIN_13
#define BUTTON_GPIO_PORT GPIOC
// GPIO初始化函数
void GPIO_Init(void) {
__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟
__HAL_RCC_GPIOC_CLK_ENABLE(); // 使能GPIOC时钟
__HAL_RCC_SYSCFG_CLK_ENABLE(); // 使能SYSCFG时钟
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 配置PA5为输出模式
GPIO_InitStruct.Pin = LED_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(LED_GPIO_PORT, &GPIO_InitStruct);
// 配置PC13为外部中断输入模式,启用上拉电阻
GPIO_InitStruct.Pin = BUTTON_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING; // 下降沿触发中断
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(BUTTON_GPIO_PORT, &GPIO_InitStruct);
}
// 中断初始化函数
void EXTI_Init(void) {
HAL_NVIC_SetPriority(EXTI15_10_IRQn, 2, 0); // 设置中断优先级
HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); // 使能EXTI15_10中断
}
// 中断回调函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
if (GPIO_Pin == BUTTON_PIN) {
HAL_GPIO_TogglePin(LED_GPIO_PORT, LED_PIN); // 切换LED状态
}
}
int main(void) {
HAL_Init(); // 初始化HAL库
GPIO_Init(); // 初始化GPIO
EXTI_Init(); // 初始化外部中断
while (1) {
// 主循环可以执行其他任务
}
}
// 中断服务程序
void EXTI15_10_IRQHandler(void) {
HAL_GPIO_EXTI_IRQHandler(BUTTON_PIN);
}
简要解读:
- GPIO初始化:
- 配置PA5为输出模式,用于控制LED。
- 配置PC13为外部中断输入模式,启用上拉电阻。
- 中断初始化:
设置EXTI15_10中断的优先级并使能中断。 - 中断回调:
当按钮按下(PC13引脚检测到下降沿)时,回调函数HAL_GPIO_EXTI_Callback被调用,切换LED状态。 - 中断服务程序:
调用HAL_GPIO_EXTI_IRQHandler处理中断,并触发回调函数。