37.2.5 Legacy I2C Transaction on I3C Bus

Until the Dynamic Address is assigned, the I3C Target operates in I2C Target mode (OPMD = 0b00) and uses the Static Address stored in the I3CxSADR register to represent itself on the bus. When in this mode, the Controller can use an I2C Write Transfer to write data to the Target directly and an I2C Read Transfer to read data from the Target directly.

Once the Target participates in the Dynamic Address Assignment procedure and receives a Dynamic Address in its I3CxDADR register, it starts to operate in I3C SDR Mode (OPMD = 0b01). The Target will then no longer respond to its Static Address in I3CxSADR register and will NACK any request to its Static Address.

Important:
  1. The I3C Target module does not support 10-bit I2C addressing and clock stretching when operating in I2C mode. Refer to I2C Backward Compatibility for details.
  2. Additional pin configuration is required for the SDA and SCL pads to be I2C/SMBus-compatible. Refer to I3C Pad Compatibility with I2C/SMBus Levels for more information.
  3. It is possible for an I2C Transaction to take place even when the bus is configured to operate in I3C SDR Mode or when an I3C SDR Transaction is in progress. For example, the Controller can choose to transmit I3C Broadcast Address 7’h7E/W, followed by a Restart and I2C Static Address to begin an I2C Transaction while in I3C SDR Mode.
  4. The Maximum Read and Write Lengths specified in the I3CxMRL and I3CxMWL registers have no effect when the module is operating in I2C Target mode (OPMD = 0b00).
CAUTION: This Target device supports a special Static Address SDR Mode where the module can operate in I3C SDR mode (OPMD = 0b01) using its Static Address without requiring a Dynamic Address. When this special mode is activated, the module will respond to the Static Address to participate in an I3C SDR transaction and not in Legacy I2C transaction.
To address the Target directly, the Controller sends a Start or a Restart, followed by the I2C Address Header. The address header consists of the Target’s 7-bit Static Address, followed by a R/W bit from the Controller and an ACK/NACK bit from the Target. The R/W bit is ‘0’ (write) for I2C Write Transfer and ‘1’ (read) for I2C Read Transfer. If the Static Address in the address header matches with the Target’s Static Address in the I3CxSADR register, the Target ACKs the request and sets the SADRIF interrupt flag bit. The RNW status bits are also set according to the R/W bit in the address header. The Target NACKs the request if the Static Address in the address header does not match the address in I3CxSADR register.
When an I2C transaction is initiated, the ACK/NACK response from the Target is controlled by the ACKP and ACKPOS bits and is also based on the status of the Transmit FIFO/Buffer. Refer to Table 37-6 for more information.
Important: The RNW status bits only apply to Private I3C/I2C Transfers. The R/W bit in the address header is not captured during non-Private Transactions (such as CCC, Hot-Join or IBI).
In I2C Write Transfer, following the address header, the Controller will continue to send the 8-bit Data on the bus. The data become available for the user to read from the I3CxRXB Receive Buffer and the Target sends an ACK on the bus. This process continues until the Controller ends the transaction with a Stop or Restart, after which the Transaction Complete TCOMPIF flag is set, and the RNW status bits continue to hold the value until cleared by the user or overwritten in the next transaction. The general frame format for I2C Write Transaction is shown in Figure 37-16 and a pseudo-code is shown in Pseudo-code for Legacy I2C Write Transaction below.
In I2C Read Transfer, following the address header from the Controller, the Target sends an 8-bit Data on the bus. The user writes the data to be sent to the I3CxTXB Transmit Buffer. If the Controller accepts the data and sends an ACK on the bus, I2CACKIF interrupt flag is set. If the Controller declines the data and sends a NACK on the bus, I2CNACKIF interrupt flag is set, the Target stops sending data until the Controller terminates the transaction with a Stop or Restart. The Transaction Complete TCOMPIF flag is then set, and the RNW status bits continue to hold the value until cleared by the user or are overwritten in the next transaction. The general frame format for I2C Read Transaction is shown in Figure 37-17 and a pseudo-code is shown in Pseudo-code for Legacy I2C Read Transaction below.
Important: All the Transmit and Receive Buffers and FIFO status and interrupt flags continue to operate as expected in I2C Target mode as well. Refer to the Transmit and Receive Buffers and FIFO section for more information.
Figure 37-16. Legacy I2C Write Transfer Frame Format
Figure 37-17. Legacy I2C Read Transfer Frame Format

Pseudo-code for Legacy I2C Write Transaction

uint8_t rxData[SIZE];

void I3C1_Target_I2CWrite_Setup()
{
    // Default I2C Write ACK setting
    I3C1CON0bits.ACKP = 0;     // 0=ACK; 1=NACK
    I3C1CON1bits.ACKPOS = 0;   // 0=One-shot disabled; 1=enabled
}

void I3C1_Target_I2CWrite()
{
    uint16_t i = 0;

    while(i < SIZE) {
        // Wait for data to become available in Rx Buffer
        while(!I3C1STAT0bits.RXBF);

        // Read data from Receive buffer and increment pointer
        rxData[i++] = I3C1RXB;

        // Perform error checking if desired
        // Check for Rx Overrun Error or Rx Read Error
        // if(I3C1ERRIR0bits.RXOIF || I3C1ERRIR1bits.RXREIF) { ... }

        // Check for End of Transaction
        if(I3C1PIR1bits.TCOMPIF) {
            return 0;   // end of transaction
        }
    }

    // Code execution reaches here when I2C write has exceeded SIZE
    // Check for Rx errors and Transaction Complete
    // if(I3C1ERRIR0bits.RXOIF || I3C1ERRIR1bits.RXREIF) { ... }
    if(I3C1PIR1bits.TCOMPIF) {
        return 0;       // end of transaction with SIZE overflow
    }
}

void main(void) {
    // Perform System and I3C Initialization
    SYSTEM_Initialize();
    I3C1_Target_Setup();

    // Private Write Setup
    I3C1_Target_I2CWrite_Setup();

    while(1) {
        // Set one-shot of I2C Write ACK if applicable
        I3C1CON1bits.ACKPOS = 1;

        // Check if Static Address Match occured in Write mode
        if(I3C1PIR0bits.SADRIF && I3C1STAT0bits.RNW == 0b10) {
            I3C1_Target_I2CWrite();
        }
    }
}

Pseudo-code for Legacy I2C Read Transaction

uint8_t txData[SIZE];

void I3C1_Target_I2CRead_Setup()
{
    // Initialize data to send
    for(uint8_t i=0; i<SIZE; i++) {
        txData[i] = i;
    }

    // Default I2C Read ACK setting
    I3C1CON0bits.ACKP = 0;     // 0=ACK; 1=NACK
    I3C1CON1bits.ACKPOS = 0;   // 0=One-shot disabled; 1=enabled

}

void I3C1_Target_I2CRead()
{
    uint8_t i = 0; 

    while(i < SIZE) {
        // Wait for Tx Buffer to become empty
        while(!I3C1STAT0bits.TXBE);

        // Write data to Transmit buffer and increment pointer
        I3C1TXB = txData[i++];

        // Perform error checking if desired
        // Check for Tx Underrun Error or Tx Write Error
        // if(I3C1ERRIR0bits.TXUIF || I3C1ERRIR1bits.TXWEIF) { ... }

        // Check for End of Transaction
        if(I3C1PIR1bits.TCOMPIF) {
            if(I3C1ERRIR0bits.I2CNACKIF) {  
                return 0;   // end of transaction w Controller NACK
            }
            else return 0;  // graceful end of transaction
        }
    }

    // Code execution reaches here when I2C read has exceeded SIZE
    // Check for Tx Errors or Transaction Complete
    // if(I3C1ERRIR0bits.TXUIF || I3C1ERRIR1bits.TXWEIF) { ... }
    if(I3C1PIR1bits.TCOMPIF) {
        return 0;       // end of transaction with SIZE overflow
    }

}

void main(void) {
    // Perform System and I3C Initialization
    SYSTEM_Initialize();
    I3C1_Target_Setup();

    // Private Read Setup
    I3C1_Target_I2CRead_Setup();

    while(1) {
        // Set one-shot of I2C Read ACK if applicable
        I3C1CON1bits.ACKPOS = 1;

        // Check if Static Address Match occured in Read mode
        if(I3C1PIR0bits.SADRIF && I3C1STAT0bits.RNW == 0b01) {
            I3C1_Target_I2CRead();
        }
    }
}