RK3562+RK817在关机状态下提升充电电流至2A解决方案

RK3562+RK817平台上: 客户要求提升关机的充电电流,也就是排查uboot的充电电流

代码: u-boot/drivers/power/fuel_gauge/fg_rk817.c

直接show代码讲解:

Step1: 主入口函数

复制代码
static int rk817_fg_init(struct rk817_battery_device *battery)
{
	int value;

	value = rk817_bat_read(battery, GG_CON);
	rk817_bat_write(battery, GG_CON, value | VOL_OUPUT_INSTANT_MODE);
	if (battery->variant == RK817_ID) {
		value =  rk817_bat_read(battery, BAT_DISCHRG);
		rk817_bat_write(battery, BAT_DISCHRG, value & (~DIS_ILIM_EN));
	}
	rk817_bat_gas_gaugle_enable(battery);
	rk817_bat_init_voltage_kb(battery);
	rk817_bat_calibration(battery);
	rk817_bat_rsoc_init(battery);
	rk817_bat_init_coulomb_cap(battery, battery->nac);
	rk817_bat_set_initialized_flag(battery);

	battery->voltage_avg = rk817_bat_get_battery_voltage(battery);
	battery->voltage_sys = rk817_bat_get_sys_voltage(battery);
	battery->voltage_usb = rk817_bat_get_USB_voltage(battery);
	battery->current_avg = rk817_bat_get_avg_current(battery);
	battery->current_pwron = rk817_bat_get_pwron_current(battery);
	battery->remain_cap = rk817_bat_get_capacity_uah(battery);
	battery->rsoc = rk817_bat_get_rsoc(battery);
	battery->sm_linek = rk817_bat_calc_linek(battery);
	battery->chrg_type = rk817_bat_get_charger_type(battery);
	battery->finish_chrg_base = get_timer(0);
	battery->term_sig_base = get_timer(0);

	battery->dbg_pwr_dsoc = battery->dsoc;
	battery->dbg_pwr_rsoc = battery->rsoc;
	battery->dbg_pwr_vol = battery->voltage_avg;

    //重点函数,电池充电部分设置的函数
	if (battery->variant == RK817_ID)
		rk817_bat_charger_setting(battery, battery->chrg_type);

	DBG("voltage_k = %d, voltage_b = %d\n",
	    battery->voltage_k, battery->voltage_b);
	DBG("voltage_sys = %d\n", battery->voltage_sys);
	DBG("voltage usb: %d\n", battery->voltage_avg);
	DBG("battery: %d\n", battery->voltage_avg);
	DBG("current_avg = %d\n", battery->current_avg);
	DBG("current_pwron = %d\n", battery->current_pwron);
	DBG("remain_cap = %d\n", battery->remain_cap);
	DBG("fcc = %d\n", battery->fcc);
	DBG("qmax = %d\n", battery->qmax);
	DBG("dsoc = %d\n", battery->dsoc);
	DBG("rsoc = %d\n", battery->rsoc);
	DBG("charge type: %d\n", battery->chrg_type);
	DBG("battery->variant = %d\n", battery->variant);

	return 0;
}

Step2: 函数

static void rk817_bat_charger_setting(struct rk817_battery_device *battery,

int charger)

复制代码
static void rk817_bat_charger_setting(struct rk817_battery_device *battery,
				      int charger)
{
	static u8 old_charger = UNDEF_CHARGER;

	rk817_bat_set_input_voltage(battery, VLIM_4300MV);
	/* charger changed */
	if (old_charger != charger) {
		if (charger == NO_CHARGER) {
			DBG("NO_CHARGER\n");
			rk817_bat_set_input_current(battery, ILIM_450MA);
		} else if (charger == USB_CHARGER) {
			DBG("USB_CHARGER\n");
			rk817_bat_set_input_current(battery, ILIM_450MA);
		} else if (charger == DC_CHARGER || charger == AC_CHARGER) {
			DBG("%s: DC OR AC CHARGE\n", __func__);
			rk817_bat_set_input_current(battery, ILIM_1500MA);
			//rk817_bat_set_input_current(battery, ILIM_2000MA);  //设置充电电流为2A
			
            // DBG("DC OR AC CHARGE\n");
			// if (battery->last_charger_state == 1) {
			// 	printf("----battery----ILIM_1500MA-----\n");
			// 	rk817_bat_set_input_current(battery, ILIM_1500MA);
			// }
			// else if (battery->last_charger_state == 0) {
			// 	printf("----battery----ILIM_450MA-----\n");
			// 	rk817_bat_set_input_current(battery, ILIM_450MA);
			// }
		} else {
			DBG("charger setting error %d\n", charger);
		}

		old_charger = charger;
	}
}

rk817_bat_set_input_current(battery, ILIM_2000MA);

//#define ILIM_2000MA (0x07) //重点讲解一下这个值0x07 = 0000 0111

剩下了的就是结合寄存器地址写入值的具体bit位运算方式

Step3:函数

static void rk817_bat_set_input_current(struct rk817_battery_device *battery,

int input_current)

复制代码
#define USB_CTRL_REG		0x00E5
#define PMIC_SYS_STS		0x00f0
#define PLUG_IN_STS		BIT(6)

/* USB_CTRL_REG */
#define INPUT_CUR_MSK		0x0f
#define INPUT_VOL_MSK		0xf0
#define VOL_OUPUT_INSTANT_MODE	0x02


//典型的位掩码操作,用于清除数据中的特定位
static void rk817_bat_set_input_current(struct rk817_battery_device *battery,
					int input_current)
{
	u8 usb_ctrl;

	usb_ctrl = rk817_bat_read(battery, USB_CTRL_REG);
	usb_ctrl &= ~INPUT_CUR_MSK;
	usb_ctrl |= ((input_current) | 0x08);
	rk817_bat_write(battery, USB_CTRL_REG, usb_ctrl);
}

将上面的详细讲解一下:

将设置位掩码操作,用于清除数据中的特定位

1. 代码解析

复制代码
#define INPUT_CUR_MSK       0x0f    // 定义掩码为十六进制0x0f
usb_ctrl &= ~INPUT_CUR_MSK;        // 清除usb_ctrl的低4位,结合上面的bit位逻辑计算分析

2. 数值分析

十六进制 0x0f

  • 十六进制:0x0f

  • 二进制:0000 1111

  • 十进制:15

按位取反操作 ~

  • INPUT_CUR_MSK = 0000 1111 (二进制)

  • ~INPUT_CUR_MSK = 1111 0000 (二进制) = 0xf0 (十六进制)

3. 操作详解

复制代码
// 假设 usb_ctrl 的初始值为:0x5A (二进制 0101 1010) usb_ctrl = 0x5A; // 二进制: 0101 1010 // 执行清除操作 usb_ctrl \&= \~INPUT_CUR_MSK; // \~INPUT_CUR_MSK = 1111 0000 // 计算过程: // 0101 1010 (usb_ctrl) // \& 1111 0000 (\~INPUT_CUR_MSK)// ---------// 0101 0000 (结果: 0x50) // 结果:低4位被清零,高4位保持不变

4. 实际应用场景

这种操作常见于硬件寄存器操作:

复制代码
// 假设USB控制寄存器的位定义:
// 位7-4: 配置模式 (保留)
// 位3-0: 输入电流设置 (0000=500mA, 0001=900mA, ...)

#define INPUT_CUR_500MA  0x00
#define INPUT_CUR_900MA  0x01
#define INPUT_CUR_1500MA 0x02
#define INPUT_CUR_2000MA 0x03

// 1. 首先清除当前的电流设置
usb_ctrl &= ~INPUT_CUR_MSK;  // 清除低4位

// 2. 设置新的电流值
usb_ctrl |= INPUT_CUR_1500MA;  // 设置电流为1500mA

// 完整示例:
void set_usb_current(uint8_t *reg, uint8_t current_setting)
{
    *reg &= ~INPUT_CUR_MSK;      // 步骤1:清零低4位
    *reg |= (current_setting & INPUT_CUR_MSK);  // 步骤2:设置新值
}

5. 为什么要这样设计?

掩码的用途:

  1. 精确控制:只操作特定位,不影响其他位

  2. 可读性:使用有意义的宏名称,提高代码可读性

  3. 可维护性:如果要修改位的位置,只需修改宏定义

类似的操作模式:

复制代码
// 设置特定位(置1)
usb_ctrl |= INPUT_CUR_MSK;      // 低4位置1

// 清除特定位(置0)
usb_ctrl &= ~INPUT_CUR_MSK;     // 低4位清零

// 切换特定位(取反)
usb_ctrl ^= INPUT_CUR_MSK;      // 低4位取反

// 检查特定位是否设置
if (usb_ctrl & INPUT_CUR_MSK) {
    // 低4位至少有一位为1
}

// 检查特定位是否全部为0
if ((usb_ctrl & INPUT_CUR_MSK) == 0) {
    // 低4位全部为0
}

6. 实际硬件寄存器操作示例

复制代码
// 典型的硬件寄存器操作序列
#define USB_CTRL_REG      (*((volatile uint8_t *)0x40005000))
#define MODE_MSK          0xC0    // 位7-6: 模式控制
#define SPEED_MSK         0x30    // 位5-4: 速度设置
#define CURRENT_MSK       0x0F    // 位3-0: 电流设置(这就是INPUT_CUR_MSK)

// 配置USB控制器
void configure_usb(uint8_t mode, uint8_t speed, uint8_t current)
{
    // 1. 先读取当前寄存器值(避免影响其他位)
    uint8_t reg_value = USB_CTRL_REG;
    
    // 2. 清除要设置的位域
    reg_value &= ~(MODE_MSK | SPEED_MSK | CURRENT_MSK);
    
    // 3. 设置新的值
    reg_value |= (mode << 6) & MODE_MSK;
    reg_value |= (speed << 4) & SPEED_MSK;
    reg_value |= current & CURRENT_MSK;
    
    // 4. 写回寄存器
    USB_CTRL_REG = reg_value;
}

// 简化版本:只设置电流
void set_usb_current_only(uint8_t current)
{
    USB_CTRL_REG = (USB_CTRL_REG & ~CURRENT_MSK) | (current & CURRENT_MSK);
}

7. 重要注意事项

复制代码
// 常见错误:
// 错误1:忘记取反
usb_ctrl &= INPUT_CUR_MSK;   // ❌ 错误:这会保留低4位,清除其他位

// 错误2:直接赋值
usb_ctrl = INPUT_CUR_MSK;    // ❌ 错误:这会覆盖所有位

// 正确做法:
usb_ctrl &= ~INPUT_CUR_MSK;  // ✅ 正确:只清除低4位,保持其他位不变

// 更安全的写法(如果current可能超出范围):
usb_ctrl = (usb_ctrl & ~INPUT_CUR_MSK) | (new_current & INPUT_CUR_MSK);

8. 位掩码操作技巧表

操作 代码 说明
清除位 reg &= ~mask 将mask对应的位清零
设置位 `reg = mask`
切换位 reg ^= mask 将mask对应的位取反
检查位 if (reg & mask) 检查mask对应的位是否有1
提取位 value = reg & mask 提取mask对应的位值
更新位 reg = (reg & ~mask) │ value 安全更新位域

这个代码模式在嵌入式系统、驱动开发和硬件编程中非常常见,是处理硬件寄存器位操作的标准方法。

再次扩展一下C语言的bit运算操作

复制代码
#define INPUT_CUR_MSK           0x0f
#define INPUT_VOL_MSK           0xf0
#define VOL_OUPUT_INSTANT_MODE  0x02
#define ILIM_2000MA             (0x07)

//uint8_t usb_ctrl;

int rk817_bat_set_input_current(int input_current)
{
        printf("input_current: %d (int), input_current: 0x%02x (hex) \n", input_current, input_current);
        return 0;
}

int main(void)
{
        //u8 usb_ctrl;
        uint8_t usb_ctrl;
        rk817_bat_set_input_current(ILIM_2000MA);
        usb_ctrl &= ~INPUT_CUR_MSK;
        printf("usb_ctrl:%d, usb_ctrl = 0x%02x \n", usb_ctrl, usb_ctrl);
        //usb_ctrl |= ((input_current) | 0x08);
        usb_ctrl |= ((ILIM_2000MA) | 0x08);
        printf("[ usb_ctrl:%d, usb_ctrl = 0x%02x ] \n", usb_ctrl, usb_ctrl);


        //input_current: 7 (int), input_current: 0x07 (hex)
        //usb_ctrl:0, usb_ctrl = 0x00
        //[ usb_ctrl:15, usb_ctrl = 0x0f ]


        return 0;
}

// 假设 usb_ctrl 的初始值为:0x5A (二进制 0101 1010)

usb_ctrl = 0x5A; // 二进制: 0101 1010

// 执行清除操作

usb_ctrl &= ~INPUT_CUR_MSK; // ~INPUT_CUR_MSK = 1111 0000

// 计算过程:

// 0101 1010 (usb_ctrl)

// & 1111 0000 (~INPUT_CUR_MSK)

// ---------

// 0101 0000 (结果: 0x50)

// 结果:低4位被清零,高4位保持不变

再继续扩展一下:

C 语言支持的位运算符。假设变量 A 的值为 60,变量 B 的值为 13

"与,或,非,异或,取反,左移,右移"

相关推荐
代码游侠2 小时前
ARM嵌入式开发代码实践——LED灯闪烁(汇编版)
arm开发·笔记·嵌入式硬件·学习·架构
陈聪.3 小时前
HRCE简单实验
linux·运维·数据库
haluhalu.3 小时前
从 Linux 线程控制到 pthread 库
java·linux·服务器
2023自学中3 小时前
Cortex-M系列,Cortex-A系列,汇编启动文件的区别
linux·嵌入式硬件
三伏5223 小时前
stm32f103系列手册IIC笔记2
笔记·stm32·嵌入式硬件
APIshop3 小时前
实战代码解析:item_get——获取某鱼商品详情接口
java·linux·数据库
楼田莉子3 小时前
Linux系统小项目——“主从设计模式”进程池
linux·服务器·开发语言·c++·vscode·学习
知数SEO3 小时前
Centos如何安装高版本Python
linux·python·centos
国科安芯3 小时前
RISC-V架构抗辐照MCU在航天器载荷中的SEU/SEL阈值测试与防护策略
单片机·嵌入式硬件·安全·架构·安全威胁分析·risc-v