LTC2944库仑计(电量计)芯片应用笔记(Arduino,ESP32)_钛酸锂库仑计
0Fh寄存器的最低4位一直都保持是0,因为ADC只有12bit。所以当寄存器读出FFF0h的时候,代表电流测量的正满量程64mV;当寄存器读出值为0000h时,代表电流测量值为负满量程-64mV。那么,假设电流的寄存器读出值为Ic,实际测量值的计算方法就是:计算结果为正的话,表示在充电;为负的话表示在放电。
0Fh寄存器的最低4位一直都保持是0,因为ADC只有12bit。所以当寄存器读出FFF0h的时候,代表电流测量的正满量程64mV;当寄存器读出值为0000h时,代表电流测量值为负满量程-64mV。
那么,假设电流的寄存器读出值为Ic,实际测量值的计算方法就是:
I=(64mV/Rsense)*((Ic-32767)/32767)

计算结果为正的话,表示在充电;为负的话表示在放电。
(3)电量mAH
电量计数器(ACR)也是16bit,每一bit代表的电量取决于M(prescaler)的值和Rsense的值。
公式如下:

通过读取ACR的值,乘以Qlsb即可得出当前读数。
在默认值M=4096和Rsense=50mΩ的情况下,计数器加或减1所代表电量变化0.34mAH。
这个0.34怎么来的我也不知道,手册上就是这么说的。
单位为mAH,要换算成库伦自己算。
(4)温度
温度ADC有效位为11位,最低5位始终保持为0,通过下面公式直接算出实际温度:

单位K,要把结果换算成℃的话自己算。
5.芯片工作模式
(1)shutdown模式
控制寄存器bit0设置为1可以将芯片设置为关闭模式,此时整个芯片模拟部分,包括电量计数器、ADC全部停止工作,但是I2C部分保持在工作,芯片耗电量降到最低约15微安。电量计数器ACR在这种模式下不再变化,无法进行电量计量。
(2)ADC模块的四种模式
控制寄存器bit7和bit6组合ADC成4中工作模式:
11:全自动模式,芯片持续进行ADC转换,约30ms完成一次;
10:扫描模式,每10秒进行一次ADC转换,其余时间休眠;
01:人工模式,人为发命令启动才进行一次ADC转换,然后休眠;
00:休眠模式
ADC转换次数越多,耗电越厉害,全自动模式下耗电约1mA,详见手册。
三、驱动程序
1.驱动源码
芯片了解完成终于要动手编写驱动了。Linear(已被ADI收购)专门为该芯片提供了一份驱动程序,地址:LTC2944 数据手册和产品信息 | 亚德诺(ADI)半导体

但是这个程序是Linduino平台的,这个东东咱也没用过,但是源码还是可以参考下的。
下面我们把链接上的LTC2944.h和LTC2944.cpp下载下来进行改造。
详细的改造过程省略了,对改造过程感兴趣的可以对比下下面代码和原来的代码之间的差异。
主要改造4个函数:
int8_t LTC2944_write(uint8_t i2c_address, uint8_t adc_command, uint8_t code);
int8_t LTC2944_write_16_bits(uint8_t i2c_address, uint8_t adc_command, uint16_t code);
int8_t LTC2944_read(uint8_t i2c_address, uint8_t adc_command, uint8_t *adc_code);
int8_t LTC2944_read_16_bits(uint8_t i2c_address, uint8_t adc_command, uint16_t *adc_code)
这4个函数是对总线的操作,改造内容是把I2C的操作方法改成Wire方法,其他函数基本都是内部计算函数,不用改造。其他的改造内容都是为了方便使用而改造的,包括增加一些操作函数。
直接给出改造后的源码如下。
LTC2944.h
#ifndef LTC2944_h
#define LTC2944_h
#include "Arduino.h"
#include "Wire.h"
#define I2C_ADDRESS_LTC2944 0x64
//#define I2C_ADDR_LTC2944 144
//Registers
#define LTC2944_STATUS_REG 0x00
#define LTC2944_CONTROL_REG 0x01
#define LTC2944_ACCUM_CHARGE_MSB_REG 0x02
#define LTC2944_ACCUM_CHARGE_LSB_REG 0x03
#define LTC2944_CHARGE_THRESH_HIGH_MSB_REG 0x04
#define LTC2944_CHARGE_THRESH_HIGH_LSB_REG 0x05
#define LTC2944_CHARGE_THRESH_LOW_MSB_REG 0x06
#define LTC2944_CHARGE_THRESH_LOW_LSB_REG 0x07
#define LTC2944_VOLTAGE_MSB_REG 0x08
#define LTC2944_VOLTAGE_LSB_REG 0x09
#define LTC2944_VOLTAGE_THRESH_HIGH_MSB_REG 0x0A
#define LTC2944_VOLTAGE_THRESH_HIGH_LSB_REG 0x0B
#define LTC2944_VOLTAGE_THRESH_LOW_MSB_REG 0x0C
#define LTC2944_VOLTAGE_THRESH_LOW_LSB_REG 0x0D
#define LTC2944_CURRENT_MSB_REG 0x0E
#define LTC2944_CURRENT_LSB_REG 0x0F
#define LTC2944_CURRENT_THRESH_HIGH_MSB_REG 0x10
#define LTC2944_CURRENT_THRESH_HIGH_LSB_REG 0x11
#define LTC2944_CURRENT_THRESH_LOW_MSB_REG 0x12
#define LTC2944_CURRENT_THRESH_LOW_LSB_REG 0x13
#define LTC2944_TEMPERATURE_MSB_REG 0x14
#define LTC2944_TEMPERATURE_LSB_REG 0x15
#define LTC2944_TEMPERATURE_THRESH_HIGH_REG 0x16
#define LTC2944_TEMPERATURE_THRESH_LOW_REG 0x17
// Command Codes
#define LTC2944_AUTOMATIC_MODE 0xC0
#define LTC2944_SCAN_MODE 0x80
#define LTC2944_MANUAL_MODE 0x40
#define LTC2944_SLEEP_MODE 0x00
#define LTC2944_PRESCALAR_M_1 0x00
#define LTC2944_PRESCALAR_M_4 0x08
#define LTC2944_PRESCALAR_M_16 0x10
#define LTC2944_PRESCALAR_M_64 0x18
#define LTC2944_PRESCALAR_M_256 0x20
#define LTC2944_PRESCALAR_M_1024 0x28
#define LTC2944_PRESCALAR_M_4096 0x30
#define LTC2944_PRESCALAR_M_4096_2 0x31
#define LTC2944_ALERT_MODE 0x04
#define LTC2944_CHARGE_COMPLETE_MODE 0x02
#define LTC2944_DISABLE_ALCC_PIN 0x00
#define LTC2944_SHUTDOWN_MODE 0x01
#define LTC2944_SHUTDOWN_MODE_UNDO 0x00
class I2CLTC2944
{
public:
I2CLTC2944(TwoWire * ptrI2CBus)
{
_ptrI2CBus=ptrI2CBus;
}
int8_t LTC2944_write(uint8_t i2c_address, uint8_t adc_command, uint8_t code);
int8_t LTC2944_write_16_bits(uint8_t i2c_address, uint8_t adc_command, uint16_t code);
int8_t LTC2944_read(uint8_t i2c_address, uint8_t adc_command, uint8_t *adc_code);
int8_t LTC2944_read_16_bits(uint8_t i2c_address, uint8_t adc_command, uint16_t *adc_code);
float LTC2944_code_to_coulombs(uint16_t adc_code, float resistor, uint16_t prescalar);
float LTC2944_code_to_mAh(uint16_t adc_code, float resistor, uint16_t prescalar );
float LTC2944_code_to_voltage(uint16_t adc_code);
float LTC2944_code_to_current(uint16_t adc_code, float resistor);
float LTC2944_code_to_kelvin_temperature(uint16_t adc_code);
float LTC2944_code_to_celcius_temperature(uint16_t adc_code);
int8_t LTC2944_register_set_clear_bits(uint8_t i2c_address, uint8_t register_address, uint8_t bits_to_set, uint8_t bits_to_clear);
int8_t LTC2944_set_mode(uint8_t i2c_address,uint8_t mode);
int8_t LTC2944_set_prescalar(uint8_t i2c_address,uint16_t prescalarVal);
int8_t LTC2944_set_ALCC(uint8_t i2c_address,uint8_t ALCCMode);
int8_t LTC2944_set_shutdown(uint8_t i2c_address,uint8_t shutdown);
int8_t LTC2944_set_ACR_full(uint8_t i2c_address);
private:
TwoWire * _ptrI2CBus;
};
#endif
LTC2944.cpp
#include "Arduino.h"
#include "LTC2944.h"
//Conversion Constants
const float LTC2944_CHARGE_lsb = 0.34E-3;
const float LTC2944_VOLTAGE_lsb = 1.068E-3;
const float LTC2944_CURRENT_lsb = 29.3E-6;
const float LTC2944_TEMPERATURE_lsb = 0.25;
const float LTC2944_FULLSCALE_VOLTAGE = 70;
const float LTC2944_FULLSCALE_CURRENT = 60E-3;
const float LTC2944_FULLSCALE_TEMPERATURE = 510;
const float resistor = .020; //!< resistor value
int8_t I2CLTC2944::LTC2944_write(uint8_t i2c_address, uint8_t adc_command, uint8_t code)
{
// Write an 8-bit code to the LTC2944.
// The function returns the state of the acknowledge bit after the I2C address write. 0=acknowledge, 1=no acknowledge.
int32_t ack;
//ack = i2c_write_byte_data(i2c_address, adc_command, code);
_ptrI2CBus->beginTransmission(i2c_address);
_ptrI2CBus->write(adc_command);
_ptrI2CBus->write(code);
ack=_ptrI2CBus->endTransmission(true);
return(ack);
}
int8_t I2CLTC2944::LTC2944_write_16_bits(uint8_t i2c_address, uint8_t adc_command, uint16_t code)
{
// Write a 16-bit code to the LTC2944.
// The function returns the state of the acknowledge bit after the I2C address write. 0=acknowledge, 1=no acknowledge.
int8_t ack;
//ack = i2c_write_word_data(i2c_address, adc_command, code);
_ptrI2CBus->beginTransmission(i2c_address);
_ptrI2CBus->write(adc_command);
_ptrI2CBus->write(code);
ack=_ptrI2CBus->endTransmission(true);
return(ack);
}
int8_t I2CLTC2944::LTC2944_read(uint8_t i2c_address, uint8_t adc_command, uint8_t *adc_code)
{
// Reads an 8-bit adc_code from LTC2944
// The function returns the state of the acknowledge bit after the I2C address write. 0=acknowledge, 1=no acknowledge.
int32_t ack;
//ack = i2c_read_byte_data(i2c_address, adc_command, adc_code);
_ptrI2CBus->flush();
_ptrI2CBus->beginTransmission(i2c_address);
_ptrI2CBus->write(adc_command);
ack=_ptrI2CBus->endTransmission(false);
uint8_t bytesReceived=0;
bytesReceived=_ptrI2CBus->requestFrom(i2c_address,1);
if(bytesReceived==1)
{
*adc_code=_ptrI2CBus->read();
}
else
{
//Serial.println("LTC2944_read reply error!");
}
return(ack);
}
int8_t I2CLTC2944::LTC2944_read_16_bits(uint8_t i2c_address, uint8_t adc_command, uint16_t *adc_code)
{
// Reads a 16-bit adc_code from LTC2944
// The function returns the state of the acknowledge bit after the I2C address write. 0=acknowledge, 1=no acknowledge.
int32_t ack;
//ack = i2c_read_word_data(i2c_address, adc_command, adc_code);
_ptrI2CBus->flush();
_ptrI2CBus->beginTransmission(i2c_address);
_ptrI2CBus->write(adc_command);
ack=_ptrI2CBus->endTransmission(false);
uint8_t bytesReceived=0;
uint8_t temp[2];
bytesReceived=_ptrI2CBus->requestFrom(i2c_address,2);
if(bytesReceived==2)
{
temp[0]=_ptrI2CBus->read();
//Serial.print("MSB=");Serial.println(temp[0]);
temp[1]=_ptrI2CBus->read();
//Serial.print("LSB=");Serial.println(temp[1]);
//_ptrI2CBus->readBytes(temp, bytesReceived);
*adc_code=temp[0]*256+temp[1];
}
else
{
//Serial.print("read NOT 2 bytes! recv bytes=");Serial.println(bytesReceived);
}
return(ack);
}
float I2CLTC2944::LTC2944_code_to_coulombs(uint16_t adc_code, float resistor, uint16_t prescalar)
{
// The function converts the 16-bit RAW adc_code to Coulombs
float coulomb_charge;
coulomb_charge = 1000*(float)(adc_code*LTC2944_CHARGE_lsb*prescalar*50E-3)/(resistor*4096);
coulomb_charge = coulomb_charge*3.6f;
return(coulomb_charge);
}
float I2CLTC2944::LTC2944_code_to_mAh(uint16_t adc_code, float resistor, uint16_t prescalar )
{
// The function converts the 16-bit RAW adc_code to mAh
float mAh_charge;
mAh_charge = 1000*(float)(adc_code*LTC2944_CHARGE_lsb*prescalar*50E-3)/(resistor*4096);
return(mAh_charge);
}
float I2CLTC2944::LTC2944_code_to_voltage(uint16_t adc_code)
{
// The function converts the 16-bit RAW adc_code to Volts
float voltage;
voltage = ((float)adc_code/(65535))*LTC2944_FULLSCALE_VOLTAGE;
return(voltage);
}
float I2CLTC2944::LTC2944_code_to_current(uint16_t adc_code, float resistor)
{
// The function converts the 16-bit RAW adc_code to Amperes
float current;
current = (((float)adc_code-32767)/(32767))*((float)(LTC2944_FULLSCALE_CURRENT)/resistor);
return(current);
}
float I2CLTC2944::LTC2944_code_to_kelvin_temperature(uint16_t adc_code)
{
// The function converts the 16-bit RAW adc_code to Kelvin
float temperature;
temperature = adc_code*((float)(LTC2944_FULLSCALE_TEMPERATURE)/65535);
return(temperature);
}
float I2CLTC2944::LTC2944_code_to_celcius_temperature(uint16_t adc_code)
{
// The function converts the 16-bit RAW adc_code to Celcius
float temperature;
temperature = adc_code*((float)(LTC2944_FULLSCALE_TEMPERATURE)/65535) - 273.15;
return(temperature);
}
int8_t I2CLTC2944::LTC2944_register_set_clear_bits(uint8_t i2c_address, uint8_t register_address, uint8_t bits_to_set, uint8_t bits_to_clear)
{
// Used to set and clear bits in a control register. bits_to_set will be bitwise OR'd with the register.
// bits_to_clear will be inverted and bitwise AND'd with the register so that every location with a 1 will result in a 0 in the register.
uint8_t register_data;
int8_t ack = 0;
ack |= LTC2944_read(i2c_address, register_address, ®ister_data);
register_data = register_data & (~bits_to_clear);
register_data = register_data | bits_to_set;
ack |= LTC2944_write(i2c_address, register_address, register_data);
return(ack);
}
int8_t I2CLTC2944::LTC2944_set_mode(uint8_t i2c_address,uint8_t mode)
{
//"mode" must be one of the defined symbols:
// LTC2944_AUTOMATIC_MODE,LTC2944_SCAN_MODE,LTC2944_MANUAL_MODE,LTC2944_SLEEP_MODE
uint8_t curCode;
uint8_t setCode;
int8_t ack = 0;
ack |= LTC2944_read(i2c_address, LTC2944_CONTROL_REG, &curCode);
setCode = mode | (curCode & B00111111);//B[7:6]
ack |= LTC2944_write(i2c_address, LTC2944_CONTROL_REG, setCode);
return(ack);
}
int8_t I2CLTC2944::LTC2944_set_prescalar(uint8_t i2c_address,uint16_t prescalarVal)
{
//"prescalarVal" must be one of the defined symbols:
// LTC2944_PRESCALAR_M_1,LTC2944_PRESCALAR_M_4,LTC2944_PRESCALAR_M_16
// LTC2944_PRESCALAR_M_64,LTC2944_PRESCALAR_M_256,LTC2944_PRESCALAR_M_1024
// LTC2944_PRESCALAR_M_4096
uint8_t curCode;
uint8_t setCode;
int8_t ack = 0;
LTC2944_read(i2c_address,LTC2944_CONTROL_REG,&curCode);
setCode = prescalarVal | (curCode & B11000111);//B[5:3]
ack |= LTC2944_write(i2c_address, LTC2944_CONTROL_REG, setCode);
return(ack);
}
int8_t I2CLTC2944::LTC2944_set_ALCC(uint8_t i2c_address,uint8_t ALCCMode)
{
//"ALCCMode" must be one of the defined symbols:
// LTC2944_ALERT_MODE,LTC2944_CHARGE_COMPLETE_MODE,LTC2944_DISABLE_ALCC_PIN,LTC2944_SHUTDOWN_MODE
uint8_t curCode;
uint8_t setCode;
int8_t ack = 0;
ack |= LTC2944_read(i2c_address, LTC2944_CONTROL_REG, &curCode);
setCode = ALCCMode | (curCode & B11111001);//B[2:1]
ack |= LTC2944_write(i2c_address, LTC2944_CONTROL_REG, setCode);
return(ack);
}
int8_t I2CLTC2944::LTC2944_set_shutdown(uint8_t i2c_address,uint8_t shutdown)
{
//"ALCCMode" must be one of the defined symbols:
// LTC2944_SHUTDOWN_MODE,LTC2944_SHUTDOWN_MODE_UNDO
uint8_t curCode;
uint8_t setCode;
int8_t ack = 0;
ack |= LTC2944_read(i2c_address, LTC2944_CONTROL_REG, &curCode);
setCode = shutdown | (curCode & B11111110);//B[0]
ack |= LTC2944_write(i2c_address, LTC2944_CONTROL_REG, setCode);
return(ack);
}
int8_t I2CLTC2944::LTC2944_set_ACR_full(uint8_t i2c_address)
{
//set ACR to 0xFFFF
uint8_t curCode;
uint8_t setCode;
int8_t ack = 0;
ack |= LTC2944_write(i2c_address, LTC2944_ACCUM_CHARGE_MSB_REG, 0xff);
ack |= LTC2944_write(i2c_address, LTC2944_ACCUM_CHARGE_LSB_REG, 0xff);
return(ack);
}
2.调用方法及测试代码
编写测试代码如下。注意我的Rsense=20mΩ,M的值保持为默认值4096我没有修改它。
#include "Wire.h"
#include "LTC2944.h"
I2CLTC2944 objI2CLTC2944(&Wire1);
const float resistor = .020; //我的Rsense是20毫欧的
void setup() {
Serial.begin(115200);
pinMode(14,OUTPUT);
digitalWrite(14,HIGH);//打开外围设备供电,用不到就删掉
uint8_t data;
## 最后
**自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**
**深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。**
**因此收集整理了一份《2024年嵌入式&物联网开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**







**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!**
[**如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!**](https://bbs.csdn.net/topics/618654289)
**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**!!
存中...(img-zakGwtlR-1715587520429)]
[外链图片转存中...(img-NqzQ4zNJ-1715587520429)]
[外链图片转存中...(img-SJVMgf8c-1715587520430)]
[外链图片转存中...(img-9LEWiIER-1715587520431)]
[外链图片转存中...(img-UKLhsEvS-1715587520431)]
[外链图片转存中...(img-h5oYhSzu-1715587520432)]
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!**
[**如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!**](https://bbs.csdn.net/topics/618654289)
**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**!!
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)