一句话: 发光序列状态机在IDLE态每100ms把所有DAC清零(安全设计)。手动设的电流100ms内就被抹掉——串口回显"设置成功"不代表硬件真有输出。加个"手动模式"标记就解决。
适合谁读:遇到"设置了没效果""回显成功但输出端测不到"的嵌入式工程师。
现象:100mA有输出,40/80mA没有
上位机设电流:回显成功,CRC校验通过,一切正常。但拿万用表测输出端:
- 设 100mA →有输出
- 设 40mA →没有
- 设 80mA →没有
同样的命令、同样的换算、同样的写码逻辑,为什么 100mA 能测到?
排查:先排除换算和写码
40mA 对应 DAC 码 546,80mA 对应 1092,100mA 对应 1365——换算没问题。写码逻辑三条路径完全相同,也排除了。
问题不在命令本身,在命令之后发生了什么。
真相:IDLE态每100ms清一次DAC
设备有个发光序列状态机:空闲 → 预热 → 运行 → 待机……设计上,空闲态必须把所有 DAC 清零(安全要求:不点亮的时候输出必须为零,防止漏电烧器件)。代码长这样:
else { /* IDLE: 全关 */ ... Dac_Write_Code(ch, 0x0000); // 每100ms清一次! }时序是这样的:
t=0ms 上位机设 40mA,DAC 输出 40mA ✓ t=100ms 序列 IDLE 定时任务跑一次:全部清零 ✗ t=200ms 再清一次……你设完电流,100ms 内就被序列的 IDLE 任务抹掉。万用表当然测不到。
那为什么 100mA 能测到?因为测试时发光序列恰好处于别的状态(那个状态会设激光电流,不清零),100mA 是它设的,不是你设的。40/80mA 测试时序列早已超时回到空闲态,一直清零。
设计冲突:安全逻辑 vs 手动测试
回头看,这是个真实的设计矛盾:
- IDLE 全关是必须的——防漏电、防误点亮,这是安全底线,不能删
- 手动设电流是调试刚需——但它的语义被 IDLE 覆盖了,设了等于没设
安全逻辑本身没错,错的是没有区分"业务输出"和"手动测试输出"。
修复:加一个手动模式标记
合理的做法是让状态机知道"谁在控制输出":
if (manual_mode) { /* 手动模式:保留上位机设的值,IDLE 不清零 */ } else { /* 序列模式:按状态机输出 */ }- 上位机手动设电流 → 置手动模式标记,IDLE 跳过清零
- 退出手动模式(比如启动序列)→ 清除标记,恢复安全逻辑
这样安全底线还在,调试也不再被静默覆盖。
经验
- 串口回显"设置成功" ≠ 硬件实际输出——协议层和硬件之间隔着一层业务逻辑,回显只证明"命令收到了"
- 周期任务会静默覆盖手动操作——状态机、轮询这类后台逻辑,调试时先搞清楚"现在它在干什么"
- 安全逻辑是调试时的隐形敌人——"禁用时全关"这类兜底设计,最容易让人误判"设备坏了"
排查这类问题最快的办法:禁用后台任务测一次。禁掉序列,再设电流,如果立刻有输出——问题就实锤了。
实测对比:直接设电流:100ms内被IDLE清掉,测不到输出 | 加手动模式标记:设值保留,调试一次过
有用的话点个收藏,下次调试直接用。有问题欢迎评论区交流,看到了都会回。
下一篇:脚本明明写对了,为什么就是匹配不到?\r\n 和 \n 的坑——跨平台脚本的行尾陷阱