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.

CCS/MSP430FR2355: I2C master multi-byte transmission ignoring first byte

Part Number: MSP430FR2355
Other Parts Discussed in Thread: PCF8574A, PCF8574

Tool/software: Code Composer Studio

Hi all,

I'm trying to communicate with a SSD1306 OLED screen via I2C. I'm having a problem with the driverlib EUSCI_B_I2C_masterSendMultiByteStart function, it is ignoring the first transmission byte. The only case it actually works is with the default MSP clock (1MHz) and 400kbps for I2C.

I'm configuring the peripheral as follows:

    GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P4, GPIO_PIN6 | GPIO_PIN7, GPIO_PRIMARY_MODULE_FUNCTION);

EUSCI_B_I2C_initMasterParam i2cParam = {0}; i2cParam.selectClockSource = EUSCI_B_I2C_CLOCKSOURCE_SMCLK; i2cParam.i2cClk = CS_getSMCLK(); i2cParam.dataRate = EUSCI_B_I2C_SET_DATA_RATE_400KBPS; i2cParam.byteCounterThreshold = 1; i2cParam.autoSTOPGeneration = EUSCI_B_I2C_NO_AUTO_STOP; EUSCI_B_I2C_initMaster(EUSCI_B1_BASE, &i2cParam); EUSCI_B_I2C_setSlaveAddress(EUSCI_B1_BASE, SSD1306_ADDRESS); EUSCI_B_I2C_setMode(EUSCI_B1_BASE, EUSCI_B_I2C_TRANSMIT_MODE); EUSCI_B_I2C_enable(EUSCI_B1_BASE);

And I'm using this function to send commands:

void OLED_sendCommand(uint8_t cmd)
{
    EUSCI_B_I2C_masterSendMultiByteStart(EUSCI_B1_BASE, 0x00);

    EUSCI_B_I2C_masterSendMultiByteNext(EUSCI_B1_BASE, cmd);

    EUSCI_B_I2C_masterSendMultiByteStop(EUSCI_B1_BASE);
}

Now for the outputs, this is using the default clock (1MHz) and 400kbps I2C:

And this is using the default clock (1MHz) with 100kbps I2C:

If I change the I2C clock to 100kbps, or change the system clock to anything but the default 1MHz, EUSCI_B_I2C_masterSendMultiByteStart ignores the first byte. Why is this happening?

Best regards,

Helder Sales

  • I've also tried to enable the interrupt mode by configuring this way:

        GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P4, GPIO_PIN6 | GPIO_PIN7, GPIO_PRIMARY_MODULE_FUNCTION);

    EUSCI_B_I2C_initMasterParam i2cParam = {0}; i2cParam.selectClockSource = EUSCI_B_I2C_CLOCKSOURCE_SMCLK; i2cParam.i2cClk = CS_getSMCLK(); i2cParam.dataRate = EUSCI_B_I2C_SET_DATA_RATE_400KBPS; i2cParam.byteCounterThreshold = 1; i2cParam.autoSTOPGeneration = EUSCI_B_I2C_NO_AUTO_STOP; EUSCI_B_I2C_initMaster(EUSCI_B1_BASE, &i2cParam); EUSCI_B_I2C_setSlaveAddress(EUSCI_B1_BASE, SSD1306_ADDRESS); EUSCI_B_I2C_setMode(EUSCI_B1_BASE, EUSCI_B_I2C_TRANSMIT_MODE); EUSCI_B_I2C_enable(EUSCI_B1_BASE); EUSCI_B_I2C_clearInterrupt(EUSCI_B1_BASE, EUSCI_B_I2C_TRANSMIT_INTERRUPT1 | EUSCI_B_I2C_NAK_INTERRUPT); EUSCI_B_I2C_enableInterrupt(EUSCI_B1_BASE, EUSCI_B_I2C_TRANSMIT_INTERRUPT1 | EUSCI_B_I2C_NAK_INTERRUPT); __bis_SR_register(GIE);

    But after the first call to EUSCI_B_I2C_masterSendMultiByteStart(EUSCI_B1_BASE, 0x00), it get stuck at 

    //Poll for transmit interrupt flag.
        while (!(HWREG16(baseAddress + OFS_UCBxIFG) & UCTXIFG)) ;

  • Hi,

    About the use of EUSCI_B_I2C_masterSendMultiByte function, you can refer to this code example:

    eusci_b_i2c_ex3_masterTxMultiple.c
    Fullscreen
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    /* --COPYRIGHT--,BSD
    * Copyright (c) 2017, 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.
    *
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

    Or I think if you add some delay after EUSCI_B_I2C_masterSendMultiByteNext(EUSCI_B1_BASE, cmd), it will be OK.

    Eason

  • Hi,

    Adding the delay didn't help. I've also copied the example (modifying to EUSCI_B1), but still stuck at 

    //Poll for transmit interrupt flag.
            while (!(HWREG16(baseAddress + OFS_UCBxIFG) & UCTXIFG)) ;

    The complete code I'm using, (heavily) based on the example is shown below:

    /* --COPYRIGHT--,BSD
     * Copyright (c) 2017, 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.
     * --/COPYRIGHT--*/
    //******************************************************************************
    //
    //! This example shows how to configure the I2C module as a master for
    //! multi byte transmission in interrupt driven mode. The address of the slave
    //! module is set in this example.
    //!
    //!  Demo - EUSCI_B0 I2C Master TX multiple bytes to MSP430 Slave
    //!
    //!  Description: This demo connects two MSP430's via the I2C bus. The master
    //!  transmits to the slave. This is the MASTER CODE. It cntinuously
    //!  transmits an array of data and demonstrates how to implement an I2C
    //!  master transmitter sending multiple bytes using the USCI_B0 TX interrupt.
    //!  ACLK = n/a, MCLK = SMCLK = BRCLK = default DCO = ~1MHz
    //!
    //!                                /|\  /|\
    //!          MSP430FR2xx_4xx Board 10k  10k MSP430FR2xx_4xx Board
    //!                   slave         |    |         master
    //!             -----------------   |    |   -----------------
    //!            |          UCB0SDA|<-|----+->|UCB0SDA          |
    //!            |                 |  |       |                 |
    //!            |                 |  |       |                 |
    //!            |          UCB0SCL|<-+------>|UCB0SCL          |
    //!            |                 |          |                 |
    //!
    //! This example uses the following peripherals and I/O signals.  You must
    //! review these and change as needed for your own board:
    //! - I2C peripheral
    //! - GPIO Port peripheral (for I2C pins)
    //! - SCL
    //! - SDA
    //!
    //! This example uses the following interrupt handlers.  To use this example
    //! in your own application you must add these interrupt handlers to your
    //! vector table.
    //! - USCI_B0_VECTOR.
    //
    //******************************************************************************
    #include "driverlib.h"
    #include "Board.h"
    
    //*****************************************************************************
    //
    //Set the address for slave module. This is a 7-bit address sent in the
    //following format:
    //[A6:A5:A4:A3:A2:A1:A0:RS]
    //
    //A zero in the "RS" position of the first byte means that the master
    //transmits (sends) data to the selected slave, and a one in this position
    //means that the master receives data from the slave.
    //
    //*****************************************************************************
    #define SLAVE_ADDRESS 0x38
    
    //*****************************************************************************
    //
    //Target frequency for SMCLK in kHz
    //
    //*****************************************************************************
    #define CS_SMCLK_DESIRED_FREQUENCY_IN_KHZ   1000
    
    //*****************************************************************************
    //
    //SMCLK/FLLRef Ratio
    //
    //*****************************************************************************
    #define CS_SMCLK_FLLREF_RATIO   30
    
    // Pointer to TX data
    uint8_t TXData = 0;
    uint8_t TXByteCtr;
    
    void main(void)
    {
        WDT_A_hold(WDT_A_BASE);
    
        //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
                );
    
        // Configure Pins for I2C
        GPIO_setAsPeripheralModuleFunctionInputPin(
                GPIO_PORT_P4,
                GPIO_PIN6,
                GPIO_PRIMARY_MODULE_FUNCTION
        );
        GPIO_setAsPeripheralModuleFunctionInputPin(
                GPIO_PORT_P4,
                GPIO_PIN7,
                GPIO_PRIMARY_MODULE_FUNCTION
        );
    
        /*
         * Disable the GPIO power-on default high-impedance mode to activate
         * previously configured port settings
         */
        PMM_unlockLPM5();
    
        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 = 0;
        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 Master in receive 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);
    
        EUSCI_B_I2C_clearInterrupt(EUSCI_B1_BASE,
                EUSCI_B_I2C_TRANSMIT_INTERRUPT1 +
                EUSCI_B_I2C_NAK_INTERRUPT
                );
        //Enable master Receive interrupt
        EUSCI_B_I2C_enableInterrupt(EUSCI_B1_BASE,
                EUSCI_B_I2C_TRANSMIT_INTERRUPT1 +
                EUSCI_B_I2C_NAK_INTERRUPT
                );
        while(1)
        {
            __delay_cycles(1000);                   // Delay between transmissions
            TXByteCtr = 4;                          // Load TX byte counter
            TXData = 0;
    
            while (EUSCI_B_I2C_SENDING_STOP == EUSCI_B_I2C_masterIsStopSent(EUSCI_B1_BASE));
    
            EUSCI_B_I2C_masterSendMultiByteStart(EUSCI_B1_BASE, TXData++);
    
            __bis_SR_register(CPUOFF + GIE);        // Enter LPM0 w/ interrupts
            // Remain in LPM0 until all data
            // is TX'd
            // Increment data byte
        }
    }
    
    #pragma vector=USCI_B1_VECTOR
    __interrupt void USCIB1_ISR(void)
    {
        switch(__even_in_range(UCB1IV, 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)
                //resend start if NACK
                EUSCI_B_I2C_masterSendStart(EUSCI_B1_BASE);
                break;
            case USCI_I2C_UCTXIFG0:     // TXIFG0
                // Check TX byte counter
                if (TXByteCtr)
                {
                    EUSCI_B_I2C_masterSendMultiByteNext(EUSCI_B1_BASE, TXData++);
                    // Decrement TX byte counter
                    TXByteCtr--;
                }
                else
                {
                    EUSCI_B_I2C_masterSendMultiByteStop(EUSCI_B1_BASE);
                    // Exit LPM0
                    __bic_SR_register_on_exit(CPUOFF);
                }
                break;
            default:
                break;
      }
    }
    
    

    I did try some other approaches. To summarise:

    • EUSCI_B1 Polling Method: Fail, skips the first byte of data
    • EUSCI_B0 Polling Method: Fail, skips the first byte of data
    • EUSCI_B1 Interrupt Example: Fail (didn't even start the communication, trapped in the transmit interrupt flag)
    • EUSCI_B0 Interrupt Example: Success after power cycling the device. Without power cycling sometimes works, sometimes not (trapped in the transmit interrupt flag)

    I also did try to use a PCF8574A, just to be sure it wasn't the I2C device that was causing the problems. At least I know that the slaves aren't a problem.

    Best regards,

    Helder

  • Hi,
    So you mean you stuck here:
    void EUSCI_B_I2C_masterSendMultiByteStart (uint16_t baseAddress,uint8_t txData)
    {
        //Store current transmit interrupt enable
        uint16_t txieStatus = HWREG16(baseAddress + OFS_UCBxIE) & UCTXIE;
        //Disable transmit interrupt enable
        HWREG16(baseAddress + OFS_UCBxIE) &= ~(UCTXIE);
        //Send start condition.
        HWREG16(baseAddress + OFS_UCBxCTLW0) |= UCTR +  UCTXSTT;
        //Poll for transmit interrupt flag.
        while (!(HWREG16(baseAddress + OFS_UCBxIFG) & UCTXIFG)) ;
        //Send single byte data.
        HWREG16(baseAddress + OFS_UCBxTXBUF) = txData;
        //Reinstate transmit interrupt enable
        HWREG16(baseAddress + OFS_UCBxIE) |= txieStatus;
    }
    You can check the UCAxTXBUF and UCTXIFG registers through debug mode and find the reason.
    Can you post your code, because there are too many versions. And it is impossible to find your problem through a example code.
    Or can you post a code that I can reproduce your problem?
    Eason
  • Hi,

    The modified example code I've mentioned above isn't working. I believe that the code above could reproduce the problem.

    One additional thing, I've hooked up my oscilloscope to see the bus lines and discovered that when the microcontroller pulls down the SDA line, the SDA remains low forever (even if I restart the microcontroller) until I remove and connect power to the PCF8574 again.

    The OLED has almost the same behavior except I can't make the SDA line high even power cycling the OLED.

    Best regards,

    Helder

  • Hi,

    PCF8574  only support 100kHz I2C. PLease double check.

    I will later use a salve to simulate your situation.

    Eason

  • Hi,

    I am back, it really takes me a lot of time. A tiny mistake.

    The code you offered has a problem.

        EUSCI_B_I2C_clearInterrupt(EUSCI_B1_BASE,
                EUSCI_B_I2C_TRANSMIT_INTERRUPT1 +
                EUSCI_B_I2C_NAK_INTERRUPT
                );
        //Enable master Receive interrupt
        EUSCI_B_I2C_enableInterrupt(EUSCI_B1_BASE,
                EUSCI_B_I2C_TRANSMIT_INTERRUPT1 +
                EUSCI_B_I2C_NAK_INTERRUPT
                );
    EUSCI_B_I2C_TRANSMIT_INTERRUPT1 should be replaced by EUSCI_B_I2C_TRANSMIT_INTERRUPT0, because as a master, only interrupt 0 can be used.
    Here is my result:
    Eason
  • Hi,

    That's it, it was really a tiny mistake that went unnoticed, sorry. I'm really grateful for your help! About the I2C skipping bytes, at least in the interrupt mode, I solved putting a

    while(EUSCI_B_I2C_isBusBusy(EUSCI_B1_BASE) == EUSCI_B_I2C_BUS_BUSY);

    After the EUSCI_B_I2C_masterSendMultiByteStart. Adding the delay without the bit check solved too.

    The only thing is that the delay (or flag check) only works after the start bit. Not even checking for EUSCI_B_I2C_SENDING_START or EUSCI_B_I2C_SENDING_STOP worked. And that is for CPU clock above 1MHz (at least in my case).

    Thanks again,

    Helder

**Attention** This is a public forum