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.

MSP430FR4133: I2C Clarification

Part Number: MSP430FR4133
Other Parts Discussed in Thread: TMP117, , CC3220SF, CC3120

Im pretty new to this MCU so I'm trying to get a grasp of how to communicate with I2C. I understand how to set the GPIO pins so that I can communicate with a slave but my problem is that my slave, TMP117, has multiple registers on it for setting limits, offsetting, configuring etc. The data sheet shows that the TMP has a pointer register that will send the data to the corresponding register that I want to write to but I am a little confused at how to initiate that. I looked at the example code for the board and i have read the driver file but I can't figure out how to write to a specific register on my device. The driver looks as if it is only able to send data to the base address of the slave and not specifically to its registers. Am I mistaken? If so are there any examples of doing this?

  • Hey Christopher,

    For setting up the MSP430FR4133 as a I2C master, there are a few software examples available here: dev.ti.com/.../

    As for interfacing directly to the TMP117, I'm not sure if there is example code for this. There is a TI design using this temp sensor that has some software. It's for one of our wireless MCUs so the firmware will be a bit different than that for MSP430.

    Looking at the TMP117 datasheet, section 7.5.3 describes the interface and figures 21 and 22 show both a write and read using the pointer register (PR). Table 3 shows the register maps and the far left column shows what the PR should be to point to these registers.

    It appears that the PR is the very first byte after every I2C write is initiated.

    Ex I2C packet for register write: START, Slave address w/ write bit, PR Value, Data1 for register, Data2 for register, STOP
    EX I2C register read: START, Slave address w/ write bit, PR Value, Re-START, Slave Address w/ read, Data1 from reg, Data2 from reg, STOP

    Let me know if you have any more questions,
    JD
  • I read those examples along with reading over the eusci_b_i2c files and I am still a little confused. Basically what I want to do with this MCU I have already done with the CC3220SF, with which i am very familiar with its driver for I2C, but I can't seem to translate it over to the MSP430. I just feel a little overwhelmed with information would you be able to give me some guidance i will provide the CC3220 code to give you a reference:

    This would be me sending data to the slave:

    /* Setting the Low limit temp */
    txBuffer[0] = LOW_LIM_REG;
    txBuffer[1] = LowLim_HByte;
    txBuffer[2] = LowLim_LByte;
    i2cTransaction3.slaveAddress = TMP117_ADDRESS;
    i2cTransaction3.writeBuf = txBuffer;
    i2cTransaction3.writeCount = 3;
    i2cTransaction3.readBuf = rxBuffer;
    i2cTransaction3.readCount = 0;
    I2C_transfer(i2c, &i2cTransaction3);

    And this would be reading from the slave:

    /* Point to the T ambient register and read its 2 bytes */
    txBuffer[0] = TEMP_REG;
    i2cTransaction1.slaveAddress = TMP117_ADDRESS;
    i2cTransaction1.writeBuf = txBuffer;
    i2cTransaction1.writeCount = 1;
    i2cTransaction1.readBuf = rxBuffer;
    i2cTransaction1.readCount = 2;

    I believe I am on the cusp of understanding the I2C on the MSP430 but I just need a little push. After reading all the information I noticed that the examples all were setting clocks. I don't know if the CC3220 had done that for me or if i really need to set the clock but basically I am trying to just read in the temperature and display it on the LCD. I don't need any interrupts because the temp sensor will be configured to only read when I send the MCU command.

    And this was all i needed to initialize on that MCU for the parameters:

    I2C_Handle i2c;
    I2C_Params i2cParams;
    
    /* Create I2C for usage */
    I2C_Params_init(&i2cParams);
    i2cParams.bitRate = I2C_400kHz;
    i2c = I2C_open(Board_I2C_TMP, &i2cParams);

  • Just to note the data received from the Sensors temperature registers is a 2 byte digital signal. In the code using the eusci_b_i2c driver how would i take this value from the buffer and set it to a variable that i would adjust and display. With the 3220 it was as simple as taking the first byte left shifting 8 bits and "Or"ing it with the second byte :
    temperature = ((Rxbuffer[0] << 8 ) | Rxbuffer [1])
  • Hey Chris,

    I updated your post to use our code posting tool. There is a </> button if you click the "Insert code, attach files and more..." link in your replys that will keep the formatting once posted.

    I'm not very familiar with the MCU on the CC3120, but the MSP430 does have many clock options that need to be first configured before we even use the I2C module. There is some additional complexity to offer more flexibility and power optimizations.

    For your second question, in the driver level example MSP430fr413x_eusci_i2c_standard_master.c, there is a ReceiveBuffer[] that should hold any received data.


    Now, I don't want to fully switch gears on you, but since you're more familiar with the SimpleLink platform I wanted to introduce the MSP DriverLib. It might be better suited for you to develop with as it's slighty more similiar because it's API based vs the direct register level examples I linked you before.

    MSP430FR4133 Driverlib can be found in MSPware here: dev.ti.com/.../

    There are mostly the same I2C examples, but using the I2C driverlib API's under the examples folder. I don't see a "standard_master" example though, which was probably the most all encompasing of the driver level examples I linked. The User's Guide and API guide will list and describe all the APIs. Here are the functions for USCI_B_I2C: dev.ti.com/.../

    Keep coming back with questions and I'll be happy to help!

    Thanks,
    JD
  • I've gotten the I2C transaction to work finally but the data recieved is not being stored in the right way. 

    #include "TempSensorMode.h"
    #include "hal_LCD.h"
    #include "main.h"
    #include "driverlib.h"
    #include "Board.h"
    
    #include <stdint.h>
    
    /* Temperature sensor hexidecimal addresses */
    #define TMP117_ADDRESS          0x48
    
    /* Hex address for TMP117 registers */
    #define TEMP_REG                0x00
    #define CONFIG_REG              0x01
    #define HIGH_LIM_REG            0x02
    #define LOW_LIM_REG             0x03
    #define EEPROM_UNLOCK_REG       0x04
    #define TEMP_OFFSET_REG         0x07
    
    /* Set temperature threshholds (High and Low) */
    const uint8_t LowLim_HByte   =  0x0B;
    const uint8_t LowLim_LByte   =  0x00;
    const uint8_t HighLim_HByte  =  0x0E;
    const uint8_t HighLim_LByte  =  0x80;
    const uint8_t Config_HByte   =  0x0C;
    const uint8_t Config_LByte   =  0x28;
    
    
    //Target frequency for SMCLK in kHz
    #define CS_SMCLK_DESIRED_FREQUENCY_IN_KHZ   1000
    
    //SMCLK/FLLRef Ratio
    #define CS_SMCLK_FLLREF_RATIO   30
    
    //Specify Expected Receive data count.
    #define RXCOUNT 0x02
    
    #define CALADC_15V_30C  *((unsigned int *)0x1A1A)       // Temperature Sensor Calibration-30 C
    #define CALADC_15V_85C  *((unsigned int *)0x1A1C)       // Temperature Sensor Calibration-85 C
    
    volatile unsigned char * tempUnit = &BAKMEM4_H;         // Temperature Unit
    volatile unsigned short *degC = (volatile unsigned short *) &BAKMEM5;                          // Celsius measurement
    volatile unsigned short *degF = (volatile unsigned short *) &BAKMEM6;                          // Fahrenheit measurement
    
    uint8_t RXData;
    //uint8_t TXData = 0;
    //uint8_t TXByteCtr;
    
    void initGPIO()
    {
        // I2C pins 5.2(SDA) and 5.3(SCL)
        GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_UCB0SCL, GPIO_PIN_UCB0SCL, GPIO_FUNCTION_UCB0SCL);
        GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_UCB0SDA, GPIO_PIN_UCB0SDA, GPIO_FUNCTION_UCB0SDA);
    
        // Disable the GPIO power-on default high-impedance mode to activate
        // previously configured port settings
        PMM_unlockLPM5();
    }
    
    void initI2C()
    {
        //Set DCO FLL reference = REFO
        CS_initClockSignal(CS_FLLREF, CS_REFOCLK_SELECT,  CS_CLOCK_DIVIDER_1);
    
        //Set Ratio and Desired MCLK Frequency and initialize DCO
        CS_initFLLSettle(CS_SMCLK_DESIRED_FREQUENCY_IN_KHZ, CS_SMCLK_FLLREF_RATIO);
                
        //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_DCOCLKDIV_SELECT, CS_CLOCK_DIVIDER_1);
    
        //Set MCLK = DCO with frequency divider of 1
        CS_initClockSignal( CS_MCLK, CS_DCOCLKDIV_SELECT, CS_CLOCK_DIVIDER_1);
        
        initGPIO();
        
        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_400KBPS;
        param.byteCounterThreshold = RXCOUNT;
        param.autoSTOPGeneration = EUSCI_B_I2C_SEND_STOP_AUTOMATICALLY_ON_BYTECOUNT_THRESHOLD;
        EUSCI_B_I2C_initMaster(EUSCI_B0_BASE, &param);
    
        //Specify slave address
        EUSCI_B_I2C_setSlaveAddress(EUSCI_B0_BASE, TMP117_ADDRESS);
        
        //Set Master in receive mode
        EUSCI_B_I2C_setMode(EUSCI_B0_BASE, EUSCI_B_I2C_RECEIVE_MODE);
        
        //Enable I2C Module to start operations
        EUSCI_B_I2C_enable(EUSCI_B0_BASE);
    
        EUSCI_B_I2C_clearInterrupt(EUSCI_B0_BASE, EUSCI_B_I2C_RECEIVE_INTERRUPT0 + EUSCI_B_I2C_BYTE_COUNTER_INTERRUPT + EUSCI_B_I2C_NAK_INTERRUPT);
    
        //Enable master Receive interrupt
        EUSCI_B_I2C_enableInterrupt(EUSCI_B0_BASE, EUSCI_B_I2C_RECEIVE_INTERRUPT0 + EUSCI_B_I2C_BYTE_COUNTER_INTERRUPT + EUSCI_B_I2C_NAK_INTERRUPT);
    }
    
    void tempSensor(){
        
        WDT_A_hold(WDT_A_BASE); //Stop watchdog timer
        
        initI2C();
        
         while(*tempSensorRunning)
        {
            __delay_cycles(2000);
            
            if (*tempSensorRunning)
            {
            	// Turn LED1 on when waking up to calculate temperature and update display
                P1OUT |= BIT0;
                
                while (EUSCI_B_I2C_SENDING_STOP == EUSCI_B_I2C_masterIsStopSent(EUSCI_B0_BASE));
                
                    EUSCI_B_I2C_masterReceiveStart(EUSCI_B0_BASE);
                    
                // Enter LPM0 w/ interrupt
                __bis_SR_register(CPUOFF+GIE);
    
                // Calculate Temperature in degree C and F
                signed short temp = (RXData * 0.0078125)* 10;//((RXData[0]<<8)|RXData[1]);
                *degC = ((long)temp);
                *degF = (*degC) * 9 / 5 + 320;
    
                // Update temperature on LCD
                displayTemp();
    
                P1OUT &= ~BIT0;
            }
        }
        
    }
    
    //-----------------------------------------------------------------------------------------------------------------------------------------------------------//
    void tempSensorModeInit()
    {
        *tempSensorRunning = 1;
    
        displayScrollText("TEMPSENSOR MODE");
    }
    
    void displayTemp()
    {
        clearLCD();
    
        // Pick C or F depending on tempUnit state
        int deg;
        if (*tempUnit == 0)
        {
            showChar('C',pos6);
            deg = *degC;
        }
        else
        {
            showChar('F',pos6);
            deg = *degF;
        }
    
        // Handle negative values
        if (deg < 0)
        {
            deg *= -1;
            // Negative sign
            LCDMEM[pos1+1] |= 0x04;
        }
    
        // Handles displaying up to 999.9 degrees
        if (deg>=1000)
            showChar((deg/1000)%10 + '0',pos2);
        if (deg>=100)
            showChar((deg/100)%10 + '0',pos3);
        if (deg>=10)
            showChar((deg/10)%10 + '0',pos4);
        if (deg>=1)
            showChar((deg/1)%10 + '0',pos5);
        if (deg == 0){
            while(1)
                displayScrollText("ERROR");
        }
    
        // Decimal point
        LCDMEM[pos4+1] |= 0x01;
    
        // Degree symbol
        LCDMEM[pos5+1] |= 0x04;
    }
    
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector=USCI_B0_VECTOR
    __interrupt
    #elif defined(__GNUC__)
    __attribute__((interrupt(USCI_B0_VECTOR)))
    #endif
    void USCIB0_ISR(void)
    {
        static uint8_t count = 0;
        switch(__even_in_range(UCB0IV, USCI_I2C_UCBIT9IFG))
        {
            case USCI_NONE:             // No interrupts break;
                break;
            case USCI_I2C_UCALIFG:      // Arbitration lost
                break;
            case USCI_I2C_UCNACKIFG:    // NAK received (master only)
                EUSCI_B_I2C_masterReceiveStart(EUSCI_B0_BASE);
                break;
            case USCI_I2C_UCSTTIFG:     // START condition detected with own address (slave mode only)
                break;
            case USCI_I2C_UCSTPIFG:     // STOP condition detected (master & slave mode)
                break;
            case USCI_I2C_UCRXIFG3:     // RXIFG3
                break;
            case USCI_I2C_UCTXIFG3:     // TXIFG3
                break;
            case USCI_I2C_UCRXIFG2:     // RXIFG2
                break;
            case USCI_I2C_UCTXIFG2:     // TXIFG2
                break;
            case USCI_I2C_UCRXIFG1:     // RXIFG1
                break;
            case USCI_I2C_UCTXIFG1:     // TXIFG1
                break;
            case USCI_I2C_UCRXIFG0:     // RXIFG0
                // Get RX data
                RXData = EUSCI_B_I2C_masterReceiveSingle(
                                        EUSCI_B0_BASE
                        );
              if (++count >= RXCOUNT) {
                  count = 0;
                  __bic_SR_register_on_exit(CPUOFF); // Exit LPM0
              }
                break; // Vector 24: RXIFG0 break;
            case USCI_I2C_UCTXIFG0:     // TXIFG0
                break;
            case USCI_I2C_UCBCNTIFG:    // Byte count limit reached (UCBxTBCNT)
                GPIO_toggleOutputOnPin(
                    GPIO_PORT_P1,
                    GPIO_PIN0
                    );
                break;
            case USCI_I2C_UCCLTOIFG:    // Clock low timeout - clock held low too long
                break;
            case USCI_I2C_UCBIT9IFG:    // Generated on 9th bit of a transmit (for debugging)
                break;
            default:
                break;
        }
    }
    

    It is storing the second byte of data from the TMP117 which for example is 246. This is a digital signal directly from the sensor that I will need to multiply by 0.0078125 to get the correct temperature and then also by 10 to display it correctly on the LCD. So i have been getting 1.9 which is correct for 246 * 0.0078125 but this eliminates the first byte of data. The signal from the TMP117 is supposed to be 2 bytes and when debugging I noticed the first byte is stored but then overwritten. I tried to solve this by making the buffer uint16 instead of uint8 but that didn't work I then tried to have it store 2 bytes initializing the buffer as RXData[2] but an error occurs saying to make it a modifiable variable. Could i get some assistance on the syntax for storing the data? 

  • When RXData is an array, the elements can be accessed with RXData[0] etc.
  • #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector=USCI_B0_VECTOR
    __interrupt
    #elif defined(__GNUC__)
    __attribute__((interrupt(USCI_B0_VECTOR)))
    #endif
    void USCIB0_ISR(void)
    {
        static uint8_t count = 0;
        switch(__even_in_range(UCB0IV, USCI_I2C_UCBIT9IFG))
        {
            case USCI_NONE:             // No interrupts break;
                break;
            case USCI_I2C_UCALIFG:      // Arbitration lost
                break;
            case USCI_I2C_UCNACKIFG:    // NAK received (master only)
                EUSCI_B_I2C_masterReceiveStart(EUSCI_B0_BASE);
                break;
            case USCI_I2C_UCSTTIFG:     // START condition detected with own address (slave mode only)
                break;
            case USCI_I2C_UCSTPIFG:     // STOP condition detected (master & slave mode)
                break;
            case USCI_I2C_UCRXIFG3:     // RXIFG3
                break;
            case USCI_I2C_UCTXIFG3:     // TXIFG3
                break;
            case USCI_I2C_UCRXIFG2:     // RXIFG2
                break;
            case USCI_I2C_UCTXIFG2:     // TXIFG2
                break;
            case USCI_I2C_UCRXIFG1:     // RXIFG1
                break;
            case USCI_I2C_UCTXIFG1:     // TXIFG1
                break;
            case USCI_I2C_UCRXIFG0:     // RXIFG0
                // Get RX data
                RXData[0] = EUSCI_B_I2C_masterReceiveSingle(
                                        EUSCI_B0_BASE
                        );
                RXData[1] = EUSCI_B_I2C_masterReceiveSingle(
                                EUSCI_B0_BASE
                );
              if (++count >= RXCOUNT) {
                  count = 0;
                  __bic_SR_register_on_exit(CPUOFF); // Exit LPM0
              }
                break; // Vector 24: RXIFG0 break;
            case USCI_I2C_UCTXIFG0:     // TXIFG0
                break;
            case USCI_I2C_UCBCNTIFG:    // Byte count limit reached (UCBxTBCNT)
                break;
            case USCI_I2C_UCCLTOIFG:    // Clock low timeout - clock held low too long
                break;
            case USCI_I2C_UCBIT9IFG:    // Generated on 9th bit of a transmit (for debugging)
                break;
            default:
                break;
        }
    }

    I updated the RXData to be an array but it seems to still only take in one byte and duplicate it. Having looked at the multiple byte example it seems like the only work around would be to make a state machine?

    TO expand on this more after testing our some coding solutions. I am trying to read 2 bytes from the TMP117 then using a bit shift and a logical "or" combine both bytes. The now combined 2 bytes will be manipulated to output onto the LCD. My problem is that this code seems to only read one byte.

  • Hey Christopher,

    The interrupt is only going to fire for each byte, so you can only store one byte at a time in your RXbuff.  So you should increment you pointer each time the ISR fires and then reset it after each full transaction. 

    Based on your code, something like this I think would work:  

     // Get RX data
                RXData[count] = EUSCI_B_I2C_masterReceiveSingle(
                                        EUSCI_B0_BASE
                        );
               
              if (++count >= RXCOUNT) {
                  count = 0;
                  __bic_SR_register_on_exit(CPUOFF); // Exit LPM0
              }

    Try this and let us know if it works as you expect.  

    JD

  • That is a slick way of doing it! I didn't realize the ISR could work that way. Thank you for the help I will surely keep replying as I am going to try to incorporate writing in the same code to set different registers on the TMP117.

**Attention** This is a public forum