SMBus(I2C)随后发送额外的ACK

时间:2016-08-26 19:45:59

标签: embedded microcontroller i2c 8051

我试图进行基本的握手。以下是C8051F120的SMBus(系统管理总线)的ISR。我试图在其上实现一个I2C设备(ads1115 7addr 0x48,对于那些好奇的人)。请注意,这主要是硅实验室为F120提供的示例。

void SMBUS_ISR (void) interrupt 7
{
   bit FAIL = 0;                       // Used by the ISR to flag failed
                                       // transfers

   static unsigned char sent_byte_counter;
   static unsigned char rec_byte_counter;

   // Status code for the SMBus (SMB0STA register)

   switch (SMB0STA)
   {
      // Master Transmitter/Receiver: START condition transmitted.
      // Load SMB0DAT with slave device address.
      case SMB_START: //0x08

      // Master Transmitter/Receiver: repeated START condition transmitted.
      // Load SMB0DAT with slave device address
      case SMB_RP_START: //0x10
         SMB0DAT = TARGET;             // Load address of the slave.
         SMB0DAT &= 0xFE;              // Clear the LSB of the address for the
                                       // R/W bit
         SMB0DAT |= SMB_RW;            // Load R/W bit
         STA = 0;                      // Manually clear STA bit

         rec_byte_counter = 1;         // Reset the counter
         sent_byte_counter = 1;        // Reset the counter

         break;

      // Master Transmitter: Slave address + WRITE transmitted.  ACK received.
      // For a READ: N/A
      //
      // For a WRITE: Send the first data byte to the slave.
      case SMB_MTADDACK: //0x18

         SMB0DAT = SMB_DATA_OUT[sent_byte_counter-1];
         sent_byte_counter++;

         break;

      // Master Transmitter: Slave address + WRITE transmitted.  NACK received.
      // Restart the transfer.
      case SMB_MTADDNACK: //0x20
         STA = 1;                      // Restart transfer
         break;

      // Master Transmitter: Data byte transmitted.  ACK received.
      // For a READ: N/A
      //
      // For a WRITE: Send all data.  After the last data byte, send the stop
      //  bit.
      case SMB_MTDBACK: //0x28

         if (sent_byte_counter <= NUM_BYTES_WR)
         {
            // send data byte
            SMB0DAT = SMB_DATA_OUT[sent_byte_counter-1];
            sent_byte_counter++;
         }
         else
         {
            STO = 1;                   // Set STO to terminate transfer
            SMB_BUSY = 0;              // And free SMBus interface
         }

         break;

      // Master Transmitter: Data byte transmitted.  NACK received.
      // Restart the transfer.
      case SMB_MTDBNACK: //0x30
         STA = 1;                      // Restart transfer

         break;

      // 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 (rec_byte_counter == NUM_BYTES_RD)
         {
            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;

      // Master Receiver: Slave address + READ transmitted.  NACK received.
      // Restart the transfer.
      case SMB_MRADDNACK: //0x48
         STA = 1;                      // Restart transfer

         break;

      // Master Receiver: Data byte received.  ACK transmitted.
      // For a READ: receive each byte from the slave.  if this is the last
      //  byte, send a NACK and set the STOP bit.
      //
      // For a WRITE: N/A
      case SMB_MRDBACK: //0x50

         if (rec_byte_counter < NUM_BYTES_RD)
         {
            SMB_DATA_IN[rec_byte_counter-1] = SMB0DAT; // Store received byte
            AA = 1;                    // Send ACK to indicate byte received
            rec_byte_counter++;        // Increment the byte counter
         }
         else
         {
            AA = 0;                    // Send NACK to indicate last byte
                                       // of this transfer
         }

         break;

      // Master Receiver: Data byte received.  NACK transmitted.
      // For a READ: Read operation has completed.  Read data register and
      //  send STOP.
      //
      // For a WRITE: N/A
      case SMB_MRDBNACK: //0x58

         SMB_DATA_IN[rec_byte_counter-1] = SMB0DAT; // Store received byte
         STO = 1;
         SMB_BUSY = 0;
         AA = 1;                       // Set AA for next transfer

         break;

      // Master Transmitter: Arbitration lost.
      case SMB_MTARBLOST: //0x38

         FAIL = 1;                     // Indicate failed transfer
                                       // and handle at end of ISR

         break;

      // All other status codes invalid.  Reset communication.
      default:
         FAIL = 1;

         break;
   }

   if (FAIL)                           // If the transfer failed,
   {
      SMB0CN &= ~0x40;                 // Reset communication
      SMB0CN |= 0x40;
      STA = 0;
      STO = 0;
      AA = 0;

      SMB_BUSY = 0;                    // Free SMBus

      FAIL = 0;
   }

   SI = 0;                             // Clear interrupt flag
}

//-----------------------------------------------------------------------------
// Support Functions
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// SMB_Write
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// Writes a single byte to the slave with address specified by the <TARGET>
// variable.
// Calling sequence:
// 1) Write target slave address to the <TARGET> variable
// 2) Write outgoing data to the <SMB_DATA_OUT> array
// 3) Call SMB_Write()
//
void SMB_Write (void)
{
   char SFRPAGE_SAVE = SFRPAGE;        // Save Current SFR page

   SFRPAGE = SMB0_PAGE;

   while (SMB_BUSY);                   // Wait for SMBus to be free.
   SMB_BUSY = 1;                       // Claim SMBus (set to busy)
   SMB_RW = 0;                         // Mark this transfer as a WRITE
   STA = 1;                            // Start transfer

   SFRPAGE = SFRPAGE_SAVE;             // Restore SFR page detector
}

//-----------------------------------------------------------------------------
// SMB_Read
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// Reads a single byte from the slave with address specified by the <TARGET>
// variable.
// Calling sequence:
// 1) Write target slave address to the <TARGET> variable
// 2) Call SMB_Write()
// 3) Read input data from <SMB_DATA_IN> array
//
void SMB_Read (void)
{
   char SFRPAGE_SAVE = SFRPAGE;        // Save Current SFR page

   SFRPAGE = SMB0_PAGE;

   while (SMB_BUSY);                   // Wait for bus to be free.
   SMB_BUSY = 1;                       // Claim SMBus (set to busy)
   SMB_RW = 1;                         // Mark this transfer as a READ

   STA = 1;                            // Start transfer

   while (SMB_BUSY);                   // Wait for transfer to complete

   SFRPAGE = SFRPAGE_SAVE;             // Restore SFR page detector
}

主要连续执行以下操作:发送3个字节。第一个字节是器件寄存器指针。然后读取相同的寄存器(因为指针已经设置)。它确实这样做了。

   while (1)
   {
      TARGET = SLAVE_ADDR;             // Target the Slave for next SMBus
                                       // transfer
      SMB_DATA_OUT[0] = 0x01;          // Device register
      SMB_DATA_OUT[1] = 0x0A;          // Register MSByte
      SMB_DATA_OUT[2] = 0x03;          // Register LSbyte
      SMB_Write();                     // Initiate SMBus write

      // SMBus Read Sequence
      TARGET = SLAVE_ADDR;             // Target the Slave for next SMBus
                                       // transfer
      SMB_Read();
   }

这是一个trace capture of transfer

在我看来,主接收器正在发送额外的ACK。所以我的主要关注点是案例:

SMB_MRADDACK:// 0x40

SMB_MRADDNACK:// 0x48

SMB_MRDBACK:// 0x50

我主要关注的是SMB_MRADDNACK:// 0x48以及它在ISR调用期间通过if语句的次数。我在确切的失败点周围缠头时遇到了一些麻烦。那么这个额外的ACK来自哪里?如果我当时没有弄明白的话,我会在星期一下午回顾这里。

奖金问题:是否存在某种嵌入式堆栈交换?在社区中没有看到任何突出的东西..

2 个答案:

答案 0 :(得分:1)

您的跟踪显示(不包括寻址)发送的三个字节和三个字节读取。我假设您希望写入三个字节,然后只读取两个字节。如果这是真的,那么问题不仅仅是一个虚假的ACK,因为你的主机也继续计时第三个字节。

如果您希望仅使用SiLabs 1中的示例代码读取两个字节,则需要将NUM_BYTES_RD定义为2而不是提供的3.该值在{{1}中使用}和SMB_MRADDACK表示决定是ACK还是STOp。

以防万一(因为你问的是ACK而不是额外的字节),如果你的问题是关于跟踪中SDL行的最终下降(SMB_MRDBACK之后),因为你害怕&#39} #39;是一个额外的确认,然后不要担心。这是STO(在高SCL期间上升)并且是主终止传输的正确行为。

编辑:klamb在下面的评论中是正确的,SMB_MRDBACK状态存在错误。在针对0xff核对SMB0DAT之前,应该保存rec_byte_counter并递增rec_byte_counter。嘲笑那样的SiLabs。

答案 1 :(得分:0)

  case SMB_MRDBACK: //0x50

     SMB_DATA_IN[rec_byte_counter-1] = SMB0DAT; // Store received byte
     rec_byte_counter++;        // Increment the byte counter

     if (rec_byte_counter < NUM_BYTES_RD)
     {
        AA = 1;                    // Send ACK to indicate byte received
     }
     else
     {
        AA = 0;                    // Send NACK to indicate last byte
                                   // of this transfer
     }

     break;