SMBus驱动调试
背景:
光缆维护平台产品中有电池,该电池声称符合SMBus规范。SMBus与I2C存在许多共同之处,使用SCL、SDA两线控制。光缆维护平台硬件主体环境为Xilinx FPGA + ARM,电池的通信控制在嵌入式Linux系统中实现。Linux下自带I2C驱动,SMBus可使用I2C驱动完成控制。
目前准备在列车网络测试产品中也添加电池,直接使用光缆维护平台的电池。列车网络测试产品硬件主体配置为Altera FPGA,在FPGA内部实现一个Nios II软核,设备端的程序运行在软核上的uC/OS-II中。
由于两个不同的产品硬件配置不一致、处理器不一致、操作系统不一致,所以很多细节需要加以研究和考虑。
归纳而言,本文档的调试环境:
符合SMBus规范的电池、FPGA + Nios II + uC/OS-II。
FPGA引脚输出为2.5V,见Quartus - pinplanner

电池中的控制芯片所识别的高电平至少为2V,如下图:
但是FPGA引脚的驱动能力不足,实际使用中达不到2.5V,甚至会低于2V。所以需要把FPGA引脚上拉,测试中使用4.7k欧的电阻上拉至3.3V可解决这问题。

SMBus基本时序与I2C基本一致:

SMBus协议包含3种操作,包括:
Read word
Write word
Block read
本文以Read word通信协议为例描述,需要注意不带阴影的部分是控制设备发送的,带阴影的部分是电池芯片发送的。

拿示波器调试,图中黄色的波形表示SCL信号,绿色波形表示SDA信号。可以看到,图中的波形包含了起始位、电池地址0001011、写类型0以及ack信号。
值得注意的是,由主控设备驱动的稳态低电平和高电平分别约为0V和3.3V,但是由电池驱动的SDA信号的低电平并不是0V,图中可以很清晰的观察到这点。
另外,SCL信号是由控制端提供的,但是设备的某些状态也会影响到SCL信号,导致SCL信号的幅值发生变化。
在电池调试过程中,通信协议Read Word最初调试不通。找同事分析并解决了部分硬件类的问题后,还是调不通。
我不懂什么上拉、下拉、开漏、电池端和控制端接口的设计形式、LVCMOS、TTL、驱动能力这类偏硬件的概念,仅仅凭借着SCL和SDA信号的高低——特别是靠着下图中ack处的这种由电池控制(或影响)的信号高低,分析出电池内部的状态以及其可能与SMBus规范不太一致的地方,最终完成电池驱动的调试工作。
为了方便描述,后文中把信号幅值正常高低电平分别称为“满高态”、“满低态”,图中ACK阶段这种逻辑低(但是比正常低电平要高一些)称为“半低态”,与之对应的那种逻辑高电平称为“半高态”。

下面的波形图显示了BA1后半部分(16h)、ACK1、Command Code(09h)、ACK2。信号的这个波形特征并没有导致链路层的数据异常,但是其存在潜在的问题。图中t1、t2、t3、t4表示时间点,下面我将详细分析这4个时间点,所有的异常分析及驱动调试都以此为基础。

t1:BA1发送完成,SCL被控制端拉低,并把SDA拉高;
注意到SDA信号变为半高态,对于这个变化,唯一的解释是——电池端检测到SCL下降后,判断BA1已经传输完成,进入ACK阶段(尽管此时控制端还没有发送判断ACK的SCL高电平)。电池端进入ACK阶段后拉低SDA信号,由于此时SDA信号还是处于out状态,由控制端驱动,所以仍然保持一个较高的电平;同时由于电池端SDA拉低的影响,幅值比正常的高电平要低一些。
t2:控制端将SDA置为in;
此时,SDA信号由电池端驱动,同时也受控制端的影响,所以处于半低态
t3:控制端将SCL拉高,延时一段时间后判断ACK信号;
在驱动程序中,拉高SCL后,延时一段时间后获取SDA状态,判断其逻辑高低作为ACK的状态。
t4:控制端将SCL拉低,结束ACK阶段;
此时控制端并没有操作SDA信号,但是可观察到其幅值直接由逻辑低上升为高电平。
唯一合理的解释是——电池端检测到SCL拉低后,认为ACK阶段已完成,拉高了SDA信号。此时控制端和电池端都处于拉高SDA的状态,所以SDA信号回到满高态。
有时会存在其它情况,如下图:
时间点t1,SDA为满低,并不能确认电池端SDA是处于拉高还是拉低;
时间点t2,控制端设置SDA为in时,发现SDA直接被拉高,说明此时电池端还没有进入ACK状态;
时间点t3,控制端拉高SCL,进入ACK获取状态——但是SCL不能拉到满高态,此时电池端也没有进入ACK状态。
时间点t3’,控制端无任何操作;电池端就绪,进入ACK状态,拉低SDA;同时SCL也恢复到正常的满高态;
时间点t4,控制端拉高SCL,结束ACK阶段;电池端也拉高SDA,结束ACK阶段。
控制端的驱动程序一般在SCL高电平的中心区域读取SDA,所以这个波形特征并不会造成SDA读取错误。

但是,有时候情况就更加恶劣。在下图中,t3’更加接近于t4,换言之,在控制端几乎要结束ACK阶段时,电池端才刚刚准备好ACK信号。这个波形特征会导致读取错误的SDA数据。控制端驱动程序获得SDA数据后,发现SDA处于高的状态,意味着电池没有正常响应之前的指令,导致通信异常。

造成这个异常的根本原因是电池端还没有准备好ACK信号时,控制端要求开始读取ACK了。能够解决这个问题的修改方式有很多,我所使用的方法是——在发送数据完成后,延时一段时间,等待设备把ACK准备好。下面来2张图为增加延时后的波形,第一张图是电池端隔了一段时间才准备好ACK的情况,第二张图是电池端立即进入ACK状态的情况。


为了方便调试,在控制端驱动程序中最好每个位操作都实现完整,包括“清理当前操作”的步骤。
以写操作为例,按照顺序,依次实现:
(s1)SDA到期望状态;
延时;
(s2)SCL拉高;
延时;
(s3)SCL拉低;
延时;
(s4)SDA拉低;
上图中,时间点s3之后SDA处于半高态,是由于电池端检测到SCL拉低,立即就绪并拉低SDA的原因。
时间点s4,控制端拉低SDA,电池端也拉低SDA,所以SDA处于满低态;
时间点s5,控制端设置SDA为in,内部实际上是拉高SDA的;主要由电池端驱动SDA,所以SDA处于半低态;
另外,在上图中,控制端拉低ACK阶段的SCL信号后,电池端也没有立即恢复SDA信号(拉高),所以控制端结束ACK阶段后,也应该延时一段时间,等待电池端准备就绪。不然会出现下图中的情况,可能会导致的数据错误和通信异常。

总结:
根据波形中信号的4种幅值状态,尤其是2种“半X态”,来推断在各个时间点控制端和电池端分别处于何种状态。只有调整控制端的驱动程序,使得两端的工作状态匹配时,通信才能保持正常。
