作为一个喜欢折腾数码产品的工程师我最近对家里的一台智能颈部按摩仪进行了一次深度的性能调优原因很简单这台按摩仪什么都好就是有一个让人很不爽的问题响应太慢了每次按按键,或者用APP切换模式调整力度都要等一两秒才有反应有时候甚至要按好几下才有响应体验非常差作为一个对性能有执念的工程师这我可忍不了,于是我花了几个晚上的时间对这台按摩仪进行了深度的分析和调优最后成功把响应时间从原来的平均1200毫秒降到了平均240毫秒降了整整80%体验提升非常明显今天就来分享一下这次调优的完整过程和心得。

先说明一下这台按摩仪是某品牌的智能颈部按摩仪支持按键操作和手机APP蓝牙控制有多种按摩模式和力度档位还能加热按摩效果其实还不错,但是就是响应慢用起来很憋屈我查了一下网上很多用户都在吐槽这个问题,但是官方一直没有解决看来只能自己动手了。

一、问题分析:响应为什么这么慢

首先,我需要搞清楚响应为什么这么慢是硬件性能不够还是软件写得烂,或者是蓝牙通信的问题,只有找到瓶颈才能针对性地优化。

我先做了一个简单的测试用手机秒表计时从按下APP上的按钮到按摩仪做出反应(比如力度变化,或者模式切换的提示音)的时间测了十次取平均值结果是平均1200毫秒也就是1.2秒最慢的一次甚至到了2秒这个响应时间确实太慢了正常的智能设备响应时间应该在200到300毫秒以内用户才会觉得跟手流畅超过500毫秒就会觉得有延迟超过1秒就会觉得很卡很难受这台按摩仪1.2秒的响应时间确实属于很卡的水平。

接下来我需要定位延迟到底在哪个环节整个操作链路是这样的用户按APP按钮→APP处理→蓝牙发送数据→按摩仪蓝牙接收数据→按摩仪主控处理数据→按摩仪执行操作(改变力度切换模式等)→用户感知到反应延迟可能在任何一个环节,或者多个环节叠加。

我先测试了蓝牙通信的延迟用手机给按摩仪发一个简单的指令,然后看按摩仪返回的ACK(确认)时间结果发现蓝牙通信本身的延迟并不大平均大概80毫秒左右最慢也不超过150毫秒这说明蓝牙通信不是主要瓶颈,虽然有一点延迟,但是不至于导致1.2秒的总延迟。

然后我测试了APP处理的延迟从用户按下按钮到APP实际通过蓝牙发送数据的时间结果发现APP处理也比较快平均大概50毫秒左右不是瓶颈。

那瓶颈就应该在按摩仪的主控处理环节了也就是按摩仪收到蓝牙数据之后,到实际执行操作的这段时间为了验证这个猜测我拆开了按摩仪(当然是过保了才敢拆,不然就没保修了)看了一下内部的硬件结构按摩仪的主控用的是一颗低端的单片机(MCU)具体型号就不说了反正是很常见的一款低功耗MCU主频,只有几十MHz内存也很小几KB的RAM和几十KB的Flash这样的硬件配置确实比较低,但是对于按摩仪这种简单的设备来说理论上应该是够用的不应该这么慢,所以问题应该还是出在软件上也就是固件写得不好。

为了进一步确认我用逻辑分析仪接在按摩仪的按键引脚和执行机构(电机驱动引脚)上测试了从按下物理按键到电机实际变化的时间结果发现,即使是物理按键操作不经过蓝牙和APP响应时间也有平均900毫秒左右这就完全确认了瓶颈就在按摩仪的主控固件处理环节和蓝牙APP都没关系就是固件写得烂处理太慢了。

找到瓶颈之后,接下来就是怎么优化了,因为是单片机的固件我需要先把固件读出来反汇编分析代码找到慢的原因,然后修改再写回去这个过程比较折腾,但是也很有意思下面详细说说。

二、固件逆向:找到慢的真正原因

首先,我需要把按摩仪的固件读出来这台按摩仪的MCU支持串口下载和调试我找到了PCB上的调试引脚用USB转串口模块连接上,然后用对应的烧录工具把Flash里的固件读了出来是一个几十KB的bin文件接下来就是反汇编分析了。

我用了对应的MCU反汇编工具把bin文件反汇编成汇编代码,然后开始分析,因为代码量不大(几十KB的固件去掉中断向量表和一些数据实际代码量不大)我花了一个晚上基本把主循环和关键的处理函数都看懂了,然后就发现了几个严重的性能问题这就是响应慢的真正原因。

问题1:主循环里有大量的延时等待(Blocking Delay)

这是最严重的问题按摩仪的固件主循环里有大量的阻塞式延时函数也就是用空循环,或者定时器等待的方式来延时,而且延时时间还不短,比如在扫描按键之后,会延时200毫秒再处理按键事件在切换模式之后,会延时500毫秒再更新显示和执行操作在处理蓝牙数据之后,会延时300毫秒再响应这些阻塞式延时加起来就导致了每次操作都要等很久才有反应,而且,因为是阻塞式的延时在延时期间CPU什么都干不了只能等这就严重浪费了CPU时间也导致了响应缓慢。

比如我分析了按键处理的流程是这样的主循环扫描按键→发现有按键按下→延时200毫秒消抖→再扫描一次确认按键→延时100毫秒→处理按键事件→执行操作→延时300毫秒等待操作稳定→更新显示光这一个按键处理流程里就有200+100+300=600毫秒的阻塞式延时再加上其他环节的延时和处理时间总响应时间接近1秒就不奇怪了,而且这些延时很多都是不必要的,或者时间设置得太长了,比如按键消抖一般10到20毫秒就够了它用200毫秒完全是浪费切换模式后也不需要延时500毫秒再执行完全可以立即执行。

问题2:事件处理是轮询方式没有用中断效率低

第二个问题是事件处理方式效率低按摩仪的按键扫描蓝牙数据接收等事件都是用轮询的方式处理的也就是在主循环里不断地去查有没有按键按下有没有蓝牙数据到来而不是用中断的方式有事件了才触发处理轮询方式的问题是响应延迟取决于轮询周期,如果轮询周期长响应就慢,如果轮询周期短就会占用很多CPU时间效率低这台按摩仪的主循环,因为有大量阻塞式延时导致主循环一轮的时间很长(大概几百毫秒到1秒多)所以轮询的周期也很长事件不能被及时处理响应自然就慢了。

比如蓝牙数据接收本来MCU的蓝牙模块收到数据后是可以触发中断的,但是固件没有用中断而是在主循环里每隔一段时间去查一下蓝牙模块有没有数据,因为主循环慢,所以经常数据到了很久才被查到处理这就增加了很多不必要的延迟,如果用中断方式数据一到就立即触发中断处理响应时间能缩短很多。

问题3:没有实时操作系统(RTOS)任务调度混乱

第三个问题是固件没有用实时操作系统(RTOS)所有功能都在一个大主循环里处理任务调度混乱不同的任务(按键扫描蓝牙处理电机控制温度控制显示更新等)互相干扰一个任务慢了就会影响其他任务的响应,比如电机控制任务里有一个长时间的延时就会导致按键扫描和蓝牙处理都被阻塞响应变慢,如果用RTOS把不同的任务分开按优先级调度就能保证高优先级的任务(比如按键响应蓝牙处理)能及时执行不会被低优先级的任务阻塞响应速度能提升很多。

不过这台按摩仪的MCU资源比较有限(RAM只有几KB)跑RTOS可能比较勉强,所以这个优化可能不太好做,但是前两个问题(阻塞式延时和轮询方式)是完全可以优化的,而且优化空间很大。

问题4:一些算法和逻辑写得低效浪费CPU时间

除了以上三个主要问题,还有一些小问题,比如一些算法和逻辑写得比较低效浪费CPU时间,比如力度调节的算法用了很复杂的浮点运算,但是MCU没有硬件浮点单元(FPU)浮点运算全靠软件模拟很慢其实完全可以用定点运算,或者查表的方式替代速度能快很多再,比如显示更新的逻辑每次都全量刷新所有显示内容其实可以只更新变化的部分节省时间这些小问题,虽然单个影响不大,但是加起来也会影响整体性能能优化就优化。

三、优化实施:一步步把响应时间降下来

找到问题之后,就开始动手优化了,因为是单片机固件修改汇编代码比较麻烦我选择了重新编写关键的处理函数,然后替换原来的代码下面说说具体的优化措施。

优化1:去掉所有不必要的阻塞式延时改用非阻塞方式

这是最重要的优化也是效果最明显的我把主循环里所有不必要的阻塞式延时都去掉了,或者改成非阻塞的方式具体来说:

  • 按键消抖:原来的200毫秒阻塞式消抖改成非阻塞的消抖方式用定时器记录按键状态变化的时间等过了10毫秒再确认按键状态这期间CPU可以去做其他事不会被阻塞,而且10毫秒的消抖时间完全够用不需要200毫秒,那么长。
  • 模式切换延时:原来切换模式后延时500毫秒再执行操作完全去掉了切换模式后立即执行操作不需要等,因为电机和加热的响应本身就很快不需要额外延时。
  • 蓝牙处理延时:原来处理蓝牙数据后延时300毫秒再响应也去掉了收到蓝牙数据后立即解析处理执行操作不需要等。
  • 其他各种小延时:主循环里,还有很多其他的小延时,比如显示更新前延时100毫秒温度采样后延时200毫秒等等能去掉的都去掉了不能去掉的都改成非阻塞方式用定时器判断时间到了再执行不阻塞主循环。

把这些阻塞式延时都去掉之后,主循环的一轮时间从原来的几百毫秒到1秒多降到了几十毫秒以内响应速度立即就提升了很多这一个优化就把响应时间从1200毫秒降到了大概400毫秒左右效果非常明显。

优化2:关键事件改用中断处理提升响应速度

第二个优化是把关键的事件从轮询方式改成中断处理方式具体来说:

  • 按键中断:原来按键是轮询扫描的改成GPIO中断方式按键按下,或者松开的时候,触发中断在中断服务程序里记录按键事件,然后主循环里及时处理这样按键事件能被立即检测到不需要等主循环轮询到响应速度提升很多。
  • 蓝牙数据接收中断:原来蓝牙数据是轮询查询的改成串口中断方式蓝牙模块收到数据后通过串口发给MCU触发串口接收中断在中断里把数据存到缓冲区,然后主循环里及时处理这样蓝牙数据一到就能被立即接收和处理不需要等轮询响应速度也提升很多。

改成中断方式之后,按键和蓝牙事件的检测延迟从原来的几百毫秒降到了几毫秒以内几乎是实时检测这又把响应时间从400毫秒降到了大概280毫秒左右效果也很明显。

优化3:优化主循环调度按优先级处理任务

虽然没有用RTOS但是我对主循环的任务调度做了优化把不同的任务按优先级分类高优先级的任务(比如按键事件处理蓝牙数据处理电机控制)每次主循环都优先处理低优先级的任务(比如温度采样显示更新状态上报)可以每隔几轮主循环才处理一次,或者用定时器定时处理这样能保证高优先级的任务能及时执行不会被低优先级的任务阻塞响应速度进一步提升。

另外我还把一些耗时的操作(比如显示全量刷新温度滤波计算)拆成了小步骤分多次主循环完成不要一次占用太多CPU时间避免阻塞其他任务这也能提升整体的响应速度和流畅度。

优化4:优化低效算法用定点运算和查表替代浮点运算

最后对一些低效的算法做了优化,比如力度调节的算法原来用了浮点运算计算电机的PWM占空比很慢我改成了定点运算,并且把常用的力度档位对应的PWM值做成了查表方式需要的时候,直接查表不用计算速度快了很多再,比如温度控制的PID算法原来也用了浮点运算我也改成了定点运算,并且简化了一些不必要的计算在保证控制精度的前提下提升了速度这些优化,虽然单个效果不大,但是加起来也把响应时间又降了几十毫秒从280毫秒降到了240毫秒左右。

四、优化结果:响应时间降了80%

经过以上几个方面的优化最后我把修改后的固件重新写回了按摩仪的MCU里,然后做了测试结果非常令人满意响应时间从原来的平均1200毫秒降到了平均240毫秒降了整整80%最慢的一次也不超过350毫秒这个响应速度已经属于流畅的水平了用户操作的时候,几乎感觉不到延迟非常跟手体验提升非常明显。

具体来说,优化前后的对,比如下:

指标优化前优化后提升
平均响应时间1200ms240ms降低80%
最大响应时间2000ms350ms降低82.5%
主循环一轮时间500-1200ms20-50ms提升约20倍
按键检测延迟200-500ms<5ms提升约100倍
蓝牙数据处理延迟300-800ms<10ms提升约80倍

从这个对比可以看出优化的效果非常显著特别是主循环时间和事件检测延迟提升了几十到上百倍这直接带来了响应速度的巨大提升现在用这台按摩仪,不管是按物理按键还是用APP蓝牙控制都是一点就有反应切换模式调整力度开关加热都非常流畅再也没有之前,那种按了没反应要等半天的憋屈感觉了体验完全是两个档次的产品。

而且除了响应速度提升之外,还有一些意外的收获,比如,因为去掉了大量阻塞式延时CPU的利用率更合理了功耗反而降低了一些续航比原来还长了一点再,比如,因为事件处理更及时了按摩的力度控制更精准了不会出现力度调节滞后的情况按摩体验也有提升这些都是优化带来的额外好处。

五、踩坑与注意事项

这次调优过程中也踩了不少坑这里也分享一下给想自己折腾的朋友一些参考和警示。

1. 备份原固件一定要备份:这是最重要的动手之前,一定要先把原来的固件完整读出来备份好最好备份两份存在不同地方,因为修改固件有风险万一改坏了,或者写回去的时候,出问题还能用原固件恢复,不然把设备弄成砖了就麻烦了我这次就差点出问题有一次写固件的时候,中途断了线导致MCU里的固件损坏设备开不了机还好我有备份原固件重新写回去就恢复了不然就惨了。

2. 先在不重要的设备上练手:如果你是第一次折腾单片机固件建议先找一个便宜的不重要的设备练手熟悉一下读固件反汇编修改写固件的流程和工具再去折腾贵的设备,不然新手上手就折腾贵设备很容易弄坏造成损失我之前,折腾过一些便宜的智能插座和灯泡有经验了才敢折腾这台按摩仪。

3. 过保了再拆,不然失去保修:这个不用多说拆设备就会失去保修,所以一定要等过了保修期再动手,不然设备出了质量问题厂家就不给保修了得不偿失我这台按摩仪是买了一年多过保了才敢拆的。

4. 修改要循序渐进不要一次改太多:修改固件的时候,建议循序渐进一次只改一个地方改完测试没问题再改下一个不要一次改一大堆,不然出了问题都不知道是哪个修改导致的不好排查我这次就是一个优化一个优化地做做完一个测试一下确认没问题再做下一个这样,虽然慢一点,但是稳妥出问题也好定位。

5. 注意硬件限制不要过度优化:最后要注意硬件的限制不要过度优化,比如这台按摩仪的MCU性能和资源都有限不要指望把它优化得像高端设备一样流畅能把主要的性能问题解决就不错了,另外也要注意优化不要影响设备的基本功能和安全性,比如按摩仪的过热保护过流保护等安全功能一定不能,因为优化而去掉,或者削弱安全永远是第一位的。

六、写在最后

以上就是我这次对按摩仪进行性能调优的完整过程和心得从分析问题到逆向固件找到原因再到一步步优化最后把响应时间降了80%整个过程,虽然折腾花了好几个晚上的时间,但是也很有乐趣很有成就感特别是看到自己动手优化后的设备用起来流畅跟手体验提升明显那种成就感是买新设备给不了的。

这次调优也让我深刻体会到很多消费电子产品的性能问题往往不是硬件不行而是软件(固件)写得太烂了就像这台按摩仪硬件配置,虽然不高,但是对于按摩仪这种简单设备来说完全够用了之,所以响应慢完全是,因为固件里有大量不必要的阻塞式延时和低效的事件处理方式这些问题,只要开发人员稍微用点心完全可以避免,但是很多厂商为了赶进度,或者节省成本根本不重视固件的质量能跑起来就行导致用户体验很差这其实是很不应该的也希望厂商们能重视软件和固件的质量不要让用户自己动手去优化本来应该出厂就做好的事情。

当然对于喜欢折腾的朋友来说自己动手优化设备也是一种乐趣能学到很多东西也能获得很大的成就感,如果你也有类似的响应慢的智能设备,而且过保了有一定的电子和编程基础不妨也试试自己动手优化一下说不定能给你带来惊喜,当然动手前一定要做好备份注意安全不要把设备弄成砖了。

最后用一句话结束这篇文章也是我这次调优最大的心得"很多时候限制我们体验的不是硬件的性能而是软件的质量和开发者的用心程度好的软件能让普通的硬件发挥出优秀的体验烂的软件能让高端的硬件也变得难用作为开发者我们应该对自己写的每一行代码负责追求更好的性能和体验作为用户我们也应该学会折腾学会优化让手上的设备发挥出更大的价值这就是技术的乐趣和意义所在。"

愿大家都能用上流畅好用的设备也能在折腾中找到乐趣获得成长加油!