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.

MSPM0G3507: MCU set as slave, I2C SCL short circuited to GND for about 1 second and then released, SCL cannot restore high level(Urgent call for help!!!)

Part Number: MSPM0G3507
Other Parts Discussed in Thread: SYSCONFIG

Tool/software:

As the title, the MCU was used as a slave, and I2C could be read and written by the external host device normally.

However, accidentally pulling the SCL low for a short period of time (about 1 second) and then releasing it, the SCL surprisingly cannot be restored(keep low forever).
After investigation, it was found that there is an issue with M0G3507 (restarting the host device is ineffective, restarting the slave will restore).
Turn on all interrupt flags, unable to respond, even TimeoutA setting cannot respond.
At present, I don't know the cause of this problem, and 3507, as a slave, does not have a flag bit to perceive this anomaly.

Observing phenomenon M0G3507, the slave has pulled SCL low(0 level).

SYSCONFIG_WEAK void SYSCFG_DL_I2C_init(void) {

    DL_I2C_setClockConfig(I2C_INST,
        (DL_I2C_ClockConfig *) &gI2CClockConfig);
    DL_I2C_disableAnalogGlitchFilter(I2C_INST);

    /* Configure Target Mode */
    DL_I2C_setTargetOwnAddress(I2C_INST, I2C_TARGET_OWN_ADDR);
    DL_I2C_setTargetTXFIFOThreshold(I2C_INST, DL_I2C_TX_FIFO_LEVEL_BYTES_1);
    DL_I2C_setTargetRXFIFOThreshold(I2C_INST, DL_I2C_RX_FIFO_LEVEL_BYTES_1);
    DL_I2C_enableTargetTXEmptyOnTXRequest(I2C_INST);

    DL_I2C_enableTargetClockStretching(I2C_INST);
    /* Configure Interrupts */
    DL_I2C_enableInterrupt(I2C_INST,
                           DL_I2C_INTERRUPT_TARGET_RXFIFO_TRIGGER |
                           DL_I2C_INTERRUPT_TARGET_START |
                           DL_I2C_INTERRUPT_TARGET_STOP);


    /* Enable module */
    DL_I2C_enableTarget(I2C_INST);


}



/*
 * 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;
uint32_t gTxFillNum;
/* 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.
     */
    gTxCount = 0;
    gTxLen   = I2C_TX_MAX_PACKET_SIZE;
    DL_I2C_setTimeoutACount(I2C_INST,2);
    DL_I2C_setTimeoutBCount(I2C_INST,2);
    DL_I2C_enableTimeoutA(I2C_INST);
    //DL_I2C_enableTimeoutB(I2C_INST);
    DL_I2C_disableTargetTXEmptyOnTXRequest(I2C_INST);
    DL_I2C_enableInterrupt(I2C_INST, DL_I2C_INTERRUPT_TARGET_RXFIFO_OVERFLOW|
                           DL_I2C_TARGET_INTERRUPT_OVERFLOW|
                           DL_I2C_INTERRUPT_TARGET_TXFIFO_UNDERFLOW|
                           DL_I2C_INTERRUPT_TARGET_TXFIFO_EMPTY |
                           DL_I2C_INTERRUPT_TIMEOUT_A |
                           DL_I2C_INTERRUPT_TIMEOUT_B|
                           DL_I2C_INTERRUPT_TARGET_PEC_RX_ERROR|
                           DL_I2C_INTERRUPT_TARGET_RX_DONE|
                           DL_I2C_INTERRUPT_TARGET_ARBITRATION_LOST|
                           DL_I2C_INTERRUPT_TARGET_GENERAL_CALL|
                           DL_I2C_INTERRUPT_TARGET_STOP|
                           DL_I2C_INTERRUPT_TARGET_RXFIFO_FULL|
                           DL_I2C_INTERRUPT_TARGET_TXFIFO_TRIGGER|
                           DL_I2C_INTERRUPT_TARGET_TX_DONE
                           );

    /* Initialize variables to receive data inside RX ISR */
    gRxCount = 0;
    gRxLen   = I2C_RX_MAX_PACKET_SIZE;
    DL_I2C_enableTargetACKOverride(I2C_INST);
    NVIC_EnableIRQ(I2C_INST_INT_IRQN);

    DL_SYSCTL_enableSleepOnExit();

    /* Go to STOP, the device will wake-up on address match */
    while (1) {
        __WFI();
    }
}
int aa;
int f1= 0;
int f2= 0;
int tmt_flag= 0;
int rvflag= 0;
void I2C_INST_IRQHandler(void)
{
    static bool dataRx = false;

    aa = DL_I2C_getPendingInterrupt(I2C_INST);
    switch (aa) {
        case DL_I2C_IIDX_TARGET_START:
            /* Initialize RX or TX after Start condition is received */
            gTxCount = 0;
            gRxCount = 0;
            dataRx   = false;
            rvflag= 0;
            /* Flush TX FIFO to refill it */
            DL_I2C_flushTargetTXFIFO(I2C_INST);
            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(gRxCount >= 2)
                {
                    DL_I2C_enableTargetACKOverride(I2C_INST);
                    DL_I2C_setTargetACKOverrideValue(I2C_INST,DL_I2C_TARGET_RESPONSE_OVERRIDE_VALUE_NACK);
                    f1 = 1;
                }
                else
                {
                    DL_I2C_enableTargetACKOverride(I2C_INST);
                    DL_I2C_setTargetACKOverrideValue(I2C_INST,DL_I2C_TARGET_RESPONSE_OVERRIDE_VALUE_ACK);
                    f2 = 1;
                }
            }
            break;
        case DL_I2C_IIDX_TARGET_TXFIFO_TRIGGER:
            /* Fill TX FIFO if there are more bytes to send */
            if (gTxCount < gTxLen) {
                gTxFillNum = DL_I2C_fillTargetTXFIFO(
                    I2C_INST, &gTxPacket[gTxCount], 1);
                gTxCount += gTxFillNum;
            } else {
                /*
                 * Fill FIFO with 0x00 if more data is requested than
                 * expected gTxLen
                 */
                while (DL_I2C_transmitTargetDataCheck(I2C_INST, 0x88) != false)
                    ;
            }
            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);
                }
                dataRx = false;
            }
            /* Toggle LED to indicate successful RX or TX */
            DL_GPIO_togglePins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_1_PIN);
            break;

        case DL_I2C_IIDX_TARGET_TXFIFO_EMPTY:
            /* Fill TX FIFO if there are more bytes to send */
            if (gTxCount < 8) {
                gTxFillNum = DL_I2C_fillTargetTXFIFO(
                    I2C_INST, &gTxPacket[gTxCount], 1);
                gTxCount += gTxFillNum;
            } else {
                /*
                 * Fill FIFO with 0x00 if more data is requested than
                 * expected gTxLen
                 */
                while (DL_I2C_transmitTargetDataCheck(I2C_INST, 0x99) != false)
                ;
            }
            break;
        case DL_I2C_IIDX_TARGET_RX_DONE:
            rvflag++;
            break;
            /* 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 */
            break;
        case DL_I2C_IIDX_TARGET_PEC_RX_ERROR:
            aa = 100;
            break;
        case DL_I2C_IIDX_TARGET_ARBITRATION_LOST:
        case DL_I2C_IIDX_TIMEOUT_A:
        case DL_I2C_IIDX_TIMEOUT_B:
        case DL_I2C_IIDX_TARGET_TXFIFO_UNDERFLOW:
        case DL_I2C_IIDX_TARGET_RXFIFO_OVERFLOW:
        case DL_I2C_IIDX_INTERRUPT_OVERFLOW:

            tmt_flag = 1;
            break;
        default:
            break;
    }
}

  • Hi, 

    Method 1:

    Please try to set this bit to 0: I2c->SCTR.SWUEN.

    This will influence wakeup from low power mode while using I2C as wakeup source.

    Method 2:

    Manually enable I2C controller mode (app is using target mode normally),

    Then using controller mode's I2C line status: I2C->MBMON.SDA and SCL,

    then set a timer to read this periodically. If SCL is low for a long timer, then reset I2C INST.

    Regards,

    Helic

  • Hi,Helic Chi,

          Method1:

           I commented out  __WFI, the problem still exists.So it has nothing to do with Wakeup.

            while (1) {
                   //__WFI();
            }

          Method2:

          I described it very clearly in the question that the MCU is causing the problem as a slave mode!

          So I think the two methods you provided are ineffective.

  • Let me describe the phenomenon again:
    MCU I2C as target mode:
    If SCL is pulled to GND for 1 second and released, the SCL line will be permanently pulled low by the MCU and cannot recover to a high level, and the MCU as a slave will not sense this abnormality!

  • Hi, 

    I know your concerned.

    For method 1, you can try to disable I2c->SCTR.SWUEN bit. This SCL pulled down issue may related to this bit's function. Whether to enter low power mode or not is not related to this issue.

    For method 2, you can enable controller and target mode at the same time.

    For controller mode, the only function you need to use is I2C line status register I2C->MBMON.SDA and SCL.

    You can use these two bit to observe I2C line status.

    then set a timer to read this periodically. If SCL is low for a long timer, then reset I2C INST.

    Regards,

    Helic

  • For method 2, then set a timer to read this periodically. If SCL is low for a long timer, then reset I2C INST.

    Don't you think this kind of monitoring is strange? Does it count as a bug in this I2C peripheral?

  • I want to know why MCU keeps lowering SCL without reporting any error indication as a slave? I have used many models of MCUs and have not encountered similar phenomena.

  • Hi, 

    Sorry for inconvenience.

    Please help me to try these two method and solve this issue together.

    Regards,

    Helic

  • Hi, Helic

    For method 1, I disable clock stretch on start,the abnormal phenomenon seems to have disappeared.

    But I don't know if turning off clock stretching will introduce other issues, because in my impression, clock stretching is also an important function for me. It involves the issue of MCU as a slave and data response speed.

  •  sorry,that is SCTR.SCLKSTRETCH disable(not SCTR.SWUEN)

  • It looks like an anomaly introduced by clock stretching(SCTR.SCLKSTRETCH =1).

    Clock stretching causes abnormal low level of i2c SCL.

    The current question is: how to automatically restore the SCL bus to normal while enabling clock stretching?

  • Hi, 

    Disable clock stretch can solve this issue.

    But other methods may solve this issue by not disable clock stretch.

    1. you can try to disable SCTR.SWUEN, this may fix this issue. but I2C no longer support wake up from low power mode.

    how to automatically restore the SCL bus to normal while enabling clock stretching?

    We can not auto restore. Only method is using:

    2. add a timer and using I2C controller mode's I2C line status bit to manually restore it. this need an additional timer and software.

    Regards,

    Helic

  • Hi,Helic

    1.I try to disable SCTR.SWUEN (START phase), this fix this issue.(But as you said:I2C no longer support wake up from low power mode.)

    2.I try to disable SCTR.SCLKSTRETCH, this also fix this issue.

        Does this mean turning off all clock stretching functions on SCL, including the START phase ?

        And what is the principle behind this problem?

        If this feature is turned off, besides being unable to wake up, will there be any other negative effects?

    3.As you said "add a timer and using I2C controller mode's I2C line status bit to manually restore it. this need an additional timer and software".--I don't think this is a good idea, but if this operation is really necessary, I will try again later.(At present, I will disable SCTR.SWUEN.)

        Tks!

  • Hi, 

    It is great to hear this~

    Regards,

    Helic

  • add a timer and using I2C controller mode's I2C line status bit to manually restore it.---I think if this is possible, it's just a precautionary measure. For I2C slave peripheral itselves, third-party monitoring of their working status should not be introduced, and it is still necessary to use the registers of the I2C master for monitoring.This is not very friendly to developers.

         thamk you.

  • Hi, 

    Sorry for your inconvenience.

    That's what we think, too.

    Regards,

    Helic

  • Hi,Helic

       Thank you very much!

        I think you can delve deeper into this issue in the future. Because I personally think that clock stretching is meaningful at any stage of SCL(include START phrase).

        If there is a better understanding or suggestion regarding this phenomenon in the future, or if it is confirmed that it is a characteristic of this chip, please let me know.

        Thank you.

  • Hi, 

    Thanks for your advice.

    I will let you know~

    Regards,

    Helic

  • Clock stretching is a very important feature, particularly on the slave side. The I2C spec prescribes no limit on how long the clock can be stretched.  But I don't know what's happening here. I don't see any Errata (G3/L1/C1) relating to this.

    I expect that TIMEOUTA is triggering, but the description in the TRM [Ref Sec 18.2.3.6] is a bit thin, and is only described for the Controller side. I wonder if/how the I2C unit's state is changed by the occurrence of a TIMEOUTA. A (maybe?) quick experiment would be to disable the timeout and see if the external symptom persists.

    Someone reported something similar in the L1306, but there was no conclusion. TI did demonstrate that this experiment Can succeed under some (unclear) conditions.

    https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/1338968/mspm0l1306-i2c-target-clock-stretch

    Is your .syscfg file something you can post? I suppose I could reconstruct it from the source above, but it would take a while.

    [Edit: Minor clarification.]

  • Hi, Bruce

         I have attached the entire project(include syscfg file).

    i2c_target_rw_multibyte_fifo_interrupts_stop_LP_MSPM0G3507_nortos_gcc.zip

       tks.

  • Hi,  and 

    I am contacting team internally for more details about this.

    Regards,

    Helic

  • I skimmed over the part about SWUEN, but now I think that's the key to it. The description talks about stretching the clock if a Start is seen, but in my test case there is no Start condition (SDA is high all the time). Unfortunately the description is a bit thin -- does this prevent an actual race, or does it just slow down the slave (slightly)?

    Choosing between clock-stretching and the SWUEN feature, perhaps giving up SWUEN is the smaller loss.

  • As I read TRM (SLAU846A) Sec 18.2.3.16, you don't need SWUEN=1 down to SLEEP mode (400kHz bus) or STOP mode (100kHz bus). For those cases, the clock can start up fast enough (or it's already running). [This is suggested as a workaround -- I don't think SWUEN is doing the right thing here.]

  •       Hi,Bruce & Helic

           At present, SWUEN=1 is directly related to the abnormal SCL=0.

          SWUEN=0 At present, there seems to be no problem in debugging.

          Now I can only compromise by setting SWUEN to 1 (abandoning the clock stretching function during the START signal phase).

          Helic said:"I am contacting team internally for more details about this".   ----- Looking forward to a more reasonable explanation from TI!

          Best Regards

  • I think setting SCLKSTRETCH=0 effectively disables SWUEN=1. SWUEN=0 (with SCLKSTRETCH=1) is the smaller change.

    I'm not sure setting SWUEN=0 prevents (all) wakeup from low power mode -- TRM Sec 18.2.3.16 seems to say the less-deep low power modes should work fine. Also, it seems that the consequence of SWUEN=0 in a deeper-sleep (too-slow wakeup) would be to lose that one transaction, which is at least recoverable.

    For clarity: My current test case is Example i2c_target_rw_multibyte_fifo_interrupts running on a (G3507) Launchpad. My only change to the Example was to enable the internal pullups on the I2C pins (my bus has no master, only this one device). Using a patch wire I connect SCL to the GND pin; this generates considerable bounce on SCL, but (according to my scope) SDA is constant (high). I see the failure every time.

    It would appear that SWUEN=1 triggers on any SCL activity, not just on a Start. As long as SDA stays high, there can be no Start (nor Stop, for that matter). As long as the stretching continues, the Master (if it's actually trying to do something) can't continue with the transaction.

    One mystery is why the stretching continues, since the Slave is (as near as I can tell) in SLEEP mode so the clock request should complete immediately.

    Another is why, if I enable a timeout, no TIMEOUTA (nor even BUSBSY) is indicated for this case.

    No one gave me the keys to the Verilog, so I hope TI has something to suggest.

    My next step would be to set up a timer to generate successively longer (bounce-free) pulses , and patch that to SCL to see more detail. I don't know when I'll be able to get to that.

  • Hi, Bruce

          I think all the phenomena you see are the same as what I see, including TimerA detecting SCL low level without response.

          I also hope for a more perfect answer to this question. After all, in practical projects, turning off clock stretching is not a very good solution.

         tks.

  • I had occasion to try this Example on the C1104, and noticed that SWUEN=0. Then I found this snippet in the generated code (ti_msp_dl_config.c):

        /* Workaround for errata I2C_ERR_04 */
        DL_I2C_disableTargetWakeup(I2C_INST);

    which sets SWUEN=0 (it's =1 at Reset).

    I don't see I2C_ERR_04 in the Errata Sheet (SLAZ753A), so I don't know what it says, but it seems a curious coincidence. I think the C1/G3/L1 series share most of their IP.

  •  “ I think the C1/G3/L1 series share most of their IP" -- I think so, too. That's just like the issue with 1306 and 3507, it may be a commonality of IP cores.

  • Hi, 

    Yes, at least G3 and L1 share the I2C IP~

    Regards,

    Helic