STM32 学习笔记 —— GPIO 入门与点亮 LED
一、芯片简介
我使用的是 STM32F103VET6 芯片,板载 USB 转串口芯片(CH340/CP2102 等)和 SWD 调试接口。
学习资料:江协科技 STM32 教程(B站)
开发平台:Keil MDK-ARM v5
库版本:STM32F10x 标准外设库(StdPeriph Library)
开发板选择:复刻开源的STM32电赛开发板

1.1 芯片型号解读
STM32 F 103 V E T 6 │ │ │ │ │ └── 温度等级:6 = 工业级 -40℃ ~ +85℃ │ │ │ │ └──── 封装类型:T = LQFP │ │ │ └────── Flash容量:E = 512KB │ │ └──────── 引脚数:V = LQFP100(100引脚) │ └──────────── 系列编号:103 = 中容量增强型 └─────────────── 产品类型:F = 通用主流闪存 MCU
| 字段 | 含义 | 本芯片参数 |
|---|---|---|
| F | 通用主流闪存 MCU | F1 系列(经典M3内核) |
| 103 | 中容量增强型 | 外设齐全:ADC、高级定时器TIM1、CAN、USB |
| V | 引脚封装 | LQFP100,共100个引脚 |
| E | Flash 容量 | 512KB Flash 程序存储器 |
| T | 封装类型 | LQFP 薄型四方扁平封装 |
| 6 | 温度等级 | 工业级,-40℃ ~ +85℃ |
1.2 核心资源概览
-
内核:ARM Cortex-M3,最高 72MHz 主频
-
存储器:512KB Flash + 64KB SRAM
-
GPIO:最多 80 个快速 IO 口,支持 5V 容忍
-
定时器:8 个定时器(含 1 个高级控制定时器 TIM1)
-
通信接口:USART×3、SPI×2、I2C×2、USB、CAN、SDIO
-
模拟外设:ADC×2(12位,共16通道)、DAC×2
-
电源:2.0V ~ 3.6V 供电,典型工作电压 3.3V
1.3 典型应用场景
-
电子竞赛、单片机课程学习、毕业设计
-
电机驱动小车、平衡车、舵机控制系统
-
数据采集、温湿度/压力传感器采集设备
-
工业简易控制器、RS485/CAN 总线节点
-
蓝牙/WiFi 串口透传设备、OLED 显示屏控制器
-
智能门锁、小型家电控制板、USB 虚拟串口工具
二、GPIO 原理详解
2.1 什么是 GPIO
GPIO(General Purpose Input/Output,通用输入输出端口)是单片机最基本的外设,可以通过软件配置引脚为输入或输出模式,实现与外部电路的数字信号交互。
STM32F103 的 GPIO 按端口分组:GPIOA、GPIOB、GPIOC …… GPIOG,每组最多 16 个引脚(Px0 ~ Px15)。
2.2 GPIO 八种工作模式
| 模式 | 分类 | 特点与应用 |
|---|---|---|
| GPIO_Mode_AIN | 输入-模拟 | 接 ADC 模拟输入,关闭施密特触发,功耗最低 |
| GPIO_Mode_IN_FLOATING | 输入-浮空 | 复位后默认状态,电平不确定,一般用于按键外部输入 |
| GPIO_Mode_IPD | 输入-下拉 | 内部下拉电阻,默认低电平 |
| GPIO_Mode_IPU | 输入-上拉 | 内部上拉电阻,默认高电平 |
| GPIO_Mode_Out_OD | 输出-开漏 | 只能拉低,高电平由外部上拉决定,可做线与、I2C |
| GPIO_Mode_Out_PP | 输出-推挽 | 高低电平都由内部 MOS 管驱动,驱动能力强,常用 |
| GPIO_Mode_AF_OD | 复用-开漏 | 片上外设(如I2C)的开漏输出 |
| GPIO_Mode_AF_PP | 复用-推挽 | 片上外设(如SPI、USART TX)的推挽输出 |
2.3 推挽输出 vs 开漏输出(重点)

推挽输出(Push-Pull)
-
输出高电平时:P-MOS 导通,N-MOS 截止 → 引脚被强拉到 VDD
-
输出低电平时:N-MOS 导通,P-MOS 截止 → 引脚被强拉到 GND
-
优点:高低电平都有驱动能力,速度快
-
缺点:不能直接线与(两个推挽输出接在一起会短路)
-
应用:LED 驱动、普通数字输出、SPI 等
开漏输出(Open-Drain)
-
输出高电平时:P-MOS、N-MOS 都截止 → 引脚呈高阻态,电平由外部上拉决定
-
输出低电平时:N-MOS 导通 → 引脚被拉到 GND
-
优点:可实现线与逻辑、电平转换(外部上拉到不同电压)
-
缺点:高电平驱动能力弱,需要外部上拉电阻
-
应用:I2C 总线、电平转换、多路共线
2.4 GPIO 输出速度
| 速度配置 | 翻转速率 | 适用场景 |
|---|---|---|
| GPIO_Speed_2MHz | 2 MHz | 低速、低功耗、抗干扰要求高 |
| GPIO_Speed_10MHz | 10 MHz | 一般应用 |
| GPIO_Speed_50MHz | 50 MHz | 高速信号、SPI 等 |
三、RCC 时钟系统(为什么要先开时钟)
3.1 为什么必须使能时钟
STM32 为了降低功耗,所有外设默认都是关闭时钟的。未开启时钟的外设,其寄存器无法读写,配置无效。这是新手最容易踩的坑之一。
GPIO 挂载在 APB2 总线上,所以要用 RCC_APB2PeriphClockCmd() 开启对应端口的时钟。
3.2 STM32 时钟树概览
┌─────────┐ HSE(8M)──┤ PLL 倍频 ├──→ 72MHz SYSCLK ──→ AHB总线 ──→ APB1(最大36M) └─────────┘ └──→ APB2(最大72M) HSI(8M)──┘ LSE(32.768K) → RTC LSI(40K) → IWDG
-
SYSCLK:系统时钟,最高 72MHz
-
AHB:高速总线,主频 72MHz,DMA、SDIO 等挂在这里
-
APB2:高速外设总线,最高 72MHz,GPIO、ADC、USART1、TIM1 等
-
APB1:低速外设总线,最高 36MHz,USART2/3、SPI2、TIM2~7 等
3.3 常用时钟使能函数
// 开启 GPIOA 时钟(APB2 总线)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 同时开启多个外设时钟(用 | 位或)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);
四、点亮 LED —— 完整详解
4.1 硬件接线
共阳接法(本笔记采用):
-
LED 阳极(长脚)→ 限流电阻 → 3.3V
-
LED 阴极(短脚)→ PA0 引脚
-
当 PA0 输出低电平时,LED 点亮
-
当 PA0 输出高电平时,LED 熄灭
3.3V ──[ 1kΩ ]───►|─── PA0
限流电阻 LED
为什么需要限流电阻?
LED 是电流型器件,正向压降约 1.8~2.2V(红色)。不加电阻会导致电流过大烧坏 LED 甚至单片机引脚。
计算公式:R = (Vcc - Vf) / If = (3.3V - 2V) / 10mA ≈ 130Ω实际常用 330Ω ~ 1kΩ,电流越小亮度越低但更安全。
4.2 初始化四步法
GPIO 初始化遵循标准的「结构体配置法」,这是 STM32 标准库的通用套路:
-
定义配置结构体 → 准备参数容器
-
开启外设时钟 → 给外设供电(最容易忘!)
-
填充结构体参数 → 设置引脚号、模式、速度
-
调用初始化函数 → 写入寄存器,生效
4.3 完整代码(逐行注释)
#include "stm32f10x.h" // STM32F10x 标准库头文件
/**
* @brief PA0 引脚初始化(推挽输出,驱动LED)
* @param 无
* @retval 无
*/
void LED_Init(void)
{
// 第一步:定义一个 GPIO 初始化结构体变量(空白模板)
// GPIO_InitTypeDef 是标准库定义的结构体类型
GPIO_InitTypeDef GPIO_InitStructure;
// 第二步:开启 GPIOA 端口时钟
// 注意:所有外设使用前必须先开时钟,否则配置无效!
// GPIOA 挂载在 APB2 总线上
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 第三步:配置结构体参数
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // 选择引脚:PA0
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 模式:推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;// 速度:50MHz
// 第四步:调用初始化函数,将配置写入寄存器
// 参数1:哪个端口(GPIOA/GPIOB...)
// 参数2:结构体指针(取地址 &)
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 初始化后默认输出高电平,LED熄灭(共阳接法)
GPIO_SetBits(GPIOA, GPIO_Pin_0);
}
int main(void)
{
// 调用 LED 初始化函数
LED_Init();
// PA0 输出低电平 → LED 点亮
GPIO_ResetBits(GPIOA, GPIO_Pin_0);
// 主循环死循环,防止程序跑飞
while(1)
{
// 空循环,程序停在这里
}
}
4.4 常用 GPIO 输出库函数
| 函数 | 功能 | 示例 |
|---|---|---|
GPIO_SetBits(GPIOx, GPIO_Pin_x) |
置位(输出高电平) | GPIO_SetBits(GPIOA, GPIO_Pin_0); |
GPIO_ResetBits(GPIOx, GPIO_Pin_x) |
复位(输出低电平) | GPIO_ResetBits(GPIOA, GPIO_Pin_0); |
GPIO_WriteBit(GPIOx, GPIO_Pin_x, BitVal) |
写指定引脚电平 | GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_RESET); |
GPIO_Write(GPIOx, PortVal) |
写整个端口16位 | GPIO_Write(GPIOA, 0x00FF); |
GPIO_ReadOutputDataBit(GPIOx, GPIO_Pin_x) |
读输出寄存器某一位 | 回读自己输出的电平 |
GPIO_ReadInputDataBit(GPIOx, GPIO_Pin_x) |
读输入引脚电平 | 按键检测时用 |
五、扩展练习:LED 闪烁
在点亮的基础上,加入延时函数实现闪烁效果:
#include "stm32f10x.h"
/**
* @brief 简单延时函数(软件延时,不准,仅入门用)
* @param time: 延时参数,数值越大延时越长
*/
void Delay(uint32_t time)
{
while(time--);
}
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
int main(void)
{
LED_Init();
while(1)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_0); // 亮
Delay(5000000); // 延时
GPIO_SetBits(GPIOA, GPIO_Pin_0); // 灭
Delay(5000000); // 延时
}
}
六、常见问题与排错
6.1 LED 不亮怎么办?按顺序排查
-
时钟开了吗? → 检查
RCC_APB2PeriphClockCmd是否正确调用 -
引脚号对吗? → 代码里的引脚和实际接线的引脚一致吗?
-
模式对吗? → 必须是
GPIO_Mode_Out_PP推挽输出 -
LED 正负极接反了吗? → 长脚阳极、短脚阴极,共阳接法阴极接单IO
-
限流电阻接了吗? → 没接可能烧坏 LED
-
编译下载成功了吗? → 检查 Keil 是否提示 "Program Done"
-
板子上电了吗? → 最基础但最容易忘
6.2 Keil 编译报错:#1 error: "..."
如果复制代码后重建工程报错,检查:
-
文件最后一行是否为空行 → C 标准要求源文件末尾必须有换行
-
头文件路径是否添加 → Options → C/C++ → Include Paths
-
标准库文件是否全部添加 → stm32f10x_gpio.c、stm32f10x_rcc.c 等
6.3 下载失败
-
检查 SWD 接线(SWDIO、SWCLK、GND 三根必接)
-
检查芯片是否被锁死(可通过串口 ISP 擦除解除)
-
检查调试器驱动是否安装(ST-Link / J-Link)
-
检查启动模式选择是否正确
七、知识总结
7.1 GPIO 输出初始化口诀
一时钟,二结构,三模式,四速度,五调用初始化
-
开时钟(RCC)
-
定义结构体
-
选引脚(Pin)
-
设模式(Mode)
-
设速度(Speed)
-
调 Init 函数
7.2 核心知识点
-
STM32 外设默认关闭时钟,使用前必须使能
-
GPIO 有 8 种工作模式,推挽输出最常用
-
推挽输出高低都能驱动,开漏输出只能拉低
-
BSRR 寄存器实现原子位操作,比改 ODR 更安全
-
LED 一定要串联限流电阻
更多推荐

所有评论(0)