LTC2944库仑计(电量计)芯片应用笔记(Arduino,ESP32)

news2025/1/12 5:00:01

一、一些基础知识

1.蓄电池的容量单位

(1)毫安时mAH

蓄电池的容量一般会采用毫安时(mAH)为单位,比如2000mAH的蓄电池意思是该蓄电池理论上可以以2000mA的电流持续放电1小时,2000mA*1H=2000mAH。当然这个是理论上的,实际上蓄电池容量受到自身寿命状态、外部温度等环境因素影响,标称2000mAH的电池往往不能放出那么多容量的。严格说来,mAH并不是电能量单位,只是个容量单位。

(2)瓦时WH

我们知道,电能量有个单位叫做瓦时(WH)或千瓦时(度)(kWH)。为什么不用瓦时来表示蓄电池容量呢?其实也不是不用,而只是习惯问题,比如现在电动汽车就用度来表示蓄电池容量了。

在小型蓄电池领域,比如18650电池,用度来做单位有点太大了。假设电池电压4V,放电电流1000mA(1A),放一个小时放出的能量才4V*1A*1H=4WH。

此外,蓄电池放电过程中,电压不是恒定不变的,而是一直在变化的,那么就需要用持续积分的方式来计算蓄电池实际放出能量了,操作起来比较麻烦。而放电过程中,放电电流是可以通过放电负载调节来做到恒流放电的。所以mAH听起来是比较直观的。

综合以上种种因素,小型电池领域,我们通常用mAH来做容量单位。

(3)库伦

此外还有个单位库伦C,这个单位是电荷单位,定义是1安培电流持续1秒所流过的电荷量为1库伦,即1C=1A*1s。其和mAH之间的关系就是1mAH=0.001A*3600s=3.6C

二、LTC2944应用知识点

1.LTC2944简单介绍

这个在网上能搜索到一些,累赘再简单介绍,详细介绍可以自行网上查找。

• 可测量累积的电池充电和放电电量
• 3.6V 至 60V 工作范围可适合多节电池
• 14 位 ADC 负责测量电池电压、电流和温度
• 1% 电压、电流和充电准确度
• ±50mV 检测电压范围
• 高压侧检测
• I2C 接口 / SMBus接口
• 适合任何电池化学组成和容量的通用测量
• 可配置警报输出 / 充电完成输入
• 静态电流小于 150μA
• 小外形 8 引线 3mm x 3mm DFN 封装

顺便提一句,这个芯片比较贵,国产有矽力杰的SA59202做Pin-to-Pin的代替,但是很不好买,缺货,也不知道性能差别有多大。

2.芯片结构

LTC2944内部包含3个主要部分:电量计数器、ADC、I2C通信模块

(1)电量计数器:比较特别,它是独立于电压、电流、温度传感器的,其工作也是独立的。

因此,可以通过控制寄存器,让ADC停止工作(sleep),而电量计数器在ADC停止后是不会受到影响的,依然会继续计数。也就是说只要芯片通电没有shutdown(该模式后面会讲到),电量计数器就会一直运行。

(2)ADC:ADC可以对电压、电流、温度三个量进行采集,通过ADC前端MUX切换输入源。

(3)I2C通信模块:提供通信功能,对寄存器读写进而控制芯片。

3.内部寄存器表

00h:状态寄存器,当发生告警时该寄存器相应位置位;

01h:控制寄存器,控制芯片的工作模式、预定标值、ALCC引脚功能控制;

02h-17h:电量、电压、电流、温度的测量值和累积值,以及告警门限设置。

3.Rsense大小的选择

该芯片的SENSE+和SENSE-引脚之间的最大电压限制在±50mV内,因此你的应用场景下蓄电池的充电和放电最大电流下,在Rsense上产生的压降不要超过这个50mV,否则芯片精度受影响,超过300mV甚至会停止工作(不知道会不会坏掉,手册没说)。

比如2A的最大充电或放电电流,应该Rsense≤(50mV/2A)=25mΩ。

4.寄存器值与实际值之间的计算方法

(1)电压V

电压ADC有效位为16bit,寄存器地址为08h和09h,满量程70.8V,因此Vlsb=70.8/65535。

如果寄存器读出值为B01Ch(十进制45084),那么测量值实际为:

V=45084*70.8/65535≈48.705V

当然这个测量值是在芯片的SENSE-引脚测量到的。

(2)电流I

电流ADC有效位为12bit,寄存器地址为0Eh和0Fh。电流分正负,取决于电池是在充电还是放电状态。

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, &register_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;
  uint16_t data16;
  float voltage;
  int8_t reply;

  Wire1.begin(41,42);//改成你自己的I2C总线引脚
  delay(1000);

  objI2CLTC2944.LTC2944_set_shutdown(I2C_ADDRESS_LTC2944,LTC2944_SHUTDOWN_MODE_UNDO);//undoshutdown,使芯片工作
  objI2CLTC2944.LTC2944_set_mode(I2C_ADDRESS_LTC2944,LTC2944_AUTOMATIC_MODE);//设置为自动模式
  reply=objI2CLTC2944.LTC2944_read(I2C_ADDRESS_LTC2944,LTC2944_CONTROL_REG,&data);
  Serial.printf("LTC2944 read data= 0x%02X\n", data);
  delay(1000);
  objI2CLTC2944.LTC2944_set_mode(I2C_ADDRESS_LTC2944,LTC2944_SCAN_MODE);
  reply=objI2CLTC2944.LTC2944_read(I2C_ADDRESS_LTC2944,LTC2944_CONTROL_REG,&data);
  Serial.printf("LTC2944 read data= 0x%02X\n", data);
  delay(1000);
  objI2CLTC2944.LTC2944_set_mode(I2C_ADDRESS_LTC2944,LTC2944_MANUAL_MODE);
  reply=objI2CLTC2944.LTC2944_read(I2C_ADDRESS_LTC2944,LTC2944_CONTROL_REG,&data);
  Serial.printf("LTC2944 read data= 0x%02X\n", data);
  delay(1000);
  objI2CLTC2944.LTC2944_set_mode(I2C_ADDRESS_LTC2944,LTC2944_SLEEP_MODE);
  reply=objI2CLTC2944.LTC2944_read(I2C_ADDRESS_LTC2944,LTC2944_CONTROL_REG,&data);
  Serial.printf("LTC2944 read data= 0x%02X\n", data);
  delay(1000);  

  objI2CLTC2944.LTC2944_set_mode(I2C_ADDRESS_LTC2944,LTC2944_AUTOMATIC_MODE);
  reply=objI2CLTC2944.LTC2944_read(I2C_ADDRESS_LTC2944,LTC2944_CONTROL_REG,&data);
  Serial.printf("LTC2944 read data= 0x%02X\n", data);
  delay(1000);

  objI2CLTC2944.LTC2944_set_mode(I2C_ADDRESS_LTC2944,LTC2944_AUTOMATIC_MODE);
  reply=objI2CLTC2944.LTC2944_read(I2C_ADDRESS_LTC2944,LTC2944_CONTROL_REG,&data);
  Serial.printf("LTC2944 read data= 0x%02X\n", data);
  delay(1000);

  //objI2CLTC2944.LTC2944_set_ACR_full(I2C_ADDRESS_LTC2944);电池充满状态下认为设置ACR为0xFFFF,便于实际计算电池电量
}

void loop() {
  byte error, address;
  int nDevices = 0;
  uint8_t data;
  uint16_t data16;
  float voltage;
  float current;
  int8_t reply;



  objI2CLTC2944.LTC2944_read_16_bits(I2C_ADDRESS_LTC2944,LTC2944_VOLTAGE_MSB_REG,&data16);
  Serial.printf("LTC2944 read data= 0x%04X\n", data16);
  voltage=objI2CLTC2944.LTC2944_code_to_voltage(data16);
  Serial.print("read voltage=");Serial.println(voltage);
  delay(1000);  
  objI2CLTC2944.LTC2944_read_16_bits(I2C_ADDRESS_LTC2944,LTC2944_CURRENT_MSB_REG,&data16);
  Serial.printf("LTC2944 read data= 0x%04X\n", data16);
  current=objI2CLTC2944.LTC2944_code_to_current(data16,resistor)*1000;
  Serial.print("read current=");Serial.print(current);Serial.println("mA");
  delay(1000);    
/*
  //扫描I2C总线看芯片在总线上不
  Serial.println("Scanning for I2C devices ...");
  for(address = 0x01; address < 0x7f; address++){
    Wire1.beginTransmission(address);
    error = Wire1.endTransmission();
    if (error == 0){
      Serial.printf("I2C device found at address 0x%02X\n", address);
      nDevices++;
    } else if(error != 2){
      Serial.printf("Error %d at address 0x%02X\n", error, address);
    }
  }
  if (nDevices == 0){
    Serial.println("No I2C devices found");
  }
  */
}

下载后CPU从串口持续输出电流和电压测量值。

3.电路部分

LTC2944的电路比较简单,把充电部分的电路也一并贴上。左边是充电模块,右边是LTC2944

PCB效果图,C13左边就是LTC2944

四、补充的东西

1.电量容量百分比的计算

这个芯片是与有些电量计芯片相比,是不内置电池模型的,因此它并不知道你电池的实际容量,只是简单的对电量进行加减计数。所以要得出电池容量百分比,需要你根据使用情况自己建立电池模型,比如在电池充满的情况下把ACR置0xFFFF,然后待电池放空时读出ACR的值,用0xFFFF减去ACR读出值来计算出你电池的实际容量。然后在后面的应用过程中就可以根据当时ACR的读出值除以电池容量来计算当前电量百分比。

然而,电池容量会随着电池寿命、环境等因素改变的,所以你也需要根据这些情况来不断修正电池总容量,以便得出更准确的百分比。

2.ACR值的保存

LTC2944并不内置flash,ACR的值在芯片通电情况下才能得以保存。因此如果芯片断电(如电池没电了),再恢复供电的情况下,ACR会复位为0x7FFF,并重新计数。所以电池模型的建立需要你自己再额外找断电不丢数据的地方(比如ESP32的flash)来保存电池容量等关键数据。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1401687.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STM32CubeMX配置定时器输入捕获功能

STM32CubeMX配置定时器输入捕获功能 0.前言一、方法简介二、STM32CubeMX配置1.生成PWM信号2.配置TIM3_CH1进行采样3.占空比计算 三、总结 参考文章&#xff1a;CubeMX系列教程——11 定时器输入捕获 0.前言 最近在学习江科大STM32教程的原理部分时&#xff0c;发现该教程中使用…

1 - 搭建Redis数据库服务器|LNP+Redis

搭建Redis数据库服务器&#xff5c;LNPRedis 搭建Redis数据库服务器相关概念Redis介绍安装RedisRedis服务常用管理命令命令set 、 mset 、 get 、 mget命令keys 、 type 、 exists 、 del命令ttl 、 expire 、 move 、 flushdb 、flushall 、save、shutdown 配置文件解析 LNP …

AlmaLinux 9.3 安装图解

风险告知 本人及本篇博文不为任何人及任何行为的任何风险承担责任&#xff0c;图解仅供参考&#xff0c;请悉知&#xff01;本次安装图解是在一个全新的演示环境下进行的&#xff0c;演示环境中没有任何有价值的数据&#xff0c;但这并不代表摆在你面前的环境也是如此。生产环境…

深度学习记录--Momentum gradient descent

Momentum gradient descent 正常的梯度下降无法使用更大的学习率&#xff0c;因为学习率过大可能导致偏离函数范围&#xff0c;这种上下波动导致学习率无法得到提高&#xff0c;速度因此减慢(下图蓝色曲线) 为了减小波动&#xff0c;同时加快速率&#xff0c;可以使用momentum…

R语言学习case5:NC基于R语言的UpSetR

step1: 安装库 install.packages("UpSetR")step2:导入包 library(UpSetR)step3&#xff1a;读取数据 otu_RA <- read.delim(./otu_RA.txt, header TRUE, row.names 1, sep \t)read.delim(): 这是R语言中的一个函数&#xff0c;用于读取文本文件&#xff0c;…

国产操作系统:VirtualBox安装openKylin-1.0.1虚拟机并配置网络

国产操作系统&#xff1a;VirtualBox安装openKylin-1.0.1虚拟机并配置网络 openKylin 操作系统目前适配支持X86、ARM、RISC-V三个架构的个人电脑、平板电脑及教育开发板&#xff0c;可以满足绝大多数个人用户及开发者的使用需求。适用于在VirtualBox平台上安装openKylin-1.0.1…

Matlab/simulink风储调频,多台飞轮储能调频,风电场调频,飞轮储能带有虚拟惯量和下垂控制,三机九节点系统一次调频,离散模型

上述为不同飞轮储能容量配比&#xff0c;风电场容量配比&#xff0c;以及有无附加频率控制的飞轮储能出力分析。 飞轮储能驱动电机为永磁同步机电机PMSG 有无飞轮储能容量较小&#xff0c;所以对频率的改善效果有限&#xff0c;不过可以继续增大容量&#xff0c;从而增大频率的…

git clone超时

本文介绍作者在Centos上链接github超时&#xff0c;无法克隆的解决方案 在出现上图所示问题时&#xff0c;有可能是连接不到github.com&#xff0c;读者可以尝试输入ping github.com&#xff0c;当输入该指令后若长时间没有反应说明可能由于本地DNS无法解析导致的。 解决方案…

力扣hot100 反转链表 指针 递归 一题多解

Problem: 206. 反转链表 文章目录 思路&#x1f496; 迭代 双指针&#x1f496; 递归 思路 &#x1f468;‍&#x1f3eb; 大佬题解 &#x1f496; 迭代 双指针 ⏰ 时间复杂度: O ( n ) O(n) O(n) &#x1f30e; 空间复杂度: O ( 1 ) O(1) O(1) /*** Definition for …

[设计模式Java实现附plantuml源码~创建型] 多态工厂的实现——工厂方法模式

前言&#xff1a; 为什么之前写过Golang 版的设计模式&#xff0c;还在重新写Java 版&#xff1f; 答&#xff1a;因为对于我而言&#xff0c;当然也希望对正在学习的大伙有帮助。Java作为一门纯面向对象的语言&#xff0c;更适合用于学习设计模式。 为什么类图要附上uml 因为很…

机械设计-哈工大课程学习-螺纹连接

圆柱螺纹主要几何参数螺纹参数 ①外径&#xff08;大径&#xff09;&#xff0c;与外螺纹牙顶或内螺纹牙底相重合的假想圆柱体直径。螺纹的公称直径即大径。 ②内径&#xff08;小径&#xff09;&#xff0c;与外螺纹牙底或内螺纹牙顶相重合的假想圆柱体直径。 ③中径&#xff…

弹性调度助力企业灵活应对业务变化,高效管理云上资源

作者&#xff1a;吴昆 什么是弹性调度 云计算时代&#xff0c;企业可以通过云平台获得大量计算资源&#xff0c;并根据业务发展和流量需求的实时变化&#xff0c;灵活调整使用的资源类型与资源量。阿里云提供了多种弹性资源&#xff0c;如云服务器 ECS 和弹性容器实例 ECI&am…

MySQL的一些综合运用

一些基本的语句&#xff1a; USE dept_emp; CREATE TABLE dept ( deptno INT(2) NOT NULL COMMENT 部门编号, dname VARCHAR (15) COMMENT 部门名称, loc VARCHAR (20) COMMENT 地理位置 ); -- 添加主键 ALTER TABLE dept ADD PRIMARY KEY (deptno); -- 添加数据 INSE…

[学习笔记]刘知远团队大模型技术与交叉应用L4-Prompt-learning Delta-learning

Prompt-Learning and Delta-Tunning 背景和概览 但是从T5开始&#xff0c;大模型越来越大了。 微调很难了。 模型的趋势 Model Scaling&#xff1a;模型越来越大 Difficult Tuning&#xff1a;微调越来越难 Prompt-Learning 基本组成与流程介绍 预训练和fine-tuning有一…

终极解决Flutter项目运行ios项目报错Without CocoaPods, plugins will not work on iOS or macOS.

前言 最近在开发Flutter项目&#xff0c;运行ios环境的时候报错没有CocoaPods&#xff0c;安卓环境可以正常运行&#xff0c;当时一脸懵逼&#xff0c;网上搜索了一下&#xff0c;有给我讲原理的&#xff0c;还有让我安装这插件那插件的&#xff0c;最终把电脑搞得卡死&#x…

【机器学习300问】15、什么是逻辑回归模型?

一、逻辑回归模型是为了解决什么问题&#xff1f; 逻辑回归&#xff08;Logistic Regression&#xff09;是一种广义线性回归分析模型&#xff0c;尤其适用于解决二分类问题&#xff08;输出为两个类别&#xff09;。 &#xff08;1&#xff09;二分类举例 邮件过滤&#xff…

HarmonyOS鸿蒙应用开发(三、轻量级配置存储dataPreferences)

在应用开发中存储一些配置是很常见的需求。在android中有SharedPreferences&#xff0c;一个轻量级的存储类&#xff0c;用来保存应用的一些常用配置。在HarmonyOS鸿蒙应用开发中&#xff0c;实现类似功能的也叫首选项&#xff0c;dataPreferences。 相关概念 ohos.data.prefe…

zabbix监控扩展

目录 一、zabbix自动发现与自动注册 &#xff08;一&#xff09;理论定义 1.自动发现 2.自动注册 &#xff08;二&#xff09;实操部署 1.自动发现 &#xff08;1&#xff09;新增一台客户端命名为zbx-agent02 ① 配置时间同步 ② 在服务端和客户端上配置 hosts 解析 …

自定义注解与拦截器实现不规范sql拦截(拦截器实现篇)

最近考虑myBatis中sql语句使用规范的问题&#xff0c;如果漏下条件或者写一些不规范语句会对程序性能造成很大影响。最好的方法就是利用代码进行限制&#xff0c;通过拦截器进行sql格式的判断在自测环节就能找到问题。写了个简单情景下的demo&#xff0c;并通过idea插件来将myB…

JavaScript进阶:WebAPIs重点知识整理1

目录 1 DOM修改元素内容 2 DOM修改元素常见属性 3 修改元素样式属性 3.1 通过style修改元素样式 3.2 通过类名className修改元素样式 3.3 通过classList修改元素样式 4 操作表单元素属性 5 自定义属性 6 定时器 7 事件监听 7.1 点击事件 click 7.2 鼠mouseenter和移…