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.

MSPM0L1306: I2C ACK/NACK issue with Controller implementation....

Part Number: MSPM0L1306
Other Parts Discussed in Thread: ASH

I've hit a bit of a wall with the I2C controller because I have a peripheral that always ACK's transmitted bytes. I can't change that behaviour, it ain't my chip - specifically it's a SOIC RTC. The datasheet is quite clear it won't NACK the last byte, and I can see why. It doesn't know how many bytes I'm going to send, until I send them. There are *lots* of I2C peripherals that work this way.

The TI datasheet is a bit gnarly, lots of words not so many functional sentences.

In respect of the NACK business, the TI datasheet is fairly clear. It won't emit a stop, unless there's a NACK, but it's never coming.

So the only question is how to break the hardware state machine so I can emit a "stop only". The thing is, even if I request a transaction without a stop, its still waiting for the NACK.

I can see that someone somewhere might have thought the master implementation should be symmetrical to the slave, but it doesn't work like that, and I've already explained why above.

The thing that gives me less confidence is that the naming convention in the TI datasheet isn't self consistent. The text refers to the driving element of the I2C bus variously as both MASTER and CONTROLLER, which makes the acronyms used - quite confusing.

The datasheet does explain how the MSPM0 I2C master/controller can be used sucessfully in loopback mode with the associated target on the I2C slice. I don't doubt that this works, but the trouble is the I2C port is looking pretty limited for a dismal reason.

What am I missing? How can I make this work?

Many thanks,

Andy Ash.

 

  • Actually, there's another gripe I have with the documentation. It's peppered with descritions of how to use the "RUN" bit.

    The trouble is that the I2C slice has no "RUN" bit.

    Indeed, there is only one slightly dodgy sentence in the 1800 page tome, that loosely associates "RUN" and "BURSTRUN", where the latter actually exists. It doesn't work quite like that. Does BURSTRUN reset itself? Is setting the register an edge or a level?  It's like somoene did a cut and paste from MSP430, but the slice it represents is new/different.

    It really does look like the R/W registers are protected whilst the machine is busy.

    ..... But what does a victim do when it's busy waiting for something that's never coming?

  • I'm not sure what statement you're referring to,  but the Master Transmitter doesn't require a NACK from the slave in order to send a Stop. A Master Receiver is required to NACK the last byte in order to retrieve control of SDA from the slave.

    Observed behavior is that BURSTRUN doesn't auto-clear, and it is the writing of a value with a 1 in that position that causes the transaction to start. You need to be wary of using RMW on that register.

  • I'm using the TI driver library 2.09.00.01.

    In the sense of the way that works, it is RMW but you don't get a choice because they have separate functions for each of the control bits in the register. Are you suggesting that using the TI driver library is wrong?

    From my perspective, I think I understand what you are saying, and I agree - which is why I clear BURSTRUN, make a modification to the control register and then set BURSTRUN to invoke it. This is exactly why I lament the documentation. The operation of BURSTRUN is obviously critical, but nevertheless ambiguous.

    Even though I clear BURSTRUN, I can see that the BUSY flag remains set, and the master remains unresponsive.

    Are you suggesting that I cannot break the wait using the TI functions, and I need to access the register directly? That is to say, set STOP, clear START and set BURSTRUN in one write? I don't know that I can do that with the driver fnctions I have.

    However I look at it, it seems fairly clear that no stop is being generated, and I can't get it to generate one.

  • Also, thanks for your time.

    :-)

  • I'm pretty sure clearing BURSTRUN doesn't cause the transfer to stop. The I2C unit will continue to run until MBLEN is reached.

    More generally you probably don't want to micro-manage the transaction. Just put your Tx data into the FIFO , call StartTransfer, and wait for it to finish. To read registers use RD_ON_TXEMPTY

    [I don't have my materials here so I might have mis-remembered some names]

    [Edit: but see also 

     MSPM0G3507: Hazard in DL_I2C_enableControllerReadOnTXEmpty() 

  • Hi Bruce,

    I'm going to give you the "resolution" because you were kind enough to try.

    I have managed to get it working. I'm not completely sure what the cause was, but I have an idea which I will explain in case it helps someone out.

    To make it work, I ditched "Read On TX Empty". I then implemented the code to simply perfrom two separate transactions for an "overall read operation". Obviously this is completely standard and should always work.

    The problem is to do with REPEAT_START. I put the scope on the I2C bus, and I could see that, as I expected, the STOP had not been generated, the controller was busy with SDA high and SCL low. Picking the transactions apart I realised there was something else going on.

    The final failing transaction was a MASTER TRANSMIT. The transmit completed correctly, but was erroneously followed by a REPEAT_START, and issuance of DEVICE ADDRESS, by the MASTER with READ/WRITE set to WRITE. After the DEVICE ADDRESS, the MASTER was stuck because it had begun a restart transaction that was never requested. The transmit FIFO was correctly empty.

    I noticed that this situation would occur on the first MASTER TRANSMIT after a correctly completed "Read On TX Empty", REPEAT_START - MASTER RECEIVE transaction. I verified correct completion of the preceeding transaction by observing the IDLE bit of the MASTER status register - using the debugger. I also received several bytes of data from the SLAVE, which were verified to be correct.

    I changed the code, to perform an identical MASTER TRANSMIT without the preceeding "Read On TX Empty" transaction. This performed flawlessly. The only thing I changed was the overall order of transactions.

    The datasheet slau847e is explicit in table 22-55; "If the DIR is not set to Read in the MSA then this bit is ignored."

    Anyway I tried the erroneous transmit both with  "Read On TX Empty" set and cleared.

    I think the "Read On TX Empty" mechanism borked the following transmit somehow. The state machine remembered and could only be caused to forget, by virtue of reset.

    Without "Read On TX Empty" everything is "Lubbly Jubbly"!

    I really wsh the TI documentation was better, but I accept that it could be *a lot* worse.

    Many thanks,

    Andy Ash.

  • Hi Bruce,

    I read your linked "Hazard in DL_I2C....." It is very close to the issue I had, but I'm sure I was already setting BURSTRUN to zero before changing RD_ON_TXEMPTY. When I coded it, I couldn't work out how to interpret the document, so I just chose what I thought would be a safe route.

    I'm not saying you're wrong at all. These things can be quite elusive.

    I would simply observe that you stated you chose a different way to work around the problem. I assume that means not using RD_ON_TXEMPTY.

    If you didn't end up using RD_ON_TXEMPTY, which is certainly what I did - maybe you weren't totally sure that your proposed solutions actually worked. I only say because I think I initially used one of them unknowingly and I still had a problem.

    Thanks again.

  • I've had no particular trouble with RD_ON_TXEMPTY. My workaround was to always do a write (not RMW) setting all the other bits to 0. My CMSIS drivers write all the bits at once (not available in driverlib).

    To tell the truth I didn't quite understand your sequence nor your symptoms. Maybe there is a corner case I haven't encountered.

  • Hi Bruce,

    I think you suggested that one solution would be to clear BURSTRUN separately before changing any of the other control bits in the same register using the RMW TI driver library functions. I was doing that. I'm still doing that.

    Perhaps there is some unseen benefit, but I can't see why one would need separate libraries for direct access. I only see disadvantages with that so it wouldn't be a first choice unless there was specific reason. I think you are saying that there is, but I don't understand.

    For me it has to be a hardware problem because if I was a clutz and requested a double transaction on the overall write, it wouldn't matter if I had a prior read, or not. If I were that clutz, then I would always get a REPEAT_START.

    As it is, that behaviour only happens when the transmit follows, specifically, a RD_ON_TXEMPTY REPEAT_START overall read. If it follows a normal read without the RD_ON_TXEMPTY REPEAT_START, then the transmit behaves correctly. It doesn't make any difference where or how I request RD_ON_EMPTY, that's the option that breaks the transmit. According to the data I should be able to invoke RD_ON_TXEMPTY once at machine initialisation, and it wouldn't affect any overall write transactions. I tried that too and it still behaves the same.

    I even verified that the RD_ON_TXEMPTY bit was being changed with the I2C MASTER idle, and BURSTRUN clear.

    I think there's something wrong with RD_ON_TXEMPTY in respect of REPEAT_START. I'm not spending any more time on it though.

    Thanks for your time.

  • Just following up [maybe just for the archaeologists]: I think you're describing an adverse effect from having RD_ON_TXEMPTY set on a Write (TX) operation.

    I hacked up a simple program which writes and reads back a register on (as it happens) an MPR121 (Adafruit breakout). The operations alternate (read/write/read/write/...), so every write is preceded by a read. Based on a compile-time setting (RS_ONCE) it either turns RD_ON_TXEMPTY on/off for each read, or leaves it on all the time. I saw no difference in behavior between the two settings.

    It appears you're doing something different, so here is the program [tested on an L1306 Launchpad with I2C on PA0/1]:

    #include "ti_msp_dl_config.h"
    #include <stdint.h>
    
    #define MPR_ADDR (0x5A)     // MPR121
    #define RS_WA    1          // Workaround for RD_ON_TXEMPTY (driverlib) hazard
    #define RS_ONCE  1          // Keep RD_ON_TXEMPTY all the time
    
    ///
    //  rs_onoff()
    //
    inline void
    rs_onoff(I2C_Regs *i2c, uint32_t onoff)
    {
        if (onoff == 0)     // Off?
        {
    #if RS_WA       // Hazard workaround
            i2c->MASTER.MCTR = 0*I2C_MCTR_RD_ON_TXEMPTY_ENABLE;// Clear repeat-start, clear others
    #else // RS_WA
            DL_I2C_diusableControllerReadOnTXEmpty(i2c);   // Write then read
    #endif // RS_WA
        }
        else {      // On
    #if RS_WA       // Hazard workaround
            i2c->MASTER.MCTR = I2C_MCTR_RD_ON_TXEMPTY_ENABLE;// Set repeat-start, clear others
    #else // RS_WA
            DL_I2C_enableControllerReadOnTXEmpty(i2c);   // Write then read
    #endif // RS_WA
        }
        return;
    }
    
    ///
    //  readreg()
    //
    uint16_t 
    readreg(I2C_Regs *i2c, uint8_t i2caddr, uint8_t reg)
    {
        uint32_t stat;
        uint8_t val[1];
        uint16_t r;
        
        while (!(DL_I2C_getControllerStatus(i2c) & DL_I2C_CONTROLLER_STATUS_IDLE)); // Just in case
    #if !RS_ONCE
        rs_onoff(i2c, 1);        // RD_ON_TXEMPTY=1
    #endif // RS_ONCE
        DL_I2C_clearInterruptStatus(i2c, (DL_I2C_INTERRUPT_CONTROLLER_RX_DONE|DL_I2C_INTERRUPT_CONTROLLER_NACK));// Clear stale
    
        val[0] = reg;
        DL_I2C_fillControllerTXFIFO(i2c, &val[0], sizeof(val)); // One byte fits
        DL_I2C_startControllerTransfer(i2c, i2caddr, DL_I2C_CONTROLLER_DIRECTION_RX, 1); // Write reg number then read 1 byte
        do {
            stat = DL_I2C_getRawInterruptStatus(i2c, (DL_I2C_INTERRUPT_CONTROLLER_RX_DONE|DL_I2C_INTERRUPT_CONTROLLER_NACK));
        }  while(!stat);
        if (stat & DL_I2C_INTERRUPT_CONTROLLER_NACK )
        {
            DL_I2C_flushControllerTXFIFO(i2c);       // Keep it neat
            r = (uint16_t)-1;
        }
        else
        {
            r = DL_I2C_receiveControllerData(i2c);  // We "know" there's only 1
        }
    #if !RS_ONCE
        rs_onoff(i2c, 0);        // RD_ON_TXEMPTY=0
    #endif // RS_ONCE
        return(r);
    }
    
    ///
    //  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;
        
        while (!(DL_I2C_getControllerStatus(i2c) & DL_I2C_CONTROLLER_STATUS_IDLE)); // Just in case
    
        DL_I2C_clearInterruptStatus(i2c, (DL_I2C_INTERRUPT_CONTROLLER_TX_DONE|DL_I2C_INTERRUPT_CONTROLLER_NACK));// Clear stale
    
        val[0] = reg;
        val[1] = regval;
        DL_I2C_fillControllerTXFIFO(i2c, &val[0], sizeof(val)); // Two bytes fit
        DL_I2C_startControllerTransfer(i2c, i2caddr, DL_I2C_CONTROLLER_DIRECTION_TX, sizeof(val)); // Write 2 bytes
        do {
            stat = DL_I2C_getRawInterruptStatus(i2c, (DL_I2C_INTERRUPT_CONTROLLER_TX_DONE|DL_I2C_INTERRUPT_CONTROLLER_NACK));
        }  while(!stat);
        if (stat & DL_I2C_INTERRUPT_CONTROLLER_NACK )
        {
            DL_I2C_flushControllerTXFIFO(i2c);       
            r = (uint16_t)-1;
        }
        else
        {
            r = 0;
        }
        return(r);
    }
    
    //  Some history
    #define VALMAX (50)
    uint8_t val_read[VALMAX];
    uint32_t val_i;
    
    ///
    //  main()
    //
    int 
    main(void)
    {
        uint8_t reg;
        uint8_t regval;
        SYSCFG_DL_init();
    #if RS_ONCE                         // Set it and leave it
        rs_onoff(I2C_INST, 1);          // RD_ON_TXEMPTY=1
    #endif // RS_ONCE
    
        DL_GPIO_setPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_1_PIN); // Make 'em think we're doing something
        reg = 0x5B;                     // MPR121 Debounce register
        regval = 0;
        while (1) {
            val_read[val_i] = readreg(I2C_INST, MPR_ADDR, reg); // Fetch current
            if (++val_i >= VALMAX)
                val_i = 0;
            if (++regval >= VALMAX)
                regval = 1;
            writereg(I2C_INST, MPR_ADDR, reg, regval);          // Set new
    
            DL_GPIO_togglePins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_1_PIN | GPIO_LEDS_USER_TEST_PIN);
            delay_cycles(1000);         // Pause for the scope
        }
    }

  • Hi Bruce,

    I looked at the MPR121 Adafruit thing which appears to be a capacitive touch sensor for turning apples and bananas into a user interface. The promotional material shows that the IC might have the right package for M0L1306, but the markings on the package aren't right for M0L1306.

    I used the TI launchpad LP-MSPM0L1306 for everything I did, so perhaps that helps.

    To be honest, I've moved on now. I don't have the time to spend on this issue.

    Many thanks,

    Andy Ash.