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)
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
"与,或,非,异或,取反,左移,右移"

