bash
void RCC_APB2PeriphResetCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);
void RCC_APB1PeriphResetCmd(uint32_t RCC_APB1Periph, FunctionalState NewState);
void RCC_BackupResetCmd(FunctionalState NewState);
void RCC_ClockSecuritySystemCmd(FunctionalState NewState);
void RCC_MCOConfig(uint8_t RCC_MCO);
FlagStatus RCC_GetFlagStatus(uint8_t RCC_FLAG);
void RCC_ClearFlag(void);
ITStatus RCC_GetITStatus(uint8_t RCC_IT);
void RCC_ClearITPendingBit(uint8_t RCC_IT);
bash
void GPIO_DeInit(GPIO_TypeDef* GPIOx);
void GPIO_AFIODeInit(void);
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct);
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_EventOutputConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
void GPIO_EventOutputCmd(FunctionalState NewState);
void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
void GPIO_ETH_MediaInterfaceConfig(uint32_t GPIO_ETH_MediaInterface);
STM32 学习笔记:GPIO 输出实验------LED 闪烁、流水灯与蜂鸣器
对应课程:
[3-2] LED 闪烁、LED 流水灯与蜂鸣器课堂记录:
D:/2026-08-30 14_32 记录 LED点亮_原文.docx整理日期:2026-08-30
芯片:STM32F103C8T6
开发方式:Keil MDK + STM32F10x 标准外设库
主要源码:
程序源码/STM32Project-有注释版/3-1~3-3前置笔记:03_GPIO输出原理与硬件设计.md
本节边界:完成 GPIO 输出的三个程序;按键与传感器输入从下一节开始
1. 老师这一节的整条思路
上一节解决"GPIO 内部为什么能输出",这一节把原理落实成三个可运行程序。老师采用的是逐层扩展思路:
#mermaid-svg-tN91VWPKqMRqookM{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-tN91VWPKqMRqookM .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tN91VWPKqMRqookM .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tN91VWPKqMRqookM .error-icon{fill:#552222;}#mermaid-svg-tN91VWPKqMRqookM .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tN91VWPKqMRqookM .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tN91VWPKqMRqookM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tN91VWPKqMRqookM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tN91VWPKqMRqookM .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tN91VWPKqMRqookM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tN91VWPKqMRqookM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tN91VWPKqMRqookM .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tN91VWPKqMRqookM .marker.cross{stroke:#333333;}#mermaid-svg-tN91VWPKqMRqookM svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tN91VWPKqMRqookM p{margin:0;}#mermaid-svg-tN91VWPKqMRqookM .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-tN91VWPKqMRqookM .cluster-label text{fill:#333;}#mermaid-svg-tN91VWPKqMRqookM .cluster-label span{color:#333;}#mermaid-svg-tN91VWPKqMRqookM .cluster-label span p{background-color:transparent;}#mermaid-svg-tN91VWPKqMRqookM .label text,#mermaid-svg-tN91VWPKqMRqookM span{fill:#333;color:#333;}#mermaid-svg-tN91VWPKqMRqookM .node rect,#mermaid-svg-tN91VWPKqMRqookM .node circle,#mermaid-svg-tN91VWPKqMRqookM .node ellipse,#mermaid-svg-tN91VWPKqMRqookM .node polygon,#mermaid-svg-tN91VWPKqMRqookM .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-tN91VWPKqMRqookM .rough-node .label text,#mermaid-svg-tN91VWPKqMRqookM .node .label text,#mermaid-svg-tN91VWPKqMRqookM .image-shape .label,#mermaid-svg-tN91VWPKqMRqookM .icon-shape .label{text-anchor:middle;}#mermaid-svg-tN91VWPKqMRqookM .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-tN91VWPKqMRqookM .rough-node .label,#mermaid-svg-tN91VWPKqMRqookM .node .label,#mermaid-svg-tN91VWPKqMRqookM .image-shape .label,#mermaid-svg-tN91VWPKqMRqookM .icon-shape .label{text-align:center;}#mermaid-svg-tN91VWPKqMRqookM .node.clickable{cursor:pointer;}#mermaid-svg-tN91VWPKqMRqookM .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-tN91VWPKqMRqookM .arrowheadPath{fill:#333333;}#mermaid-svg-tN91VWPKqMRqookM .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-tN91VWPKqMRqookM .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-tN91VWPKqMRqookM .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tN91VWPKqMRqookM .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-tN91VWPKqMRqookM .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tN91VWPKqMRqookM .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-tN91VWPKqMRqookM .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-tN91VWPKqMRqookM .cluster text{fill:#333;}#mermaid-svg-tN91VWPKqMRqookM .cluster span{color:#333;}#mermaid-svg-tN91VWPKqMRqookM div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-tN91VWPKqMRqookM .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-tN91VWPKqMRqookM rect.text{fill:none;stroke-width:0;}#mermaid-svg-tN91VWPKqMRqookM .icon-shape,#mermaid-svg-tN91VWPKqMRqookM .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tN91VWPKqMRqookM .icon-shape p,#mermaid-svg-tN91VWPKqMRqookM .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-tN91VWPKqMRqookM .icon-shape .label rect,#mermaid-svg-tN91VWPKqMRqookM .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tN91VWPKqMRqookM .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-tN91VWPKqMRqookM .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-tN91VWPKqMRqookM :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 按接线图搭硬件
回顾工程配置
查RCC和GPIO库函数
PA0输出高低电平
加入Delay形成LED闪烁
验证推挽和开漏差异
扩展PA0~PA7做流水灯
换成PB12控制蜂鸣器
总结库函数查阅方法
三个程序的变化看似很多,实际都在复用同一套框架:
text
开启端口时钟 → 配置引脚模式 → 在 while(1) 中改变输出 → 延时 → 观察负载
本节真正要学会的不是背三份程序,而是:看到接线图后,能独立替换端口、引脚和输出节奏。
2. 三个实验先建立全局认识
| 工程 | 输出引脚 | 外部器件 | 有效电平 | 主要新知识 |
|---|---|---|---|---|
3-1 LED闪烁 |
PA0 | 单个 LED | 低电平点亮 | 时钟、结构体初始化、单个位输出、延时 |
3-2 LED流水灯 |
PA0~PA7 | 8 个 LED | 低电平点亮 | 引脚位掩码、按位或、整端口写入、按位取反 |
3-3 蜂鸣器 |
PB12 | 有源蜂鸣器模块 | 低电平鸣响 | 更换端口和引脚、用时间序列设计声音节奏 |
#mermaid-svg-58AlKskbjCT7izq1{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-58AlKskbjCT7izq1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-58AlKskbjCT7izq1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-58AlKskbjCT7izq1 .error-icon{fill:#552222;}#mermaid-svg-58AlKskbjCT7izq1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-58AlKskbjCT7izq1 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-58AlKskbjCT7izq1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-58AlKskbjCT7izq1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-58AlKskbjCT7izq1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-58AlKskbjCT7izq1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-58AlKskbjCT7izq1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-58AlKskbjCT7izq1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-58AlKskbjCT7izq1 .marker.cross{stroke:#333333;}#mermaid-svg-58AlKskbjCT7izq1 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-58AlKskbjCT7izq1 p{margin:0;}#mermaid-svg-58AlKskbjCT7izq1 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-58AlKskbjCT7izq1 .cluster-label text{fill:#333;}#mermaid-svg-58AlKskbjCT7izq1 .cluster-label span{color:#333;}#mermaid-svg-58AlKskbjCT7izq1 .cluster-label span p{background-color:transparent;}#mermaid-svg-58AlKskbjCT7izq1 .label text,#mermaid-svg-58AlKskbjCT7izq1 span{fill:#333;color:#333;}#mermaid-svg-58AlKskbjCT7izq1 .node rect,#mermaid-svg-58AlKskbjCT7izq1 .node circle,#mermaid-svg-58AlKskbjCT7izq1 .node ellipse,#mermaid-svg-58AlKskbjCT7izq1 .node polygon,#mermaid-svg-58AlKskbjCT7izq1 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-58AlKskbjCT7izq1 .rough-node .label text,#mermaid-svg-58AlKskbjCT7izq1 .node .label text,#mermaid-svg-58AlKskbjCT7izq1 .image-shape .label,#mermaid-svg-58AlKskbjCT7izq1 .icon-shape .label{text-anchor:middle;}#mermaid-svg-58AlKskbjCT7izq1 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-58AlKskbjCT7izq1 .rough-node .label,#mermaid-svg-58AlKskbjCT7izq1 .node .label,#mermaid-svg-58AlKskbjCT7izq1 .image-shape .label,#mermaid-svg-58AlKskbjCT7izq1 .icon-shape .label{text-align:center;}#mermaid-svg-58AlKskbjCT7izq1 .node.clickable{cursor:pointer;}#mermaid-svg-58AlKskbjCT7izq1 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-58AlKskbjCT7izq1 .arrowheadPath{fill:#333333;}#mermaid-svg-58AlKskbjCT7izq1 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-58AlKskbjCT7izq1 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-58AlKskbjCT7izq1 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-58AlKskbjCT7izq1 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-58AlKskbjCT7izq1 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-58AlKskbjCT7izq1 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-58AlKskbjCT7izq1 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-58AlKskbjCT7izq1 .cluster text{fill:#333;}#mermaid-svg-58AlKskbjCT7izq1 .cluster span{color:#333;}#mermaid-svg-58AlKskbjCT7izq1 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-58AlKskbjCT7izq1 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-58AlKskbjCT7izq1 rect.text{fill:none;stroke-width:0;}#mermaid-svg-58AlKskbjCT7izq1 .icon-shape,#mermaid-svg-58AlKskbjCT7izq1 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-58AlKskbjCT7izq1 .icon-shape p,#mermaid-svg-58AlKskbjCT7izq1 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-58AlKskbjCT7izq1 .icon-shape .label rect,#mermaid-svg-58AlKskbjCT7izq1 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-58AlKskbjCT7izq1 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-58AlKskbjCT7izq1 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-58AlKskbjCT7izq1 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 同一套GPIO输出模板
PA0
一个LED
PA0~PA7
八个LED
PB12
蜂鸣器
课堂位置:录音
00:02--00:56介绍接线;03:21--20:54完成三个程序。
3. 实验一的硬件:PA0 低电平点亮 LED

图 1 有注释版源码中的 3-1 LED闪烁 接线图。
3.1 接线关系
接线图中的关键连接是:
- 最小系统板
3.3V接面包板正电源轨; - 最小系统板
GND接面包板负电源轨; - LED 正极接
3.3V; - LED 负极接
PA0; - ST-LINK 使用
3.3V、GND、SWCLK、SWDIO与最小系统板连接。
电流路径为:
text
3.3V → LED → PA0 → STM32内部N-MOS → GND
所以:
| PA0 输出 | LED 两端情况 | LED 状态 |
|---|---|---|
0,低电平 |
形成正向压差并有电流 | 点亮 |
1,高电平 |
两端都接近 3.3 V | 熄灭 |
这叫低电平有效。因此代码中的"清零"并不等于"关闭设备",必须根据原理图判断实际效果。
!WARNING
课堂为了搭建方便省略了串联限流电阻。自行设计和长期使用时,应给普通 LED 串联合适的限流电阻,例如
680 Ω~1 kΩ,不能把课堂临时接法当成通用硬件规范。
3.2 ST-LINK 接线不能只看排针位置
不同批次 ST-LINK 和最小系统板的排针顺序可能不同,要按照外壳或 PCB 丝印逐一对应:
| ST-LINK 信号 | 最小系统板信号 | 作用 |
|---|---|---|
3.3V |
3.3/3V3 |
目标电压参考或供电,按具体下载器使用 |
GND |
G/GND |
共地 |
SWCLK |
DCLK/SWCLK |
SWD 时钟 |
SWDIO |
DIO/SWIO/SWDIO |
SWD 数据 |
不要根据"第几个针脚"盲接。先看丝印,再确认电源和地没有接反。
课堂位置:录音
00:02--00:56;师兄笔记 PDF 第 20 页。
4. 工程建立回顾:为什么后续直接复制工程
老师先快速重建了一次标准库工程,之后的实验直接复制已有工程修改。完整过程已在 02_软件安装与新建工程.md 中整理,本节只记住工程运行所需的几个条件:
Start中有正确启动文件、CMSIS 和芯片头文件;Library中加入标准外设库源码;User中有main.c、配置文件和中断文件;- Include Paths 包含
Start、Library、User,加入延时模块后还要包含System; - 定义
USE_STDPERIPH_DRIVER; - Debug 选择 ST-LINK,Flash Download 勾选
Reset and Run。
4.1 keilkill.bat 的作用
Keil 编译产生的 Listings、Objects 等中间文件会占用较多空间。课程提供的批处理文件用于分享工程前清理这些可重新生成的文件,从而显著减小工程体积。
它不会代替源码备份;分享前仍应确认 Start、Library、System、User 和 Keil 工程文件都在。
课堂位置:录音
00:56--03:21。
5. 使用标准库控制 GPIO 的三步模板
老师把 GPIO 输出程序概括为三步:
#mermaid-svg-s5nHvKmUHKNkSfOq{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-s5nHvKmUHKNkSfOq .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-s5nHvKmUHKNkSfOq .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-s5nHvKmUHKNkSfOq .error-icon{fill:#552222;}#mermaid-svg-s5nHvKmUHKNkSfOq .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-s5nHvKmUHKNkSfOq .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-s5nHvKmUHKNkSfOq .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-s5nHvKmUHKNkSfOq .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-s5nHvKmUHKNkSfOq .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-s5nHvKmUHKNkSfOq .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-s5nHvKmUHKNkSfOq .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-s5nHvKmUHKNkSfOq .marker{fill:#333333;stroke:#333333;}#mermaid-svg-s5nHvKmUHKNkSfOq .marker.cross{stroke:#333333;}#mermaid-svg-s5nHvKmUHKNkSfOq svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-s5nHvKmUHKNkSfOq p{margin:0;}#mermaid-svg-s5nHvKmUHKNkSfOq .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-s5nHvKmUHKNkSfOq .cluster-label text{fill:#333;}#mermaid-svg-s5nHvKmUHKNkSfOq .cluster-label span{color:#333;}#mermaid-svg-s5nHvKmUHKNkSfOq .cluster-label span p{background-color:transparent;}#mermaid-svg-s5nHvKmUHKNkSfOq .label text,#mermaid-svg-s5nHvKmUHKNkSfOq span{fill:#333;color:#333;}#mermaid-svg-s5nHvKmUHKNkSfOq .node rect,#mermaid-svg-s5nHvKmUHKNkSfOq .node circle,#mermaid-svg-s5nHvKmUHKNkSfOq .node ellipse,#mermaid-svg-s5nHvKmUHKNkSfOq .node polygon,#mermaid-svg-s5nHvKmUHKNkSfOq .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-s5nHvKmUHKNkSfOq .rough-node .label text,#mermaid-svg-s5nHvKmUHKNkSfOq .node .label text,#mermaid-svg-s5nHvKmUHKNkSfOq .image-shape .label,#mermaid-svg-s5nHvKmUHKNkSfOq .icon-shape .label{text-anchor:middle;}#mermaid-svg-s5nHvKmUHKNkSfOq .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-s5nHvKmUHKNkSfOq .rough-node .label,#mermaid-svg-s5nHvKmUHKNkSfOq .node .label,#mermaid-svg-s5nHvKmUHKNkSfOq .image-shape .label,#mermaid-svg-s5nHvKmUHKNkSfOq .icon-shape .label{text-align:center;}#mermaid-svg-s5nHvKmUHKNkSfOq .node.clickable{cursor:pointer;}#mermaid-svg-s5nHvKmUHKNkSfOq .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-s5nHvKmUHKNkSfOq .arrowheadPath{fill:#333333;}#mermaid-svg-s5nHvKmUHKNkSfOq .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-s5nHvKmUHKNkSfOq .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-s5nHvKmUHKNkSfOq .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-s5nHvKmUHKNkSfOq .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-s5nHvKmUHKNkSfOq .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-s5nHvKmUHKNkSfOq .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-s5nHvKmUHKNkSfOq .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-s5nHvKmUHKNkSfOq .cluster text{fill:#333;}#mermaid-svg-s5nHvKmUHKNkSfOq .cluster span{color:#333;}#mermaid-svg-s5nHvKmUHKNkSfOq div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-s5nHvKmUHKNkSfOq .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-s5nHvKmUHKNkSfOq rect.text{fill:none;stroke-width:0;}#mermaid-svg-s5nHvKmUHKNkSfOq .icon-shape,#mermaid-svg-s5nHvKmUHKNkSfOq .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-s5nHvKmUHKNkSfOq .icon-shape p,#mermaid-svg-s5nHvKmUHKNkSfOq .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-s5nHvKmUHKNkSfOq .icon-shape .label rect,#mermaid-svg-s5nHvKmUHKNkSfOq .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-s5nHvKmUHKNkSfOq .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-s5nHvKmUHKNkSfOq .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-s5nHvKmUHKNkSfOq :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 1. RCC开启GPIO时钟
2. GPIO_Init配置引脚
3. GPIO输出函数改变电平
对应到 PA0 点灯:
c
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_ResetBits(GPIOA, GPIO_Pin_0); // PA0低电平,LED亮
代码分为"配置"和"运行"两层:
- 时钟和
GPIO_Init()通常只执行一次,决定 PA0 以后怎样工作; SetBits/ResetBits/WriteBit可以反复调用,决定 PA0 此刻输出什么电平。
6. 第一步:打开 GPIOA 的 APB2 时钟
c
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
6.1 两个参数分别表示什么
| 参数 | 本例取值 | 含义 |
|---|---|---|
RCC_APB2Periph |
RCC_APB2Periph_GPIOA |
选择 GPIOA 外设时钟 |
NewState |
ENABLE |
开启时钟;DISABLE 则关闭 |
标准库头文件中:
c
#define RCC_APB2Periph_GPIOA ((uint32_t)0x00000004)
#define RCC_APB2Periph_GPIOB ((uint32_t)0x00000008)
这些数值分别对应 RCC_APB2ENR 的 IOPAEN、IOPBEN 位。库函数内部最终执行:
c
RCC->APB2ENR |= RCC_APB2Periph;
所以标准库不是绕开寄存器,而是用可读的名字封装寄存器位操作。
#mermaid-svg-O41B1EShVrihHFEe{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-O41B1EShVrihHFEe .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-O41B1EShVrihHFEe .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-O41B1EShVrihHFEe .error-icon{fill:#552222;}#mermaid-svg-O41B1EShVrihHFEe .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-O41B1EShVrihHFEe .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-O41B1EShVrihHFEe .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-O41B1EShVrihHFEe .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-O41B1EShVrihHFEe .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-O41B1EShVrihHFEe .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-O41B1EShVrihHFEe .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-O41B1EShVrihHFEe .marker{fill:#333333;stroke:#333333;}#mermaid-svg-O41B1EShVrihHFEe .marker.cross{stroke:#333333;}#mermaid-svg-O41B1EShVrihHFEe svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-O41B1EShVrihHFEe p{margin:0;}#mermaid-svg-O41B1EShVrihHFEe .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-O41B1EShVrihHFEe .cluster-label text{fill:#333;}#mermaid-svg-O41B1EShVrihHFEe .cluster-label span{color:#333;}#mermaid-svg-O41B1EShVrihHFEe .cluster-label span p{background-color:transparent;}#mermaid-svg-O41B1EShVrihHFEe .label text,#mermaid-svg-O41B1EShVrihHFEe span{fill:#333;color:#333;}#mermaid-svg-O41B1EShVrihHFEe .node rect,#mermaid-svg-O41B1EShVrihHFEe .node circle,#mermaid-svg-O41B1EShVrihHFEe .node ellipse,#mermaid-svg-O41B1EShVrihHFEe .node polygon,#mermaid-svg-O41B1EShVrihHFEe .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-O41B1EShVrihHFEe .rough-node .label text,#mermaid-svg-O41B1EShVrihHFEe .node .label text,#mermaid-svg-O41B1EShVrihHFEe .image-shape .label,#mermaid-svg-O41B1EShVrihHFEe .icon-shape .label{text-anchor:middle;}#mermaid-svg-O41B1EShVrihHFEe .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-O41B1EShVrihHFEe .rough-node .label,#mermaid-svg-O41B1EShVrihHFEe .node .label,#mermaid-svg-O41B1EShVrihHFEe .image-shape .label,#mermaid-svg-O41B1EShVrihHFEe .icon-shape .label{text-align:center;}#mermaid-svg-O41B1EShVrihHFEe .node.clickable{cursor:pointer;}#mermaid-svg-O41B1EShVrihHFEe .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-O41B1EShVrihHFEe .arrowheadPath{fill:#333333;}#mermaid-svg-O41B1EShVrihHFEe .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-O41B1EShVrihHFEe .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-O41B1EShVrihHFEe .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-O41B1EShVrihHFEe .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-O41B1EShVrihHFEe .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-O41B1EShVrihHFEe .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-O41B1EShVrihHFEe .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-O41B1EShVrihHFEe .cluster text{fill:#333;}#mermaid-svg-O41B1EShVrihHFEe .cluster span{color:#333;}#mermaid-svg-O41B1EShVrihHFEe div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-O41B1EShVrihHFEe .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-O41B1EShVrihHFEe rect.text{fill:none;stroke-width:0;}#mermaid-svg-O41B1EShVrihHFEe .icon-shape,#mermaid-svg-O41B1EShVrihHFEe .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-O41B1EShVrihHFEe .icon-shape p,#mermaid-svg-O41B1EShVrihHFEe .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-O41B1EShVrihHFEe .icon-shape .label rect,#mermaid-svg-O41B1EShVrihHFEe .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-O41B1EShVrihHFEe .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-O41B1EShVrihHFEe .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-O41B1EShVrihHFEe :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} RCC_APB2PeriphClockCmd
标准库检查参数
RCC->APB2ENR置位
GPIOA时钟打开
参考:有注释版
stm32f10x_rcc.c第 1095--1107 行;参考手册 PDF 第 70--71 页。
6.2 为什么 GPIOA 和 GPIOB 不能混用
后面的 GPIO_Init(GPIOA, ...) 和输出函数都访问 GPIOA。如果只打开 GPIOB 时钟,GPIOA 仍未工作,配置不会得到预期结果。
端口必须前后一致:
text
RCC开启GPIOA → GPIO_Init传GPIOA → 输出函数传GPIOA
蜂鸣器改用 PB12 时,这三处都要改成 GPIOB。
7. 第二步:用结构体描述 GPIO 配置
7.1 为什么使用结构体
一个 GPIO 初始化动作同时需要模式、引脚和速度。标准库把这些参数组合到 GPIO_InitTypeDef:
c
GPIO_InitTypeDef GPIO_InitStructure;
它是一个局部结构体变量。随后使用点运算符访问成员:
c
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
老式 C 编译器常要求局部变量定义放在函数开头。课程当前编译器允许在执行语句之后定义,但为了兼容性和可读性,也可以统一把局部变量放在函数开头。
7.2 为什么选择通用推挽输出
c
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
名字可以拆开:
text
Out = 输出
PP = Push-Pull,推挽
LED 由 CPU 普通 GPIO 控制,不需要片上外设接管;同时需要引脚能够主动输出高、低电平,所以选择通用推挽输出。
标准库的 8 种模式定义在 stm32f10x_gpio.h 第 71--80 行。当前课程最常见的几项:
| 标准库枚举 | 模式 |
|---|---|
GPIO_Mode_AIN |
模拟输入 |
GPIO_Mode_IN_FLOATING |
浮空输入 |
GPIO_Mode_IPD |
下拉输入 |
GPIO_Mode_IPU |
上拉输入 |
GPIO_Mode_Out_OD |
通用开漏输出 |
GPIO_Mode_Out_PP |
通用推挽输出 |
GPIO_Mode_AF_OD |
复用开漏输出 |
GPIO_Mode_AF_PP |
复用推挽输出 |
7.3 GPIO_Pin_0 为什么是 0x0001
c
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
标准库把每个引脚定义成一个独立位:
| 引脚宏 | 十六进制 | 16 位二进制中的置位位置 |
|---|---|---|
GPIO_Pin_0 |
0x0001 |
bit0 |
GPIO_Pin_1 |
0x0002 |
bit1 |
GPIO_Pin_2 |
0x0004 |
bit2 |
GPIO_Pin_12 |
0x1000 |
bit12 |
GPIO_Pin_All |
0xFFFF |
bit0~bit15 全部为 1 |
这样既能选择一个引脚,也能用按位或同时选择多个引脚:
c
GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2
结果低 3 位为 111,表示 PA0、PA1、PA2 都被选中。
7.4 GPIO_Speed_50MHz 表示什么
c
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
这是 GPIO 输出速度等级,影响输出边沿和最大切换能力,并不是调用后引脚会自动产生 50 MHz 方波。老师在教学工程中统一选择 50 MHz;正式工程宜选择满足需求的最低速度。
7.5 为什么 GPIO_Init() 要传地址
c
GPIO_Init(GPIOA, &GPIO_InitStructure);
- 第一个参数
GPIOA:指定要配置哪个端口; - 第二个参数
&GPIO_InitStructure:把结构体变量的地址传给函数。
函数拿到地址后,能读取三个成员并完成寄存器配置。对于 PA0,它最终修改 GPIOA 的 CRL;对于 PB12,它最终修改 GPIOB 的 CRH。
库函数的主要逻辑是:
- 从模式枚举中取出 CNF 配置;
- 合并速度对应的 MODE 位;
- 遍历
GPIO_Pin中被选中的位; - Px0~Px7 写入
CRL,Px8~Px15 写入CRH。
参考:有注释版
stm32f10x_gpio.c第 173--258 行;参考手册 PDF 第 106、113--114 页。
8. 第三步:四个 GPIO 输出函数怎样选择
本节学习了四个输出函数:
| 函数 | 控制范围 | 参数表达 | 底层寄存器 | 适合用途 |
|---|---|---|---|---|
GPIO_SetBits() |
一个或多个选定位 | 置为高电平 | BSRR |
把指定引脚置 1 |
GPIO_ResetBits() |
一个或多个选定位 | 置为低电平 | BRR |
把指定引脚清 0 |
GPIO_WriteBit() |
单个引脚 | Bit_SET/Bit_RESET |
BSRR/BRR |
用一个函数表达高低电平 |
GPIO_Write() |
整个 16 位端口 | 16 位数值 | ODR |
同时更新一个端口的全部位 |
8.1 SetBits 和 ResetBits
c
GPIO_SetBits(GPIOA, GPIO_Pin_0); // PA0 = 1
GPIO_ResetBits(GPIOA, GPIO_Pin_0); // PA0 = 0
在本实验的低电平点亮电路中:
text
ResetBits → PA0低 → LED亮
SetBits → PA0高 → LED灭
函数内部非常直接:
c
GPIOx->BSRR = GPIO_Pin; // SetBits
GPIOx->BRR = GPIO_Pin; // ResetBits
8.2 GPIO_WriteBit
c
GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_RESET); // PA0 = 0
GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_SET); // PA0 = 1
BitAction 是枚举类型:
c
typedef enum
{
Bit_RESET = 0,
Bit_SET
} BitAction;
如果直接写数字:
c
GPIO_WriteBit(GPIOA, GPIO_Pin_0, 0);
编译器可能警告"枚举类型与其他类型混用"。课程演示了显式强制转换:
c
GPIO_WriteBit(GPIOA, GPIO_Pin_0, (BitAction)0);
GPIO_WriteBit(GPIOA, GPIO_Pin_0, (BitAction)1);
工程中优先使用 Bit_RESET、Bit_SET,语义更清楚,也避免把其他数字误传给枚举参数。
8.3 GPIO_Write
c
GPIO_Write(GPIOA, 0xFFFE);
它会直接执行:
c
GPIOA->ODR = 0xFFFE;
因此 GPIOA 的 16 个输出位会一起更新。它适合流水灯这种整组端口输出,但如果同一端口还有其他设备,就可能把那些引脚的状态一起覆盖。
参考:有注释版
stm32f10x_gpio.c第 358--423 行;参考手册 PDF 第 115--116 页。
9. 程序一:LED 闪烁
9.1 课堂逻辑的最小版本
c
#include "stm32f10x.h"
#include "Delay.h"
int main(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
while (1)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_0); // 低电平,LED亮
Delay_ms(500);
GPIO_SetBits(GPIOA, GPIO_Pin_0); // 高电平,LED灭
Delay_ms(500);
}
}
9.2 一轮循环发生了什么
LED PA0 CPU LED PA0 CPU #mermaid-svg-MwC3eKocnDJKW1Bc{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-MwC3eKocnDJKW1Bc .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-MwC3eKocnDJKW1Bc .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-MwC3eKocnDJKW1Bc .error-icon{fill:#552222;}#mermaid-svg-MwC3eKocnDJKW1Bc .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-MwC3eKocnDJKW1Bc .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-MwC3eKocnDJKW1Bc .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-MwC3eKocnDJKW1Bc .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-MwC3eKocnDJKW1Bc .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-MwC3eKocnDJKW1Bc .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-MwC3eKocnDJKW1Bc .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-MwC3eKocnDJKW1Bc .marker{fill:#333333;stroke:#333333;}#mermaid-svg-MwC3eKocnDJKW1Bc .marker.cross{stroke:#333333;}#mermaid-svg-MwC3eKocnDJKW1Bc svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-MwC3eKocnDJKW1Bc p{margin:0;}#mermaid-svg-MwC3eKocnDJKW1Bc .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-MwC3eKocnDJKW1Bc text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-MwC3eKocnDJKW1Bc .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-MwC3eKocnDJKW1Bc .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-MwC3eKocnDJKW1Bc .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-MwC3eKocnDJKW1Bc .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-MwC3eKocnDJKW1Bc #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-MwC3eKocnDJKW1Bc .sequenceNumber{fill:white;}#mermaid-svg-MwC3eKocnDJKW1Bc #sequencenumber{fill:#333;}#mermaid-svg-MwC3eKocnDJKW1Bc #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-MwC3eKocnDJKW1Bc .messageText{fill:#333;stroke:none;}#mermaid-svg-MwC3eKocnDJKW1Bc .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-MwC3eKocnDJKW1Bc .labelText,#mermaid-svg-MwC3eKocnDJKW1Bc .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-MwC3eKocnDJKW1Bc .loopText,#mermaid-svg-MwC3eKocnDJKW1Bc .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-MwC3eKocnDJKW1Bc .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-MwC3eKocnDJKW1Bc .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-MwC3eKocnDJKW1Bc .noteText,#mermaid-svg-MwC3eKocnDJKW1Bc .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-MwC3eKocnDJKW1Bc .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-MwC3eKocnDJKW1Bc .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-MwC3eKocnDJKW1Bc .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-MwC3eKocnDJKW1Bc .actorPopupMenu{position:absolute;}#mermaid-svg-MwC3eKocnDJKW1Bc .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-MwC3eKocnDJKW1Bc .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-MwC3eKocnDJKW1Bc .actor-man circle,#mermaid-svg-MwC3eKocnDJKW1Bc line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-MwC3eKocnDJKW1Bc :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ResetBits,输出0 灌电流,点亮 Delay_ms(500) SetBits,输出1 电流停止,熄灭 Delay_ms(500)
亮 500 ms、灭 500 ms,一轮约 1 s,所以完整闪烁频率约为 1 Hz。
9.3 有注释版为什么连续写了三种方法
3-1 LED闪烁/User/main.c 第 24~42 行依次执行:
GPIO_ResetBits/SetBits;GPIO_WriteBit + Bit_RESET/Bit_SET;GPIO_WriteBit + (BitAction)0/1。
它们是为了教学对照,不是一个闪烁程序必须做三遍。实际项目保留一种风格即可。由于三组代码依次重复相同的亮灭动作,当前有注释版在每次外层循环中会连续完成三次 500 ms 亮、500 ms 灭。
9.4 上电初始状态的工程补充
GPIO 复位后 ODR 初始通常为 0。PA0 一旦被切换为推挽输出,低电平有效 LED 可能立即点亮。如果要求上电默认熄灭,可在配置输出模式前先把输出锁存位置 1:
c
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_SetBits(GPIOA, GPIO_Pin_0); // 先准备高电平
GPIO_Init(GPIOA, &GPIO_InitStructure);
这是工程安全状态补充,不是本段课堂的必写步骤。
课堂位置:录音
05:44--14:46;师兄笔记 PDF 第 20--21 页。
10. Delay 模块:为什么加入 System 文件夹
课程提供:
text
System/
├─ Delay.c
└─ Delay.h
加入工程时要完成三件事:
- 磁盘工程目录中建立
System并复制文件; - Keil 中建立同名 Group,把
Delay.c加入工程; - C/C++ Include Paths 加入
System,在main.c中#include "Delay.h"。
10.1 延时函数提供什么接口
c
void Delay_us(uint32_t us);
void Delay_ms(uint32_t ms);
void Delay_s(uint32_t s);
本节只使用:
c
Delay_ms(500);
10.2 内部怎样延时
Delay_us() 使用 Cortex-M3 内核的 SysTick 定时器:
- 按 72 MHz 主频计算重装值;
- 清空当前计数;
- 启动 SysTick;
- 等待计数到 0;
- 关闭计数器。
Delay_ms() 则循环调用 Delay_us(1000)。
!NOTE
该模块中的系数
72假设系统时钟为 72 MHz。如果后来修改主频,延时会产生比例误差,需要同步调整或改成基于SystemCoreClock的实现。老师在本节要求先会调用,不要求立即研究 SysTick 细节。
课堂位置:录音11:20--13:32;源码3-1 LED闪烁/System/Delay.c。
11. 推挽和开漏的实物验证
老师通过改变 LED 接法和 GPIO 模式验证上一节理论。
11.1 推挽输出
当 PA0 配置为:
c
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
- 低电平时,N-MOS 主动下拉,可以灌电流;
- 高电平时,P-MOS 主动上拉,可以提供电流。
所以把 LED 改成高电平点亮接法后,仍能正常闪烁。
11.2 开漏输出
改为:
c
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
- 低电平仍由 N-MOS 主动拉低,具有灌电流能力;
- 写高电平时只是关闭 N-MOS,引脚处于高阻态,不会主动输出高电平。
因此:
| LED 接法 | 开漏输出结果 |
|---|---|
| 高电平点亮,PA0 需要向外提供电流 | 没有外部上拉时不亮 |
| 低电平点亮,PA0 只需向地灌电流 | 可以点亮 |
结论不是"开漏不能输出高电平逻辑",而是"开漏自身不能主动拉高";加外部上拉后,引脚释放时可由上拉电阻形成高电平。
课堂位置:录音
14:46--15:32。
12. 程序二:PA0~PA7 LED 流水灯

图 2 有注释版源码中的 3-2 LED流水灯 接线图。8 个 LED 均为低电平点亮。
12.1 硬件变化
- 8 个 LED 正极接 3.3 V 电源轨;
- 8 个 LED 负极依次接 PA0~PA7;
- 每个 LED 都是对应 GPIO 输出低电平时点亮。
12.2 源码初始化
c
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
课堂源码使用 GPIO_Pin_All,把 GPIOA 的 16 个引脚全部配置为推挽输出,写法简单,但真正连接的只有 PA0~PA7。
12.3 更稳妥的引脚选择
真实项目应尽量只配置需要的引脚:
c
GPIO_InitStructure.GPIO_Pin =
GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3
| GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
这样不会无意改变 PA8~PA15。尤其 PA13、PA14 默认用于 SWD 调试,PA15 还与 JTAG 调试功能复用,整端口配置会让工程意图变得不清晰。
12.4 为什么要按位取反
源码第一步:
c
GPIO_Write(GPIOA, ~0x0001);
0x0001 的低 16 位只有 bit0 为 1:
text
0000 0000 0000 0001
按位取反并转换为 16 位后:
text
1111 1111 1111 1110
结果是 PA0 输出 0,PA1~PA15 输出 1。由于 LED 低电平点亮,所以只有 PA0 对应 LED 亮。
完整序列:
| 写入表达式 | 低电平所在位 | 点亮 LED |
|---|---|---|
~0x0001 |
bit0 | PA0 |
~0x0002 |
bit1 | PA1 |
~0x0004 |
bit2 | PA2 |
~0x0008 |
bit3 | PA3 |
~0x0010 |
bit4 | PA4 |
~0x0020 |
bit5 | PA5 |
~0x0040 |
bit6 | PA6 |
~0x0080 |
bit7 | PA7 |
每次保持 100 ms,就形成从 PA0 向 PA7 移动的亮点。
12.5 源码的完整控制部分
c
while (1)
{
GPIO_Write(GPIOA, ~0x0001); Delay_ms(100);
GPIO_Write(GPIOA, ~0x0002); Delay_ms(100);
GPIO_Write(GPIOA, ~0x0004); Delay_ms(100);
GPIO_Write(GPIOA, ~0x0008); Delay_ms(100);
GPIO_Write(GPIOA, ~0x0010); Delay_ms(100);
GPIO_Write(GPIOA, ~0x0020); Delay_ms(100);
GPIO_Write(GPIOA, ~0x0040); Delay_ms(100);
GPIO_Write(GPIOA, ~0x0080); Delay_ms(100);
}
12.6 师兄笔记中的循环写法
上面 8 组代码遵循同一规律,可以改为:
c
for (uint8_t i = 0; i < 8; i++)
{
uint16_t pattern = (uint16_t)~(1U << i);
GPIO_Write(GPIOA, pattern);
Delay_ms(100);
}
1U << i 每次把唯一的 1 左移一位,再取反得到唯一的 0。这属于师兄笔记中的等价优化,不是老师本段录音逐行写出的版本。
12.7 GPIO_Write 的风险
GPIO_Write(GPIOA, value) 会覆盖整个 ODR。如果 GPIOA 的其他引脚还控制别的器件,这种写法可能破坏它们的状态。
因此:
- 教学流水灯:整端口写入直观,可以使用;
- 多功能项目:优先使用
BSRR/BRR或SetBits/ResetBits精确改变目标位; - 若仍整端口写入,应先规划端口资源和保留位。
课堂位置:录音
15:32--19:31;师兄笔记 PDF 第 21 页;源码3-2 LED流水灯/User/main.c。
13. 程序三:PB12 控制有源蜂鸣器

图 3 有注释版源码中的 3-3 蜂鸣器 接线图。
13.1 接线关系与有效电平
| 蜂鸣器模块引脚 | 连接 |
|---|---|
VCC |
3.3 V |
GND |
GND |
I/O |
PB12 |
该模块内部带驱动电路,为低电平有效:
text
PB12 = 0 → 蜂鸣器响
PB12 = 1 → 蜂鸣器停
13.2 从 PA0 程序迁移到 PB12
只需要替换三类信息:
| 项目 | LED 闪烁 | 蜂鸣器 |
|---|---|---|
| 时钟 | RCC_APB2Periph_GPIOA |
RCC_APB2Periph_GPIOB |
| 初始化端口 | GPIOA |
GPIOB |
| 引脚 | GPIO_Pin_0 |
GPIO_Pin_12 |
模式仍为推挽输出,速度仍按课堂设置为 50 MHz。
13.3 源码
c
#include "stm32f10x.h"
#include "Delay.h"
int main(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
while (1)
{
GPIO_ResetBits(GPIOB, GPIO_Pin_12); // 响
Delay_ms(100);
GPIO_SetBits(GPIOB, GPIO_Pin_12); // 停
Delay_ms(100);
GPIO_ResetBits(GPIOB, GPIO_Pin_12); // 再响
Delay_ms(100);
GPIO_SetBits(GPIOB, GPIO_Pin_12); // 长停顿
Delay_ms(700);
}
}
13.4 声音节奏怎样形成
#mermaid-svg-mYDO3a18C1conphz{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-mYDO3a18C1conphz .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-mYDO3a18C1conphz .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-mYDO3a18C1conphz .error-icon{fill:#552222;}#mermaid-svg-mYDO3a18C1conphz .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mYDO3a18C1conphz .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-mYDO3a18C1conphz .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mYDO3a18C1conphz .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mYDO3a18C1conphz .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-mYDO3a18C1conphz .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mYDO3a18C1conphz .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mYDO3a18C1conphz .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mYDO3a18C1conphz .marker.cross{stroke:#333333;}#mermaid-svg-mYDO3a18C1conphz svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mYDO3a18C1conphz p{margin:0;}#mermaid-svg-mYDO3a18C1conphz .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-mYDO3a18C1conphz .cluster-label text{fill:#333;}#mermaid-svg-mYDO3a18C1conphz .cluster-label span{color:#333;}#mermaid-svg-mYDO3a18C1conphz .cluster-label span p{background-color:transparent;}#mermaid-svg-mYDO3a18C1conphz .label text,#mermaid-svg-mYDO3a18C1conphz span{fill:#333;color:#333;}#mermaid-svg-mYDO3a18C1conphz .node rect,#mermaid-svg-mYDO3a18C1conphz .node circle,#mermaid-svg-mYDO3a18C1conphz .node ellipse,#mermaid-svg-mYDO3a18C1conphz .node polygon,#mermaid-svg-mYDO3a18C1conphz .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-mYDO3a18C1conphz .rough-node .label text,#mermaid-svg-mYDO3a18C1conphz .node .label text,#mermaid-svg-mYDO3a18C1conphz .image-shape .label,#mermaid-svg-mYDO3a18C1conphz .icon-shape .label{text-anchor:middle;}#mermaid-svg-mYDO3a18C1conphz .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-mYDO3a18C1conphz .rough-node .label,#mermaid-svg-mYDO3a18C1conphz .node .label,#mermaid-svg-mYDO3a18C1conphz .image-shape .label,#mermaid-svg-mYDO3a18C1conphz .icon-shape .label{text-align:center;}#mermaid-svg-mYDO3a18C1conphz .node.clickable{cursor:pointer;}#mermaid-svg-mYDO3a18C1conphz .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-mYDO3a18C1conphz .arrowheadPath{fill:#333333;}#mermaid-svg-mYDO3a18C1conphz .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-mYDO3a18C1conphz .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-mYDO3a18C1conphz .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mYDO3a18C1conphz .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-mYDO3a18C1conphz .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mYDO3a18C1conphz .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-mYDO3a18C1conphz .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-mYDO3a18C1conphz .cluster text{fill:#333;}#mermaid-svg-mYDO3a18C1conphz .cluster span{color:#333;}#mermaid-svg-mYDO3a18C1conphz div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-mYDO3a18C1conphz .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-mYDO3a18C1conphz rect.text{fill:none;stroke-width:0;}#mermaid-svg-mYDO3a18C1conphz .icon-shape,#mermaid-svg-mYDO3a18C1conphz .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mYDO3a18C1conphz .icon-shape p,#mermaid-svg-mYDO3a18C1conphz .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-mYDO3a18C1conphz .icon-shape .label rect,#mermaid-svg-mYDO3a18C1conphz .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mYDO3a18C1conphz .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-mYDO3a18C1conphz .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-mYDO3a18C1conphz :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 0~100 ms
响
100~200 ms
停
200~300 ms
响
300~1000 ms
停
一轮总时长:
text
100 + 100 + 100 + 700 = 1000 ms
改变延时时间就能改变节奏,但有源蜂鸣器的音调主要由模块内部振荡器决定;本节改变的是"响多久、停多久",不是音调频率。
13.5 录音中调试引脚说明的校正
录音在蜂鸣器前出现了"几个引脚默认是调试端口"的说明,转写位置和名称较混乱。STM32F103 的 SWJ 调试相关引脚为:
- PA13:JTMS/SWDIO;
- PA14:JTCK/SWCLK;
- PA15:JTDI;
- PB3:JTDO/TRACESWO;
- PB4:JNTRST。
PB12 不是默认 JTAG/SWD 调试引脚,本实验不需要为 PB12 关闭调试功能。若以后要把 PA15、PB3、PB4 当普通 GPIO,需要配置 AFIO 的 SWJ_CFG,并保留需要的 SWD 下载通道。
课堂位置:录音
19:31--20:54;师兄笔记 PDF 第 22 页;参考手册 PDF 第 117--118 页;数据手册 PDF 第 18、20 页。
14. 从库函数一路追到寄存器
老师强调可以在 Keil 中输入函数前缀、查看补全列表,再使用"Go To Definition"查看声明、注释和实现。以点灯程序为例:
#mermaid-svg-VzT1wSGC8e5PFQ5i{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-VzT1wSGC8e5PFQ5i .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-VzT1wSGC8e5PFQ5i .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-VzT1wSGC8e5PFQ5i .error-icon{fill:#552222;}#mermaid-svg-VzT1wSGC8e5PFQ5i .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-VzT1wSGC8e5PFQ5i .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-VzT1wSGC8e5PFQ5i .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-VzT1wSGC8e5PFQ5i .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-VzT1wSGC8e5PFQ5i .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-VzT1wSGC8e5PFQ5i .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-VzT1wSGC8e5PFQ5i .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-VzT1wSGC8e5PFQ5i .marker{fill:#333333;stroke:#333333;}#mermaid-svg-VzT1wSGC8e5PFQ5i .marker.cross{stroke:#333333;}#mermaid-svg-VzT1wSGC8e5PFQ5i svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-VzT1wSGC8e5PFQ5i p{margin:0;}#mermaid-svg-VzT1wSGC8e5PFQ5i .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-VzT1wSGC8e5PFQ5i .cluster-label text{fill:#333;}#mermaid-svg-VzT1wSGC8e5PFQ5i .cluster-label span{color:#333;}#mermaid-svg-VzT1wSGC8e5PFQ5i .cluster-label span p{background-color:transparent;}#mermaid-svg-VzT1wSGC8e5PFQ5i .label text,#mermaid-svg-VzT1wSGC8e5PFQ5i span{fill:#333;color:#333;}#mermaid-svg-VzT1wSGC8e5PFQ5i .node rect,#mermaid-svg-VzT1wSGC8e5PFQ5i .node circle,#mermaid-svg-VzT1wSGC8e5PFQ5i .node ellipse,#mermaid-svg-VzT1wSGC8e5PFQ5i .node polygon,#mermaid-svg-VzT1wSGC8e5PFQ5i .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-VzT1wSGC8e5PFQ5i .rough-node .label text,#mermaid-svg-VzT1wSGC8e5PFQ5i .node .label text,#mermaid-svg-VzT1wSGC8e5PFQ5i .image-shape .label,#mermaid-svg-VzT1wSGC8e5PFQ5i .icon-shape .label{text-anchor:middle;}#mermaid-svg-VzT1wSGC8e5PFQ5i .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-VzT1wSGC8e5PFQ5i .rough-node .label,#mermaid-svg-VzT1wSGC8e5PFQ5i .node .label,#mermaid-svg-VzT1wSGC8e5PFQ5i .image-shape .label,#mermaid-svg-VzT1wSGC8e5PFQ5i .icon-shape .label{text-align:center;}#mermaid-svg-VzT1wSGC8e5PFQ5i .node.clickable{cursor:pointer;}#mermaid-svg-VzT1wSGC8e5PFQ5i .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-VzT1wSGC8e5PFQ5i .arrowheadPath{fill:#333333;}#mermaid-svg-VzT1wSGC8e5PFQ5i .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-VzT1wSGC8e5PFQ5i .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-VzT1wSGC8e5PFQ5i .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VzT1wSGC8e5PFQ5i .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-VzT1wSGC8e5PFQ5i .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VzT1wSGC8e5PFQ5i .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-VzT1wSGC8e5PFQ5i .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-VzT1wSGC8e5PFQ5i .cluster text{fill:#333;}#mermaid-svg-VzT1wSGC8e5PFQ5i .cluster span{color:#333;}#mermaid-svg-VzT1wSGC8e5PFQ5i div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-VzT1wSGC8e5PFQ5i .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-VzT1wSGC8e5PFQ5i rect.text{fill:none;stroke-width:0;}#mermaid-svg-VzT1wSGC8e5PFQ5i .icon-shape,#mermaid-svg-VzT1wSGC8e5PFQ5i .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VzT1wSGC8e5PFQ5i .icon-shape p,#mermaid-svg-VzT1wSGC8e5PFQ5i .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-VzT1wSGC8e5PFQ5i .icon-shape .label rect,#mermaid-svg-VzT1wSGC8e5PFQ5i .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VzT1wSGC8e5PFQ5i .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-VzT1wSGC8e5PFQ5i .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-VzT1wSGC8e5PFQ5i :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} main.c调用库函数
stm32f10x_gpio.h
看类型、宏和函数声明
stm32f10x_gpio.c
看函数实现
CRL/CRH、BSRR、BRR、ODR寄存器
引脚电平
LED或蜂鸣器现象
14.1 本节常查的文件
| 文件 | 查什么 |
|---|---|
stm32f10x_rcc.h |
GPIOA/GPIOB 时钟宏、函数声明 |
stm32f10x_rcc.c |
RCC_APB2PeriphClockCmd() 怎样写 APB2ENR |
stm32f10x_gpio.h |
模式枚举、速度枚举、引脚宏、输出函数声明 |
stm32f10x_gpio.c |
GPIO_Init() 和输出函数怎样操作寄存器 |
stm32f10x.h |
GPIO、RCC 等寄存器结构和外设基地址 |
14.2 三种资料查法
老师最后给出三条途径:
- 在 Keil 中查看头文件声明与函数定义;
- 查看标准外设库用户手册或固件包内的 HTML 帮助;
- 搜索其他代码作为思路参考。
使用时应再加一条原则:别人的代码只能帮助找到 API,最终参数含义要回到当前版本的头文件、源码和官方手册确认。 课堂提到的中文用户手册对应较旧标准库版本,可能与当前 V3.5.0 有少量差异。
课堂位置:录音
03:21--05:44、20:54--22:21。
15. 三个程序的共同模板
以后控制继电器、模块使能脚或其他数字负载,可以套用下面的推理模板:
#mermaid-svg-ySPmZuXsVRQtHGDK{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ySPmZuXsVRQtHGDK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ySPmZuXsVRQtHGDK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ySPmZuXsVRQtHGDK .error-icon{fill:#552222;}#mermaid-svg-ySPmZuXsVRQtHGDK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ySPmZuXsVRQtHGDK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ySPmZuXsVRQtHGDK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ySPmZuXsVRQtHGDK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ySPmZuXsVRQtHGDK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ySPmZuXsVRQtHGDK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ySPmZuXsVRQtHGDK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ySPmZuXsVRQtHGDK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ySPmZuXsVRQtHGDK .marker.cross{stroke:#333333;}#mermaid-svg-ySPmZuXsVRQtHGDK svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ySPmZuXsVRQtHGDK p{margin:0;}#mermaid-svg-ySPmZuXsVRQtHGDK .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ySPmZuXsVRQtHGDK .cluster-label text{fill:#333;}#mermaid-svg-ySPmZuXsVRQtHGDK .cluster-label span{color:#333;}#mermaid-svg-ySPmZuXsVRQtHGDK .cluster-label span p{background-color:transparent;}#mermaid-svg-ySPmZuXsVRQtHGDK .label text,#mermaid-svg-ySPmZuXsVRQtHGDK span{fill:#333;color:#333;}#mermaid-svg-ySPmZuXsVRQtHGDK .node rect,#mermaid-svg-ySPmZuXsVRQtHGDK .node circle,#mermaid-svg-ySPmZuXsVRQtHGDK .node ellipse,#mermaid-svg-ySPmZuXsVRQtHGDK .node polygon,#mermaid-svg-ySPmZuXsVRQtHGDK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ySPmZuXsVRQtHGDK .rough-node .label text,#mermaid-svg-ySPmZuXsVRQtHGDK .node .label text,#mermaid-svg-ySPmZuXsVRQtHGDK .image-shape .label,#mermaid-svg-ySPmZuXsVRQtHGDK .icon-shape .label{text-anchor:middle;}#mermaid-svg-ySPmZuXsVRQtHGDK .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ySPmZuXsVRQtHGDK .rough-node .label,#mermaid-svg-ySPmZuXsVRQtHGDK .node .label,#mermaid-svg-ySPmZuXsVRQtHGDK .image-shape .label,#mermaid-svg-ySPmZuXsVRQtHGDK .icon-shape .label{text-align:center;}#mermaid-svg-ySPmZuXsVRQtHGDK .node.clickable{cursor:pointer;}#mermaid-svg-ySPmZuXsVRQtHGDK .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ySPmZuXsVRQtHGDK .arrowheadPath{fill:#333333;}#mermaid-svg-ySPmZuXsVRQtHGDK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ySPmZuXsVRQtHGDK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ySPmZuXsVRQtHGDK .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ySPmZuXsVRQtHGDK .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ySPmZuXsVRQtHGDK .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ySPmZuXsVRQtHGDK .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ySPmZuXsVRQtHGDK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ySPmZuXsVRQtHGDK .cluster text{fill:#333;}#mermaid-svg-ySPmZuXsVRQtHGDK .cluster span{color:#333;}#mermaid-svg-ySPmZuXsVRQtHGDK div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ySPmZuXsVRQtHGDK .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ySPmZuXsVRQtHGDK rect.text{fill:none;stroke-width:0;}#mermaid-svg-ySPmZuXsVRQtHGDK .icon-shape,#mermaid-svg-ySPmZuXsVRQtHGDK .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ySPmZuXsVRQtHGDK .icon-shape p,#mermaid-svg-ySPmZuXsVRQtHGDK .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ySPmZuXsVRQtHGDK .icon-shape .label rect,#mermaid-svg-ySPmZuXsVRQtHGDK .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ySPmZuXsVRQtHGDK .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ySPmZuXsVRQtHGDK .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ySPmZuXsVRQtHGDK :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 看原理图
确定端口和引脚
确定高有效还是低有效
看负载电流
判断是否需要驱动器
开启对应GPIO时钟
配置模式、引脚和速度
先设置安全初始状态
在主循环中输出和延时
编译、下载、观察现象
代码骨架:
c
#include "stm32f10x.h"
#include "Delay.h"
int main(void)
{
/* 1. 开时钟 */
RCC_APB2PeriphClockCmd(/* 对应GPIO端口 */, ENABLE);
/* 2. 配GPIO */
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = /* 对应引脚 */;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(/* 对应GPIO端口 */, &GPIO_InitStructure);
/* 3. 循环控制 */
while (1)
{
/* 输出有效电平 */
Delay_ms(/* 保持时间 */);
/* 输出无效电平 */
Delay_ms(/* 保持时间 */);
}
}
16. 常见错误与排查顺序
| 现象 | 优先检查 |
|---|---|
编译提示找不到 Delay.h |
System 是否加入 Include Paths |
链接提示找不到 Delay_ms |
Delay.c 是否真正加入 Keil Group 并参与编译 |
| 下载后完全无现象 | ST-LINK 接线、共地、目标芯片、Reset and Run |
| 代码能运行但 LED 逻辑相反 | 看原理图确认高有效/低有效 |
| GPIO 操作无效 | 是否打开正确端口的 APB2 时钟 |
| 配置了 PA0 却接在别的孔 | 对照最小系统板丝印和面包板内部连通关系 |
| LED 一直不亮 | 正负极、限流电阻、PA0 接线、输出模式 |
| 开漏模式高电平点不亮 LED | 开漏高状态为高阻,是否有外部上拉 |
| 流水灯顺序不对 | PA0~PA7 接线次序、位掩码、低有效取反 |
| 流水灯影响其他器件 | GPIO_Write 覆盖整个 ODR,检查端口共享 |
| 蜂鸣器逻辑反了 | 模块是否低电平有效、I/O 是否接 PB12 |
| 延时明显不准 | 系统主频是否仍为 72 MHz |
排查时从硬件到软件逐层缩小:
text
电源与共地 → 物理接线 → 端口时钟 → GPIO初始化 → 输出函数 → 延时与循环
17. 本节转写与表述校正
| 录音转写 | 正确内容 |
|---|---|
RTC、RTCAHBY 等 |
多数是在说 RCC 及 RCC_AHB/APB 时钟函数 |
SPIO/GPIOD 混乱 |
本节外设为 GPIO,初始化函数为 GPIO_Init() |
label |
ENABLE;另一取值为 DISABLE |
推广输出 |
推挽输出,GPIO_Mode_Out_PP |
开路输出 |
开漏输出,GPIO_Mode_Out_OD |
GPIOREST... |
GPIO_ResetBits() |
GPIOSSPID... |
GPIO_SetBits() |
GPIORTBT |
GPIO_WriteBit() |
图像处理 |
GPIO_Write() |
位设置/位清除 |
标准库底层分别使用 BSRR、BRR |
颜色函数 |
延时函数 Delay_ms() |
分明器 |
蜂鸣器 |
| PB12 附近的调试口描述 | 默认调试 GPIO 是 PA13、PA14、PA15、PB3、PB4,不包括 PB12 |
18. 复习自测
- 为什么使用 GPIO 前必须先调用 RCC 时钟函数?
GPIO_InitTypeDef的三个成员分别描述什么?- 为什么 PA0 点灯选择
GPIO_Mode_Out_PP? GPIO_Pin_0为什么等于0x0001?GPIO_Pin_12为什么等于0x1000?GPIO_Init(GPIOA, &GPIO_InitStructure)中的&有什么作用?SetBits、ResetBits、WriteBit、Write分别适合什么场景?- 为什么本实验
ResetBits反而会让 LED 点亮? - 500 ms 亮、500 ms 灭时,闪烁周期和频率约为多少?
- 推挽输出和开漏输出在高电平驱动能力上有什么区别?
GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2为什么能同时选择三个引脚?- 流水灯数据为什么要对
0x0001、0x0002...取反? GPIO_Write()为什么可能影响同一端口的其他器件?- 为什么不建议为了 8 个 LED 直接把 GPIOA 全部引脚都初始化?
- 蜂鸣器的两次鸣响和长停顿分别由哪些延时组成?
- 如果把系统主频从 72 MHz 改成 36 MHz,当前 Delay 模块会发生什么?
- PA15、PB3、PB4 不能直接当普通 GPIO 时,应查参考手册的哪个功能?
19. 参考资料与页码/源码索引
19.1 课堂与工程源码
| 来源 | 本笔记采用内容 |
|---|---|
D:/2026-08-30 14_32 记录 LED点亮_原文.docx |
全文,录音 00:02--22:21 |
3-1 LED闪烁/User/main.c |
PA0 初始化、三种单个位输出方式、LED 闪烁 |
3-1 LED闪烁/System/Delay.c/.h |
SysTick 延时模块 |
3-2 LED流水灯/User/main.c |
GPIOA 整端口输出、8 路低有效流水灯 |
3-3 蜂鸣器/User/main.c |
PB12 低有效蜂鸣器节奏 |
1-1 接线图/3-1、3-2、3-3 |
三个实验的实际接线图 |
源码根目录:../程序源码/STM32Project-有注释版/
19.2 师兄笔记
| PDF 页码 | 内容 |
|---|---|
| 第 20 页 | LED 闪烁接线、RCC/GPIO 函数、8 种模式和输出函数 |
| 第 21 页 | LED 闪烁完整代码、流水灯循环优化写法 |
| 第 22 页 | 蜂鸣器接线、PB12 初始化和声音节奏 |
文件:../STM32笔记.pdf
19.3 《STM32F10xxx参考手册(中文)》
| PDF 页码 | 内容 |
|---|---|
| 第 70--71 页 | RCC_APB2ENR,GPIOA/GPIOB 时钟使能 |
| 第 106 页 | GPIO 位结构和 MODE/CNF 配置 |
| 第 113--114 页 | GPIOx_CRL/CRH 配置寄存器 |
| 第 115--116 页 | ODR、BSRR、BRR 输出寄存器 |
| 第 117--118 页 | JTAG/SWD 调试引脚和 SWJ_CFG |
文件:../参考文档/STM32F10xxx参考手册(中文).pdf
19.4 《STM32F103x8/B数据手册(中文)》
| PDF 页码 | 内容 |
|---|---|
| 第 17 页 | PA0 引脚定义与复用功能 |
| 第 18 页 | PB12 引脚定义与复用功能 |
| 第 20 页 | PB3/PB4 调试引脚定义及引脚表注释 |
| 第 40--41 页 | GPIO 输入、驱动电流、输出电压和输出速度条件 |
文件:../参考文档/STM32F103x8B数据手册(中文).pdf
20. 一句话总结
GPIO 输出程序的核心不是函数名,而是因果链:接线图决定有效电平,RCC 让端口工作,GPIO_Init 决定引脚怎样驱动,输出函数改变寄存器位,延时把电平序列变成可见的灯光或可听的节奏。