4.5 实战:从点灯到呼吸灯


4.5 实战:从点灯到呼吸灯

本节摘要:本节用一条完整的项目线串起本章知识:从最朴素的按键控灯开始,两次迭代升级为带渐变效果的呼吸灯,最后加上串口上报变成一个小型演示系统。每一步按背景、操作、结果、解读、变式展开,重点不在代码本身,而在"每次升级动了哪个硬件机制、现象为什么变化"的因果链。这是第 4 章的汇演,也是全书第一台"能演示的作品"。

项目背景:为什么从点灯开始再抛弃点灯

"点灯"是嵌入式的 Hello World,但它只用了 GPIO 一个开关量——现实中的控制几乎都带"程度":灯光要多亮、电机要多快、声音要多大。本节故意设计一条递进路线:先点灯(开关量),发现"开关灯太生硬",于是引入 PWM(程度量),再用定时器让渐变自动发生,最后用串口把系统状态报出来。每一次迭代都被一个真实的不满驱动,这种"由需求拉动技术"的过程,正是产品开发的缩影。

第一轮:按键控灯——最朴素的闭环

操作:PA0 配输入上拉接按键,PC13 配推挽输出接 LED,主循环里轮询按键状态控制灯的亮灭。代码骨架在 4.1 节已经备齐:读按键加状态机消抖,读到有效按下就翻转 LED。

/* 第一轮:轮询式按键控灯 */ while (1) { debounce_scan(); /* 5ms 节拍扫描按键(定时器触发) */ if (key_event) { /* 消抖确认的状态变化 */ key_event = 0; toggle_pc13(); /* 翻转灯 */ printf("led=%d\r\n", read_pc13()); } }

结果:功能正常,但观察两个现象。其一,长按按键灯不连续翻转——状态机只在状态变化时报事件,这是对的;其二,把主循环里加一行 delay_ms(100) 模拟耗时任务,按键响应明显变得迟钝甚至漏检——扫描被阻塞了。解读:轮询架构的致命伤曝光——CPU 忙于别的事时,输入事件就会漏。这个现象不必现在修,记住它,第 5 章中断就是它的解药。变式:先试着自己用"长按切换连闪模式"改造一轮,体会轮询架构在多任务叠加时的力不从心。

第二轮:PWM 调光——从开关量到程度量

背景:开关灯太生硬,想要"调亮度"。操作:把 PC13 从 GPIO 输出改成定时器 PWM 输出(查复用表确认该引脚对应的定时器通道),配置 ARR = 71 得 1MHz 步频、周期 1kHz;CCR 从 0 到 72 可调。写一个简单循环让 CCR 阶梯变化。

/* 第二轮:PWM 调光,CCR 每 10ms 一档 */ for (;;) { for (uint8_t d = 0; d <= 72; d += 6) { /* 渐亮 */ set_pwm_compare(d); delay_ms(10); } for (uint8_t d = 72; d > 0; d -= 6) { /* 渐暗 */ set_pwm_compare(d); delay_ms(10); } }

结果:灯真的会呼吸了,但有两个瑕疵:渐变有轻微的"台阶感",且调到最低几档时亮度变化不均匀——前几档几乎不变暗,后几档骤灭。解读:台阶感来自 10ms 一档的粗颗粒;低占空比区的非线性来自人眼与 LED 的双重非线性(人眼在暗区更敏感)。变式与修正:把步进间隔缩到 2ms 台阶即消失;低区非线性用 gamma 校正——预先算好一张 72 项的查找表(用指数曲线生成),CCR 按表取值,渐变立刻均匀。查找表放 Flash(const 数组,4.4 与 3.3 的知识在此汇合)。

图 4-3:呼吸灯亮度曲线的线性与伽马校正对比

图 4-3:呼吸灯亮度曲线的线性与伽马校正对比

第三轮:定时器接管节拍——把 delay 赶出主循环

背景:delay 占着 CPU,主循环啥也干不了,且渐变节奏受主循环其他任务拖累。操作:启用另一个定时器产生 5ms 更新中断,在中断服务程序里只做一件事——更新 CCR 档位(查表取下一个值);主循环从此没有 delay,只剩业务逻辑。结果:渐变节奏稳定精确,主循环空出来了,就算插入串口打印也不影响呼吸节奏。解读:"时间任务归定时器、主循环干业务"是裸机架构的分水岭——这一步迈过去,程序从"演示"变成"系统"。同时注意:中断里改 CCR 时主循环可能也在改(第三轮只有中断改,暂无冲突),若两边都改就产生了竞态——这正是第 5 章要正面处理的问题,此处先埋伏笔。

第四轮:串口上报——让系统会说话

操作:初始化串口 1(4.2 的配置),定时器每秒触发一次上报,把当前 CCR 值、按键计数打包成一行文本发出。为了不阻塞,上报采用"主循环查询标志位"方式:定时器中断置标志,主循环发现标志就组装发送。结果:串口终端每秒收到一行状态,呼吸灯照常运行两不误。解读:这个"中断置标志、主循环干活"的模式是裸机编程最常用的低配协作——比在中断里直接 printf 安全得多(printf 慢,中断里调它就是 5.3 节要批判的"ISR 干重活")。变式:把文本协议换成二进制帧(帧头加长度加校验),为第 7 章可靠性设计里的协议校验提前热身。

项目复盘:四个机制各自的贡献

回顾整条路线:GPIO 提供了开关量与按键输入的物理基础;定时器提供了精确节拍与 PWM 输出的硬件自动化;串口提供了可观测性;而对"程度量"的需求驱动了从 GPIO 到 PWM 的架构升级。四个知识点不是并列的清单,而是在真实需求里相互咬合的齿轮。建议亲手把四轮都做一遍——尤其体会第一轮的"按键漏检"与第三轮的"节奏稳定",这两个现象分别通向第 5 章中断与第 6 章程序架构的大门。

交付标准:这份代码凭什么能带走

四轮迭代完成后,用什么标准判断它已经从"能跑的实验"变成"能复用的模块"?给一份自检清单。一看初始化的完整性:外设初始化函数是否自洽——时钟使能、引脚复用、参数配置、中断使能四件套齐全,换一块板只改宏定义就能用,不用到处补配置。二看时间的来源:全工程检索 delay,应当一个都不剩(或只剩上电瞬间的粗延时)——任何业务节奏都应来自定时器或节拍计数,这是第 6 章程序架构的地基。三看共享数据的纪律:中断与主循环共同读写的变量,类型是否 atomic(单条指令可完成)、是否加了 volatile——两个关键词现在记不住没关系,第 5 章会给完整解释,但清单先立在这。四看可观测性:状态能否随时用串口问出来——产品化的代码都留调试口,"哑巴系统"在现场等于盲飞。四条全过,这套骨架就值得存进你的个人代码库,它会在未来每个项目里反复生长。

两个延展方向:把这套骨架继续长大

带走骨架之后,给它留两个生长方向。方向一,把串口上报升级为命令通道:接收侧加一行解析(收到特定字节就调灯的开关或调档),系统就从"会汇报"变成"可遥控"——顺手体会命令解析该放主循环还是中断(答案是主循环,中断只收字节,这正是 ISR 纪律的应用)。方向二,给呼吸灯加渐变曲线的配置接口:把 gamma 表改成可由串口下发刷新的 RAM 表,重启后从 Flash 读回——一次小小的"参数持久化",把第 3.3 节的 Flash 写入知识用上。两个方向加起来不到一百行代码,却把本章四个外设拧成了真正的系统雏形,强烈建议做完再进第 5 章。

本节要点回顾

  • 迭代由不满驱动:开关量生硬引出 PWM,delay 霸占 CPU 引出定时节拍,不可观测引出串口上报。
  • 调光的两个修正:细颗粒步进消台阶,gamma 查找表消低区非线性——现象背后的感知非线性要用人眼特性解释。
  • 架构分水岭:时间任务归定时器中断、主循环只干业务,是从演示到系统的门槛。
  • 中断置标志、主循环干活:裸机下最安全的协作模式,ISR 保持轻短的原则从这一刻建立。
  • 体系位置:主循环与中断开始分工协作,它们的交界处正是下一章的主战场——中断、优先级与竞态。

作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U