简介:这是面向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 是冷光占空比在总占空比里的占比。取 Tcold=6500K、Twarm=2700K,从 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 进入 S3;S2 亮度调节,继续按住则以固定速率连续变化,松手回 S1;S3 色温调节,继续按住则档位连续变化,松手回 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×2ms=16ms,比常见 50ms 去抖响应快不少,长按 500ms 的判断也从同一个计数器累加,不额外占用定时器。需要注意返回值只是“当前时刻按键处于稳定状态”,真正触发动作还要在调用处记录前一拍状态,用“前一拍没按下、这一拍按下”来产生沿事件,否则主循环每轮都会把长按当成新的单击。
3.3 主循环与定时中断的分工:2ms 节拍 + 整数渐变
NY8A051 这类芯片没有操作系统,程序通常写成“主循环轮询 + 定时中断更新输出”。PWM 硬件自动输出,中断里只做三件事:累加节拍计数、把当前占空比向目标占空比逼近一步、刷新到 PWM 寄存器。所有查表、乘法、状态迁移都放主循环,避免中断占时过长导致节拍漂移。主循环写出来就是三个子程序串行:
MAIN_LOOP: CALL KEY_SCAN ; 处理按键沿与按住事件,更新亮度与色温目标 CALL COMPUTE_DUTY ; 查表、插值、乘亮度系数,算出两路目标占空比 CALL RAMP_OUTPUT ; 当前占空比向目标占空比走一步并写寄存器 GOTO MAIN_LOOPKEY_SCAN 只改两个全局目标值:level 和 idx;COMPUTE_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_osc=8MHz,PWM 分辨率 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 旁 100nF+10uF | 烧录中断、校验失败 |
这些不是流程表演。普通 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 一致后再放量产。
本文还有配套的精品资源,点击获取