首页 / 快讯大厅

电池电量检测方法-库仑计-基于LTC2941

2025-11-30 05:17:31快讯大厅 8078

1. 电池电量格数的定义:

一般手机设计待机电量时, 比如有**4格5档(4-3-2-1-0)**的电量指示。

由于要考虑到电池使用一段时间(比如1年)后, 其放电平台会降低,上述的比例肯定会失调。

所以需要考虑一点点这方面的余量.以新电池的60%-40%-20%-5%这样的比例进行设计。

4.20V~3.90V满格

3.90V~3.80V三格

3.80V~3.72V两格

3.72V~3.65V一格

3.65以下,低电压告警。

虽然在使用新电池时, 第一格会待机比较长的时间, 最后一格待机比较短。但是当这个电池使用个一年半载后, 其容量分布就比较接近均分的情况。

2 .库伦计

2.1基本内容

mAh:电池容量的计量单位;

库仑的国际标准单位为电流乘于时间的安培秒. 1mAh=0.001安培*3600秒=3.6安培秒=3.6库仑;

库仑计的芯片,即电量计量芯片是装在手机电池里面的,通常是与锂离子电池的保护线路设计在一起。

2.2库伦计工作原理

基本原理:芯片上集成了一个取样电阻,当流过不同电流后产生不同的压差,芯片就对这个电压(实际转换为电流)和时间进行积分,得到用户使用时的正确电量(注意,电量的单位是mAh),该芯片通过实时积分得到容量以后,把容量(单位是mAh)数据存储在芯片的EEPROM中,并根据手机的需求,通过通讯线传递给手机.那么手机就得到了这块电池的准确容量。

在库仑计芯片的存储器里面通常有如下的基本电池信息

▲电池的初始容量(mAh),即额定容量,一个电池完全充放后得到的容量

▲电池的当前容量(mAh),处于使用状态是的电池容量

▲当前流经的电流(mA),即手机的电流损耗

例子:一块额定容量为600mAh的电池,如果当前电池容量只有456mAh,手机就显示

456/600=76%;

电池额定容量调整

1、电池循环充电次数(电池使用率)会影响到电池的额定容量

如:比如初始容量是600mAh的电池,在100次循环以后,其实际容量已经变成500mAh了,如果在进行容量显示是仍然按照600mAh这个数来计算的话,势必造成电池永远充不饱这个问题.。

解决措施:库仑计的里面还有一个容量对使用次数调整的算法,会根据电池循环次数调整其实际容量。

2、电池使用温度会影响到电池的可用容量

如:比如在25度时,电池可以得到几乎100%的可用容量,而在0度是,电池只能放出80%的可用容量。

解决措施:库仑计里面还有一个容量对温度调整的算法,会根据电池的实际温度进行可用容量调整;

请您定期的对你所用的电池进行一次完全的充分,以便库仑计进行容量调整和归一化的进行.确保库仑计一直工作在最佳状态。

基于LTC2941芯片进行库仑计法检测电池电量

对于这个将不会做过多的说明;一切的说明都不如直接看芯片手册;总的来说就是使用IIC通信进行读写芯片中相应的寄存器;

这里只做简要说明,具体看后面上传的中英文芯片数据手册;

1)、LTC2941芯片寄存器

A:状态寄存器;

B:控制寄存器;

C:累积电荷寄存器(高八位);

D:累积电荷寄存器(低八位);

E:充电阈值高MSB;

F:充电阈值高LSB;

G:充电阈值低MSB;

H:充电阈值低LSB;

(寄存器的描述请看手册)

2)、电池容量计算公式

QBAT = qLSB(C*256 + D)

Qlsb:累积电荷(寄存器C,D)的最低有效位(qLSB);

Rsense:外部检测电阻值;

M:可编程的预分频器(1-128);

Imax:应用的最大电流;

请注意,当预分频器设置为其默认值M =128时,1mAh = 3.6A•s=3.6C(库仑)。

3)、IIC驱动程序;

//系统时钟为24M时

//count = 200时,时间约等于100us

void delayNop(u32 count)

{

while(count -- > 0)

{

__NOP();

}

}

注意:经过测试,该IIC通讯读写有问题;建议自己修改,但是下面的LTC2941的时序没有问题;

/*****************************IIC通信协议***************************/

#define _IIC1 1

#define _IIC2 0

#define IIC1_SCL PB6

#define IIC1_SDA PB7

#define IIC2_SCL PB10

#define IIC2_SDA PB11

#if _IIC1

#define SDA_IN() {GPIOB->CRL &= 0X0FFFFFFF; GPIOB->CRL |= (u32)8<<28;}

#define SDA_OUT() {GPIOB->CRL &= 0X0FFFFFFF; GPIOB->CRL |= (u32)3<<28;}

#endif

#if _IIC2

#define SDA_IN() {GPIOB->CRH &= 0XFFFF0FFF; GPIOB->CRH |= (u32)8<<12;}

#define SDA_OUT() {GPIOB->CRH &= 0XFFFF0FFF; GPIOB->CRH |= (u32)3<<12;}

#endif

#define IIC1_SCL_High writeGpioHigh(IIC1_SCL)

#define IIC1_SCL_Low writeGpioLow(IIC1_SCL)

#define IIC1_SDA_High writeGpioHigh(IIC1_SDA)

#define IIC1_SDA_Low writeGpioLow(IIC1_SDA)

#define IIC2_SCL_High writeGpioHigh(IIC2_SCL)

#define IIC2_SCL_Low writeGpioLow(IIC2_SCL)

#define IIC2_SDA_High writeGpioHigh(IIC2_SDA)

#define IIC2_SDA_Low writeGpioLow(IIC2_SDA)

void iicStart(void)

{

SDA_OUT(); //sda线输出

IIC1_SCL_High;

IIC1_SDA_High;

delayNop(2);

IIC1_SDA_Low;

delayNop(2);

IIC1_SCL_Low; //钳住I2C总线,准备发送或接收数据

}

void iicStop(void)

{

SDA_OUT(); //sda线输出

IIC1_SDA_Low;

IIC1_SCL_Low;

delayNop(2);

IIC1_SCL_High;

IIC1_SDA_High;

delayNop(2);

}

u8 iicWaitAck(void)

{

u8 errorAck = 0;

SDA_IN();

IIC1_SCL_High;

delayNop(2);

IIC1_SDA_High;

delayNop(2);

while( GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_7) == Bit_SET )

{

errorAck++;

if(errorAck > 250)

{

iicStop();

return 0;

}

}

IIC1_SCL_Low;

return 1;

}

//产生应答

void iicACK(void)

{

IIC1_SCL_Low;

SDA_OUT();

IIC1_SDA_Low;

delayNop(2);

IIC1_SCL_High;

delayNop(2);

IIC1_SCL_Low;

}

void iicNOack(void)

{

IIC1_SCL_Low;

SDA_OUT();

IIC1_SDA_High;

delayNop(2);

IIC1_SCL_High;

delayNop(2);

IIC1_SCL_Low;

}

void iicWrite_Byte(u8 byte)

{

u8 i;

SDA_OUT();

IIC1_SCL_Low;

for(i = 0; i < 8; i++)

{

if( (byte&0x80) == 0x80 )

{

IIC1_SDA_High;

}

else

{

IIC1_SDA_Low;

}

byte <<= 1;

delayNop(2);

IIC1_SCL_High;

delayNop(2);

IIC1_SCL_Low;

delayNop(2);

}

}

//读1个字节(高位开始读),ack=1时,发送ACK,ack=0,发送nACK

//在C语言中x<<=1等于x=x<<1,是把x左移1位以后值保存回x里,x发生变化了

u8 iicRead_Byte(unsigned char ack)

{

u8 i;

u8 recvByte = 0;

SDA_IN();

for(i = 0; i < 8; i++)

{

IIC1_SCL_Low;

delayNop(2);

recvByte <<= 1;//高位开始读,所以左移

if( GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_7) == Bit_SET )

{

recvByte |= 0x01;

}

delayNop(2);

}

if(ack)

{

iicACK();

}

else

{

iicNOack();

}

return recvByte;

}

/***************************** LTC2941 ***************************/

#define ADDR_DeviceWrite 0xC8

#define ADDR_DeviceRead 0xC9

//A:状态寄存器

#define ADDR_A 0x00

//B:控制寄存器

#define ADDR_B 0x01

//CD:累积电荷寄存器

#define ADDR_C 0x02

#define ADDR_D 0x03

//EF:充电阀值高

#define ADDR_E 0x04

#define ADDR_F 0x05

//GH:充电阀值低

#define ADDR_G 0x06

#define ADDR_H 0x07

//对寄存器B进行写0xFC操作;

//警报电压:3v(11)

//预分频:128(111)

//充电完成模式(10);

//关掉模拟;(0)

void LTC2941_writeB(u8 data)

{

iicStart();

iicWrite_Byte(ADDR_DeviceWrite);//11001000

delayNop(5);

if(iicWaitAck() == 0 )

{

iicStop();

return;

}

iicWrite_Byte(ADDR_B);//o1h

delayNop(5);

if(iicWaitAck() == 0 )

{

iicStop();

return;

}

iicWrite_Byte(data);

delayNop(5);

if(iicWaitAck() == 0 )

{

iicStop();

return;

}

iicStop();

}

//对寄存器CD EF GF 进行写操作;

//CD默认:7FFF; 充电完成后:FFFF

//EF:电池满电量提醒:4200mv

//GH:电池低电量提醒:3600mv

void LTC2941_writeTwoByte(u8 addrRegister, u8 Qmsb, u8 Qlsb)

{

iicStart();

iicWrite_Byte(ADDR_DeviceWrite);//11001000

delayNop(3);

if(iicWaitAck() == 0 )

{

iicStop();

return;

}

iicWrite_Byte(addrRegister);//o2h / 04h / 06h

delayNop(3);

if(iicWaitAck() == 0 )

{

iicStop();

return;

}

iicWrite_Byte(Qmsb);

delayNop(3);

if(iicWaitAck() == 0 )

{

iicStop();

return;

}

iicWrite_Byte(Qlsb);

delayNop(5);

if(iicWaitAck() == 0 )

{

iicStop();

return;

}

iicStop();

}

//A[7]:芯片识别; 1 : LTC2941; 0 : LTC2942;

//A[5]:指示累积电荷的值达到顶部或底部。

//A[3]:指示累积电荷值超过了电荷阈值上限。

//A[2]:指示累积电荷值下降到电量阈值下限以下。

//A[1]:VBAT警报指示电池电压(SENSE-)下降低于选定的VBAT阈值。

//A[0]:

u8 LTC2941_readA(void)

{

u8 receByte = 0;

iicStart();

iicWrite_Byte(ADDR_DeviceWrite);//11001000

delayNop(5);

if(iicWaitAck() == 0 )

{

iicStop();

return 0;

}

iicWrite_Byte(ADDR_A);//o0h

delayNop(5);

if(iicWaitAck() == 0 )

{

iicStop();

return 0;

}

iicStart();

iicWrite_Byte(ADDR_DeviceRead);//11001001

delayNop(5);

if(iicWaitAck() == 0 )

{

iicStop();

return 0;

}

receByte = iicRead_Byte(0);

iicStop();

return receByte;

}

u16 LTC2941_readTwoByte(u8 addrRegister)

{

u16 receTwoByte = 0;

u16 Qmsb = 0;

u8 Qlsb = 0;

iicStart();

iicWrite_Byte(ADDR_DeviceWrite);//11001000

delayNop(3);

if(iicWaitAck() == 0 )

{

iicStop();

return 0;

}

iicWrite_Byte(addrRegister);//o2h / 04h / 06h

delayNop(3);

if(iicWaitAck() == 0 )

{

iicStop();

return 0;

}

iicStart();

iicWrite_Byte(ADDR_DeviceRead);//11001001

delayNop(3);

if(iicWaitAck() == 0 )

{

iicStop();

return 0;

}

Qmsb = iicRead_Byte(1);

Qlsb= iicRead_Byte(0);

iicStop();

receTwoByte = (Qmsb<<8) | Qlsb;

return receTwoByte;

}

float Qlsb = 0;

float QBAT = 0;

float Rsense = 50;

float M = 128;

void getbatteryCapacity(void)

{

u16 recvData = 0;

u8 C = 0;

u8 D = 0;

recvData = LTC2941_readTwoByte(ADDR_C);

C = recvData >> 8;

D = recvData&0x00ff;

Qlsb = 0.085*(50/Rsense)*(M/128);

QBAT = Qlsb*recvData;

//得到电荷量后,进行电量显示

}

//电池百分比: 当前电量值除以总额度电量值;

u8 getbatteryPercent(void)

{

u16 currentBAT = 0;//当前电量值

u16 rateBAT = 0;//额定电量值

currentBAT = LTC2941_readTwoByte(ADDR_C);

rateBAT = LTC2941_readTwoByte(ADDR_E);

return (u8)currentBAT/rateBAT;

}

void batteryWarn(void)

{

u8 receA = 0;

u8 batteryHigh;

u8 batteryLow;

u8 batteryVeryLow;

receA = LTC2941_readA();

batteryVeryLow = (receA&0x02);

batteryLow = (receA&0x04);

batteryHigh = (receA&0x08);

if(batteryLow)

{

//低电量提醒

}

if(batteryHigh)

{

//电池满电状态

}

if(batteryVeryLow)

{

//警报指示电池电压(SENSE-)下降低于选定的VBAT阈值。

}

}