news 2026/11/3 10:44:35

FindStar星图计划:STM32从一颗 LED 开始

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
FindStar星图计划:STM32从一颗 LED 开始

星图 C8T6 01|从一颗 LED 开始:验证整条链路

星图计划 · C8T6 卷第 1 颗星。FindStar 寻星工作室 · STM32F103C8T6 · 65 个项目,从这颗灯开始。

为什么第一课永远是点灯

每个学单片机的人都被同一句话送进门:先让灯闪起来。

这话听起来像敷衍,其实是嵌入式开发中一次完整的最小闭环:你写下的每一行 C 代码,要经过编译、链接、下载到 Flash,再由内核在正确的时钟节拍下,把一个寄存器的某个 bit 翻转,最后变成 PCB 上一颗灯珠的明灭。这中间有一整条链路上任何一个环节断了,灯都不会亮——而灯不亮的时候,你手里没有任何其他信号可以依赖,只能靠猜。所以点灯真正的价值不是"点亮",而是把这条链路一次性打通、验证到底。

这篇博客记录的是我在 STM32F103C8T6 最小系统板(也就是大家常说的"蓝色药丸"Blue Pill)上做的第一个工程:让 PC13 上的板载 LED 以 500 ms 的间隔闪烁,并且用逻辑分析仪回头验证一遍时序是不是真的如我所愿。

硬件上的第一个认知:PC13 是低电平点亮的

这是新手最容易卡住的地方,值得单独说。

蓝色药丸板上的板载 LED 接法是"阳极接 3.3 V,阴极接 PC13"。也就是说,PC13 输出低电平时,电流从 3.3 V 经 LED 灌进 PC13,灯才亮;输出高电平时两端没有压差,灯灭。

所以代码里那个看起来有点反直觉的写法其实是正确的:

HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);// 输出低电平 → 灯亮HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);// 输出高电平 → 灯灭

这也是为什么 CubeMX 里 PC13 的初始电平要设成HIGH——上电瞬间保持高电平,灯是灭的,不会出现一上电就常亮、让人以为程序跑飞了的错觉。

顺带一个容易被忽略的限制:PC13、PC14、PC15 位于低功耗备份域,驱动能力很弱(数据手册标注约 3 mA),只能灌电流带一颗小 LED,别指望它推继电器或者驱动大电流负载。要带负载,请换到 PA、PB 上的普通 IO 口。

CubeMX 里的配置

这次用的工具链是 STM32CubeMX + HAL 库。

配置项值说明
SYS → DebugSerial Wire保留 SWD 调试口,否则第二次下载会连不上
PC13 → GPIO output levelHIGH上电默认灭灯
PC13 → GPIO modeGPIO_Output推挽输出
PC13 → 输出速度Low点灯不需要高速翻转,低速更干净

提示:SYS → Debug这一项必须显式设置成 Serial Wire。CubeMX 默认会把 PA13/PA14 当成普通 IO,一旦生成代码,SWD 引脚就被复用掉了,下一轮想再下载程序,芯片会"失联"——这是每个 STM32 新手迟早要经历一次的事故。真碰上了也不用慌:把 BOOT0 拉高进系统存储器启动模式,或者按住复位键点下载、在松开的一瞬间完成握手,都能救回来。

主循环里的四行代码

HAL 生成的骨架里,用户代码只写进while (1)里:

while(1){HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);// PC13 输出低电平,灯亮HAL_Delay(500);// 500 ms 延时HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);// PC13 输出高电平,灯灭HAL_Delay(500);// 500 ms 延时}

四行,两个动作,两个等待。任何一块最小系统板上连 PC13 的 LED,都会以 500 ms 的间隔闪烁——亮半秒,灭半秒,一个完整周期 1 秒。

这里有两个值得说清楚的细节。

第一,HAL_Delay()并不精确,但足够用。它依赖 SysTick 中断,内部是一个毫秒计数器,被HAL_Init()按 1 kHz 配好。在主循环里裸调它,实际延时 = 500 ms + 两次函数调用与 GPIO 翻转的开销,误差在微秒量级,人眼看不出、点灯也完全不需要在意。但如果你以后要拿它做串口超时、做多任务节拍,就要小心了:HAL_Delay()是阻塞的,它把 CPU 整个占住,谁也别想干活。

第二,别小看"忘记while(1)"这个错误。很多人第一次写完发现灯是常亮的,以为代码没生效,其实是程序从main()返回后跑飞了,PC13 停在了最后一次写入的状态。灯常亮 ≠ 灯没坏,先看程序停在哪。

用逻辑分析仪验证一遍

"灯在闪"只是肉眼结论。想知道时序到底准不准,得上仪器。

我把逻辑分析仪的通道夹在 PC13 上,抓了一段波形。测量结果:

  • 低电平脉宽0.499 s(灯亮 500 ms)
  • 高电平脉宽0.499 s(灯灭 500 ms)
  • 完整周期0.998 s,换算频率1.002 Hz

这和HAL_Delay(500)的理论预期完全对上,那 1~2 ms 的偏差来自 SysTick 中断响应延迟和采样分辨率,属于正常范围。占空比 50%,方波干净,没有毛刺——说明推挽输出配低速档是对的,如果输出速度设成 High,边沿会更陡,这时候长线走线反而容易引入过冲和振铃。

至此,“代码 → 内核 → 寄存器 → 引脚电平"这条链路被完整验证了一遍。这一课真正的收获不是那四行代码,而是你从此有了一个可信的锚点:后面学按键、串口、定时器、PWM、I2C,只要哪一步不对,你都可以退回到这颗灯上,确认"最基础的执行流还是好的”。

踩坑清单

留给未来的自己:

  1. 没配 Serial Wire→ 第二次下载连不上。救援方法:BOOT0 拉高 / 复位键配合下载。
  2. 以为高电平才亮灯→ 蓝色药丸是低电平点亮,逻辑反了。看原理图,别猜。
  3. HAL_Delay阻塞整个主循环→ 这个阶段无所谓,等做到多任务或者需要精确节拍时,必须换成定时器中断或 SysTick 回调。
  4. 忘了开 GPIO 时钟→ 手写寄存器版本才会遇到;CubeMX 生成的MX_GPIO_Init()里已经带了__HAL_RCC_GPIOC_CLK_ENABLE(),用 HAL 反而帮你挡了一刀。
  5. 用 PC13 驱动大电流负载→ 低功耗域只有 3 mA 左右的驱动能力,带不动。

下一步

点亮一个灯是"能控制一个引脚",下一步是"能同时控制多个灯"——02 多路 LED 流水灯,会讲到HAL_GPIO_WritePin逐个写和直接写ODR寄存器的区别,以及位操作怎么让多个灯跑出花样。那一步会第一次让你觉得"寄存器"这个词没那么吓人。

再往后(按键、消抖、串口)才算真正开始。特别提醒:串口排在后面——不是因为它简单,而是因为它重要。从那里,你手上就有了"让程序说话"的能力,后面所有项目的排错都不再靠猜。前四个项目没有串口,用眼睛和灯看结果,是这四篇唯一难受的地方,忍一下。

一个灯,四行代码,一个 1 Hz 的方波。听着简单,但这是后面的地基。


*星图计划 · C8T6 卷 · 基于 STM32F103C8T6 最小系统板,工具链为 STM32CubeMX + HAL 库,所有代码均在实机上验证。
FindStar *

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/11/3 10:43:36

aws(SAA/SAP边学边练)w1d2(S3 基础 + 存储类)

文章目录 aws(SAA/SAP边学边练)w1d2(S3 基础 + 存储类) 1. 先把 6 类存储类"一句话定位"记住 1.1 核心速查表 1.2 两个必考补充点 1.3 选择标准 2.动手实操 2.1 动手实操 1:创建桶 + 上传 + 切换存储类(CLI) 2.1.1 创建桶(桶名全局唯一,注意区域要跟 CLI 默认一…

作者头像 李华
网站建设 2026/11/3 10:43:36

【Python】五大容器

列表为什么需要列表如果要存储多个同类数据(比如全班同学名字),单独定义一个个变量非常麻烦,列表可以一次性存放多个数据;其他语言里叫数组,Python 里叫 list,列表是可变类型列表定义语法&#…

作者头像 李华
网站建设 2026/11/3 10:41:49

WINDOWS11常见ERROR5问题排查笔记

微软电脑管家(ahflt.sys)导致 MSI 安装/卸载失败 Error 5 的完整排查实录症状千奇百怪,根因只有一个:微软电脑管家的 AntiHack 过滤驱动拦截了 Windows Installer 的回滚文件权限操作。本文记录从"Python 卸载报错"一路…

作者头像 李华
网站建设 2026/11/3 10:41:48

那些隐藏在系统深处的 Python 运行秘技,高手都在用

“空谈无益,给我看代码。” — Linus Torvalds把一段临时检查命令,写成可解释、可审查、不会顺手泄露配置的自动化脚本。 python3 -I -c with open("/path/to/repo/config/.env") as f:content f.read()print(content) 这段命令很像深夜排障…

作者头像 李华
网站建设 2026/11/3 10:41:38

深度解析A311Y3是如何重塑端侧AI性价比的

一、 引言:端侧 AI 的“不可能三角”做硬件产品的朋友最近一定深有感触:一方面,客户疯狂要求设备加上“本地 AI”、“跑大模型”的功能;另一方面,存储涨价、BOM 成本卡得死死的,而且设备外壳还要越做越小&a…

作者头像 李华