
简介这是面向NY8A051单片机的补光灯固件程序专为锂电池供电的便携照明设备设计集成低功耗管理、亮度调节和冷暖色温切换功能可用于台灯、氛围灯等补光场景。压缩包约300KB共18个文件包含C源码、头文件、汇编源文件、编译生成的bin/elf/map/lst等中间文件以及烧录与Option配置截图从工程源码到烧录验证所需的关键材料基本齐全便于直接编译和烧录调试。工程中TimerWDTInterrupt等模块展示了定时器与看门狗中断的配置方法main.c体现了亮度、色温控制与锂电管理的主逻辑对想了解NY8A051开发流程、LED驱动控制或低功耗策略的嵌入式开发者可参考其代码结构快速上手也可按需修改移植到同类照明产品中。目前已有869人学习下载适合正在做补光灯、台灯或便携照明系统的软硬件开发者作为实用参考。1. 补光灯程序在 NY8A051 上到底要解决什么问题NY8A051 是一颗典型的 8 位 OTP 单片机程序烧进去就没法改所以“Program_补光灯程序_NY8A051_冷暖_”这个名字本质是把冷暖补光灯一整天的交互都做成一份以字节计的固件。硬件那块很简单一串冷白 LED、一串暖白 LED、两个按键、USB 供电程序要保证用户能调出 6500K 到 2700K 的连续色温顺手把待机功耗按到微安级。这套玩法放 STM32 上没难度正因为在 NY8A051 这种资源紧、只能烧录一次的芯片上问题才有嚼头两路 PWM 怎么分、按键去抖怎么省定时器、渐变怎么做成整数运算、空着的 I/O 怎么摆才不漏电。写这篇主要是给做小家电、桌面灯具或 LED 调光控制的人新手能照步骤把程序骨架搭起来老手可以对照参数表和坑做一轮排雷。文里出现的寄存器名以你手上型号的数据手册为准代码只依赖公开资源。2. 冷暖混光程序的核心模型把色温拆成两路 PWM 占空比2.1 为什么补光灯不用电位器而要用两路 PWM 混色温偷懒的做法是冷光串电阻、暖光串电阻再用电位器或波段开关手动切换也能做出一台“冷暖补光灯”。但它只能让两串 LED 在固定电流下交替点亮亮度一变色温跟着漂想要 3000K 到 4500K 之间的过渡色就只能让两串同时亮。PWM 混光的本质是时间合成利用人眼对高频亮灭的积分效应让两路 LED 在不同占空比组合下呈现中间色温这个特性正好落在 NY8A051 这类带硬件 PWM 输出的 OTP 小芯片上。为什么不直接模拟调流LED 的正向电压随电流变化很小但光通量随电流的非线性很明显要做大范围线性调光模拟电路的功耗和器件成本都远高于一个 PWM 脚。NY8A051 的 PWM 输出由硬件自动翻转程序只需改占空比寄存器不用管高频开关细节这也是它在台灯、氛围灯、玩具补光这一类产品里出现频率高的原因。程序里真正难的从来不是 PWM 本身而是怎么把“色温”这个物理量换算成每天要改几百次的两路占空比。2.2 色温–占空比换算按 mired 均分再反算冷光占比色温的物理定义是黑体辐射温度但人眼对色温差异的感知更接近倒数尺度。2700K 和 2900K 之间 200K 的差别比 6300K 和 6500K 之间 200K 的差别醒目得多所以工程上常用 mired微倒度1/色温×10^6来划分档位。混合色温近似满足倒数插值1/Tmix a/Tcold (1-a)/Twarm其中 a 是冷光占空比在总占空比里的占比。取 Tcold6500K、Twarm2700K从 370 mired 到 154 mired 等距分成 8 步就能反推出 9 个色温档位对应的冷光占比。这张表可以直接做成固件里的 ROM 表| 档位 | 混合色温约(K) | mired | 冷光占比 a | 冷光 PWM(满亮) | 暖光 PWM(满亮) | | 0 | 2700 | 370 | 0.00 | 0 | 255 | | 1 | 2915 | 343 | 0.13 | 32 | 223 | | 2 | 3165 | 316 | 0.25 | 64 | 191 | | 3 | 3460 | 289 | 0.38 | 96 | 159 | | 4 | 3817 | 262 | 0.50 | 128 | 127 | | 5 | 4255 | 235 | 0.63 | 159 | 96 | | 6 | 4808 | 208 | 0.75 | 191 | 64 | | 7 | 5525 | 181 | 0.87 | 223 | 32 | | 8 | 6494 | 154 | 1.00 | 255 | 0 |注意表中冷光与暖光两路 PWM 之和恒为 255含义是“满亮度下的占空比分配”。实际运行时还要乘一个亮度系数每次按键只修改亮度 level 或色温档位 idx刷新 PWM 时统一换算/* 每次刷新都从色温档位 idx 与亮度 level 重新算两路占空比 */ unsigned char pwm_cold (unsigned char)((unsigned int)level * cct_table[idx] / 255); unsigned char pwm_warm (unsigned char)((unsigned int)level * (255 - cct_table[idx]) / 255);这里 cct_table 只存一列冷光占比暖光占比用 255 减冷光占比得到ROM 表从 18 字节省到 9 字节。乘法必须转成 16 位中间量否则 8 位机器上 level×cct_table 直接溢出调出来的色温会在高档位突然跳变。两行代码同时算完、同时写寄存器才能保证过渡过程中 C 与 W 的比例始终等于目标色温比例。2.3 调色温时为什么还要做亮度锁定调色温的过程中如果只改 a 而不锁定亮度级别两路 LED 的总光通量会随 a 轻微变化。原因是冷白与暖白 LED 的发光效率不一致同一电流下光通量能差几个百分点在中间混光区经常表现为“调着调着暗了一下”。这不是 PWM 算错而是混光时光通量在漂。工程上的锁亮度方案要借助实测满亮度下把照度计贴在灯面固定位置记录 9 个档位的相对照度得到一组修正系数 G[9]刷新占空比时将两路同时乘以对应的 G。这样处理之后从纯暖一路长按到纯冷照度曲线能保持在一个相对平直的区间。如果产品没有照度计可以先按 2.2 的公式直接烧录再把中间档位按目测微调表里的冷光占比这也是可以做但在量产前必须补的校准步骤。2.4 档位表与计算量K 级 ROM 的代价意识前面那张表只有 18 字节在 PC 上一个 int 的大小放在 OTP 里就开始敏感。NY8A051 这类小型号程序空间按 K Word 计算主程序、去抖逻辑、渐变算法都要占空间任何多出来的常量数组都在压缩可维护性。所以 2.2 里把表压缩成 9 字节不是过度优化而是给后面的 gamma 表和中断处理留余量。计算量同样要省。每次刷新都做乘法和除法8 位 MCU 上除法最贵所以代码里用 255 做分母时可以先乘再右移 8 位近似或者直接按 256 归一化pwm_cold (unsigned char)((unsigned int)level * a 8)。代价是色温最大误差不到 0.4%肉眼完全看不出但主循环每轮能省几十个指令周期这个划算。3. 用 NY8A051 写冷暖补光灯程序引脚、状态机与渐变逼近3.1 引脚规划六个 I/O 怎么安排才不纠结NY8A051 常见封装下可用 I/O 在 6 个左右其中还包含复位、烧录相关的特殊功能脚。一份补光灯最低配需要两路 LED 的 PWM 输出、两个按键输入正好四个 I/O。剩下两个引脚一个建议留作 LED 总电流检测或充电指示灯另一个在量产板上做成工厂测试点用来在产品组装后跑一遍自动校准。分配原则比具体引脚号更重要。按键必须接支持唤醒的引脚否则睡眠后按不醒两路 PWM 尽量选同一个定时器时基这样两路节拍同源混光时占空比沿口对齐不容易出现拍频闪烁。如果两路 PWM 来自不同时基哪怕标称频率一样相位差也会造成周期性亮度抖动实机上表现为“呼吸感”。| 引脚功能 | 方向 | 关键要求 | | 冷光 PWM 输出 | 输出 | 与暖光同一时基频率一致 | | 暖光 PWM 输出 | 输出 | 与冷光同一时基沿口对齐 | | 开关/亮度键 | 输入上拉 | 必须支持睡眠唤醒 | | 色温键 | 输入上拉 | 必须支持睡眠唤醒 | | 过流检测/充电状态 | 输入 | 量产品建议保留 | | 工厂校准点 | 输入 | 测试治具用量产可省 |先在数据手册上把引脚复用表画出来再写程序。OTP 单片机烧进去不能改引脚分配错了只能换片子重新烧这个返工成本比任何代码 bug 都贵。3.2 状态机关灯、亮灯、调亮度、调色温四个状态补光灯的按键手感八成在状态机。推荐四态设计S0 关灯PWM 停止、睡眠S1 亮灯短按开关键回到 S0长按 500ms 进入 S2色温键短按切档长按 500ms 进入 S3S2 亮度调节继续按住则以固定速率连续变化松手回 S1S3 色温调节继续按住则档位连续变化松手回 S1。这个状态机不需要调度器只要定时中断维护一个 2ms 节拍主循环按当前状态调用对应处理。按键处理里最容易错的是把“按下沿”和“按住中”混为一谈。沿事件触发单击、切换档位按住中事件触发连续调节。也就是主循环里要分别判断“刚按下”和“仍然按住”同一个按键在这两种事件下做不同的事。/* 按键扫描每 2ms 一拍连续 8 拍电平一致才确认按下 */ static unsigned char key_cnt_pwr; static unsigned char key_prev_pwr; unsigned char key_press(unsigned char pin_now) { if (pin_now key_prev_pwr) { if (key_cnt_pwr 8) key_cnt_pwr; } else { key_cnt_pwr 0; key_prev_pwr pin_now; } return key_cnt_pwr 8; }去抖窗口 8×2ms16ms比常见 50ms 去抖响应快不少长按 500ms 的判断也从同一个计数器累加不额外占用定时器。需要注意返回值只是“当前时刻按键处于稳定状态”真正触发动作还要在调用处记录前一拍状态用“前一拍没按下、这一拍按下”来产生沿事件否则主循环每轮都会把长按当成新的单击。3.3 主循环与定时中断的分工2ms 节拍 整数渐变NY8A051 这类芯片没有操作系统程序通常写成“主循环轮询 定时中断更新输出”。PWM 硬件自动输出中断里只做三件事累加节拍计数、把当前占空比向目标占空比逼近一步、刷新到 PWM 寄存器。所有查表、乘法、状态迁移都放主循环避免中断占时过长导致节拍漂移。主循环写出来就是三个子程序串行MAIN_LOOP: CALL KEY_SCAN ; 处理按键沿与按住事件更新亮度与色温目标 CALL COMPUTE_DUTY ; 查表、插值、乘亮度系数算出两路目标占空比 CALL RAMP_OUTPUT ; 当前占空比向目标占空比走一步并写寄存器 GOTO MAIN_LOOPKEY_SCAN 只改两个全局目标值level 和 idxCOMPUTE_DUTY 每次重新计算目标RAMP_OUTPUT 做渐变逼近。三条子程序各管一段ICE 上单步时能直接看寄存器值出问题很容易定位。渐变的核心是限制每次更新的最大变化量/* current 会持续向 target 靠近RAMP_STEP 是每 2ms 的最大变化量 */ void ramp_clamp(unsigned char *current, unsigned char target) { signed short delta (signed short)target - *current; if (delta RAMP_STEP) delta RAMP_STEP; if (delta -RAMP_STEP) delta -RAMP_STEP; *current (signed char)delta; }RAMP_STEP 是补光灯程序手感的第一参数。取 3 时从灭到全亮约 255/3×2ms≈170ms按一下很快亮起适合直播补光这类需要即时反馈的场景取 1 时约 510ms适合床头灯慢慢点亮。不要照抄默认值这个参数直接决定用户对产品“快不快”的判断做成可调常量放在文件头量产时按销售地区习惯改。3.4 睡眠前的引脚处置悬空脚比程序 bug 更耗电待机功耗是补光灯这类 USB 供电产品最容易翻车的一项。进入睡眠前两路 PWM 输出口要置为低电平而不是高阻避免 LED 阳极悬空漏电按键脚保留内部上拉并配置为唤醒源未用引脚统一设为输出低防止高阻输入状态在下拉电阻、PCB 漏电之间来回跳动。很多 NY8A051 待机电流偏高查到最后是某个空着的引脚悬空被寄生电容反复充放电实际功耗比正常工作还难看。睡眠不是简单执行一条指令。执行前要关掉 PWM 输出使能再按数据手册配置唤醒事件把两个按键脚的中断唤醒打开最后才进 SLEEP。按键按下产生电平变化唤醒后程序从主循环继续跑外设需要重新初始化。漏掉这一步会出现“睡下去醒不过来”或“亮一下就熄灭”这是光靠台式机仿真验证不出来的问题。4. NY8A051 补光灯程序的关键参数PWM 频率、gamma 表与烧录检查4.1 PWM 频率20kHz 以上才能躲开电源声与视频频闪PWM 频率由主频、预分频和分辨率三者决定F_pwm F_osc / (prescaler × 2^PWM_BITS)假设 F_osc8MHzPWM 分辨率 8 位预分频 8 时频率约 3.9kHz预分频 1 时约 31.25kHz。补光灯有两个硬约束一是驱动电路在几 kHz 时可能把开关频率变成可听噪声二是手机拍照或视频录制的卷帘快门对低频明暗极其敏感PWM 低于 10kHz 时画面会出现横向波纹。工程上优先把频率推到 20kHz 以上此时分辨率可能从 8 位降到 7 位亮度级数变少但配合下一节的 gamma 表观感依然连续。如果追求分辨率与频率兼得可以适当调高主频再反推预分频前提是数据手册里内部振荡器支持该频率档位。补光灯不建议低于 10kHz也不建议高于 40kHz频率太高后单个周期的占空比宽度太窄MOS 驱动开关损耗上升电池供电的产品续航明显变差。先按 25kHz 试再结合实际驱动电路微调是稳妥路线。4.2 gamma 表生成用 Python 把 256 字节压进 OTP 前的准备LED 亮度感知近似指数关系占空比 128 并不是人眼认为的“一半亮度”。补光灯调光要顺滑至少做一次 gamma 2.2 校正。最省资源的方式是先生成一张 32 点查找表运行时在相邻节点间线性插值# 生成 32 点 gamma 2.2 查找表输出 C 语言数组缩进留给编译器 gamma, points, max_pwm 2.2, 32, 255 print(static const unsigned char gamma_table[32] {) for i in range(points): duty int(round((i / (points - 1)) ** gamma * max_pwm)) end , if i points - 1 else }; print(f {duty}{end})这个脚本在 PC 上跑一次把生成结果编译进固件运行时不做浮点运算。8 位机上的插值就是两次加法和一次移位对 2ms 节拍没有压力。gamma 值越小低亮度区提亮越明显补光灯常用 2.2 到 2.4最终以目测最低亮度档不糊为基准。若 ROM 连 32 字节都紧张可以把 gamma 改成 2.0运行时用完整平方计算但那样每次刷新多一次 16 位乘法属于拿 CPU 换 ROM两边取舍看剩余资源。4.3 OTP 烧录检查单只烧一次所以检查比调试更值钱OTP 芯片烧坏不能擦除重来量产时每一片烧废都是成本。任何做过补光灯这类产品的人手里都该有一份烧录检查单。以下几项是 NY8A051 项目的必查项| 检查项 | 做法 | 出错后果 | | 引脚分配与 PCB 一致 | 对照原理图逐个核对 | 烧完板子不工作只能扔片 | | 未用 I/O 已固定电平 | 输出低或接电阻 | 待机漏电、唤醒不稳定 | | 内部振荡器配置正确 | 对照数据手册确认频率档 | PWM 频率偏移、时序错乱 | | Code Protect 开启 | 量产固件必须开 | 固件被读出复制 | | Writer 座子与封装匹配 | SOP8/DIP 不能混用 | 接触不良报烧录失败 | | 供电电容足够 | VDD 旁 100nF10uF | 烧录中断、校验失败 |这些不是流程表演。普通 Flash 单片机烧错了可以擦除重来NY8A051 没有第二次机会能在静态阶段确认的项目都值得在生产前过一遍。特别是 Code Protect很多小团队量产第一版忘记开后续被抄板复制固件返工代价远高于烧录前勾选一个选项。4.4 档位记忆没有 EEPROM 时怎么处理“上次的色温”不少产品要求关灯再开之后恢复上次的色温与亮度。如果所选型号内部没有掉电保存区域常规做法是外挂一颗 I2C EEPROM比如 24C02。每次按键松手后把档位写入上电时读回。这个方案要注意掉电瞬间写不完整的问题EEPROM 页面写周期约 5ms程序要在掉电检测发生后立刻停止 PWM把剩余时间留给写操作。如果产品定义里不想加 EEPROM另一条路是取消档位记忆上电固定回到中间色温档。对补光灯来说这是一种可接受的产品定义但要在需求文档里明确写出来而不是代码里悄悄省掉。否则后续接手的人看到“上电不恢复上次状态”会先当 bug 查半天。程序里加一段注释说明这是产品决策比省 5 个字节 ROM 更重要。5. 实机验证冷暖补光灯程序最值得盯的 4 个细节5.3 两路 PWM 沿口要同时看别只量直流电实机调试第一步示波器两个通道分别接冷光、暖光 PWM 输出用冷光触发看暖光的上升沿是否对齐。两路沿口不齐会形成“先亮一下、再换色”的打拍感相机快门一快就出横条纹。如果芯片只有一路硬件 PWM另一路用 IO 翻转模拟那就要保证两路在同一个定时器中断里同时写寄存器中断里的赋值顺序不能有延迟。看到两路对称上升沿之后再继续调色温表否则后面所有参数都是在对一套错误时序做补偿。5.1 冷白和暖白 Vf 不同混光中间档会先偏冷冷白 LED 与暖白 LED 的正向压降通常差 0.2V 以上同一电源下如果限流电阻按同一个公式算暖白串电流偏大结果 50% 色温点看起来并不在 50% 档位。调试方法用电流钳分别串进两串 LED记录每一档的实际电流比值反推占空比修正。修正后的冷光占比往往比理论表略高或略低几个百分点这是正常现象不影响混光公式的正确性。5.2 从一个色温连续调到另一个全程盯照度计专业补光灯最怕档位切换时亮度跳变。补完 gamma 表后把照度计贴在灯面固定位置从纯暖一路长按到纯冷看照度曲线有没有凹坑。凹坑多半出现在冷光占比 0.4 到 0.6 区间这是冷暖两串 LED 光效交叠的区域。针对凹坑单独修正 1 到 2 个中间档位而不是改整个 gamma 值否则其他档位又会偏亮。5.4 ICE 上跑完整按键时序再决定烧录最后一步永远是在 ICE 上把五类按键时序跑完单击、长按、长按后短按、上电瞬间按键按下、亮灯状态下快速切色温。ICE 能直接看寄存器值和状态机跳转比实机打点快得多。补光灯这类 OTP 产品单次烧录成本不高但排错成本高花半小时把按键时序跑完再上 Writer 烧第一片是成本最低的质量关。烧录后的首件还要把前面几秒动作重新做一遍确认上电时序与 ICE 一致后再放量产。本文还有配套的精品资源点击获取