问题:
最近遇到个问题,android11 rk3566 的系统,产品是个平板,系统正常一直处于耗电的状态,系统会正常挂掉(这里指的是电池的电量不足以带动负载继续正常工作),就是我们所说的系统强制关机了(物理关机),但系统关机完了之后,又会自动的重启,然后重启到一半之后,又会挂掉,一直在这循环,一直把电池的电量一直耗着不足以自动启动时,就不会开机了。
注意:这种情况是系统是由电池供电,不插入适配器的情况。
分析:
系统挂掉的原因肯定是由于电池的电量,已经不足以带动系统负载正常工作,就是我们所说的低电量自动关机(物理关机),而有会自动重启的原因是,当系统关机后,电池目前的状态就是一个空载的状态,而我的使用的电池有个诟病,也有可能是我所使用的电池太挫了的原因吧,电池空载后它的电池的电压会升高 0.2-0.3V 左右而系统中会去判断这个电压值,此时系统感觉这电池好像有行了!!!又可以支持系统开机了,然后又自动开机了,发现开到一半发现电池带不动了,然后就挂掉了。。。。好家伙这不是坑机嘛这!!当然这里的自动开机后续会讲到,在代码中也有相关的判断。我目前的系统需要实现的是关机充电,以及不能够让它插入适配器的时候自动开机,以及解决上面的问题。
关机充电以及相关的开机处理都在u-boot 阶段,而涉及到的文件有:
u-boot/drivers/power/charge_animation.c //重要,负责管理整个充电过程,它会获取电量、充电类型、按键事件,发起低功耗休眠等。
u-boot/include/power/charge_animation.h
u-boot/drivers/power/fuel_gauge/fg_rk817.c
u-boot/drivers/power/fuel_gauge/fuel_gauge_uclass.c
u-boot/drivers/power/pmic/rk8xx.c
重要代码,结构体分析:
该结构体是用于充电管理的相关的值,具体的作用,都注释的比较清楚。
struct charge_animation_pdata {
int android_charge; /* android charge, 1: enable, 0: disable */
int uboot_charge; /* u-boot charge, 1: enable, 0: disable */
int auto_exit_charge; /* energy enough auto exit uboot charging*/
int exit_charge_voltage;/* lowest voltage allowed to exit charging */
int exit_charge_level; /* lowest soc level allowed to exit charging */
int low_power_voltage; /* below this voltage, force system into charge mode anyway */
int screen_on_voltage; /* lowest voltage allowed to turn on screen */
int system_suspend; /* enter ATF system suspend, 1: enable, 0: disable */
int auto_wakeup_interval;/* timeout seconds to auto wakeup system */
int auto_wakeup_screen_invert;/* auto wakeup system, 1: enable, 0: disable */
int auto_off_screen_interval;/* timeout seconds to auto turn off screen */
};
dts配置文件解析:
charge-animation {
compatible = "rockchip,uboot-charge";
status = "okay";
rockchip,uboot-charge-on = <0>; // 是否开启U-Boot充电
rockchip,android-charge-on = <1>; // 是否开启Android充电
rockchip,uboot-exit-charge-level = <5>; // U-Boot充电时,允许开机的最低电量
rockchip,uboot-exit-charge-voltage = <3650>;// U-Boot充电时,允许开机的最低电压
rockchip,screen-on-voltage = <3400>; // U-Boot充电时,允许点亮屏幕的最低电
rockchip,uboot-low-power-voltage = <3350>; // U-Boot无条件强制进入充电模式的最低
rockchip,system-suspend = <1>; // 是否灭屏时进入trust低功耗待机(要压电压ATF支持)
rockchip,auto-off-screen-interval = <20>; // 自动灭屏超时,单位秒,默认15s
rockchip,auto-wakeup-interval = <10>; // 休眠自动唤醒时间,单位秒。如果值为0或没// 有这个属性,则禁止休眠自动唤醒,一般用于 压力测试使用
rockchip,auto-wakeup-screen-invert = <1>;
};
注意:这些都需要根据你自己的环境进行配置,具体可根据万用表测试,然后再配置成相应的值。
charge_animation.c 中所中的操作函数都是在fg_rk817.c 实现的如下:
static struct dm_fuel_gauge_ops fg_ops = { //charge_animatio.c 中的一些回调函数都是都是调用的fg_rk817中的函数实现。
.bat_is_exist = rk817_bat_bat_is_exist,
.get_soc = rk817_bat_update_get_soc,
.get_voltage = rk817_bat_update_get_voltage,
.get_current = rk817_bat_update_get_current,
.get_chrg_online = rk817_bat_update_get_chrg_online,
};
整个uboot管理充电过程的实现函数主要是charge_animation_show() 函数,它是比较重要的一个函数,下面我们来分析它的过程。
charge_animation_show();
|--fuel_gauge_bat_is_exist();->rk817_bat_bat_is_exist
|--rk817_bat_get_battery_voltage(battery); //读取pmic中的寄存器值获取battery的电量。
|--charge_extrem_low_power(dev); //该函数主要是检测电池低电量(无法满足系统工作的电量)的,且无供电电源时,关闭PMIC 下电。下面会讲一下该函数。
|--fuel_gauge_get_voltage(fg);
|--rk817_bat_update_get_voltage();
|--rk817_bat_get_battery_voltage();
|--fg_charger_get_chrg_online(dev); // --->>>/* PMIC shutdown */ pmic_shutdown(pmic);static int rk8xx_shutdown(struct udevice *dev)
|--rk817_bat_update_get_chrg_online();
|--rk817_bat_dwc_otg_check_dpdm(battery); //检测充电类型
|--autowakeup_timer_init(dev,5); ///* Enable auto wakeup */
|--fuel_gauge_update_get_soc(fg);-->static int rk817_bat_update_get_soc(struct udevice *dev)//该函数设置充电电流,以及外接的充电类,rk817_bat_smooth_charge(struct rk817_battery_device *battery)(每隔5S检测一次,保证是否在平稳的充电)
|--eds_update(dev, soc);
|--system_suspend_enter(dev);/* System suspend */
|--fuel_gauge_get_voltage(fg); //更新电压 |--rk817_bat_update_get_voltage();
|--rockchip_get_boot_mode();//检测充电模式是否有效。
|--fg_charger_get_chrg_online(dev);--->rk817_bat_update_get_chrg_online();rk817_bat_dwc_otg_check_dpdm(battery);//获取充电头类型
/* Not charger online and low power, shutdown */
soc = fuel_gauge_update_get_soc(fg);
voltage = fuel_gauge_get_voltage(fg);
if (soc < pdata->exit_charge_level) {
printf("soc(%d%%) < exit_charge_level(%d%%)\n",
soc, pdata->exit_charge_level);
exit_charge = true;
}
if (voltage < pdata->exit_charge_voltage) {
printf("voltage(%d) < exit_charge_voltage(%d)\n",
voltage, pdata->exit_charge_voltage);
exit_charge = true;
}
if (exit_charge) {
printf("Not charging and low power, Shutdown...\n");
show_idx = IMAGE_LOWPOWER_IDX(image_num);
charge_show_bmp(image[show_idx].name);
mdelay(1000);
pmic_shutdown(pmic);
}
}
//以下是判断:
|--pdata->android_charge // Enter android charge, set property for kernel */
|--if (!pdata->uboot_charge)///* Not enable U-Boot charge, exit */
|--fuel_gauge_get_voltage(fg);
|--if (voltage <= pdata->screen_on_voltage + 50)//charge_show_bmp(NULL);
|-----!!!!!!/* Charging ! */ while (1) 开始充电 //获取电量状态等驱动中利用的是定时器轮询的方式来获取,避免通过i2c来一直读取寄存器的值。在此期间是禁止中断的,而是通过power key产生事件中断。
|--local_irq_disable();
接下来就是充电5个步骤。
|--第一步:检测是否现在充电。
|--charging = fg_charger_get_chrg_online(dev);//如果充电器在此过程中拔出,则会调用rockchip_eink_show_charge_logo(),并关闭PMIC。
|--第二步:获取系统的电量与电池的电压,以及电流。判断目前的电压是否可以退出uboot阶段的充电(pdata->auto_exit_charge)。判断此时电量是否足以打开屏幕。
|--fuel_gauge_update_get_soc(fg);
|--fuel_gauge_get_voltage(fg);
|--fuel_gauge_get_current(fg);
|--下面的一部分就是处理关机充电时,亮屏以及充电图标状态显示的处理。当系统充电到100时则退出充电状态,以引导android 系统。
。。。。。。
|--第三步:主要是显示充电图标的处理。这部分我们不重点关注。
|--第四步:检查按键事件。检测power key :short key event:亮灭屏。长按键:显示logo 图标,引导系统开机继续保持充电状态。
|--key_state = check_key_press(dev);//key_state == KEY_PRESS_DOWN /key_state == KEY_PRESS_LONG_DOWN
|--第五步:主要是检测 ctrl+c 的处理。
charge_extrem_low_power()函数主要是为了检测当前电池的电量值,以及是否插入适配器对电池充电。如果电池的电量小于我们设置的最低电量值的话,且无适配器插入,就会关闭pmic。以保证上面所出现的一直反复重启的情况出现。
static int charge_extrem_low_power(struct udevice *dev)
{
struct charge_animation_pdata *pdata = dev_get_platdata(dev);
struct charge_animation_priv *priv = dev_get_priv(dev);
struct udevice *pmic = priv->pmic;
struct udevice *fg = priv->fg;
int voltage, soc, charging = 1;
static int timer_initialized;
int ret;
voltage = fuel_gauge_get_voltage(fg);//获取当前电池电量值
if (voltage < 0)
return -EINVAL;
while (voltage < pdata->low_power_voltage + 50) {
/* Check charger online */
charging = fg_charger_get_chrg_online(dev);//判断是否插入输入源,及适配器。返回输入源类型
if (charging <= 0) {
printf("%s: Not charging, online=%d. Shutdown...\n",
__func__, charging);
/* wait uart flush before shutdown */
mdelay(5);
/* PMIC shutdown */
pmic_shutdown(pmic);//关闭pmic.
printf("Cpu should never reach here, shutdown failed !\n");
continue;
}
/* Enable auto wakeup */
if (!timer_initialized) {
timer_initialized = 1;
autowakeup_timer_init(dev, 5);
}
/*
* Just for fuel gauge to update something important,
* including charge current, coulometer or other.
*/
soc = fuel_gauge_update_get_soc(fg); //获取系统的电量值。
if (soc < 0 || soc > 100) {
printf("get soc failed: %d\n", soc);
continue;
}
/* Update led */
ret = leds_update(dev, soc);
if (ret)
printf("update led failed: %d\n", ret);
printf("Extrem low power, force charging... threshold=%dmv, now=%dmv\n",
pdata->low_power_voltage, voltage);
/* System suspend */
system_suspend_enter(dev);
/* Update voltage */
voltage = fuel_gauge_get_voltage(fg);
if (voltage < 0) {
printf("get voltage failed: %d\n", voltage);
continue;
}
if (ctrlc()) {
printf("Extrem low charge: exit by ctrl+c\n");
break;
}
}
autowakeup_timer_uninit();
return 0;
}
所以基本上我们上面出现的问题就比较清楚了,剩下的只需要我们去配置好相关的参数就行了。
而我目前的板子没有关机充电图标,因为我的显示屏是通过mipi dsi 转LVDS 的,是通过GM8775C转换芯片的,所以我需要在uboot 中也需要将该芯片初始化成功。