发现问题
客户反馈说我们的硬件关机漏电流很大,但是拔掉电池之后再上电(仍处于关机状态)就会恢复为 16~20uA 左右。这让我也讶异,因为亲自测试过,漏电流只有 MCU 的休眠电流 16~20uA 左右 (包含一些电子元器件),远远低于项目要求的 <100uA。
观看客户的复现步骤,发现客户每次在关机之前,都会通过触摸触发设备一些功能,而我的测试是开机之后,等上位机运行正常就直接关机(未驱动电机转动)。
分析问题
触摸触发的功能其中最直观的是让电机转动,因此高度怀疑跟电机驱动部分功能有关,通过断开上位机,直接通过串口控制设备转一下电机,然后再关机,果然复现问题,漏电流为 380uA 左右。当问题出现之后,必须拔掉电池再重新上电,电流才会下降到正常的范围(20uA 左右)。
电机驱动 IC 有 6 颗,分别平均分布在 A、B 两块副板上,拔掉副板的电机驱动连接线,电流直接降至 200uA 左右,反复测试都是如此,跟带不带电机没有太大关系。因此确认与电机驱动IC有关,通过示波器测量控制芯片的控制信号,确认软件控制逻辑正确,此时发现该电机芯片的电源不受控,直接通过电池供电。
电机驱动IC型号是 DW7888,我们查到规格书,待机电流 <2uA,因此即使电源未关断,在待机模式下,漏电流也不至于那么大才对。
然后回想问题的复现流程,开机之后默认为待机模式,为什么一驱动电机,再进入待机模式,就会出现问题呢?
这与芯片规格书描述不符呀?跟我们的硬件工程师沟通之后,让我在出现问题的时候让芯片进入刹车模式,然后再进入待机模式。想到规格书有写到刹车模式是可以释放马达能量的,觉得有机会,顺手就用飞线出来的3.3V,同时给其中一颗电机驱动IC的两个输入信号碰一下(FI\BI 都置高电平),奇迹出现了,电流居然下降了 60uA 左右,依次将所有的电机都这么操作一次,电流最终降到 20uA 的水平,说明每颗驱动IC漏电流在 60uA 左右。
我很兴奋的马上通过 MCU 来模拟这种操作,结果让人大跌眼镜,居然没效果,拿出示波器来测量,发现确实有给出刹车信号,百思不得其解(原因下文有解)。
最后电话咨询供应商,供应商拖了蛮久才回复说是这个芯片本身的待机电流就是在 50uA~60uA 左右,但是这个回复解释不了我们实测的现象,就请他帮忙联系芯片原厂的工程师沟通一下这事情。
解决问题
跟原厂技术重新沟通了整个情况,最后他们确认这是该芯片的设计缺陷,鉴于我们的新发现,他也十分诧异,希望我们寄一套平台给他们,分析原因。
最后给出的答复是 FI、BI 两个信号同时拉低的间隔要非常短,而我们两个信号拉低间隔了 900ns,他们用 8M 的 MCU 同时设置拉低(受示波器精度影响,测量不出时差),就能解决此问题。
恍然大悟,我们咋没想到呢,我们当时测试的时候是将 DW7888 输入信号飞的线拧在一块,然后再去碰 3.3V 只高电平,从而进入刹车模式,电压是同时传给两个信号。而 MCU 是顺序执行代码,所以会有先后之分,总会有间隔。只是我们 MCU 的主频是 72M 没理由比他们原厂 8M 的 MCU 还慢,直接上代码分析,看有没有优化空间。
我们当时模拟的代码,考虑可移植和可配置,通过一个数组来存储每个在配置文件配置的 GPIO,因此实现如下:
测的波形如下,两个控制信号间隔约有 900ns:
上图的代码是逐个引脚控制,两根引脚控制信号的时间间隔有 900ns,可以移除循环语句和同时设置多个寄存器位,来缩短控制信号的时间间隔。因此修改代码如下:
测的波形如下,同组的 GPIO 几乎是同时拉低(示波器精度有限):
发现有一颗还存在 60uA 电流,查看原理图发现该芯片的 FI\BI 控制脚分别在GPIOA和GPIOB组,测得波形如下(500ns):
由于 GPIOA、GPIOB 两组是分别通过调用函数来实现拉低,考虑到函数入栈出栈的消耗,采用直接操作寄存器的方式:
测得波形如下,间隔约 124ns (这里直观地展现出调用普通函数的入栈出栈消耗有多大了,72MHz的主频,慢了4倍多):
测得结果通过,16uA:
最终补丁
上面的只是工程验证,实际应用还需要考虑到代码维护性以及稳定性,如需要有明确的注释,以及需要考虑中断的影响。
--- configuration.h 2020-12-10 11:28:07.000000000 +0800
+++ configuration.h 2020-12-10 11:23:40.000000000 +0800
@@ -169,12 +169,35 @@
#define MOTOR_FI_RFRONT_PWM_CHANNEL 8
#define MOTOR_BI_LBACK_PWM_CHANNEL 9 // 左后电机
#define MOTOR_FI_LBACK_PWM_CHANNEL 10
#define MOTOR_BI_RBACK_PWM_CHANNEL 11 // 右后电机
#define MOTOR_FI_RBACK_PWM_CHANNEL 12
+// ------------------------------------ 注意(lmx:20201210) -----------------------------------
+
+// 修复 DW7888 芯片缺陷(需要注意此相关代码仅适用于 DW7888 驱动芯片):
+// 1.当驱动电机之后再进入待机模式, 每颗芯片会存在 60uA 左右的漏电流, 而这些芯片的电源不受控制
+// 2.有效修复方法是先进入刹车模式, 再进入待机模式, 两个输入信号同时拉低的时间一定要小于 500ns
+// 3.由于当前硬件设计有一颗驱动芯片分别用了 GPIOA-15\GPIOB-3 两组引脚, 因此不能一次性同时设置
+// 4.实测可以通过直接操作寄存器的方式缩短两组 GPIO 控制时间间隔(124ns):
+// unsigned int groupa_allpin = FIX_DW7888_CHIP_BUG_GPIO_PIN_1;
+// unsigned int groupb_allpin = FIX_DW7888_CHIP_BUG_GPIO_PIN_2;
+// GPIO_BOP(FIX_DW7888_CHIP_BUG_GPIO_GROUP_1) = groupa_allpin;
+// GPIO_BOP(FIX_DW7888_CHIP_BUG_GPIO_GROUP_2) = groupb_allpin;
+// delay_1ms(100U);
+// disable all interrupt code
+// GPIO_BC(FIX_DW7888_CHIP_BUG_GPIO_GROUP_1) = groupa_allpin;
+// GPIO_BC(FIX_DW7888_CHIP_BUG_GPIO_GROUP_2) = groupb_allpin;
+// enable all interrupt code
+
+#define FIX_DW7888_CHIP_BUG_GPIO_GROUP_1 GPIOA
+#define FIX_DW7888_CHIP_BUG_GPIO_PIN_1 (GPIO_PIN_9 | GPIO_PIN_8 | GPIO_PIN_15 | GPIO_PIN_10 | GPIO_PIN_11)
+#define FIX_DW7888_CHIP_BUG_GPIO_GROUP_2 GPIOB
+#define FIX_DW7888_CHIP_BUG_GPIO_PIN_2 (GPIO_PIN_13 | GPIO_PIN_12 | GPIO_PIN_15 | GPIO_PIN_14 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_3)
+// ------------------------------------ 注意(lmx:20201210) -----------------------------------
+
// Action 计时模块
#define CFG_ACTION_TIMER TIMER13
#define CFG_ACTION_TIMER_RCU_CLOCK RCU_TIMER13
#define CFG_ACTION_TIMER_IRQ_NUMBER TIMER13_IRQn
#define CFG_ACTION_TIMER_IRQ_FUNCTION TIMER13_IRQHandler
--- Interface\common.c 2020-12-10 11:28:40.000000000 +0800
+++ Interface\common.c 2020-12-10 11:26:15.000000000 +0800
@@ -18,12 +18,43 @@
#include "common.h"
#include "sensor_infra_red.h"
#include "motor_control.h"
#include "action_process.h"
#include "function_switch.h"
+// ------------------------------------ 注意(lmx:20201210) -----------------------------------
+// 修复 DW7888 芯片缺陷(需要注意此相关代码仅适用于 DW7888 驱动芯片):
+// 1.当驱动电机之后再进入待机模式, 每颗芯片会存在 60uA 左右的漏电流, 而这些芯片的电源不受控制
+// 2.有效修复方法是先进入刹车模式, 再进入待机模式, 两个输入信号同时拉低的时间一定要小于 500ns
+// 3.由于当前硬件设计有一颗驱动芯片分别用了 GPIOA-15\GPIOB-3 两组引脚, 因此不能一次性同时设置
+// 4.实测可以通过直接操作寄存器的方式缩短两组 GPIO 控制时间间隔(124ns):
+// ------------------------------------ 注意(lmx:20201210) -----------------------------------
+static void dw7888_low_power_mode()
+{
+ unsigned int groupa_allpin = FIX_DW7888_CHIP_BUG_GPIO_PIN_1;
+ unsigned int groupb_allpin = FIX_DW7888_CHIP_BUG_GPIO_PIN_2;
+
+ // 刹车模式
+ GPIO_BOP(FIX_DW7888_CHIP_BUG_GPIO_GROUP_1) = groupa_allpin;
+ GPIO_BOP(FIX_DW7888_CHIP_BUG_GPIO_GROUP_2) = groupb_allpin;
+ delay_1ms(100U);
+
+ // 待机模式
+ __disable_irq();
+ GPIO_BC(FIX_DW7888_CHIP_BUG_GPIO_GROUP_1) = groupa_allpin;
+ GPIO_BC(FIX_DW7888_CHIP_BUG_GPIO_GROUP_2) = groupb_allpin;
+ __enable_irq();
+
+ return ;
+}
+
+static void common_fix_chip_bug_release()
+{
+ dw7888_low_power_mode();
+}
+
static void common_periph_clock_init()
{
// 系统滴答时钟初始化
systick_config();
// 启用各个公共时钟
@@ -85,14 +116,14 @@
simple_sensor_release();
adc_control_release();
led_control_release();
master_transfer_release();
power_manager_release();
time_base_release();
+ common_fix_chip_bug_release();
common_periph_clock_release();
-
return ;
}
// 进入升级模式(实质是跳转到 boot )
void common_enter_update_mode(void)
{
问题总结
1. 以后写完程序之后要测试低功耗的时候,应该全功能跑完之后,再关机来看漏电流。
2. 后面我们发现芯片承认书里面的 DW7888 规格书里面的被改为待机电流为 <50uA,也就是一开始选型就能发现问题,只是我们硬件工程师不够细心(虽然有他签字),这也从侧面证实了供应商其实是知道有这么一个问题,原厂之前应该也不知道有这种解决办法。
3. 像多个信号需要同时触发,使用手动去测试可以通过,但是MCU模拟却不通过,就需要首先考虑到 GPIO 之间的间隔时间,因为 MCU 代码是串行执行的,不像 FPGA 可以通过一个时钟触发多个代码块同时执行。那么简单的道理,居然让我给忽略了,忽略了,忽略了,唉。。。