背景:有些客户要求机器放电关机后,在不插充电器的情况下开机时不能开机,需要显示低电的logo,但是插充电器的时候就需要能够直接开机。
分析:我们知道如果电池曲线调试ok后,一般关机后的电压在3.45v左右,如果差的太多说明电池曲线还需要再进行优化,我们假定电池曲线调试的ok的,从代码里面来看开机时会有一个门限的电压,这个电压值其实就是3450,只有目前电压大于3.45v时我们才会让机器开始,毕竟很多屏都有一个3.3v的电压要求,而且开机时电压还会虚高,因此3.45v才准开机的这个门限电压也是合适的,我们需要做的就是再设置一个比3.45v更低一点的电压,当电池当前电压小于3.45v不能开机的情况下,又大于比如3.4v时我们让机器显示一个低电的logo,即当 3.4 < Vbat < 3.45 情况显示低电logo。
实现过程:
标志性的打印log
dprintf(CRITICAL, "[BATTERY] battery voltage(%dmV) <= CLV ! Can not Boot Linux Kernel !! \n\r",bat_vol);
看下代码的实现:
在lk阶段的mt65xx_bat_init函数里面
#ifndef MTK_DISABLE_POWER_ON_OFF_VOLTAGE_LIMITATION
#ifndef MTK_BATLOWV_NO_PANEL_ON_EARLY
if (bat_vol < BATTERY_LOWVOL_THRESOLD) // 这里门槛是3450
#else
if (is_low_battery(bat_vol))
#endif
{
// 当电压小于3450后,开始检查启动模式,如果是关机充电
if (g_boot_mode == KERNEL_POWER_OFF_CHARGING_BOOT && upmu_is_chr_det() == KAL_TRUE) {
dprintf(CRITICAL, "[%s] Kernel Low Battery Power Off Charging Mode\n", __func__);
g_boot_mode = LOW_POWER_OFF_CHARGING_BOOT;
check_bat_protect_status();
} else { // 如果不是关机充电,比如我们直接开机,我们的需求其实满足这个条件
dprintf(CRITICAL, "[BATTERY] battery voltage(%dmV) <= CLV ! Can not Boot Linux Kernel !! \n\r",bat_vol); // 这个是标志性打印,不多说了
// add start
if(bat_vol >= 3400)
{
dprintf(CRITICAL, "[BATTERY] show low battery logo\n");
mt_disp_power(TRUE);
mt_disp_show_low_battery();
mt65xx_leds_brightness_set(6, 110);
mdelay(3000);
mt_disp_power(FALSE);
}
// add end
#ifndef NO_POWER_OFF
mt6575_power_off();
#endif
while (1) {
dprintf(CRITICAL, "If you see the log, please check with RTC power off API\n\r");
if (fix_coverity == 1)
return;
}
}
}
#endif
实现的主要代码:
if(bat_vol >= 3400) 判断当前电压是否大于3.4v
{
dprintf(CRITICAL, "[BATTERY] show low battery logo\n");
mt_disp_power(TRUE); 打开显示电源
mt_disp_show_low_battery(); 主要的是这句,显示低电logo
mt65xx_leds_brightness_set(6, 110); 设置背光亮度,6表示类型是lcd,亮度为110(亮度范围0~255)
mdelay(3000); 延时3s
mt_disp_power(FALSE);
}
我们看下显示低电的关键函数:
void mt_disp_show_low_battery(void)
{
dprintf(INFO, "[lk logo: %s %d]\n",__FUNCTION__,__LINE__);
mt_logo_get_custom_if();
if (logo_cust_if->show_boot_logo) {
logo_cust_if->show_boot_logo();
} else {
init_fb_screen(); 初始化fb
//show_logo(2);
fill_animation_logo(LOW_BATTERY_INDEX, mt_get_fb_addr(), (void *)mt_get_tempfb_addr(), logo_addr, phical_screen); 这里的第一个参数决定了显示的是低电这张图片
mt_disp_update(0, 0, CFG_DISPLAY_WIDTH, CFG_DISPLAY_HEIGHT); 更新显示
}
return;
}
到这里我们的功能已经实现了,但是还想继续分析一下代码,低电图片的定义index号:
// Common LOGO index
#define BOOT_LOGO_INDEX 0 启动logo
#define KERNEL_LOGO_INDEX 38 内核logo
#define ANIM_V0_BACKGROUND_INDEX 1
#define ANIM_V1_BACKGROUND_INDEX 35
#define LOW_BATTERY_INDEX 2 低电就是这个
#define CHARGER_OV_INDEX 3 充电过压
#define FULL_BATTERY_INDEX 37 充满图片
顺便把设置亮度的函数也看一下:
int mt65xx_leds_brightness_set(enum mt65xx_led_type type, enum led_brightness level)
{
struct cust_mt65xx_led *cust_led_list = get_cust_led_dtsi();
if (cust_led_list == NULL) {
cust_led_list = get_cust_led_list();
LEDS_ERR("Cannot not get the LED info from device tree. \n");
}
if (type >= MT65XX_LED_TYPE_TOTAL)
return -1;
if (level > LED_FULL)
level = LED_FULL;
// else if (level < 0) //level cannot < 0
// level = 0;
if (g_lastlevel[type] != (int)level) {
g_lastlevel[type] = level;
dprintf(CRITICAL,"[LEDS]LK: %s level is %d \n\r", cust_led_list[type].name, level);
return mt65xx_led_set_cust(&cust_led_list[type], level); 主要是这个
} else {
return -1;
}
}
继续看下:
static int mt65xx_led_set_cust(struct cust_mt65xx_led *cust, int level)
{
unsigned int BacklightLevelSupport = Cust_GetBacklightLevelSupport_byPWM();
if (level > LED_FULL)
level = LED_FULL;
else if (level < 0)
level = 0;
switch (cust->mode) {
case MT65XX_LED_MODE_PWM:
if (level == 0) {
//LEDS_INFO("[LEDS]LK: mt65xx_leds_set_cust: enter mt_pwm_disable()\n");
mt_pwm_disable(cust->data, cust->config_data.pmic_pad);
return 1;
}
if (strcmp(cust->name,"lcd-backlight") == 0) {
if (BacklightLevelSupport == BACKLIGHT_LEVEL_PWM_256_SUPPORT)
level = brightness_mapping(level);
else
level = brightness_mapto64(level);
return brightness_set_pwm(cust->data, level,&cust->config_data); 这里
} else {
return led_set_pwm(cust->data, level);
}
case MT65XX_LED_MODE_GPIO:
return ((cust_brightness_set)(cust->data))(level);
case MT65XX_LED_MODE_PMIC:
return brightness_set_pmic(cust->data, level);
case MT65XX_LED_MODE_CUST_LCM:
return ((cust_brightness_set)(cust->data))(level);
case MT65XX_LED_MODE_CUST_BLS_PWM:
return ((cust_brightness_set)(cust->data))(level);
case MT65XX_LED_MODE_NONE:
default:
break;
}
return -1;
}
蛮多调用的,再看一看:最后是设置pwm寄存器的值来实现的,也很好理解
static int brightness_set_pwm(int pwm_num, enum led_brightness level,struct PWM_config *config_data)
{
struct pwm_spec_config pwm_setting;
unsigned int BacklightLevelSupport = Cust_GetBacklightLevelSupport_byPWM();
pwm_setting.pwm_no = pwm_num;
if (BacklightLevelSupport == BACKLIGHT_LEVEL_PWM_256_SUPPORT)
pwm_setting.mode = PWM_MODE_OLD;
else
pwm_setting.mode = PWM_MODE_FIFO; // New mode fifo and periodical mode
pwm_setting.pmic_pad = config_data->pmic_pad;
if (config_data->div) {
pwm_setting.clk_div = config_data->div;
backlight_PWM_div = config_data->div;
} else
pwm_setting.clk_div = CLK_DIV1;
if (BacklightLevelSupport== BACKLIGHT_LEVEL_PWM_256_SUPPORT) {
if (config_data->clock_source) {
pwm_setting.clk_src = PWM_CLK_OLD_MODE_BLOCK;
} else {
pwm_setting.clk_src = PWM_CLK_OLD_MODE_32K; // actually. it's block/1625 = 26M/1625 = 16KHz @ MT6571
}
pwm_setting.pwm_mode.PWM_MODE_OLD_REGS.IDLE_VALUE = 0;
pwm_setting.pwm_mode.PWM_MODE_OLD_REGS.GUARD_VALUE = 0;
pwm_setting.pwm_mode.PWM_MODE_OLD_REGS.GDURATION = 0;
pwm_setting.pwm_mode.PWM_MODE_OLD_REGS.WAVE_NUM = 0;
pwm_setting.pwm_mode.PWM_MODE_OLD_REGS.DATA_WIDTH = 255; // 256 level
pwm_setting.pwm_mode.PWM_MODE_OLD_REGS.THRESH = level;
LEDS_INFO("[LEDS][%d] LK: backlight_set_pwm:duty is %d/%d\n", BacklightLevelSupport, level, pwm_setting.pwm_mode.PWM_MODE_OLD_REGS.DATA_WIDTH);
LEDS_INFO("[LEDS][%d] LK: backlight_set_pwm:clk_src/div is %d%d\n", BacklightLevelSupport, pwm_setting.clk_src, pwm_setting.clk_div);
if (level >0 && level < 256) {
pwm_set_spec_config(&pwm_setting);
LEDS_INFO("[LEDS][%d] LK: backlight_set_pwm: old mode: thres/data_width is %d/%d\n", BacklightLevelSupport, pwm_setting.pwm_mode.PWM_MODE_OLD_REGS.THRESH, pwm_setting.pwm_mode.PWM_MODE_OLD_REGS.DATA_WIDTH);
} else {
LEDS_ERR("[LEDS][%d] LK: Error level in backlight\n", BacklightLevelSupport);
mt_pwm_disable(pwm_setting.pwm_no, config_data->pmic_pad);
}
return 0;
} else {
if (config_data->clock_source) {
pwm_setting.clk_src = PWM_CLK_NEW_MODE_BLOCK;
} else {
pwm_setting.clk_src = PWM_CLK_NEW_MODE_BLOCK_DIV_BY_1625;
}
if (config_data->High_duration && config_data->low_duration) {
pwm_setting.pwm_mode.PWM_MODE_FIFO_REGS.HDURATION = config_data->High_duration;
pwm_setting.pwm_mode.PWM_MODE_FIFO_REGS.LDURATION = pwm_setting.pwm_mode.PWM_MODE_FIFO_REGS.HDURATION;
} else {
pwm_setting.pwm_mode.PWM_MODE_FIFO_REGS.HDURATION = 4;
pwm_setting.pwm_mode.PWM_MODE_FIFO_REGS.LDURATION = 4;
}
pwm_setting.pwm_mode.PWM_MODE_FIFO_REGS.IDLE_VALUE = 0;
pwm_setting.pwm_mode.PWM_MODE_FIFO_REGS.GUARD_VALUE = 0;
pwm_setting.pwm_mode.PWM_MODE_FIFO_REGS.STOP_BITPOS_VALUE = 31;
pwm_setting.pwm_mode.PWM_MODE_FIFO_REGS.GDURATION = (pwm_setting.pwm_mode.PWM_MODE_FIFO_REGS.HDURATION + 1) * 32 - 1;
pwm_setting.pwm_mode.PWM_MODE_FIFO_REGS.WAVE_NUM = 0;
LEDS_INFO("[LEDS] LK: backlight_set_pwm:duty is %d\n", level);
LEDS_INFO("[LEDS] LK: backlight_set_pwm:clk_src/div/high/low is %d%d%d%d\n", pwm_setting.clk_src, pwm_setting.clk_div, pwm_setting.pwm_mode.PWM_MODE_FIFO_REGS.HDURATION, pwm_setting.pwm_mode.PWM_MODE_FIFO_REGS.LDURATION);
if (level > 0 && level <= 32) {
pwm_setting.pwm_mode.PWM_MODE_FIFO_REGS.GUARD_VALUE = 0;
pwm_setting.pwm_mode.PWM_MODE_FIFO_REGS.SEND_DATA0 = (1 << level) - 1;
pwm_set_spec_config(&pwm_setting);
} else if (level > 32 && level <= 64) {
pwm_setting.pwm_mode.PWM_MODE_FIFO_REGS.GUARD_VALUE = 1;
level -= 32;
pwm_setting.pwm_mode.PWM_MODE_FIFO_REGS.SEND_DATA0 = (1 << level) - 1 ;
pwm_set_spec_config(&pwm_setting);
} else {
LEDS_ERR("[LEDS] LK: Error level in backlight\n");
mt_pwm_disable(pwm_setting.pwm_no, config_data->pmic_pad);
}
return 0;
}
}
总结:直接开机显示低电logo功能,只需要实现当电池电压在3.4~3.45v时加入显示低电logo的图片即可。