注册 登录
电子工程世界-论坛 返回首页 EEWORLD首页 频道 EE大学堂 下载中心 Datasheet 专题
liufan的个人空间 https://home.eeworld.com.cn/space-uid-53362.html [收藏] [复制] [分享] [RSS]
日志

关于C8051F040 官方 SMBus 源码的 随机EEPROM读操作的 BUG

已有 2681 次阅读2014-10-30 10:02 |个人分类:MCU| C8051, 040, SMBus, EEPROM

最近调试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了!



评论 (0 个评论)

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 注册

热门文章