STM32H743实现BQ40Z50-R1模拟SMBUS电量读取
SMBUS协议
SMBUS协议与IIC协议的区别并不是特别大,只是需要注意在设置时保证其通信速度不超过100khz就好。其它会遇到的问题TI有篇文档详细的介绍了,可以参考这篇文档,文档我放在末尾。
BQ40Z50-R1
这个网上其它博客也有详细介绍,非常感谢这些代码给的一些思路,遇到问题时参考这些文章还是给我很多调试思路的,相关的文章放在文末。
遇到的问题
无法与BQ40Z50-R1通讯成功
这个问题我觉得主要还是模拟SMBUS的时序问题,可以参考下成功的案例代码里的延时。基本我设置的延时在10us左右,通讯非常稳定,没有再出现过无法通讯的问题。
读到的高位数据为0XFF
我这里主要还是犯蠢了,在接收完数据,发送ACK信号时,没有先将SCK置低,导致SCK的高电平与接收最后一个字节的高电平连到一起了,可能把通信搞呆了,于是就一直发送0XFF。在我解决这个问题的时候,我还看到了还有人遇到这个问题的原因是SMBUS通讯速率不够,会拉低SCK电平,直到准备好之后才能置高。这个问题叫做Clock stretching,解决方案就是在拉高SCK电平值后,检测是否此时SCK电平是否为高,只有高之后再进行后面的操作,保证读取数据的正确。还有一种可能是IIC的问题,因为IIC有两种写法,如果按照其中一种写法来实现的话,是需要进行RESTART的,否则也会导致这种问题。建议遇到这个问题,看一下自己的波形,看到底是哪里出现了问题。
通信成功的波形
分别发送0X16, 0X0D,0X17查询电量信息。
分别发送0X16, 0X09,0X17查询电压信息。
分别发送0X16, 0X21,0X17查询模块名称。
核心代码
给BQ40Z50-R1发送指令进行查询
uint8_t bq40z50_Get_Data(uint8_t address,char* buff)
{
I2CStart();
I2CSendByte(0x16);//发送地址
if(I2CWaitAck() == 1)//若设备正常,BQ40Z50此时会将SDA拉低
{
//未应答
return 1;
}
I2CSendByte(address); //根据不同的address读取不同的数据,例如电压、电流、剩余电量
HAL_Delay_us(100);
if(I2CWaitAck() == 1)//若设备正常,BQ40Z50此时会将SDA拉低
{
//未应答
return 1;
}
I2CStart();
I2CSendByte(0x17);
if(I2CWaitAck() == 1)//若设备正常,BQ40Z50此时会将SDA拉低
{
//未应答
return 1;
}
HAL_Delay_us(100);
*(buff+0) = I2CReceiveByte();
HAL_Delay_us(10);
*(buff+1) = I2CReceiveByte();
HAL_Delay_us(10);
I2CStop();
return 0;
}
写在最后
这个模拟的SMBUS一开始以为还是挺好调的,结果搞了两天,都是一些时序的问题,还是要细心一点调通讯协议的时候,并且调通讯协议一定要看自己的波形,不然我就很难发现一直发送0XFF的问题到底出在那里,最后希望如果大家调这个的话,能够顺利。
参考文献
[1]: https://www.cnblogs.com/ramlife/p/8319897.html
[2]: https://www.cnblogs.com/jiuliblog-2016/p/15894156.html
[3]: https://blog.csdn.net/huizzz6/article/details/108600968
[4]: https://www.ti.com.cn/cn/lit/an/slua475/slua475.pdf?ts=1603002370120&ref_url=https%253A%252F%252Fwww.ti.com.cn%252Fsitesearch%252Fcn%252Fdocs%252Funiversalsearch.tsp%253FsearchTerm%253Dsmbus