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.

MSP430FR50431: I2C master never calls ISR

Part Number: MSP430FR50431

Hi all,

I am working on i2c master communication on MSP430. The problem is, the ISR is never called, even though, it should be enabled when I set UCB1IE |= (UCTXIE0 | UCRXIE0 | UCNACKIE | UCALIE)

Since inserting code to this page results in error, all code is uploaded to this gist. It's using delay() and millis() function from this gist.

Thank you for any suggestion :)

  • Hi Jan,

    In i2c_master.c line 132 (in i2c_master_init()), I would use GPIO_SECONDARY_MODULE_FUNCTION instead of primary based on the Input/Output Diagrams section in the device datasheet.

    Some other things I would try if you are still seeing issues:

    - Check that SCL and SDA are both pulled up with a logic analyzer/oscilloscope

    - Verify that the master is sending the correct address with a logic analyzer/oscilloscope

    Thanks,

    Urica Wang

  • Setting the IEs is ineffective while UCSWRST=1. 

    More generally, putting the USCI into reset (UCSWRST->1) clears the IE register.  Typically the IEs would be set according to the type (R/W) of transaction, so this mostly doesn't get in the way. (Yes, this is kind of a bother when using UCASTP.) 

  • In i2c_master.c line 132 (in i2c_master_init()), I would use GPIO_SECONDARY_MODULE_FUNCTION instead of primary based on the Input/Output Diagrams section in the device datasheet.

    You are absolutely right, for some reason, I used MODULE_FUNCTION for GPIO_PORT_P3 instead of GPIO_PORT_P5.

  • Thank you for your reply Bruce,

    I updated the gist.

    1) IEs are set after UCSWRST

    2) Auto stop is moved to separate function auto_stop_set, which preserve IEs.

    Now, ISR is entered. But only once. UCB1IV equals to UCSTTIFG. It happens at the beginning of the i2c_master_wait() function. Is it possible, that because millis() function, which disables interrupts may cause some interference with I2C ISR? It is called in while loop so there may not be enough time to start another interrupt...

    In addition I don't even know what to do with UCSTTIFG. Should I send first byte like this:

    if (tx_buffer && tx_buffer_remaining_length) {

        UCB1TXBUF = tx_buffer[--tx_buffer_remaining_length];

    }

    Or wait until the START is sent?

    while(UCB1CTLW0 & UCTXSTT);

    Or just ignore it all together and wait until eUSCI does it's thing?

    And why is ISR called for UCSTTIFG? I though that setting BIT_CLEAR(UCB1IE, UCSTPIE | UCSTTIE); would disable this interrupt.

    EDIT:

    Logic analyzer shows, that SDA and SCL lines are both held HIGH. Not sending the START signal.

  • > case UCSTTIFG:

    You won't get a UCSTTIFG vector value since you haven't enabled UCSTTIE.

    The values in UCB1IV are not IFG (bit) values, but small-integer indices. [Ref User Guide (SLAU367P) Table 32-20] The 2 you see is actually UCIV__UCALIFG.

    I'm not sure why you would get this on a single-master bus. (Truth be known, I've never designed a multi-master bus, so while I understand the arbitration mechanism I don't  know how ALIFG works exactly.) My first thought is to Not set UCALIE for the moment, and see how far you get. 

    What slave device are you using?

    [Edit: A shot in the dark: Maybe ALIFG is always set if UCMM=0? (I've seen it on at random times, but never investigated.) Try setting UCMM=1.]

  • You won't get a UCSTTIFG vector value since you haven't enabled UCSTTIE.

    Yes, that was the point. I was mixing UCIV__* and IFG values.

    Once again I updated the gist with the latest changes [LINK]. Now, what I get is UCIV__UCNACKIFG for some reason. However I don't see any START signal or device address being transported on logical analyzer. Maybe I have deeper problem in HW. 

    Is it possible, that interrupts are disabled till first call to millis() (which contains call to __enable_interrupt()) and thus previous TX interrupts are not visible in debuger? Every time i step through the program, i2c_isr() pops up right after calling millis() function in i2c_master_wait.

    What slave device are you using?

    I was trying both VEML60 [Datasheet] RGBW sensor and CAT24 [Datasheet] EEPROM module. Both end in UCIV__UCNACKIFG

    I'll try to connect some I2C device directly to launchpad instead of custom HW to make sure its not HW problem.

  • The MSP430 starts up with interrupts disabled (GIE=0) . This is different from e.g. Cortex-M devices. If main() doesn't __enable_interrupt() then the first call to millis() will.

    I don't think I've seen the I2C generate a NACK without any bus activity at all. My first suspicion would be my scope, but I blame everything on my scope (:-)).

    A common mistake is to use a SLA (SLave Address) byte (shifted left) instead of an I2C address. For the VEML60 you should be using 0x10 [data sheet p. 7] and for the CAT24 0x50 (A0-A2 not connected). [data sheet p. 5]

    Unsolicited: Yes, millis probably shouldn't be arbitrarily enabling interrupts. There is a mechanism somewhere in the header files for capturing SR (GIE) and restoring it . Personally, I would use a voting loop (read both words until the high half agrees).

  • Hi Jan,

    I think you are getting a NACK before any other bus activity (START, etc.) due to lines 75 and 76 in your send_start() routine. Your comment on line 75 says that you are trying to clean the NACK interrupt flag, but I see that you are using BIT_SET(...., UCNACKIFG) in line 76 before you send START.

    Thanks,

    Urica Wang

  • Thank you guys. You pointed out yet another mistakes in the code.

    The MSP430 starts up with interrupts disabled (GIE=0) . This is different from e.g. Cortex-M devices. If main() doesn't __enable_interrupt() then the first call to millis() will.

    I am now starting interrupts in main() and millis() function is not touching them again.

    A common mistake is to use a SLA (SLave Address) byte (shifted left) instead of an I2C address. For the VEML60 you should be using 0x10 [data sheet p. 7] and for the CAT24 0x50 (A0-A2 not connected). [data sheet p. 5]

    I double checked the addresses and I am using those. For now I am sticking with CAT24, which should operate much simpler - just read and write. Address is set to 0x50.

    Write function for CAT24 is as follows:

    #define DEVICE_ADDRESS (0x50)
    
    // Parameter is memory address and it's value
    error cat24_write(uint8_t address, uint8_t value) {
        error err = ERROR_FAIL;
    
        uint8_t buffer[] = {
            address,
            value
        };
    
        err = i2c_master_write(EEPROM_ADDRESS, buffer, sizeof(buffer), I2C_SEND_STOP);
        RET_IF_FAIL(err);
    
        err = i2c_master_wait(1000);    // This ends with ERROR_I2C_TIMEOUT
        return err;
    }

    Unsolicited: Yes, millis probably shouldn't be arbitrarily enabling interrupts. There is a mechanism somewhere in the header files for capturing SR (GIE) and restoring it . Personally, I would use a voting loop (read both words until the high half agrees).

    Would you be so kind and point me to some resources about voting loop? Why would not upper 8 bit match between loop cycles? Should not I check for the whole value?

    I think you are getting a NACK before any other bus activity (START, etc.) due to lines 75 and 76 in your send_start() routine. Your comment on line 75 says that you are trying to clean the NACK interrupt flag, but I see that you are using BIT_SET(...., UCNACKIFG) in line 76 before you send START.

    Yes, that was it. I removed it. Now I am back at the start. I2C ISR is not called even once.

    I walked through the code in debuger and watched register values. This is the state right after i2c_master_write()

    UCB1CTLW0 = 0x0F92 (= UCTXSTT | UCTR | UCSSEL_2 | UCSYNC |  UCMODE_3 | UCMST)
    UCB1CTLW1 = 0x0 (I turned off automatic STOP generation for now)
    UCB1BRW = 0x000A (= 10, to get 100kHz)
    UCB1STATW = 0x0010 (= I2C bus is busy)
    UCB1TXBUF = 0x0 (= ISR was not called yet, so there is no data to be sent)
    UCB1RXBUF = 0x0 (= no data to be read)
    UCB1I2CSA = 0x0050 (= slave is CAT24, with A0 - A2 pins not connected)
    UCB1IE = 0x0002 (= UCTXIE0)
    UCB1IFG = 0x0
    UCB1IV = 0x0

    For some reason, UCB1STATW stays set to UCBBUSY during i2c_master_wait(1000). Like it was unable to send START to begin with. So I suppose since eUSCI is unable to send START, it halts and never calls ISR with UCIV__UCTXIFG0.

    What could be the error? Is it again in the code? Or may it be HW issue?


    Edit: I used modified code from this driverlib example to send 2 bytes of data to CAT24:

    #include <driverlib.h>
    
    // CAT24 EEPROM
    #define SLAVE_ADDRESS (0x50)
    
    // Store value 0x2A on address 0x10
    uint8_t transmitData[] = {
        0x10,
        0x2A
    };
    
    int main(void) {
    
        WDT_A_hold(WDT_A_BASE);
        //Set DCO frequency to 1MHz
        CS_setDCOFreq(CS_DCORSEL_0,CS_DCOFSEL_0);
        //Set ACLK = VLO with frequency divider of 1
        CS_initClockSignal(CS_ACLK,CS_VLOCLK_SELECT,CS_CLOCK_DIVIDER_1);
        //Set SMCLK = DCO with frequency divider of 1
        CS_initClockSignal(CS_SMCLK,CS_DCOCLK_SELECT,CS_CLOCK_DIVIDER_1);
        //Set MCLK = DCO with frequency divider of 1
        CS_initClockSignal(CS_MCLK,CS_DCOCLK_SELECT,CS_CLOCK_DIVIDER_1);
    
        GPIO_setAsPeripheralModuleFunctionInputPin(
            GPIO_PORT_P5,
            GPIO_PIN6 + GPIO_PIN5,
            GPIO_SECONDARY_MODULE_FUNCTION
        );
    
        /*
         * Disable the GPIO power-on default high-impedance mode to activate
         * previously configured port settings
         */
        PMM_unlockLPM5();
    
        //Initialize Master
        EUSCI_B_I2C_initMasterParam param = {0};
        param.selectClockSource = EUSCI_B_I2C_CLOCKSOURCE_SMCLK;
        param.i2cClk = CS_getSMCLK();
        param.dataRate = EUSCI_B_I2C_SET_DATA_RATE_100KBPS;
        param.byteCounterThreshold = 1;
        param.autoSTOPGeneration = EUSCI_B_I2C_NO_AUTO_STOP;
        EUSCI_B_I2C_initMaster(EUSCI_B1_BASE, &param);
    
        //Specify slave address
        EUSCI_B_I2C_setSlaveAddress(EUSCI_B1_BASE,
            SLAVE_ADDRESS
        );
    
        //Set in transmit mode
        EUSCI_B_I2C_setMode(EUSCI_B1_BASE,
            EUSCI_B_I2C_TRANSMIT_MODE
        );
    
        //Enable I2C Module to start operations
        EUSCI_B_I2C_enable(EUSCI_B1_BASE);
    
        uint8_t index = 0;
        for(index = 0; index < 2; index++) {
            //Send single byte data.
            EUSCI_B_I2C_masterSendSingleByte(EUSCI_B1_BASE,
                transmitData[index]
            );
    
            //Delay until transmission completes
            while (EUSCI_B_I2C_isBusBusy(EUSCI_B1_BASE)) ;
    
            //Delay between each transaction
            __delay_cycles(50);
    
        }
    
    }

    It halts on line 

    while (!(HWREG16(baseAddress + OFS_UCBxIFG) & UCTXIFG)) from EUSCI_B_I2C_masterSendSingleByte function. So for some reason, START is not sent...

  • Failing to get that initial TXIFG indicates that the bus is stuck in some way. A prime suspect is the slave holding SCL and/or SDA low, since the I2C unit needs control of the bus to issue a Start (or Stop). Try power-cycling the slave (just unplug/re-plug the Vcc wire).

    The CAT24 claims to draw 3.5mA max, so it's feasible to power it using a GPIO pin. This gives you power (reset) control over the slave, to recover under program control.

  • Hi Jan,

    In addition to Bruce's suggestion, I'd recommend that you also check out this app report [link] on solutions to common serial communication issues on MSP430 MCUs. There are some tips in here for implementing I2C with MSP devices.

    Thanks,

    Urica Wang

  • Is it possible, that function PMM_unlockLPM5() which contains following code:

    void PMM_unlockLPM5 (void)
    {
    	//Direct register access to avoid compiler warning - #10420-D  
    	//For FRAM devices, at start up, the GPO power-on default 
    	//high-impedance mode needs to be disabled to activate previously 
    	//configured port settings. This can be done by clearing the LOCKLPM5
    	//bit in PM5CTL0 register
    
    	PM5CTL0 &= ~LOCKLPM5;
    }
    

    would help me in any way? During last few day, I made some changes and PMM_unlockLPM5() call after GPIO_setAsPeripheralModuleFunctionnOutputPin() was one of them. And it seems to be working now. Both i2c_master_read and i2c_master_write still return error status, but the data is written and read properly. I'll create separate question about remaining problems.

    Final code is posted in the gist [LINK]

    Thank you both and . Without your help, I would have probably set the whole MCU on fire and started my own farm :).

  • PMM_unlockLPM5() needs to be called sometime/somewhere. Normally (similar to __enable_interrupt) it would be called by main(), not a library function, but I didn't see main() in your Gist. If you could post main.c maybe that would reduce the guesswork.