This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

【FAQ】BQ76940:I2C通信问题

BQ76940          BQ76940 Datasheet

 

Q: 我正在使用BQ7694003设计13s BMS。我在写入BQ寄存器时遇到了问题。IC在第9个周期未给出ACK。在第9个周期内,SDA线未拉低。之前提过相同的问题:https://e2e.ti.com/support/power-management/f/196/t/323329,但未指出解决方案或原因。我更换了BMS IC,但问题仍未解决。

我已研究了http://www.ti.com/lit/zip/sluc583 和应用注释http://www.ti.com/lit/pdf/slva626上给出的代码,并据此自己编写了代码。 

在我的I2C设置中- 

主机 MCU -> 主站(始终)
BQ76940 ->从站(始终)

我认为此设置正确,因为BQ不会生成SCL,因此其在任何情况下(tx或rx)都不可能是一个主站。因此,它将始终是从站,而MCU将始终是主站,因为它会生成SCL。我的理解正确吗?

转到I2C软件。为了解释我的软件流程,我将考虑CC_CFG寄存器的示例

BQ_addr = 0x08 or 0000 1000
CC_CFG : addr = 0x0B or 0000 1011
                  data = 0x19 or 0001 1001
 CRC = 0x89 or 1000 1001

CRC 多项式: x8 + x2 + x + 1 = 1 0000 0111

CRC计算依据: BQ_addr(8bit)  |  CC_CFG(8bit)  |  DATA(8bit) 

                                          0000 1000  |  0000 1011  |  0001 1001

由于CRC多项式的幂是8,因此在上述二进制数后附加了8个零,然后计算出CRC

对于上述情况,我得到作为CRC的0x89。

然后将整个I2C帧发送到BQ。BQ接收到每个字节后都会发送一个ACK。帧格式为
START  |  SLAVE_ADDR(7)+W(1)   |  REGISTER_ADDR(8)  |  DATA(8)  |  CRC(8)  |  STOP

n->表示位数

SLAVE_ADDR + W = 0001 0000(BQ_ADDR左移1位)
REGISTER_ADDR = 0000 1011
DATA = 0001 1001
CRC = 1000 1001

SLAVE_ADDR是左移1位的BQ地址。该字段的第8位为零,表示主站处于传输模式(MT模式)。因此,主站会将数据写入从站的寄存器。

现在的问题是,在SLAVE_ADDR + W. SLAVE_ADDR + W字节成功发送之后,BQ在第9个周期没有发送ACK,但BQ在第9个周期给出了NACK,且SDA未被拉低。 
下述我在BQ IC的不同管脚上观察到的电压读数- 

(所有电压均处于WRT接地)

TS1 = 0.0 V (启动后)
TS2 = 20.2 V
TS3 = 36.4 V
CAP1 = 3.28 V
CAP2 = 23.7 V
CAP3 = 40.01 V
REGSRC = 18.4 V
VC5X = 20.4 V [C1,C2,C3,C4,C5]
VC10X = 36.7 V [C6,C7.C8.C10]
BAT = 53.2 V [C11,C12,C13,C15]
PACK+ = 53.3 V

我使用了3个10k NTC热敏电阻,每个热敏电阻都焊接在TS1-gnd、TS2-VC5X和TS3-VC10X之间。我按下启动按钮时,TS1电压升至2.7V。此外,连接电池后会施加启动信号。BQ启动后开始I2C通信。
我还使用了3.3-5v双向逻辑电平转换器来实现5V MCU和3.3v BQ IC之间的安全正确通信。

那么,导致该问题的原因是什么呢?为什么BQ不发送ACK?(PS:我无法使用EVM电路板,因此不要要求在EVM上测试I2C)

 

A:看来您可能已使用未移位的地址计算了CRC:0000 1000 | 0000 1011 | 0001 1001

如果使用发送到设备的实际字节(0001 0000 | 0000 1011 | 0001 1001)进行计算,则CRC应该为0x7A

 

Q: 在那种情况下,在发送CRC字节后我是否会获得NACK?为什么即使发送正确,我在SLA + W字节之后也收到NACK?

 

A: 要真正排除错误,您需要使用示波器捕获波形。您应该在电平转换器的BQ76940侧捕获波形。

 

Q: 问题解决了!

经过数周的调试排错,我终于实现了BMS与MCU的通信。因此,我将发布检查清单、待办事项、预防措施和调试步骤

我的设置是:

MCU: Arduino Promini(5v版本)

BMS: BQ7694003(3.3v版本)

电池组:13-15秒可配置

 

硬件调试

1. 从原理图开始,请确保遵循与BQ相关的数据表和参考设计。我发现TIDR773和TIDA00792非常有用。TIDR使用P-ch MOSFET进行平衡,而TIDA00792使用N-ch MOSFET进行平衡。您可根据自己的偏好进行选择。这两种设计的其他方面在概念上都很相似。

2. 如需要,确保将电容器靠近ic管脚放置。使负载电路远离敏感的数字电路,以最大限度地减少耦合到数字电路中的噪声。 

3. 完整的接地用PCB层将大大降低噪声。 

4. 尽管BQ被设计为处理随机的电池连接,但为安全起见,请先让电池接地,然后再插入电池1,插入电池2,再插入电池3,依此类推,直到插入最后一个电池。

5. 保持I2C轨迹短,并确保两者之间没有太多的电容耦合。这种耦合会导致I2C通信过程中的位识别错误

6. 使用开尔文连接进行R_sense连接。这会产生更好的CC结果。https://www.analog.com/en/analog-dialogue/articles/optimize-high-current-sensing-accuracy.html

7. 切勿忘记I2C的上拉电阻!否则整个通信将出现故障。通常来讲,上拉使用4.7k-10k电阻。这是有关I2C通信基础知识的优秀指南。

8. 如果你和我一样,BMS和MCU之间存在逻辑电平差异,请使用逻辑电平转换器。就我而言,尽管5v MCU能够从3.3v BMS读取,但它错误地读取了一些寄存器。因此,为避免这种情况,请使用逻辑电平转换器。https://learn.sparkfun.com/tutorials/bi-directional-logic-level-converter-hookup-guide/all .就我而言,BMS HIGH(最大值)和MCU HIGH(最小值)仅相差0.1v。因此,我只能依靠运气来正确识别I2C信号。

9. 现在最重要的是,请检查数据表并验证以下管脚上的标称电压读数。

TS0, TS1, TS2
CAP1, CAP2, CAP3
V5X, VC10X, BAT
REGSRC
REGOUT

CAP1 - GND = 3.3V
CAP2 - VC5X = 3.3V
CAP3 - VC10X = 3.3V
REGOUT = 3.3V 

如果所有这些管脚上的电压读数均正确,则您可不必担心!您的硬件没有问题。

10. 也要连接NC管脚!另外,如果未使用热敏电阻,则通过10k电阻器将相应的TSx管脚下拉至GND。

11. TS1管脚上需要启动信号以唤醒BQ76940IC。不要指望IC自行唤醒:)

12. 将BQ的ALERT管脚连接到MCU的中断管脚。这将帮助您快速识别和清除故障

 

软件 调试

1. 确保所有的管脚声明和ISR均已设置并正常工作。逐个调试整个代码。请勿一次调试1000行代码。

2. 如要测试I2C通信,请首先运行I2C扫描器代码以找到BQ IC。这需要立即完成两件事——您可以验证BQ IC的I2C地址,此外,如果设备响应,则可以确保设备仍在运行!

3. 在MCU识别出BQ后,请测试I2C读写功能。(可选)读取BQ设备的所有寄存器并记录下来。

对于所有Arduino狂热粉丝,请使用Wire()库。读取顺序是- 

/* READS 1 BYTE */
byte registerRead(byte regAddress)
{
Wire.beginTransmission(bqI2CAddress);
Wire.write(regAddress);
Wire.endTransmission();
Wire.requestFrom(bqI2CAddress, 1);

return (Wire.read());
}

/* READS 2 BYTES  */
int registerDoubleRead(byte regAddress)
{
Wire.beginTransmission(bqI2CAddress);
Wire.write(regAddress);
Wire.endTransmission();

Wire.requestFrom(bqI2CAddress, 2);

byte reg1 = Wire.read();
byte reg2 = Wire.read();

int combined = (int)reg1 << 8;
combined |= reg2;

return (combined);
}

4. 查找数据表以查找您的BQ IC是否启用了CRC。如果未启用CRC,则可以使用常规I2C通信协议直接写入BQ寄存器。如果启用了CRC,则必须计算CRC并将其发送给BQ,否则它将不接受对其寄存器的任何写入请求。 

再次,德州仪器在I2Chttp://www.ti.com/lit/an/slva704/slva704.pdf上有一个超级有用且简单的指南 

/* Write without CRC */
void registerWrite(byte regAddress, byte regData)

{
Wire.beginTransmission(bqI2CAddress);
Wire.write(regAddress);
Wire.write(regData);
Wire.write(0x7A);
Wire.endTransmission();
}



/*Write with CRC*/

void registerWriteCRC(unsigned char regAddress, unsigned char regData)
{
uint8_t values[3] = { (bqI2CAddress << 1), regAddress, regData };
uint8_t crc = calc_crc(values, 3);

Wire.beginTransmission(bqI2CAddress);
Wire.write(regAddress);
Wire.write(regData);
Wire.write(crc);
Wire.endTransmission();
}

unsigned char calc_crc(unsigned char *ptr, unsigned char len)
{
unsigned char key = 7;
unsigned char i;
unsigned char crc = 0;

while (len-- != 0)
{
for (i = 0x80; i != 0; i /= 2)
{
if ((crc & 0x80) != 0)
{
crc *= 2;
crc ^= key;
}
else
crc *= 2;

if ((*ptr & i) != 0)
crc ^= key;
}
ptr++;
}
return (crc);
}

 

对于BQ769x0,CRC多项式为x^8 + x^2 + x + 1。答案是7。https://www.digikey.com/eewiki/display/microcontroller/CRC+Basics 

随时了解CRC计算的步骤。另外,使用CRC时的传输序列和CRC计算有些不同,因此请确保查看数据表以了解更多详细信息。

通过发送的字节计算CRC

5. 确保将接收的字节转换为实际值(伏特、安培等)。查找数据表中的公式。正如我之前提到的,你得像喜欢摇滚乐一样!编写软件将涉及很多 

位触发和“撞头”情况。您必须让自己适应HEX、DEC和BIN的世界

6. 要检查写入功能是否起作用,只需将一个值写入寄存器并回读该寄存器。如果您的MCU读取的值与您先前写入的值相同,则写入功能起作用,读取功能也起作用!

7. 设置时,请确保清除XREADY位。这样做可以在BQ Ic遇到某些问题(例如过多的系统瞬态)时可帮您识别事件。