前言
本节是 STM32 入门体系中第一个完整的硬件实操章节,承接工程搭建、GPIO 基础原理的前置内容,通过三个最经典的外设案例 ------LED 单灯闪烁、多灯流水灯、有源蜂鸣器驱动,完整掌握 STM32 通用 IO 口配置为推挽输出模式 的全流程,建立库函数配置硬件 → 代码控制电平 → 外设执行动作的嵌入式开发核心思维。
本节学习目标
- 掌握
GPIO_Init标准库函数的参数含义与调用流程 - 理解延时函数在 IO 时序控制中的作用
- 独立完成 LED 闪烁、流水灯、蜂鸣器发声三个基础实验
- 建立嵌入式程序「初始化 + 死循环」的架构认知
一、GPIO 初始化通用模板
scss
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
//使用各个外设前必须开启时钟,否则对外设的操作无效
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure; //定义结构体变量
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //GPIO模式,赋值为推挽输出模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //GPIO引脚,赋值为第0号引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //GPIO速度,赋值为50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); //将赋值后的构体变量传递给GPIO_Init函数
//函数内部会自动根据结构体的参数配置相应寄存器
//实现GPIOA的初始化
- 浮空输入 IN_FLOATING
- 上拉输入 IPU
- 下拉输入 IPD
- 模拟输入 AIN
- 推挽输出 Out_PP
- 开漏输出 Out_OD
- 复用推挽输出 AF_PP
- 复用开漏输出 AF_OD
GPIO 推挽输出模式
STM32 的 GPIO 配置为输出时,最常用的是推挽输出(GPIO_Mode_Out_PP) :
-
推挽输出模式下 IO 口可直接输出高低电平,驱动能力强,适配 LED、蜂鸣器这类小功率外设
-
固定配置三步法:开启对应端口时钟 → 填充 GPIO 初始化结构体 → 调用
GPIO_Init完成硬件配置 -
核心输出控制函数:
GPIO_SetBits:指定引脚输出高电平GPIO_ResetBits:指定引脚输出低电平GPIO_WriteBit:直接写入引脚电平状态
二、LED闪烁实验
1.实验原理
LED 亮灭由引脚电平直接控制:当 LED 阴极接 GPIO、阳极通过电阻接电源时,引脚输出低电平 LED 点亮,输出高电平 LED 熄灭。闪烁的核心逻辑是电平翻转 + 延时保持:设置引脚电平 → 延时维持状态 → 翻转电平 → 再次延时,在死循环中往复执行。
未剪裁的LED灯长脚位阳极,短脚为阴极
接线图如下图所示

2.LED闪烁核心逻辑

while
{
/*点亮LED持续一段时间,熄灭LED持续一段时间*/
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);//点亮LED
Delay_ms(500);
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);//熄灭LED
Delay_ms(500);
GPIO_ResetBits(GPIOA,GPIO_Pin_0);
Delay_ms(500);
GPIO_SetBits(GPIOA,GPIO_Pin_0);
Delay_ms(500);
GPIO_WriteBit(GPIOA,GPIO_Pin_0,(BitAction)0);
Delay_ms(500);
GPIO_WriteBit(GPIOA,GPIO_Pin_0,(BitAction)1);
Delay_ms(500);//如果直接使0为低电平,1为高电平会导致警告:"枚举类型出现其他变量"321
//所以需要加上进行强制类型转换,把他们转换为Bitaction枚举类型
}
注意:使用已准备好的延时函数Delay模块需要在主函数开头加入头文件❗❗❗同时一定要学会如何在工程中加入函数模块

三、LED 流水灯
1.LED 流水灯实现原理
在单灯闪烁的基础上扩展,将多个 LED 分别接入不同 GPIO 引脚,常见两种实现思路:
- 顺序点亮法:依次点亮第 1 个灯、延时、熄灭,再点亮第 2 个灯...... 循环执行,逻辑直观易懂。
- 移位遍历法:通过位运算批量控制端口电平,代码更简洁,适合多引脚场景流水灯重点锻炼多 IO 口的批量管理能力,帮助理解端口寄存器与引脚的对应关系。
本次只考虑第一种方法
接线图如下图所示

2.LED流水灯核心逻辑

scss
#include "stm32f10x.h" // Device header
#include "Delay.h"
int main(void)
{
/*开启时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
//使用各个外设前必须开启时钟,否则对外设的操作无效
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure; //定义结构体变量
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //GPIO模式,赋值为推挽输出模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All; //GPIO引脚,赋值为所有引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //GPIO速度,赋值为50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); //将赋值后的构体变量传递给GPIO_Init函数
//函数内部会自动根据结构体的参数配置相应寄存器
//实现GPIOA的初始化
/*主循环,循环体内的代码会一直循环执行*/
while (1)
{
/*使用GPIO_Write,同时设置GPIOA所有引脚的高低电平,实现LED流水灯*/
GPIO_Write(GPIOA, ~0x0001); //0000 0000 0000 0001,PA0引脚为低电平,其他引脚均为高电平,注意数据有按位取反
Delay_ms(100); //延时100ms
GPIO_Write(GPIOA, ~0x0002); //0000 0000 0000 0010,PA1引脚为低电平,其他引脚均为高电平
Delay_ms(100); //延时100ms
GPIO_Write(GPIOA, ~0x0004); //0000 0000 0000 0100,PA2引脚为低电平,其他引脚均为高电平
Delay_ms(100); //延时100ms
GPIO_Write(GPIOA, ~0x0008); //0000 0000 0000 1000,PA3引脚为低电平,其他引脚均为高电平
Delay_ms(100); //延时100ms
GPIO_Write(GPIOA, ~0x0010); //0000 0000 0001 0000,PA4引脚为低电平,其他引脚均为高电平
Delay_ms(100); //延时100ms
GPIO_Write(GPIOA, ~0x0020); //0000 0000 0010 0000,PA5引脚为低电平,其他引脚均为高电平
Delay_ms(100); //延时100ms
GPIO_Write(GPIOA, ~0x0040); //0000 0000 0100 0000,PA6引脚为低电平,其他引脚均为高电平
Delay_ms(100); //延时100ms
GPIO_Write(GPIOA, ~0x0080); //0000 0000 1000 0000,PA7引脚为低电平,其他引脚均为高电平
Delay_ms(100); //延时100ms
}
}
流水灯用到GPIOA的0到7号端口,需要加一些端口。加入端口方式 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|......; //一直到7号端口 使用按位或操作逻辑
或者直接使用GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;//将16个端口全部配置为推挽输出模式
四、蜂鸣器驱动
1.蜂鸣器驱动原理
有源蜂鸣器内部自带振荡源,通电即发出固定频率声音,驱动逻辑与 LED 完全一致: 输出对应电平使蜂鸣器通电发声,翻转电平则停止 通过「发声 + 延时 + 停止 + 延时」的循环,可实现间歇报警效果 注意有源蜂鸣器区分正负极,接线错误会导致无声音输出
接线图如下图所示
VCC正极接入正极供电孔,GND负极接到负极供电孔,I/O控制极选择一个IO口接入
注意:IO口选择中A15,B3,B4请勿选择,从STM32引脚定义图可知这三个口默认是JTAG调试端口,如果用作普通端口还需要进行一定的配置。
2.蜂鸣器驱动核心逻辑

scss
#include "stm32f10x.h" // Device header
#include "Delay.h"
int main(void)
{
/*开启时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟
//使用各个外设前必须开启时钟,否则对外设的操作无效
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure; //定义结构体变量
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //GPIO模式,赋值为推挽输出模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; //GPIO引脚,赋值为第12号引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //GPIO速度,赋值为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //将赋值后的构体变量传递给GPIO_Init函数
//函数内部会自动根据结构体的参数配置相应寄存器
//实现GPIOB的初始化
/*主循环,循环体内的代码会一直循环执行*/
while (1)
{
GPIO_ResetBits(GPIOB, GPIO_Pin_12); //将PB12引脚设置为低电平,蜂鸣器鸣叫
Delay_ms(300); //延时100ms
GPIO_SetBits(GPIOB, GPIO_Pin_12); //将PB12引脚设置为高电平,蜂鸣器停止
Delay_ms(500); //延时100ms
GPIO_ResetBits(GPIOB, GPIO_Pin_12); //将PB12引脚设置为低电平,蜂鸣器鸣叫
Delay_ms(300); //延时100ms
GPIO_SetBits(GPIOB, GPIO_Pin_12); //将PB12引脚设置为高电平,蜂鸣器停止
Delay_ms(500); //延时700ms
}
}
注意时钟为GPIOB,端口为Pin12
五、本小节问题与总结
1.按位或操作逻辑

2,推挽输出、开漏输出驱动问题

3.本节课实体接线图
