下面把 HAL 库 和 标准外设库(SPL) 初始化 GPIO 点亮/熄灭 LED 的完整步骤、示例代码和常用 API 逐一说清楚。用例默认 PC13 接 LED(蓝板常见;低电平点亮,高电平熄灭——若板子相反,只把写 1/0 对调即可)。
一、HAL 库:GPIO 初始化与点亮/熄灭 LED
步骤(通用顺序)
使能端口时钟
__HAL_RCC_GPIOx_CLK_ENABLE()
填写
GPIO_InitTypeDef
结构体(Pin/Mode/Speed …)调
HAL_GPIO_Init(GPIOx, &init)
完成配置用
HAL_GPIO_WritePin / TogglePin / ReadPin
控制或读引脚
(可选)5) 若需 EXTI:还要配置中断优先级并使能 NVIC,使用HAL_GPIO_EXTI_IRQHandler
/HAL_GPIO_EXTI_Callback
示例代码(HAL)
led.h
#ifndef __LED_H__
#define __LED_H__
#include "stm32f1xx_hal.h"
/* ====== 硬件相关宏定义 ====== */
#define LED_GPIO_PORT GPIOC // LED 所在的端口(此处为 GPIOC)
#define LED_GPIO_PIN GPIO_PIN_13 // LED 引脚号(PC13)
/* ====== LED 控制函数声明 ====== */
void LED_Init(void); // 初始化 LED 引脚
void LED_On(void); // 点亮 LED
void LED_Off(void); // 熄灭 LED
void LED_Toggle(void); // 翻转 LED 状态
#endif
led.c:
#include "led.h"
/**
* @brief 初始化 LED 引脚
* @note 配置为推挽输出,默认熄灭(PC13高电平)
*/
void LED_Init(void)
{
/* 1. 使能 GPIOC 时钟(如果不打开,GPIO 寄存器无法操作) */
__HAL_RCC_GPIOC_CLK_ENABLE();
/* 2. 配置 GPIO 参数 */
GPIO_InitTypeDef gpio = {0}; // 定义配置结构体并清零
gpio.Pin = LED_GPIO_PIN; // 选择 PC13
gpio.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出模式
gpio.Speed = GPIO_SPEED_FREQ_LOW; // 低速输出(足够驱动 LED)
HAL_GPIO_Init(LED_GPIO_PORT, &gpio); // 初始化 GPIOC 的 13 引脚
/* 3. 缺省熄灭 LED(PC13 高电平 = 灭) */
HAL_GPIO_WritePin(LED_GPIO_PORT, LED_GPIO_PIN, GPIO_PIN_SET);
}
/**
* @brief 点亮 LED
*/
void LED_On(void)
{
HAL_GPIO_WritePin(LED_GPIO_PORT, LED_GPIO_PIN, GPIO_PIN_RESET); // PC13 输出低电平
}
/**
* @brief 熄灭 LED
*/
void LED_Off(void)
{
HAL_GPIO_WritePin(LED_GPIO_PORT, LED_GPIO_PIN, GPIO_PIN_SET); // PC13 输出高电平
}
/**
* @brief 翻转 LED 状态
*/
void LED_Toggle(void)
{
HAL_GPIO_TogglePin(LED_GPIO_PORT, LED_GPIO_PIN); // HAL 内部用 BSRR 实现
}
main.c
#include "stm32f1xx_hal.h"
#include "led.h"
/* 系统时钟配置函数(具体实现依赖 CubeMX 或手写) */
static void SystemClock_Config(void);
int main(void)
{
/* 1. HAL 库初始化:包括时钟源、SysTick 配置等 */
HAL_Init();
/* 2. 配置系统时钟(比如 HSE=8MHz → SYSCLK=72MHz) */
SystemClock_Config();
/* 3. 初始化 LED 引脚 */
LED_Init();
/* 4. 主循环:控制 LED 闪烁 */
while (1)
{
LED_On(); // 点亮
HAL_Delay(300); // 延时 300 ms
LED_Off(); // 熄灭
HAL_Delay(300); // 延时 300 ms
LED_Toggle(); // 翻转状态
HAL_Delay(300); // 延时 300 ms
}
}
/* ===== 系统时钟配置函数(此处仅示意,实际需根据工程生成) ===== */
static void SystemClock_Config(void)
{
/* 如果使用 CubeMX,一般会自动生成这里的代码。
自己手写时,需要配置 HSE/PLL,把 SYSCLK 提升到 72MHz。
如果不写,默认 SystemInit() 可能只运行在 8MHz HSI。 */
}
HAL 结构体/参数要点(F1)
Pin
:引脚位图,可或起来(如GPIO_PIN_0 | GPIO_PIN_1
)。Mode
:GPIO_MODE_OUTPUT_PP / _OD
(推挽/开漏)GPIO_MODE_INPUT
、GPIO_MODE_ANALOG
GPIO_MODE_AF_PP / _AF_OD
(复用推挽/开漏)GPIO_MODE_IT_RISING/FALLING/RISING_FALLING
(外部中断)GPIO_MODE_EVT_*
(事件)
Speed
:GPIO_SPEED_FREQ_LOW/MEDIUM/HIGH
(≈2/10/50 MHz)
注:F1 的 HAL 不带
Pull
字段(上拉/下拉由 CRL/CRH 的 CNF 决定;若要上拉/下拉,需把Mode
设为输入并用ODR
置 1/0 完成上/下拉,或直接用 HAL 的GPIO_MODE_INPUT
+ 后续写 ODR)。
二、标准外设库(SPL):GPIO 初始化与点亮/熄灭 LED
步骤
使能端口时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOx, ENABLE)
填
GPIO_InitTypeDef
(Pin/Mode/Speed)调
GPIO_Init(GPIOx, &init)
用
GPIO_SetBits / ResetBits / WriteBit
控制
示例代码(SPL)
led.h:
#ifndef __LED_H__
#define __LED_H__
#include "stm32f10x.h"
/* ====== 硬件相关宏定义 ====== */
#define LED_GPIO_PORT GPIOC // LED 所在端口
#define LED_GPIO_PIN GPIO_Pin_13 // LED 引脚 PC13
#define LED_GPIO_CLK RCC_APB2Periph_GPIOC // GPIOC 时钟
/* ====== LED 控制函数声明 ====== */
void LED_Init(void); // 初始化 LED 引脚
void LED_On(void); // 点亮 LED
void LED_Off(void); // 熄灭 LED
void LED_Toggle(void); // 翻转 LED 状态
#endif
led.c:
#include "led.h"
/**
* @brief 初始化 LED 引脚
* @note PC13 配置为推挽输出,默认熄灭
*/
void LED_Init(void)
{
/* 1. 使能 GPIOC 时钟 */
RCC_APB2PeriphClockCmd(LED_GPIO_CLK, ENABLE);
/* 2. 配置 PC13 为推挽输出 */
GPIO_InitTypeDef gpio;
gpio.GPIO_Pin = LED_GPIO_PIN; // 选择引脚
gpio.GPIO_Speed = GPIO_Speed_2MHz; // 输出速度 2MHz
gpio.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出
GPIO_Init(LED_GPIO_PORT, &gpio);
/* 3. 缺省熄灭 LED(PC13 高电平 = 灭) */
GPIO_SetBits(LED_GPIO_PORT, LED_GPIO_PIN);
}
/**
* @brief 点亮 LED
*/
void LED_On(void)
{
GPIO_ResetBits(LED_GPIO_PORT, LED_GPIO_PIN); // 低电平
}
/**
* @brief 熄灭 LED
*/
void LED_Off(void)
{
GPIO_SetBits(LED_GPIO_PORT, LED_GPIO_PIN); // 高电平
}
/**
* @brief 翻转 LED 状态
*/
void LED_Toggle(void)
{
if (GPIO_ReadOutputDataBit(LED_GPIO_PORT, LED_GPIO_PIN))
GPIO_ResetBits(LED_GPIO_PORT, LED_GPIO_PIN); // 如果当前是 1 → 清零
else
GPIO_SetBits(LED_GPIO_PORT, LED_GPIO_PIN); // 如果当前是 0 → 置 1
}
main.c:
#include "stm32f10x.h"
#include "led.h"
/* 时钟配置函数 */
static void Clock_Config(void);
int main(void)
{
/* 1. 配置系统时钟(如果需要) */
Clock_Config();
/* 2. 初始化 LED 引脚 */
LED_Init();
/* 3. 主循环:控制 LED 闪烁 */
while (1)
{
LED_On(); // 点亮
for(volatile int i=0;i<600000;i++); // 简单延时
LED_Off(); // 熄灭
for(volatile int i=0;i<600000;i++);
LED_Toggle(); // 翻转状态
for(volatile int i=0;i<600000;i++);
}
}
/**
* @brief 时钟配置
* @note SPL 启动文件里默认调用 SystemInit(),会把 HSE/PLL 配置成 72MHz。
* 如果已经够用,这里可以留空。
*/
static void Clock_Config(void)
{
/* 一般用默认 SystemInit 即可 */
}
SPL 结构体/参数要点
GPIO_Mode
:GPIO_Mode_AIN
(模拟输入)GPIO_Mode_IN_FLOATING
(浮空输入)GPIO_Mode_IPD / GPIO_Mode_IPU
(下拉/上拉输入)GPIO_Mode_Out_PP / _Out_OD
(通用推挽/开漏输出)GPIO_Mode_AF_PP / _AF_OD
(复用推挽/开漏输出)
GPIO_Speed
:GPIO_Speed_2MHz / 10MHz / 50MHz
(仅对输出/复用输出有效)
三、图里 HAL GPIO 其他函数的作用(并给出 SPL 对应函数)
HAL 函数 | 作用(要点) | 关键参数 | SPL/等价做法 |
---|---|---|---|
HAL_GPIO_Init(GPIOx, &init) |
根据 Pin/Mode/Speed 配置端口(本质写 CRL/CRH) |
GPIOx :端口;Pin 位图;Mode ;Speed |
GPIO_Init(GPIOx, &init) |
HAL_GPIO_DeInit(GPIOx, GPIO_Pin) |
复位指定引脚到缺省(模拟输入),清除 EXTI 绑定 | GPIO_Pin 位图 |
GPIO_DeInit(GPIOx) (注意 SPL 是“整个端口复位”;单独复位需手写寄存器) |
HAL_GPIO_WritePin(GPIOx, GPIO_Pin, GPIO_PinState) |
通过 BSRR 原子置 1/清 0 | GPIO_PinState :GPIO_PIN_SET/RESET |
GPIO_WriteBit/SetBits/ResetBits 或 GPIOx->BSRR/BRR |
HAL_GPIO_TogglePin(GPIOx, GPIO_Pin) |
翻转输出(对 ODR 异或) | GPIO_Pin 位图 |
无直接 API,可 GPIO_WriteBit(GPIOx,pin, (BitAction)!GPIO_ReadOutputDataBit(...)) 或操作 ODR |
HAL_GPIO_ReadPin(GPIOx, GPIO_Pin) |
读 IDR,返回 GPIO_PIN_SET/RESET |
GPIO_Pin |
GPIO_ReadInputDataBit(GPIOx, pin) |
HAL_GPIO_LockPin(GPIOx, GPIO_Pin) |
通过 LCKR 锁定配置,直到下次复位(防误改) | GPIO_Pin 位图 |
GPIO_PinLockConfig(GPIOx, pin) |
HAL_GPIO_EXTI_IRQHandler(GPIO_Pin) |
通用 EXTI 线中断处理:清挂起位并调用回调 | GPIO_Pin :哪条线触发 |
SPL:在 EXTIxx_IRQHandler 里 EXTI_GetITStatus /EXTI_ClearITPendingBit ,然后自己回调 |
HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) |
弱定义(__weak)回调,用户重写做业务 | GPIO_Pin :触发的线 |
SPL:自己写回调或在 IRQHandler 里直接处理 |
EXTI 的初始化(两库都需要额外步骤)
HAL:把引脚
Mode
设为GPIO_MODE_IT_*
,然后用HAL_NVIC_SetPriority
与HAL_NVIC_EnableIRQ
开中断即可。SPL:
GPIO_EXTILineConfig(AFIO_PORTx, PinSourcex) + EXTI_Init(&cfg) + NVIC_Init(&nvic)
。
四、常见易错点与小技巧
时钟别忘开:F1 的 GPIO 都在 APB2,HAL 用
__HAL_RCC_GPIOx_CLK_ENABLE()
;SPL 用RCC_APB2PeriphClockCmd(...)
。蓝板 PC13 低电平点亮:别把“写 1 点亮”写反。
原子操作首选 BSRR:HAL 的
WritePin/TogglePin
已经用 BSRR,SPL 用GPIOx->BSRR/BRR
更安全。速度只对输出有效:输入模式时
Speed
无意义。锁定功能:量产固件防误改时可用
HAL_GPIO_LockPin / GPIO_PinLockConfig
。多脚同时配置:
Pin
可位或(如GPIO_PIN_0|GPIO_PIN_1
),一次初始化多个引脚。