This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

MSPM0G1507: When communicating in Fast-mode (400kHz), the controller side shifts the data by 1 byte in repeated start operation

Part Number: MSPM0G1507

H iexperts,

This is a continuation of the thread below.
:(1) MSPM0G1507: When communicating in Fast-mode (400kHz), the controller side shifts the data by 1 byte. - Arm-based microcontrollers forum - Arm-based microcontrollers - TI E2E support forums 

I have confirmed the customer’s actual usage. The MSPM0G1507 will be used as the target device, and the controller may perform both normal START/STOP communication and repeated START communication.

In the actual use case, this is not an echo operation. As shown in the figure below, the data received from the controller is treated as address information, and the target returns the data stored in its address space starting from the address indicated by the last received data byte. Therefore, each time the target receives data, it needs to move the corresponding data into the TXFIFO in advance.

With that in mind, although this is a repeated question:
Q: When using repeated START, are there any concerns with moving data into the TXFIFO at the timing of the RxFIFO trigger (threshold = 1) as a method of preparing the TXFIFO before the repeated START trigger occurs? If there is any other recommended approach, please let me know.

For reference, I modified the program original thread provided as attached so that the data is moved into the TXFIFO at the timing of the RxFIFO trigger (threshold = 1). I would appreciate it if you could let me know whether there are any concerns with this method or whether there is another recommended approach.

/*
 * Copyright (c) 2021, Texas Instruments Incorporated
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * *  Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * *  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * *  Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "ti_msp_dl_config.h"

/* Maximum size of TX packet */
#define I2C_TX_MAX_PACKET_SIZE (16)

/* Maximum size of RX packet */
#define I2C_RX_MAX_PACKET_SIZE (16)

/* Data sent to Controller in response to Read transfer */
uint8_t gTxPacket[I2C_TX_MAX_PACKET_SIZE] = {0x00};

/* Counters for TX length and bytes sent */
uint32_t gTxLen, gTxCount;
uint8_t TX_Flag;
/* Data received from Controller during a Write transfer */
uint8_t gRxPacket[I2C_RX_MAX_PACKET_SIZE];
/* Counters for TX length and bytes sent */
uint32_t gRxLen, gRxCount;

int main(void)
{
    SYSCFG_DL_init();

    /* Set LED to indicate start of transfer */
    //DL_GPIO_setPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_1_PIN);

    /*
     * Fill FIFO with data.
     * Note that transactions are initiated by the Controller, so this example
     * only fills the buffer and the Target device will send this data when
     * requested by the Controller.
     * The TX FIFO is initialized to zero and then it will echo the data sent
     * by Controller.
     */
     TX_Flag = 0;
    gTxCount = 0;
    gTxLen   = I2C_TX_MAX_PACKET_SIZE;
    DL_I2C_enableInterrupt(I2C_INST, DL_I2C_INTERRUPT_TARGET_TXFIFO_TRIGGER);

    /* Initialize variables to receive data inside RX ISR */
    gRxCount = 0;
    gRxLen   = I2C_RX_MAX_PACKET_SIZE;

    NVIC_EnableIRQ(I2C_INST_INT_IRQN);

    /* Calling WFI after calling DL_SYSCTL_enableSleepOnExit will result in
     * only ISR code to be executed. This is done to showcase the device's
     * low power consumption when sleeping.
     */
    DL_SYSCTL_enableSleepOnExit();

    while (1) {
        __WFI();
    }
}

void I2C_INST_IRQHandler(void)
{
    static bool dataRx = false;
 DL_GPIO_setPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_1_PIN);
    switch (DL_I2C_getPendingInterrupt(I2C_INST)) {
        case DL_I2C_IIDX_TARGET_START:
            /* Initialize RX or TX after Start condition is received */

            gRxCount = 0;
            dataRx   = false;
            /* Flush TX FIFO to refill it */
            if(TX_Flag == 0)
            {
                DL_I2C_flushTargetTXFIFO(I2C_INST);
                gTxCount = 0;
            }

             
            break;
        case DL_I2C_IIDX_TARGET_RXFIFO_TRIGGER:
            /* Store received data in buffer */
            dataRx = true;
            while (DL_I2C_isTargetRXFIFOEmpty(I2C_INST) != true) {
                if (gRxCount < gRxLen) {
                    gRxPacket[gRxCount++] = DL_I2C_receiveTargetData(I2C_INST);
                } else {
                    /* Prevent overflow and just ignore data */
                    DL_I2C_receiveTargetData(I2C_INST);
                }
            }
            if (dataRx == true) {
                for (uint16_t i = 0;
                     (i < gRxCount) && (i < I2C_TX_MAX_PACKET_SIZE); i++) {
                    gTxPacket[i] = gRxPacket[i];
                    DL_I2C_flushTargetTXFIFO(I2C_INST);
                }
                gTxCount = 0;
                gTxCount += DL_I2C_fillTargetTXFIFO(
                    I2C_INST, &gTxPacket[gTxCount], (gTxLen - gTxCount));
                TX_Flag = 1;
            }

            break;
        case DL_I2C_IIDX_TARGET_TXFIFO_TRIGGER:
            /* Fill TX FIFO if there are more bytes to send */
            if (gTxCount < gTxLen) {
                gTxCount += DL_I2C_fillTargetTXFIFO(
                    I2C_INST, &gTxPacket[gTxCount], (gTxLen - gTxCount));
            } else {
                /*
                 * Fill FIFO with 0x00 if more data is requested than
                 * expected gTxLen
                 */
                while (DL_I2C_transmitTargetDataCheck(I2C_INST, 0x00) != false)
                    ;
            }
            DL_GPIO_togglePins(GPIO_LEDS_PORT, GPIO_LEDS_PIN_0_PIN);
            break;
        case DL_I2C_IIDX_TARGET_STOP:
            /* If data was received, echo to TX buffer */
            if (dataRx == true) {
                for (uint16_t i = 0;
                     (i < gRxCount) && (i < I2C_TX_MAX_PACKET_SIZE); i++) {
                    gTxPacket[i] = gRxPacket[i];
                    DL_I2C_flushTargetTXFIFO(I2C_INST);
                }
                gTxCount = 0;
                gTxCount += DL_I2C_fillTargetTXFIFO(
                    I2C_INST, &gTxPacket[gTxCount], (gTxLen - gTxCount));
                TX_Flag = 1;
                dataRx = false;
            }else {
            TX_Flag = 0;
            }
        

            
            /* Toggle LED to indicate successful RX or TX */
           
            break;
        case DL_I2C_IIDX_TARGET_RX_DONE:
            /* Not used for this example */
        case DL_I2C_IIDX_TARGET_RXFIFO_FULL:
            /* Not used for this example */
        case DL_I2C_IIDX_TARGET_GENERAL_CALL:
            /* Not used for this example */
        case DL_I2C_IIDX_TARGET_EVENT1_DMA_DONE:
            /* Not used for this example */
        case DL_I2C_IIDX_TARGET_EVENT2_DMA_DONE:
            /* Not used for this example */
        default:
            break;
    }
     DL_GPIO_clearPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_1_PIN);
}

Best regards,
O.H

  • Hi O.H,

    I'm not sure whether I understand correctly, you want to fill I2C TXFIFO when there is first RX byte received in I2C target, so the I2C data will be ready before the "R" sequence in repeat start situation, am I right?

    It sounds working, just notice to flush TXFIFO before the first time you fill the TXFIFO. Have you tested on this logic and meet any problem?

  • Hi Pengfei Xie,

    Thank you for your reply.

    I'm not sure whether I understand correctly, you want to fill I2C TXFIFO when there is first RX byte received in I2C target, so the I2C data will be ready before the "R" sequence in repeat start situation, am I right?

    That is mostly correct. More precisely, I am not particular about the exact timing of when the RX byte is received. Any timing is acceptable as long as the I2C data is prepared before the "R" sequence in the repeated START transaction.

    It sounds working, just notice to flush TXFIFO before the first time you fill the TXFIFO. Have you tested on this logic and meet any problem?

    At this point, I have not seen any issues in the actual behavior.
    The reason I am asking is that I would like to confirm whether TI has any recommended method for this use case, and whether there are any potential concerns or issues that could be expected with this approach.

    Best regards,
    O.H

  • Hi Pengfei Xie,

    Sorry for rush you. Is there any additional comment or information?

    Best regards,
    O.H

  • Hi O.H

    Sorry for late reply.

    Understood that you just want to make sure the TX data is well prepared before read sequence of repeat start command.

    Just one comment from my side, if the TX data is fixed, then it is fine that you prepare the TX data at any time before it is read by controller. But if this TX data could be changed by application or should be different according to received data from controller, then the TX data has better to be prepared when the first byte of repeat start command received from controller.

  • Hi Pengfei Xie,

    Thank you for your reply.

    then the TX data has better to be prepared when the first byte of repeat start command received from controller.

    If "the TX data is prepared when the first byte of the repeated START command is received", the data shift into the TX FIFO cannot complete in time, resulting in a 1-byte shift. This is exactly the issue we are facing, and that is why we are asking this question.

    As background, when using the sample project as a base at 400 kHz with both STOP+START and repeated START, we encountered the issue that the data shifts by 1 byte.
    For STOP+START(400 kHz), we understand from your answer in the other thread that the recommended solution from TI is to prepare the TX data at the STOP interrupt.

    For repeated START(400 kHz), does TI have any recommended usage or recommended implementation method? Or is there no specific recommended method?
    At present, may I consider the current method — moving the data into the TX FIFO at the RxFIFO trigger timing (threshold = 1) — to be the recommended approach?

    The hardware supports communication up to 1 MHz. If you have already performed communication tests at 400 kHz or higher, I would appreciate it if you could share any operating method or implementation approach that worked well in those cases.

    Best regards,
    O.H

  • Hi O.H,

    From your current code, it looks like the data transmitted from TXFIFO in "R" sequence is the same data with previous received data in RX buffer. If this is the function you desired in the application, then your current implementation would work well, no other recommendation from my side.

  • Hi Pengfei Xie,

    Thank you for your reply.

    I understand that there is no particular recommended approach for this case, and that it needs to be evaluated and confirmed based on actual operation.

    If you have any information on this, could you please let me know whether your team has conducted communication tests using repeated START at 400 kHz or higher? The sample project appears to be based on 100 kHz.
    Best regards,
    O.H
  • Do you intend to do this without clock-stretching (as mentioned in the other thread)? Clock-stretching is the primary (arguably the only) flow control mechanism in I2C. Without flow-control, there will always be a race. The magnitude of the race in the repeated-start case is (1+8+1) bit times, or 25usec with a 400kHz I2C -- 800 SYSCLKs at 32MHz, but that includes any Wakeup time.

    Your charts seem to describe a very restricted version of the "register" (or perhaps "EEPROM") model, where:

    1) No data is written, so every Write is 3 bytes (memory address).

    2) Every Write is followed by a Read of a known length.

    If so, your Rx code could count to 3 and start filling the TXFIFO immediately. Without (2), there's a chance this will leave stale data in the TXFIFO; there is a mechanism in the I2C unit for dealing with this but it requires clock-stretching to work. Without (1), the Target won't know whether there is a Read coming until the R/W bit in the subsequent Start, so the magnitude of the race is approximately 0.

    Alternatively, you could require a Stop (no repeated-Start) and an artificial delay in the Controller between the Stop(W) and Start (R). There is some precedent for this (rather rare though), and it just improves the race rather than eliminating it.