作为德州仪器(TI)推出的标杆级电量计芯片,BQ40Z50在1-4串锂电池管理领域凭借其高精度、高安全性和全集成特性,已成为笔记本、无人机、医疗设备及工业应用中智能电池方案的首选。然而,在实际调试过程中,工程师们常常会遇到各种连接、配置和通信问题。本文将基于实际项目经验,系统梳理BQ40Z50的调试流程,并分享实际项目代码及通信问题的解决方案。
调试过程中最宝贵的经验往往来自实际遇到的问题和解决方案。如果你在BQ40Z50调试中有其他独特的经验或疑问,欢迎在评论区分享交流。
对于BQ40Z50按常规的IIC协议修改部分代码可满足SMBUS协议,但是在读取数据时偶尔会出错,解决办法:
1.在发送完开始信号后增加2US延时
2.在发送完停止信号后增加200US延时
3.在等待应答时,改为5000US延时
4.等待应答结束后增加200US延时
5.发送完应答信号后增加1ms延时
做完以上5步即可让读数稳定。
另外读电池电压数据时两个字节,所以需要读两次,读其他数据只要一个字节只需要读一次,下面贴上代码:
/************************************
此程序基于STM32F0系列单片机,用于驱动BQ40Z50芯片,发送完命令在等待应答完之后必须要加50US以上的延时,后面发的命令才能正常响应,读取数据完成发送应答信号后,必须加50US以上延时,读下一个字节才会正常,硬件方面SDA,SCL两个脚在3.3V供电时必须外部上拉10K电阻,单片机配置时不需要上下拉。
读取一帧数据流程:发起始信号->发设备写地址->等待应答->发寄存器地址->等待应答->重发起始信号->发设备读地址(设备地址+1)->等待应答->读取低字
节->发送应信号->读取高字节->发送非应答信号(读完最后一个字节发送非应答信号)->发送停止信号。
作者:不忘初心
************************************/
#include "BQ40Z50.h"
uint8_t DEVICE_NUMBER[5]={0x44,0x02,0x02,0x00,0x46};//返回15个数据,FW版本命令一样
uint8_t HW_VERSION[5]={0x44,0x02,0x03,0x00,0x53};//返回6个数据
uint8_t UNSEAL_FULL_ACCESS[5]={0x44,0x02,0x54,0x00,0x34};//返回8个数据
//初始化BQ40Z50
void BQ40Z50_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11|GPIO_Pin_10;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_Level_1;
GPIO_Init(GPIOB,&GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_10|GPIO_Pin_11);
delay_ms(10);
}
//配置SDA脚为输入
void SDA_In(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_Level_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
//配置SDA脚为输出
void SDA_Out(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_Level_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
//IIC Start
void IIC_Start()
{
SDA_Out();
SDA_H;delay_us(2);
SCL_H;delay_us(2);
SDA_L;
}
//IIC Stop
void IIC_Stop()
{
SDA_Out();
SCL_H;
SDA_L;delay_us(2);
SDA_H;
delay_us(120);
}
/****************************************************************************
* 名 称:uint8_t IIC_Wait_Ack(void)
* 功 能:等待ACK应答
* 入口参数:无
* 出口参数:返回真值=应答,返回假值=无应答
* 说 明:收到应答信号后要加50US以上延时,发送下一个数据才正常
* 调用方法:无
****************************************************************************/
uint8_t IIC_Wait_Ack(void)
{
uint16_t time = 0;
SDA_In();
while(SDA_Read)
{
time++;
if(time > 65000)
{
IIC_Stop();
return FALSE;
}
}
SCL_H;delay_us(10);
SCL_L;delay_us(500);//此值改成500读数据更稳定
return TRUE;
}
/****************************************************************************
* 名 称:void IIC_Ack(uint8_t Ack)
* 功 能:IIC ACK应答
* 入口参数: Ack 用于切换应答和非应答
* 出口参数:无
* 说 明:Ack=1 应答;Ack=0 非应答
* 调用方法:无
****************************************************************************/
void IIC_Ack(uint8_t Ack)
{
SDA_Out();
if(Ack)SDA_L;
else SDA_H;
SCL_H;delay_us(10);
SCL_L;delay_us(500);//此处延时短了,读出的数据容易出错,此值修改成500更稳定
}
// IIC Write byte
void Write_IIC_Byte(uint8_t IIC_Byte)
{
uint8_t i;
delay_us(10);
SCL_L;
SDA_Out();
for(i=0;i<8;i++)
{
if(IIC_Byte&0x80)SDA_H;
else SDA_L;
IIC_Byte<<=1;
delay_us(4);
SCL_H;
delay_us(10);
SCL_L;
delay_us(6);
}
}
// Read_IIC_Byte
uint8_t Read_IIC_Byte(uint8_t Ack)
{
uint8_t i,ReceiveByte=0;
SDA_In();
SCL_H;delay_us(10);
for(i=0;i<8;i++)
{
ReceiveByte<<=1;
SCL_H;
delay_us(8);
if(SDA_Read)ReceiveByte|=0x01;
delay_us(2);
SCL_L;
delay_us(10);
}
if(Ack)IIC_Ack(1);
else IIC_Ack(0);
return ReceiveByte;
}
// Read_BQ40Z50_Data
uint16_t Read_BQ40Z50_Data(uint8_t Reg,uint8_t len)
{
uint8_t bit=0;
uint16_t data=0,data1=0;
IIC_Start();
Write_IIC_Byte(BQ40Z50_ADDR);
bit=IIC_Wait_Ack();
if(bit==0)return 0;
Write_IIC_Byte(Reg);
bit=IIC_Wait_Ack();
if(bit==0)return 0;
IIC_Start();
Write_IIC_Byte(BQ40Z50_ADDR+1);
bit=IIC_Wait_Ack();
if(bit==0)return 0;
if(len==2)
{
data = Read_IIC_Byte(1);
data1 = Read_IIC_Byte(0);
}
else if(len==1)data = Read_IIC_Byte(0);
IIC_Stop();
if(len==2)data=data|data1<<8;
return data&0x7fff;
}
void Read_BQ40Z50_Verison(uint8_t *Reg,uint8_t *buf,uint8_t len)
{
uint8_t i=0,bit=0;
IIC_Start();
Write_IIC_Byte(BQ40Z50_ADDR);
bit=IIC_Wait_Ack();
if(bit==0)return;
for(i=0;i<5;i++)
{
Write_IIC_Byte(Reg[i]);
bit=IIC_Wait_Ack();
if(bit==0)return;
}
IIC_Stop();
delay_ms(20);
IIC_Start();
Write_IIC_Byte(BQ40Z50_ADDR);
bit=IIC_Wait_Ack();
if(bit==0)return;
Write_IIC_Byte(0x44);
bit=IIC_Wait_Ack();
if(bit==0)return;
IIC_Start();
Write_IIC_Byte(BQ40Z50_ADDR+1);
bit=IIC_Wait_Ack();
if(bit==0)return;
for(i=0;i<len-1;i++)
{
buf[i] = Read_IIC_Byte(1);
if(i==(len-2))
{
buf[len-1]=Read_IIC_Byte(0);
}
}
IIC_Stop();
//DEBUG("buf=");
//for(i=0;i<15;i++)DEBUG("0x%02x ",buf[i]);
//DEBUG("\r\n");
}
void Reset_BQ40Z50(void)
{
uint8_t i=0,bit=0;
uint8_t buf[5]={0x44,0x02,0x41,0x00,0x22};
IIC_Start();
Write_IIC_Byte(BQ40Z50_ADDR);
bit=IIC_Wait_Ack();
if(bit==0)return;
for(i=0;i<5;i++)
{
Write_IIC_Byte(buf[i]);
bit=IIC_Wait_Ack();
if(bit==0)return;
}
IIC_Stop();
}
void UNSEAL_BQ40Z50(void)
{
uint8_t i=0,bit=0;
uint8_t buf[4]={0x00,0x14,0x04,0x0c};
uint8_t buf2[4]={0x00,0x72,0x36,0x19};
IIC_Start();
Write_IIC_Byte(BQ40Z50_ADDR);
bit=IIC_Wait_Ack();
if(bit==0)return;
for(i=0;i<4;i++)
{
Write_IIC_Byte(buf[i]);
bit=IIC_Wait_Ack();
if(bit==0)return;
}
delay_ms(12);
IIC_Start();
Write_IIC_Byte(BQ40Z50_ADDR);
bit=IIC_Wait_Ack();
if(bit==0)return;
for(i=0;i<4;i++)
{
Write_IIC_Byte(buf2[i]);
bit=IIC_Wait_Ack();
if(bit==0)return;
}
IIC_Stop();
}
void ShutDown_BQ40Z50(void)
{
uint8_t i=0,bit=0;
uint8_t buf[5]={0x44,0x02,0x10,0x00,0x3b};
IIC_Start();
Write_IIC_Byte(BQ40Z50_ADDR);
bit=IIC_Wait_Ack();
if(bit==0)return;
for(i=0;i<5;i++)
{
Write_IIC_Byte(buf[i]);
bit=IIC_Wait_Ack();
if(bit==0)return;
}
IIC_Stop();
}
// 单个字节命令,读数据函数
uint16_t BYTE1_Read_BQ40Z50(uint8_t Reg,uint8_t len)
{
uint8_t i=0,j=0;
uint16_t data=0,data1=0,buf[5],temp=0;
for(i=0;i<5;i++)
{
IIC_Start();
Write_IIC_Byte(BQ40Z50_ADDR);
IIC_Wait_Ack();
Write_IIC_Byte(Reg);
IIC_Wait_Ack();
IIC_Start();
Write_IIC_Byte(BQ40Z50_ADDR+1);
IIC_Wait_Ack();
if(len==2)
{
data = Read_IIC_Byte(1);
data1 = Read_IIC_Byte(0);
}
else if(len==1)data = Read_IIC_Byte(0);
IIC_Stop();
if(len==2)data=data|data1<<8;
buf[i]=data;
}
for(j=0;j<4;j++)
{
for(i=0;i<4;i++)
{
if(buf[i]>buf[i+1])
{
temp=buf[i];
buf[i]=buf[i+1];
buf[i+1]=temp;
}
}
}
return buf[3];
}
// 单个字节命令,读n个数据函数
void BYTEn_Read_BQ40Z50(uint8_t Reg,uint8_t *buf,uint8_t len)
{
uint8_t i=0;
IIC_Start();
Write_IIC_Byte(BQ40Z50_ADDR);
IIC_Wait_Ack();
Write_IIC_Byte(Reg);
IIC_Wait_Ack();
IIC_Start();
Write_IIC_Byte(BQ40Z50_ADDR+1);
IIC_Wait_Ack();
for(i=0;i<len-1;i++)
{
buf[i] = Read_IIC_Byte(1);
if(i==(len-2))
{
buf[i+1]=Read_IIC_Byte(0);
}
}
IIC_Stop();
}
//双字节读数据函数:*Reg要写的命令,*buf读出的数据,len要读的字节长度
void BYTE2_Read_BQ40Z50(uint16_t Reg,uint8_t *buf,uint8_t len)
{
uint8_t i=0;
Write_Command_BQ40Z50(Reg);
delay_ms(20);
IIC_Start();
Write_IIC_Byte(BQ40Z50_ADDR);
IIC_Wait_Ack();
Write_IIC_Byte(0x44);
IIC_Wait_Ack();
IIC_Start();
Write_IIC_Byte(BQ40Z50_ADDR+1);
IIC_Wait_Ack();
for(i=0;i<len-1;i++)
{
buf[i] = Read_IIC_Byte(1);
if(i==(len-2))
{
buf[i+1]=Read_IIC_Byte(0);
}
}
IIC_Stop();
}
//把数据从小到大排列
void Compare_data(uint16_t *buf,uint16_t len)
{
uint16_t i=0,j=0,temp=0;
for(j=0;j<len-1;j++)
{
for(i=0;i<len-1;i++)
{
if(buf[i]>buf[i+1])
{
temp=buf[i];
buf[i]=buf[i+1];
buf[i+1]=temp;
}
}
}
}
/******以下是头文件*****/
#ifndef __BQ40Z50_H__
#define __BQ40Z50_H__
#include "head_config.h"
#define SCL_L GPIO_ResetBits(GPIOB,GPIO_Pin_10)//CLK
#define SCL_H GPIO_SetBits(GPIOB,GPIO_Pin_10)
#define SDA_L GPIO_ResetBits(GPIOB,GPIO_Pin_11)//DIN
#define SDA_H GPIO_SetBits(GPIOB,GPIO_Pin_11)
#define SDA_Read GPIOB->IDR & GPIO_Pin_11
#define BQ40Z50_ADDR 0x16 //BQ40Z50设备地址
extern uint8_t DEVICE_NUMBER[];
extern uint8_t HW_VERSION[];
extern uint8_t UNSEAL_FULL_ACCESS[];
void BQ40Z50_Init(void);
void IIC_Start(void);
void IIC_Stop(void);
uint8_t IIC_Wait_Ack(void);
void IIC_Ack(uint8_t Ack);
void Write_IIC_Byte(uint8_t IIC_Byte);
uint8_t Read_IIC_Byte(uint8_t Ack);
uint16_t Read_BQ40Z50_Data(uint8_t Reg,uint8_t len);
void Read_BQ40Z50_Verison(uint8_t *Reg,uint8_t *buf,uint8_t len);
void Reset_BQ40Z50(void);
void UNSEAL_BQ40Z50(void);
void ShutDown_BQ40Z50(void);
#endif
夜雨聆风