背景:有些客户要求机器放电关机后,在不插充电器的情况下开机时不能开机,需要显示低电的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的图片即可。