AM13E23019: Support UC3 in basic I2C

Part Number: AM13E23019

Hi Experts,

My cusotmer is try to implement UC3 I2C in the basic I2C module of AM13 

image.png

There is no repeated start in the tree bytes.

But the basic I2C seems to be limited to one byte per transfer, and we can not avoid the stop after one byte.

image.png

How to do this in the basic I2C module?

Regards,

Hang

  • Hi Hang,

    The AM13E230x is operating as the controller/master I2C device, is that correct? Are they using a specific example from the SDK as a starting point? Sending more than one byte without a STOP condition should absolutely be possible on the basic module instance.

    Best Regards,

    Delaney

  • Hi Delaney,

    Yes, we don't have a starting yet because we don't know how to do it. Could you please help demonstrating on how this can be achieved? You can pick any example as starting point.

    Regards,

    Hang

  • Hi Hang,

    From the register level, to send more than one byte in a frame on a basic I2CC instance, more software intervention is needed than on the advanced I2CC instances. They need to set the CTR.FRM_START bit after each byte is transmitted and wait to set the CTR.STOP bit until right before the last data byte. For example, to send a frame with 3 data bytes: START | TARGET ADDR | W | ACK | DATA0 | ACK | DATA1 | ACK | DATA2 |ACK | STOP, you would do the following on the I2CC:

    • Initialize I2CC
    • Set TA.ADDR and TA.MODE accordingly
    • Set TA.DIR to transmit mode
    • Write some or all data bytes to the TX FIFO
    • Enable the TXDONE interrupt in IMASK - this will trigger after each data byte because BLEN is hardcoded on 1
    • Set CTR.START and CTR.FRM_START
      • After this, START | TARGET ADDR | W | ACK | DATA0 should get automatically sent on the line
    • In each TXDONE ISR - not the last byte:
      • (Optional) Write the next data byte to the TX FIFO if this wasn't done earlier
      • Set CTR.FRM_START
    • In the TXDONE IDR for the last byte:
      • (Optional) Write the last data byte to the TX FIFO if this wasn't done earlier
      • Set CTR.FRM_START and CTR.STOP

    Let me know if this explanation works for them, if not I can provide a simple software example. 

    Best Regards,

    Delaney

  • Thank you. I think you just answered this question over here as well:

     RE: MSPM33C321A: Does UC13_X I2C Support Multi-Byte Write on MSPM33C321A? 

    (I wasn't able to glean this from the MSPM33 TRM.)

  • Hi Bruce, 

    Yes, it looks like the question wasn't fully answered/resolved on that other thread. I'm also not sure that the UNICOMM IP implementation they are mentioning has the same feature set as AM13E230x basic instances.

    Hang,

    Either way please let me know if the above sequence works for the customer. I have taken a note to update the AM13E230x TRM with this information in a future release and also have the software team add this example in the SDK. 

    Best Regards,

    Delaney

  • Hi Dalaney,

    Sorry for the delay. 

    I tried the above sequence, however the stop are still there

    image.png

    I am not sure if I am following the sequence correctly. Could you please help providing an demo? 

    Just generate the blow sequence:

    image.png

    Regards,

    Hang

  • This appears to show the SLA (slave address) byte encountering a NACK; in that case the master will Stop immediately.

    Are you fairly certain about your target device's address? It looks like you're using 0x3C.

  • Hi Hang,

    Bruce is correct, that scopeshot shows a NACK to the address byte. Can you verify that the target address is correct?

    Best Regards,

    Delaney

  • If it's useful, this is what I came up with for the MSPM33. Its Unicomm appears very similar to that in the AM13E23.

    You'll still need to resolve the NACK you're seeing.

    -----------------------------------------------------------------------------------------------------

    [Please pardon the formatting; Insert->Code appears to be broken.]

    -----------------------------------------------------------------------------------------------------
    #define I2C_Regs UNICOMM_Inst_Regs
    ///
    //  writereg()
    //
    uint16_t
    writereg(I2C_Regs *i2c, uint8_t i2caddr, uint8_t reg, uint8_t regval)
    {
        uint32_t stat;
        uint8_t val[2];
        uint16_t r;
        uint32_t len = sizeof(val);
       
        while (!(DL_I2CC_getStatus(i2c) & DL_I2CC_STATUS_IDLE)); // Keep it neat

        DL_I2CC_clearInterruptStatus(i2c, (DL_I2CC_INTERRUPT_TX_DONE|DL_I2CC_INTERRUPT_NACK));// Clear stale

        val[0] = reg;
        val[1] = regval;
        DL_I2CC_fillTXFIFO(i2c, &val[0], len);
       
    #if I2CC_BASIC      // Byte at a time (Basic)
        uint32_t rotxe = i2c->i2cc->CTR & UNICOMMI2CC_CTR_RD_ON_TXEMPTY_MASK; // Preserve this
        i2c->i2cc->TA = ((i2caddr << UNICOMMI2CC_TA_ADDR_OFS) | UNICOMMI2CC_TA_DIR_TRANSMIT);
        i2c->i2cc->CTR = rotxe | (UNICOMMI2CC_CTR_FRM_START_ENABLE
                        | UNICOMMI2CC_CTR_START_ENABLE);

        while (1)
        {
            do {    // Spin for next byte
               stat = DL_I2CC_getRawInterruptStatus(i2c, (DL_I2CC_INTERRUPT_TX_DONE|DL_I2CC_INTERRUPT_NACK));
            }  while(!stat);
            DL_I2CC_clearInterruptStatus(i2c, (DL_I2CC_INTERRUPT_TX_DONE|DL_I2CC_INTERRUPT_NACK)); // Clear 'em
            if (stat & DL_I2CC_INTERRUPT_NACK )
                break;                  // We won't progress now
            --len;
            if (len == 0)
                break;
            i2c->i2cc->CTR = rotxe | UNICOMMI2CC_CTR_FRM_START_ENABLE;  // Poke for next byte
        }
        i2c->i2cc->CTR = rotxe | UNICOMMI2CC_CTR_STOP_ENABLE|UNICOMMI2CC_CTR_FRM_START_ENABLE; // Stop

    #else // !I2CC_BASIC        // Burst (Advanced)
        DL_I2CC_startTransfer(i2c, i2caddr, DL_I2CC_DIRECTION_TX, sizeof(val)); // Write 2 bytes
        do {
            stat = DL_I2CC_getRawInterruptStatus(i2c, (DL_I2CC_INTERRUPT_TX_DONE|DL_I2CC_INTERRUPT_NACK));
        }  while(!stat);
    #endif // I2CC_BASIC    
        if (stat & DL_I2CC_INTERRUPT_NACK)
        {
            DL_I2CC_flushTXFIFO(i2c);      
            r = (uint16_t)-1;
        }
        else
        {
            r = 0;
        }
        return(r);
    }
    -----------------------------------------------------------------------------------------------------
    [Edit: Multiple, trying to get Insert->Code to work.]
  • Hi Hang,

    Let me know if the above code works for the customer.

    Best Regards,

    Delaney

  • Hi Delaney,

    It make senses. I will test a come back soon.

    Regards,

    Hang

  • Hi Hang,

    Sounds good, let me know.

    Best Regards,

    Delaney

  • Hi Delenay & Bruce,

    I used an external loopback to handle the ACK. After adding it, the above code yields an unexpected waveform, where Sr and P is inserted in the middle of the transfer. Although the loopback is receiving the correct data, I think this result is not enough.

    I attached example I test. The example is modified on top of the loopback exmaple and it;s largly simplified (BTW, I don't understand why the original extern  loopback are so big when the purpose is only to demo a simple loopback. It took a long time to get rid of the part that's not necessary. I believe it's better to keep examples as simple as possible, it would help espcially for ones who is add more codes on top of it. Please help conveying this message to SDK team if possible..)

    i2c_uc3_test.zip

    The external connection of this example is identical to the original example, you may take the connection in the original readme.

    Regards,

    Hang

  • I don't see this symptom on the MSPM33. I think I mostly duplicated your experiment, though my device only has one Basic-mode Unicomm (UC13) so I gave it to the Controller side. 

    The (false) Sr occurs during the ACK cycle; I don't know how software (alone) can do this. My first thought was something about clock-stretching, though I see that's enabled on both ends. 

    The Target didn't seem to notice the stray Sr/P sequence, since it went ahead and ACK-ed the next byte. I'm wondering if your Analyzer has a different view of Vih than the Target. Are you relying (only) on the internal pullups? If you have one, a scope could disambiguate a slow-rise condition. Alternatively, try slowing the bus down.

  • Hi Burce,

    Upon examating the waveform in the osciliscope, I found that the Sr and P are actually some the simutainous flip of SCl and SDA, occured at the last clock of the second byte. This should be the behavious of the ACK, but recognized as Sr P due to some propagation delay or threshold issues.

    I would consdier the basic I2C is achieved and would share with customer to text, thanks for the support!

    Meanwhile, may I know what's introducing the SCL gap between the sccond and the third byte?

    Regards,

    Hang 

  • The gap is almost certainly software. Though the TRM doesn't say explicitly, I expect TXDONE is issued only after the ACK (the I2C is thus momentarily idle), so there's a delay while the software is scrambling to load the next byte FRM_START. 

    In Burst (Advanced) mode the TXDONE only happens after the last byte in the burst; since this is typically combined with the Stop you don't see the gap. I suppose you would see this in Burst mode when using Repeated Start, though I use Read On TXEmpty, so there's no software involved in the Repeated Start.

    [Edit: Minor clarification]

  • If it's useful, here is the companion "readreg" function:

    ----------------------------------------------

    [Please pardon the formatting. Insert->Code still seems to be malfunctioning.

    ----------------------------------------------

    ///
    //  readreg()
    //
    uint16_t
    readreg(I2C_Regs *i2c, uint8_t i2caddr, uint8_t reg)
    {
        uint32_t stat;
        uint8_t val[1];
        uint8_t rval[2];    // Artificially extended 1->2 bytes for demonstration
        uint32_t len;
        uint32_t i;
        uint16_t r;
       
        while (!(DL_I2CC_getStatus(i2c) & DL_I2CC_STATUS_IDLE));    // Keep it neat
        DL_I2CC_clearInterruptStatus(i2c, (DL_I2CC_INTERRUPT_RX_DONE|DL_I2CC_INTERRUPT_NACK));// Clear stale

        val[0] = reg;
        DL_I2CC_fillTXFIFO(i2c, &val[0], sizeof(val));
        len = sizeof(rval);
    #if I2CC_BASIC      // Non-burst (Basic)
        uint32_t ctr;
        uint32_t rotxe = UNICOMMI2CC_CTR_RD_ON_TXEMPTY_MASK; // We always use this
        //  Slave address, Rx
        i2c->i2cc->TA = ((i2caddr << UNICOMMI2CC_TA_ADDR_OFS) | UNICOMMI2CC_TA_DIR_RECEIVE);
       
        //  Issue the start, then the Tx, then Rx the first byte
        ctr = rotxe | (UNICOMMI2CC_CTR_FRM_START_ENABLE
                        | UNICOMMI2CC_CTR_START_ENABLE);
        if (len > 1)
            ctr |= UNICOMMI2CC_CTR_ACK_ENABLE;  // Ack all but the final byte
        i2c->i2cc->CTR = ctr;                   // Start

        i = 0;
        while (1)
        {
            do {
               stat = DL_I2CC_getRawInterruptStatus(i2c, (DL_I2CC_INTERRUPT_RX_DONE|DL_I2CC_INTERRUPT_NACK));
            }  while(!stat);
            DL_I2CC_clearInterruptStatus(i2c, (DL_I2CC_INTERRUPT_RX_DONE|DL_I2CC_INTERRUPT_NACK));// Clear stale
            if (stat & DL_I2CC_INTERRUPT_NACK )
            {
                DL_I2CC_flushTXFIFO(i2c);       // Keep it neat
                r = (uint16_t)-1;
                break;
            }
            rval[i++] = DL_I2CC_receiveData(i2c);
            --len;
            if (len == 0)
                break;

            ctr = rotxe | UNICOMMI2CC_CTR_FRM_START_ENABLE; // rotxe may not be needed here
            if (len > 1)
                ctr |= UNICOMMI2CC_CTR_ACK_ENABLE;  // Ack all but the final byte
     
            i2c->i2cc->CTR = ctr;   // Next byte
        }
        //  Observed behavior: Combining STOP with the last byte doesn't issue the Stop.
        i2c->i2cc->CTR = rotxe|UNICOMMI2CC_CTR_STOP_ENABLE|UNICOMMI2CC_CTR_FRM_START_ENABLE;
        r = rval[0];

    #else // !I2CC_BASIC    // Burst (Advanced)
        DL_I2CC_startTransfer(i2c, i2caddr, DL_I2CC_DIRECTION_RX, len); // Write reg number then read 1 byte
        do {
            stat = DL_I2CC_getRawInterruptStatus(i2c, (DL_I2CC_INTERRUPT_RX_DONE|DL_I2CC_INTERRUPT_NACK));
        }  while(!stat);
        if (stat & DL_I2CC_INTERRUPT_NACK )
        {
            DL_I2CC_flushTXFIFO(i2c);       // Keep it neat
            r = (uint16_t)-1;
        }
        else
        {
            for (i = 0 ; i < len ; ++i)
            {
                r = DL_I2CC_receiveData(i2c);
            }
            r = rval[0];
        }
    #endif // I2CC_BASIC
        return(r);
    }
  • Hi Hang, Bruce,

    Though the TRM doesn't say explicitly, I expect TXDONE is issued only after the ACK (the I2C is thus momentarily idle), so there's a delay while the software is scrambling to load the next byte FRM_START. 

    I will check internally to confirm this.

    Best Regards,

    Delaney