最近调试C8051F040 SMBus的时候,参考了官方的文件包,文件名为:F04x_SMBus_EEPROM.c
经过修改,去掉了Timer4超时复位SMBus的中断,然后直接在板上跑演示没有任何问题,DEMO中是写一次读一次,没有问题,但是如果连续调用
EEPROM_ByteRead 或者 EEPROM_ReadArray 会出现死等状态,即函数中的如下地方:
// Initiate SMBus Transfer
STA = 1;
while(SMB_BUSY); // Wait until data is read
经过对SMBus的中断函数和EEPROM I2C协议的对比发现了一个问题,在中断的如下代码片段
// Master Receiver: Slave address + READ transmitted. ACK received.
// For a READ: check if this is a one-byte transfer. if so, set the
// NACK after the data byte is received to end the transfer. if not,
// set the ACK and receive the other data bytes.
//
// For a WRITE: N/A
case SMB_MRADDACK: //0x40
if (i == SMB_DATA_LEN)
{
AA = 0; // Only one byte in this transfer,
// send NACK after byte is received
}
else
{
AA = 1; // More than one byte in this transfer,
// send ACK after byte is received
}
break;
这里是EEPROM在随机读操作中重新启动总线发送器件地址+读指令位,收到应答的时候发生的中断,红色部分有问题,如果是读1个字节的话,经过分析代码,i 其实是等于0的,SMB_DATA_LEN则是1,那么会将AA = 1,即,当下一次收到数据字节的时候,SMBus会发送ACK应答信号给器件,这实际上和EEPROM的随机读操作的协议是冲突的,EEPROM的随机读操作中,如果是接收最后一个数据字节的话,最后要停止总线传输是要发非应答信号的,所以这里应该要发AA=0,即接收到器件发来的数据字节后,发送非应答信号NACK给器件。所以这里要改过来,如下:
case SMB_MRADDACK: //0x40
if( SMB_DATA_LEN == 1 ) // 如果只读一个字节数据,那么这里要将AA设置为0,
{ // 即代表收到这一个字节后,立即发送非应答位,准备停止总线的传输, 即下次中断将到达 0x58的状态
AA = 0;
}
else //如果要读取多个字节数据的话,则收到这一个字节后,立即发送应答位, 下次中断将达到 0x50 的状态
{
AA = 1;
}
break;
如果是只读一个数据则下次中断将会到达0x58的状态,如下:
// 读操作中,如果接收到最后一个数据,并发出了非应答信号后,则中断到这里
case SMB_MRDBNACK: //0x58
*pSMB_DATA_IN = SMB0DAT; // 保存最后一个数据
AA = 1; // 恢复 AA 状态
STO = 1; // 发停止总线操作
SMB_BUSY = 0; // 设置传输完成标志位
break;
最后STO=1,停止总线,并设置忙标志为空闲态,表示读操作完成,总线空闲。
假如是要读取多个字节,则在 上面的 SMB_MRADDACK = 0x40 的时候,AA = 1; 的即 if( SMB_DATA_LEN == 1 ) 条件是不满足的,
那么收到器件发送的数据后,然后发送完ACK后,会进到 SMB_MRADDACK = 0x50 中断中,代码如下:
// 读操作中,如果接收到新的数据,并发出了ACK应答信号后,则中断到这里
case SMB_MRDBACK: //0x50
*pSMB_DATA_IN = SMB0DAT; // 保存数据
pSMB_DATA_IN++; // 指针调整
i++; // 接收计数
if( i < SMB_DATA_LEN - 1) // 如果下次收到数据不是最后一个数据,发应答信号,即将中断到 自身的 0X50 状态,不断循环
{
AA = 1;
}
else // 如果下次接收到数据是最后一个数据,则设置收到数据后,就发非应答信号,即将中断到 0x58 状态
{
AA = 0;
}
break;
红色部分是当接收的数据字节不是倒数第2个字节的时候,这个时候,发送“下次接收数据发应答位”的指令,则下次收到数据,中断的时候,还是在0x50态,一直循环读取数据并判断,直到接收到倒数第2个字节的时候,则发送AA = 0,即发送“下次接收数据发非应答位”的指令,那么下次中断的时候,将进入0x58态,获取最后一个数据字节,然后STO = 1,完成传输。
具体的修改就以上几个地方,对照源码和EEPROM协议看看,就OK了!