TI BQ27421电量计驱动的调试

编程入门 行业动态 更新时间:2024-10-24 12:20:31

TI BQ27421<a href=https://www.elefans.com/category/jswz/34/1761614.html style=电量计驱动的调试"/>

TI BQ27421电量计驱动的调试

TI BQ27421电量计驱动的调试

目录

  • TI BQ27421电量计驱动的调试
    • 运行环境
    • TI 电量计技术资料
      • 资料的下载
      • 参考手册讲解
    • TI工具的使用
    • 驱动开发与调试
    • 调试时遇到的问题
    • 结论

运行环境

  • 硬件环境:瑞芯微 rv1108
  • 操作系统: linux

TI 电量计技术资料

资料的下载

进入TI 电量计产品的官网 下载技术手册 Technical Reference
data sheet 的下载 .pdf?ts=1686216383022&ref_url=https%253A%252F%252Fwww.ti%252Fproduct%252FBQ27421-G1

参考手册讲解

  • 从数据手册(data sheet )上有二种比较重要的点,一是I2C的频率 ,这里是400Khz, 另一个是I2C的地址,这里是0x55

The 7-bit device address (ADDR) is the most significant 7 bits of the hex address and is fixed as
1010101.

  • 另一个很重要的文档是Technical Reference 技术参考手册,这里讲明了每个寄存器表示的意义,以及举例如何进行操作。

Standard Commands 标准命令; 标准命令了解就可以了,使用时知道查询哪个寄存器获取电量百分比等就行。
Extended Data Commands 扩展数据命令,主要是寄存器参数的一些设计,影响电量的功能,需要设置正确。

  • Technical Reference 技术参考手册中的 ** 3.1 Data Memory Parameter Update Example ** 是要重点读的,而且要读懂
    电量计有几个参数必须是要设置的,如电池容量,充放电最高和截止电压等,举例中描述的是如何设置电池容量。

1.设置unseal
2.发送SET_CFGUPDATE
3.查询进入CFGUPDATE 的标志,大约需要1S
4.往0x61 BlockDataControl() 写0
5.往(using the DataBlockClass() command (0x3E) ) 0x3e写Subclass ID
6.往(Block data starts at 0x40) 0x40 写 Data Memory Summary Tables 中的offset
7.写data block, 或者把data block读出来,设置对应的寄存器
8.写chechsum.

退出CFGUPDATE ,查询CFGUPDATE 标志

  • 第六章 ** 6.3 Data Memory Summary Tables ** 这里的寄存器主要是查询

    注意这二个地方的寄存器

CFGUPMODE = Fuel gauge is in CONFIG UPDATE mode. True when set. Default is 0. Refer to Section 2.4.3 for details.
INITCOMP = Initialization completion bit indicating the initialization is complete. True when set

TI工具的使用

使用工具对电池进行一次充电放电的学习。再导出配置文件

配置文件如下所示(部分配置)

;--------------------------------------------------------
;Verify Existing Firmware Version
;--------------------------------------------------------
; -- ????
W: AA 00 01 00
C: AA 00 21 04
; -- FWVERSION 0x0109
W: AA 00 02 00
C: AA 00 09 01
;--------------------------------------------------------
;SET_CFGUPDATE
;--------------------------------------------------------
W: AA 00 13 00
X: 1100
;----- DataClass 02 (safety)
W: AA 3E 02 00
; ---- write block data 0x40~0x60   ;550 - 0 - 50
W: AA 40 02 26 00 00 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
; ---- write blockdata 0xA5
W: AA 60 A5
X: 10
;  ---- DataClass 02
W: AA 3E 02 00
;  ---- Read Checksum 0xA5
C: AA 60 A5
;  -- write block 0x24(36) (Charge Termination)
W: AA 3E 24 00   
W: AA 40 00 19 28 63 5F FF 62 00 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
;  -- write blockdata checksum
W: AA 60 69

配置文件解析

W: 表示该行是写入一个或多个字节数据的命令。
C: 表示该行是读取和比较一个或多个字节数据的命令。
X: 表示该行是在继续之前等待给定毫秒数的命令。

W: AA 00 01 00 的意思
把01写到0x00地址
往00写到0x01地址
AA为I2C的地址,AA后面的00表示寄存器地址

驱动开发与调试

关于bq27421的驱动,网上有二种
第一种是linux 最新的内核中自带的 见
第二种是其它平台下的 下载地址
当然把BQ27421参考手册读懂后完全自己写,这里参考第种方式,在第二种方式 上进行修改。
这里有个地方要看懂

static struct dm_reg bq274xx_dm_regs[] = {{64, 2, 1, 0x0F},	/* Op Config B */{80, 78, 1, 10},	/* TermV Valid t 10s */{82, 0, 2, 17312},	/* Qmax */{82, 3, 2, 15},		/* Reserve capacity */{82, 10, 2, 300},	/* Design Capacity */{82, 12, 2, 1140},	/* Design Energy */{82, 16, 2, 3300},	/* Terminate Voltage */{82, 27, 2, 33},	/* Taper rate */
};
static void rom_mode_gauge_dm_init(struct bq27xxx_device_info *di)
{int i;int timeout = INITCOMP_TIMEOUT_MS;u8 subclass, offset;u32 blk_number;u32 blk_number_prev = 0;u8 buf[32];bool buf_valid = false;struct dm_reg *dm_reg;dev_dbg(di->dev, "%s:\n", __func__);while (!rom_mode_gauge_init_completed(di) && timeout > 0) {msleep(100);timeout -= 100;}if (timeout <= 0) {dev_err(di->dev, "%s: INITCOMP not set after %d seconds\n",__func__, INITCOMP_TIMEOUT_MS/100);return;}if (!di->dm_regs || !di->dm_regs_count) {dev_err(di->dev, "%s: Data not available for DM initialization\n",__func__);return;}dev_warn(di->dev, "start rom_mode_gauge_dm_init\n");enter_cfg_update_mode(di);for (i = 0; i < di->dm_regs_count; i++) {dm_reg = &di->dm_regs[i];subclass = dm_reg->subclass;offset = dm_reg->offset;/** Create a composite block number to see if the subsequent* register also belongs to the same 32 btye block in the DM*/blk_number = subclass << 8;blk_number |= offset >> 5;if (blk_number == blk_number_prev) {copy_to_dm_buf_big_endian(di, buf, offset,dm_reg->len, dm_reg->data);} else {if (buf_valid)update_dm_block(di, blk_number_prev >> 8,(blk_number_prev << 5) & 0xFF , buf);elsebuf_valid = true;read_dm_block(di, dm_reg->subclass, dm_reg->offset,buf);copy_to_dm_buf_big_endian(di, buf, offset - ((blk_number << 5) & 0xFF),dm_reg->len, dm_reg->data);}blk_number_prev = blk_number;}/* Last buffer to be written */if (buf_valid)update_dm_block(di, subclass, offset, buf);exit_cfg_update_mode(di);
}

bq274xx_dm_regs 为电量计寄存器参数的配置,主要是配合关键重要的信息。 rom_mode_gauge_dm_init 函数中的流程与前面讲的 ** 3.1 Data Memory Parameter Update Example ** 基本一致

		if (blk_number == blk_number_prev) {copy_to_dm_buf_big_endian(di, buf, offset,dm_reg->len, dm_reg->data);} else {if (buf_valid)update_dm_block(di, blk_number_prev >> 8,(blk_number_prev << 5) & 0xFF , buf);elsebuf_valid = true;read_dm_block(di, dm_reg->subclass, dm_reg->offset,buf);copy_to_dm_buf_big_endian(di, buf, offset - ((blk_number << 5) & 0xFF),dm_reg->len, dm_reg->data);}blk_number_prev = blk_number;

这一段代码的意思是如果有相同的subclass 由组成32个字节的块写到电量计中(先读出块的内容,然后再更新要对应的字节)。

对于我们的项目,硬件通过TI 提供的工具,对电池进行了一个轮回的充电放电,导出了电量的配置文件,就是上在提到的类似下方的文件,所以不能用原来的方式。

W: AA 00 01 00
C: AA 00 21 04

结合上述ti工具导出的配置文件,软件修改如下

// 根据ti 的工具导出的学习数据
struct dm_block {u8 subclass;u8 offset;// 块默认长度为32u8 block_data[32];
};
static struct dm_block bq274xx_dm_block[] = {// safety{2, 0, .block_data = {0x02, 0x26, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},// Charge Termination{36, 0, .block_data = {0x00, 0x19, 0x28, 0x63, 0x5F, 0xFF, 0x62, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},// Data{48, 0, .block_data = {0x0E, 0x10, 0xFD, 0xFF, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},// Discharge{49, 0, .block_data = {0x0A, 0x0F, 0x02, 0x05, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},// Registers{64, 0, .block_data = {0x25, 0xF8, 0x0F, 0x48, 0x00, 0x14, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},// Power{68, 0, .block_data = {0x05, 0x00, 0x32, 0x01, 0xC2, 0x14, 0x14, 0x00, 0x03, 0x08, 0x98, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},// IT Cfg{80, 0, .block_data = {0x02, 0xBC, 0x01, 0x2C, 0x00, 0x1E, 0x00, 0xC8, 0xC8, 0x14, 0x08, 0x00, 0x3C, 0x0E, 0x10, 0x00, 0x0A, 0x46, 0x05, 0x14, 0x05, 0x0F, 0x03, 0x20, 0x00, 0x64, 0x46, 0x50, 0x0A, 0x01, 0x90, 0x00}},{80, 32, .block_data = {0x64, 0x19, 0xDC, 0x5C, 0x60, 0x00, 0x7D, 0x00, 0x04, 0x03, 0x19, 0x25, 0x0F, 0x14, 0x0A, 0x78, 0x60, 0x28, 0x01, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x80, 0x04, 0x01, 0x14, 0x00}},{80, 64, .block_data = {0x0B, 0x0B, 0xB8, 0x01, 0x2C, 0x0A, 0x01, 0x0A, 0x00, 0x00, 0x00, 0xC8, 0x00, 0x64, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},// Current Thresholds{81, 0, .block_data = {0x00, 0xA7, 0x00, 0x64, 0x00, 0xFA, 0x00, 0x3C, 0x3C, 0x01, 0xB3, 0xB3, 0x01, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},// State{82, 0, .block_data = {0x43, 0xF8, 0x00, 0x00, 0x00, 0x81, 0x0E, 0xDB, 0x0E, 0xA8, 0x03, 0xE8, 0x0E, 0x74, 0x05, 0x3C, 0x0C, 0x80, 0x00, 0xC8, 0x00, 0x32, 0x00, 0x14, 0x03, 0xE8, 0x01, 0x00, 0x64, 0x10, 0x04, 0x00}},{82, 32, .block_data = {0x0A, 0x10, 0x5E, 0xFF, 0xCD, 0xFF, 0xCD, 0x00, 0x02, 0x02, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},// R_a RAM{89, 0, .block_data = {0x00, 0x66, 0x00, 0x66, 0x00, 0x63, 0x00, 0x6B, 0x00, 0x48, 0x00, 0x3B, 0x00, 0x3E, 0x00, 0x3F, 0x00, 0x35, 0x00, 0x2F, 0x00, 0x3C, 0x00, 0x46, 0x00, 0x8C, 0x01, 0x71, 0x02, 0x4C, 0x00, 0x00}},// Security Class Codes  是默认值{112, 0, .block_data = {0x80, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
};static void dm_init(void)
{int i;int timeout = INITCOMP_TIMEOUT_MS;u8 subclass, offset;int size = ARRAY_SIZE(bq274xx_dm_block);struct dm_block *p_dm_block;while (!rom_mode_gauge_init_completed() && timeout > 0) {msleep(100);timeout -= 100;}if (timeout <= 0) {printk("%s: INITCOMP not set after %d seconds\n",__func__, INITCOMP_TIMEOUT_MS / 100);return;}enter_cfg_update_mode();for (i = 0; i < size; i++) {p_dm_block = &bq274xx_dm_block[i];subclass = p_dm_block->subclass;offset = p_dm_block->offset;update_dm_block(subclass, offset, p_dm_block->block_data);}
#if 0//读出数据for (i = 0; i < size; i++) {u8 data[32]={0};int ret =0;int k=0;p_dm_block = &bq274xx_dm_block[i];subclass = p_dm_block->subclass;offset = p_dm_block->offset;ret = read_dm_block(subclass, offset, data);if(ret) {printk("subclass = %d  , offset = %d\n",subclass,offset);for(k=0;k<32;k++){printk("%02x ",data[k]);}printk("\n");}}
#endif exit_cfg_update_mode();
}

调试时遇到的问题

在测试中发现电量百分比不准,调试发现 Full Charge Capacity 变回了默认值。检测发现写的配置已经到RAM中。Full Charge Capacity 的值是由写入的值再通过BQ27421计算出来的。不能直接修改。检测Data Memory 的值与写入的值一样,表示写入是正常的。

后面经过反复阅读TI bq27421的寄存器手册,对比发现如果CFGUPMODE 为1(表示没有退出配置模式), Full Charge Capacity 的值不会更新。说明退出的配置有问题。

static int exit_cfg_update_mode(void){int i = 0;u16 flags;/*W: AA 00 00 00W: AA 00 42 00*/control_cmd_wr(0);control_cmd_wr(BQ274XX_SOFT_RESET); //退出配置模式while (i < CFG_UPDATE_POLLING_RETRY_LIMIT) {i++;flags = bq27xxx_read(BQ27XXX_REG_FLAGS, false);if (!(flags & (1 << 4)))break;msleep(100);}if (i == CFG_UPDATE_POLLING_RETRY_LIMIT) {printk("%s: failed %04x\n", __func__, flags);return 0;}if (seal())return 1;elsereturn 0;return 1;
}

这里的退出配置模式由原来的 BQ274XX_EXIT_CFGUPDATE 改为BQ274XX_SOFT_RESET 。且为了保证每次都能退出设置模式,不报错,结合官方工具的文件

W: AA 00 00 00
W: AA 00 42 00

再在代码中增加 control_cmd_wr(0);

结论

在使用bq27421时,只进行了简单的配置(电池容量,充放电电压等等)然后让电量计自学习,测试时发现不同的机器电量表现不一样(关机时bq27421是断电的)。后面改为统一写入电量计量产配置文件的方式。所有就有了上面的工作。

最后记录一下i2ctool 工具的使用(用i2ctool 工具想验证写入电量计的数据是否正确,但未能成功,第三步返回error)

//进入UNSEALED模式 ,否则设置 BlockDataControl(): 0x61 无效
./i2cset -f -y 2 0x55 0x0 0x00
./i2cset -f -y 2 0x55 0x01 0x80
./i2cset -f -y 2 0x55 0x0 0x00
./i2cset -f -y 2 0x55 0x01 0x80//查询sealed模式写入情况 ,w 表示读字
./i2cget -f -y 2 0x55 0x0 w//进入 配置更新模式 
./i2cset -f -y 2 0x55 0x0 0x13//BLOCK_DATA_CONTROL
./i2cset -f -y 2 0x55 0x61 0x0 //BLOCK_DATA_CLASS
./i2cset -f -y 2 0x55 0x3e 0x52//BLOCK_DATA
// -r 指定地址范围
./i2cdump -f -y -r 0x40-0x5f 2 0x55 

更多推荐

TI BQ27421电量计驱动的调试

本文发布于:2024-02-27 04:30:14,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1705176.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:电量   TI

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!