德州仪器BQ27220电量计驱动详解

最近在调试BQ27220电量计,记录一下,供大家参考。

一、先上图

图一图二为在“Battery Management Studio”上进行调试,也为后续驱动的顺序做准备。小白可以对比驱动手册,理解上面的意思,下文会详细描述。

图三图四图五自己用单片机成功驱动的读写例子。

下面是代码说明:

        注意:如果确定硬件没问题,调不通的,用逻辑分析仪去抓,大概率是时序的问题,后面代码需要注意的地方也会标注出来,供大家参考

 //补上自己控制io口的相关代码  最好是直接操作寄存器的方式

#define SDA_OUT()   //配置sda为输出
#define IIC_SDA_H() //配置sda为输出高
#define IIC_SDA_L() //配置sda为输出低
#define IIC_SDA_IN() //配置sda为输入
#define IIC_SCL_H() //配置scl为输出高
#define IIC_SCL_L() //配置scl为输出低
#define READ_SDA()  //sda为输入时 读取sda的状态

//产生IIC起始信号
static void IIC_Start(void)
{
    SDA_OUT();     //sda线输出
    IIC_SDA_H();
    IIC_SCL_H();
    delay1us(5);//注意这里的延时 一般情况下,这里不需要修改
     IIC_SDA_L();//START:when CLK is high,DATA change form high to low
    delay1us(5);
    IIC_SCL_L();//钳住I2C总线,准备发送或接收数据
}

//产生IIC停止信号
static void IIC_Stop(void)
{
    SDA_OUT();//sda线输出
    IIC_SCL_L();
    IIC_SDA_L();//STOP:when CLK is high DATA change form low to high
     delay1us(5);//注意这里的延时 一般情况下,这里不需要修改
    IIC_SCL_H();
    delay1us(3);//注意这里的延时 一般情况下,这里不需要修改
    IIC_SDA_H();//发送I2C总线结束信号
    delay1us(5);//注意这里的延时 一般情况下,这里不需要修改
}

//等待应答信号 这个函数一定要注意

static uint8_t IIC_Wait_Ack(void)
{
    uint8_t ucErrTime=0;
    IIC_SDA_IN();      //SDA设置为输入
    delay1us(3);
    IIC_SCL_H();
    delay1us(3);
    while(READ_SDA())
    {
        ucErrTime++;
        //delay(1);
        if(ucErrTime>250)
        {
            IIC_Stop();
            return 1;
        }
    }
    IIC_SCL_L();//时钟输出0
    return 0;
}

//产生ACK应答
static  void IIC_Ack(void)
{
    IIC_SCL_L();
    SDA_OUT();
    IIC_SDA_L();
    delay1us(3);//注意这里的延时 一般情况下,这里不需要修改
    IIC_SCL_H();
    delay1us(5);//注意这里的延时 一般情况下,这里不需要修改
    IIC_SCL_L();
}
//不产生ACK应答
static void IIC_NAck(void)
{
    IIC_SCL_L();
    SDA_OUT();
    IIC_SDA_H();
    delay1us(3);//注意这里的延时 一般情况下,这里不需要修改
    IIC_SCL_H();
    delay1us(3);//注意这里的延时 一般情况下,这里不需要修改
    IIC_SCL_L();
}

//IIC发送一个字节
//注意这里的延时 一般情况下,这里不需要修改
static void IIC_Send_Byte(uint8_t txd)
{
    uint8_t t;
    SDA_OUT();
    IIC_SCL_L();//拉低时钟开始数据传输
    for(t=0;t<8;t++)
    {
        if((txd&0x80)>>7)
            IIC_SDA_H();
        else
          IIC_SDA_L();
        //hal_gpio_write(IIC_SDA,((txd&0x80)>>7));
        txd<<=1;
        delay1us(5);   //对TEA5767这三个延时都是必须的
        IIC_SCL_H();
        delay1us(5);
        IIC_SCL_L();
        delay1us(5);
    }
}
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK

//注意这里的延时 一般情况下,这里不需要修改
static uint8_t IIC_Read_Byte(unsigned char ack)
{
    unsigned char i,receive=0;
    IIC_SDA_IN();//SDA设置为输入
    for(i=0;i<8;i++ )
    {
        IIC_SCL_L();
        delay1us(5);
        IIC_SCL_H();
        receive<<=1;
        if(READ_SDA())receive++;
        delay1us(5);
    }
    if (!ack)
        IIC_NAck();//发送nACK
    else
        IIC_Ack(); //发送ACK
    return receive;
}

//向BQ27220的寄存器发送字节 这里后续这里的len一直为2字节 
//这个函数必须注意延时函数的延时时间和是否需要延时  不同的平台,可能都不一样
static uint8_t BQ27220_Write(uint8_t addr,uint8_t reg,uint8_t *data,uint8_t len)
{
    IIC_Start();
    IIC_Send_Byte(addr);//发送器件地址+写命令
    if(IIC_Wait_Ack())    //等待应答
    {
        IIC_Stop();
        return 0;
    }
    IIC_Send_Byte(reg);    //写寄存器地址
    delay1us(30);//用过两款单片机驱动,一款这里需要延时 一款不需要,请注意
    if(IIC_Wait_Ack())    //等待应答
    {
        IIC_Stop();
        return 0;
    }
    IIC_Send_Byte(data[0]);//发送数据
    delay1us(30);//用过两款单片机驱动,一款这里需要延时 一款不需要,请注意
    if(IIC_Wait_Ack())    //等待ACK
    {
        IIC_Stop();
        return 0;
    }
    IIC_Send_Byte(data[1]);//发送数据
    delay1us(30);//用过两款单片机驱动,一款这里需要延时 一款不需要,请注意
    if(IIC_Wait_Ack())    //等待ACK
    {
        IIC_Stop();
        return 0;
    }
    delay1us(20);//用过两款单片机驱动,一款这里需要延时 一款不需要,请注意
    IIC_Stop();
    mdelay(2);//这里最好加上延时,因为BQ27220手册里说了,结尾要加66uS的延时
    return 1;
}

//读取BQ27220的寄存器字节 这里后续这里的len一直为2字节 
//这个函数必须注意延时函数的延时时间和是否需要延时  不同的平台,可能都不一样
static uint8_t BQ27220_Read(uint8_t addr, uint8_t reg,uint8_t cmd,uint8_t *date,uint8_t len)
{
    //uint8_t res;
    IIC_Start();
    IIC_Send_Byte(addr);//发送器件地址+写命令
    if(IIC_Wait_Ack())    //等待ACK
    {
        IIC_Stop();
        return 0;
    }
    IIC_Send_Byte(reg);    //写寄存器地址
    delay1us(30);//用过两款单片机驱动,一款这里需要延时 一款不需要,请注意
    if(IIC_Wait_Ack())    //等待ACK
    {
        IIC_Stop();
        return 0;
    }
    delay1us(10);//用过两款单片机驱动,一款这里需要延时 一款不需要,请注意
    IIC_Start();
    IIC_Send_Byte(addr|0x01);//发送器件地址+读命令
    if(IIC_Wait_Ack())    //等待ACK
    {
        IIC_Stop();
        return 0;
    }
    delay1us(50);//用过两款单片机驱动,两款需要延时的大小都不一样,建议是以150uA为延时
    date[0]=IIC_Read_Byte(1);//读取数据,发送nACK
    delay1us(50);/用过两款单片机驱动,两款需要延时的大小都不一样,建议是以150uA为延时
    date[1]=IIC_Read_Byte(0);//读取数据,发送nACK
    IIC_Stop();            //产生一个停止条件
    return 1;

}

接下来是步骤说明,根据图一图二的步骤来的,本人也是按照这个步骤来的,实测基本上问题不大了。注意 注意 注意 一定要按照图一图二的顺序来。

(1):上电先判断电量计的容量配置是否为自己要的配置(举例而已(0x3C,因为我有改容量所以读取容量寄存器,判断是否需要修改),具体看自己,图一图二没有该步骤),如果需要修改,则开始进行图一图二的步骤,反之则执行读取电量等操作。

(2):执行图一图二的操作。有些说明地方可能不对,请结合配置手册来看,W表示写,R表示读。

  我是写了一个定时器,每50ms发送一条配置指令,可以通过下面的方式,通过加延时去实现

W 0x00 0x1404   这里只说明一次 W +0x00,0x1404表示在寄存器00写入0x1404

delayms(50);

W 0x00  0x7236//这两个指令需要配置时都需要先发送

delayms(50);

说明:我的方法是通过一个标志位和一个定时器来实现的,如下,每个配置的指令的定时都大同小异,后面会按照延时的方式去写,这样方便大家理解。

               

W 0x4100  //重启操作,这里最好加上重启操作,因为有时候万一出了问题,导致配置丢失 ,这里重启,恢复初始状态

delayms(4000);//重启需要4秒钟 4秒后在操作27220

W 0x00  0xFFFF

  delayms(50);

W 0x00  0xFFFF//两次发送FFFF让27220进入FULL ACCESS 模式

  delayms(50);

W 0x00  0x9000//进入CFGUPDATE模式

  delayms(1100);

R 0x3B //读取两字节 通过第一节判断是否成功进入CFGUPDATE模式  没有进入的话 需要重新开始,不可以往下进行 判断的依据为 第一字节&0x04==1表示进入成功.

W 0x3E  0x9B92  //通过3E寄存器配置接下来要操作的地址 这个寄存器很重要

  delayms(50);

上面的0x9B92 其实是0x929B,但程序必须写成9B92,这个操作的是下面这个寄存器,也是很重要的一个寄存器  默认值为0x102A 没啥要求的话,就按照我的配置来吧 我测试过,确实能用。

W 0x40  0x0021  //上面的0x3E告诉27220要配置的地址,这里的0x40是告诉27220 要配置的值 这里说一下,0021表示启动平滑处理 就是电量可以从100 99…..往下掉 不会跳变,21表示使用FIXED_EDV0(即用自己配置的东西),请对照手册去理解

delayms(50);

W 0x60 0xCC24 
//
上面的0x3E告诉27220要配置的地址,0x40是告诉27220 要配置的值,这里的0x60是告诉27220  配置好了 请根据这个值进行校验,非常注意 这里的校验很奇怪,请不要按照官方的文档进行配置,按照我这里的描述来,按照官方的文档很容易出错,只需要用到官方提供的两个公式即可,不要问为什么,我也很奇怪。

校验值说明:

         公式如下:

                Temp = mod(255 – Old_Chksum – OLD_DC_MSB – OLD_DC_LSB, 256);

                New_Chksum = 255 – mod(temp + 0x04 + 0Xb0, 256);

开始说明:

(1)通过大量的实验表明,27220第一次读取“Old_Chksum,OLD_DC_MSB,OLD_DC_LSB”都是一样的,意思就是假如 上电进入配置模式,配置要操作的地址为0x9D92,读取0x40(就是这个地址原来的值)都为0x0BB8(记住就行,可能例子的0bb8写反了),读取0x60的值都为0x3D24(3D表示Old_Chksum,24表是长度)不要管3D24怎么来的 记住这两个值就行,记住只需要你读一次值记下来就行,千万不要第二次读取,因为第二次读取出来的值跟第一次读取出来的值不一样,这样会导致后续的配置不成功。

(2)通过步骤一读取到的“Old_Chksum,OLD_DC_MSB,OLD_DC_LSB”用带入上面的公式进行计算,得到新的校验值,上例子解析, Temp=(255-0x3D-0x0B-0xB8)%256=255(结果为负的话就用补码来实现,(这里建议在网页收搜一下mod的计算器,直接填数字就行)这里假设要配置的值为0x2710(10000mAh)

New_Chksum = 255 – (0x27+0x10+0x3D)%256=0xC9;记住这个(0xC924就行),把这两个值写入到0x60寄存器就行,同理,修改别的地址的值也是一样的。

delayms(50);

W 0x3E  0x9D92  //这个是配置电池可以的容量的 跟0x9F92是配合使用的 

delayms(50);

W 0x40  0x2710 //配置的电池容量为10000mAh 

delayms(50);

W 0x60  0xC924  //校验值按照上面的方法进行

delayms(50);

W 0x3E  0x9F92  //这个是配置电池可以的容量的 跟0x9F92是配合使用的 

delayms(50);

W 0x40  0x2710 //配置的电池容量为10000mAh 

delayms(50);

W 0x60  0x2D24  //校验值按照上面的方法进行

delayms(50);

W 0x3E  0x0692  //这个操作是进制27220进入休眠的  进入休眠后 所有数据都会丢失,所以禁止它休眠 

delayms(50);

W 0x40  0x0084 //

delayms(50);

W 0x60  0x1524  //校验值按照上面的方法进行

delayms(50);

W 0x3E  0x7192  //这个操作是让27220平滑处理  尽量加上吧  每试过不加上会不会有影响

delayms(50);

W 0x40  0x0F0E //

delayms(50);

W 0x60  0xCF92  //校验值按照上面的方法进行

delayms(50);

W 0x3E  0xB792  //因为是用自己设定的值,所以这里配的3%的电压为3.100V 这个可以自己定,别定太高,免得出现电量跳变

delayms(50);

W 0x40  0x0C1C //

delayms(50);

W 0x60  0x2821  //校验值按照上面的方法进行  这里的21 不是24  因为通过上面的校验方法 读出来的长度就为0x21

delayms(50);

W 0x3E  0xBA92  //因为是用自己设定的值,所以这里配的7%的电压为3.200V 这个可以自己定,别定太高,免得出现电量跳变

delayms(50);

W 0x40  0x0C80 //

delayms(50);

W 0x60  0x7C1E  //校验值按照上面的方法进行  这里的1E 不是24  因为通过上面的校验方法 读出来的长度就为0x1E

delayms(50);

//到此,已经把电池容量的3000mA配置到了10000mA  去掉休眠,加上平滑处理,自定义电量百分之七的电压(防止跳变,别定太高)接下来就是最后两条指令,重置计量。

W 0x00  0x9100//退出配置模式  重启计量器  开始生效配置

  delayms(50);

W 0x00  0x3000//进入CFGUPDATE模式

对了  图一图二配置的是5000mAh的

作者:qq_37681280

物联沃分享整理
物联沃-IOTWORD物联网 » 德州仪器BQ27220电量计驱动详解

发表回复