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.

MSP430FR2433: Example Driverlib code for I2C for combined transaction?

Part Number: MSP430FR2433

Is there example code using DriverLib for the common case where the slave has an address space of registers and the master first transmits a register address and then receives one or more bytes from the slave? This common case is discussed in TI’s App Report “Understanding the I2C Bus” SLVA704. The case is also called a “combined transaction” where the master sends a restart bit to turn the link around.

IMO this is so common that TI should provide an example, even a new API in DriverLib.

I have struggled to find the right combination of DriverLib calls. For example, this doesn’t work (It hangs waiting for RXIFG):

... disable, configure, set slave address, and enable....
// start then send register address (whose MSB==0 tells slave this is a read)
EUSCI_B_I2C_masterSendMultiByteStart(I2CInstanceAddress, registerAddress);
// restart and receive data and stop
result = EUSCI_B_I2C_masterReceiveSingleByte(I2CInstanceAddress);

I have tried other combinations. I wish the documentation described the legal sequences of calls to driver lib.

My context is: CCSv8, MSP430FR2433 Launchpad, STMicro LIS3MDL compass chip. I don’t have a logic analyzer.

I wish there were an API:

readSingle(eUSCIInstance, slaveAddress, registerAddress)
readMultiple(eUSCIInstance, slaveAddress, registerAddress, buffer, length)
writeSingle(eUSCIInstance, slaveAddress, registerAddress)
writeMultiple(eUSCIInstance, slaveAddress, registerAddress, buffer, length)

I think there is another thread suggesting it can be done. There is also another thread suggesting multiple bytes cannot be received without using an ISR that calls EUSCI_B_I2C_masterReceiveNext(). (“I2C Driver Lib Timeout Value”)

  • Hi lloyd,

    Thanks for your suggestion. I will feedback it to MSP software team.

    At the same time, please check the attached bare metal code example. Is it the combined transaction example you need?

    //******************************************************************************
    //   MSP430FR243x Demo - eUSCI_B0, I2C Master multiple byte TX/RX
    //
    //   Description: I2C master communicates to I2C slave sending and receiving
    //   3 different messages of different length. I2C master will enter LPM0 mode
    //   while waiting for the messages to be sent/receiving using I2C interrupt.
    //   ACLK = NA, MCLK = SMCLK = DCO 16MHz.
    //
    //                                     /|\ /|\
    //                   MSP430FR2633      4.7k |
    //                 -----------------    |  4.7k
    //            /|\ |             P1.3|---+---|-- I2C Clock (UCB0SCL)
    //             |  |                 |       |
    //             ---|RST          P1.2|-------+-- I2C Data (UCB0SDA)
    //                |                 |
    //                |                 |
    //                |                 |
    //                |                 |
    //                |                 |
    //                |                 |
    //
    //   Nima Eskandari and Ryan Meredith
    //   Texas Instruments Inc.
    //   January 2018
    //   Built with CCS V7.3
    //******************************************************************************
    
    #include <msp430.h> 
    #include <stdint.h>
    #include <stdbool.h>
    
    //******************************************************************************
    // Pin Config ******************************************************************
    //******************************************************************************
    
    #define LED_OUT     P1OUT
    #define LED_DIR     P1DIR
    #define LED0_PIN    BIT0
    #define LED1_PIN    BIT1
    
    //******************************************************************************
    // Example Commands ************************************************************
    //******************************************************************************
    
    #define SLAVE_ADDR  0x48
    
    /* CMD_TYPE_X_SLAVE are example commands the master sends to the slave.
     * The slave will send example SlaveTypeX buffers in response.
     *
     * CMD_TYPE_X_MASTER are example commands the master sends to the slave.
     * The slave will initialize itself to receive MasterTypeX example buffers.
     * */
    
    #define CMD_TYPE_0_SLAVE      0
    #define CMD_TYPE_1_SLAVE      1
    #define CMD_TYPE_2_SLAVE      2
    
    #define CMD_TYPE_0_MASTER      3
    #define CMD_TYPE_1_MASTER      4
    #define CMD_TYPE_2_MASTER      5
    
    #define TYPE_0_LENGTH   1
    #define TYPE_1_LENGTH   2
    #define TYPE_2_LENGTH   6
    
    #define MAX_BUFFER_SIZE     20
    
    /* MasterTypeX are example buffers initialized in the master, they will be
     * sent by the master to the slave.
     * SlaveTypeX are example buffers initialized in the slave, they will be
     * sent by the slave to the master.
     * */
    
    uint8_t MasterType2 [TYPE_2_LENGTH] = {'F', '4', '1', '9', '2', 'B'};
    uint8_t MasterType1 [TYPE_1_LENGTH] = { 8, 9};
    uint8_t MasterType0 [TYPE_0_LENGTH] = { 11};
    
    
    uint8_t SlaveType2 [TYPE_2_LENGTH] = {0};
    uint8_t SlaveType1 [TYPE_1_LENGTH] = {0};
    uint8_t SlaveType0 [TYPE_0_LENGTH] = {0};
    
    //******************************************************************************
    // General I2C State Machine ***************************************************
    //******************************************************************************
    
    typedef enum I2C_ModeEnum{
        IDLE_MODE,
        NACK_MODE,
        TX_REG_ADDRESS_MODE,
        RX_REG_ADDRESS_MODE,
        TX_DATA_MODE,
        RX_DATA_MODE,
        SWITCH_TO_RX_MODE,
        SWITHC_TO_TX_MODE,
        TIMEOUT_MODE
    } I2C_Mode;
    
    
    /* Used to track the state of the software state machine*/
    I2C_Mode MasterMode = IDLE_MODE;
    
    /* The Register Address/Command to use*/
    uint8_t TransmitRegAddr = 0;
    
    /* ReceiveBuffer: Buffer used to receive data in the ISR
     * RXByteCtr: Number of bytes left to receive
     * ReceiveIndex: The index of the next byte to be received in ReceiveBuffer
     * TransmitBuffer: Buffer used to transmit data in the ISR
     * TXByteCtr: Number of bytes left to transfer
     * TransmitIndex: The index of the next byte to be transmitted in TransmitBuffer
     * */
    uint8_t ReceiveBuffer[MAX_BUFFER_SIZE] = {0};
    uint8_t RXByteCtr = 0;
    uint8_t ReceiveIndex = 0;
    uint8_t TransmitBuffer[MAX_BUFFER_SIZE] = {0};
    uint8_t TXByteCtr = 0;
    uint8_t TransmitIndex = 0;
    
    
    
    /* I2C Write and Read Functions */
    
    /* For slave device with dev_addr, writes the data specified in *reg_data
     *
     * dev_addr: The slave device address.
     *           Example: SLAVE_ADDR
     * reg_addr: The register or command to send to the slave.
     *           Example: CMD_TYPE_0_MASTER
     * *reg_data: The buffer to write
     *           Example: MasterType0
     * count: The length of *reg_data
     *           Example: TYPE_0_LENGTH
     *  */
    I2C_Mode I2C_Master_WriteReg(uint8_t dev_addr, uint8_t reg_addr, uint8_t *reg_data, uint8_t count);
    
    /* For slave device with dev_addr, read the data specified in slaves reg_addr.
     * The received data is available in ReceiveBuffer
     *
     * dev_addr: The slave device address.
     *           Example: SLAVE_ADDR
     * reg_addr: The register or command to send to the slave.
     *           Example: CMD_TYPE_0_SLAVE
     * count: The length of data to read
     *           Example: TYPE_0_LENGTH
     *  */
    I2C_Mode I2C_Master_ReadReg(uint8_t dev_addr, uint8_t reg_addr, uint8_t count);
    void CopyArray(uint8_t *source, uint8_t *dest, uint8_t count);
    
    
    I2C_Mode I2C_Master_ReadReg(uint8_t dev_addr, uint8_t reg_addr, uint8_t count)
    {
        /* Initialize state machine */
        MasterMode = TX_REG_ADDRESS_MODE;
        TransmitRegAddr = reg_addr;
        RXByteCtr = count;
        TXByteCtr = 0;
        ReceiveIndex = 0;
        TransmitIndex = 0;
    
        /* Initialize slave address and interrupts */
        UCB0I2CSA = dev_addr;
        UCB0IFG &= ~(UCTXIFG + UCRXIFG);       // Clear any pending interrupts
        UCB0IE &= ~UCRXIE;                       // Disable RX interrupt
        UCB0IE |= UCTXIE;                        // Enable TX interrupt
    
        UCB0CTLW0 |= UCTR + UCTXSTT;             // I2C TX, start condition
        __bis_SR_register(LPM0_bits + GIE);              // Enter LPM0 w/ interrupts
    
        return MasterMode;
    
    }
    
    
    I2C_Mode I2C_Master_WriteReg(uint8_t dev_addr, uint8_t reg_addr, uint8_t *reg_data, uint8_t count)
    {
        /* Initialize state machine */
        MasterMode = TX_REG_ADDRESS_MODE;
        TransmitRegAddr = reg_addr;
    
        //Copy register data to TransmitBuffer
        CopyArray(reg_data, TransmitBuffer, count);
    
        TXByteCtr = count;
        RXByteCtr = 0;
        ReceiveIndex = 0;
        TransmitIndex = 0;
    
        /* Initialize slave address and interrupts */
        UCB0I2CSA = dev_addr;
        UCB0IFG &= ~(UCTXIFG + UCRXIFG);       // Clear any pending interrupts
        UCB0IE &= ~UCRXIE;                       // Disable RX interrupt
        UCB0IE |= UCTXIE;                        // Enable TX interrupt
    
        UCB0CTLW0 |= UCTR + UCTXSTT;             // I2C TX, start condition
        __bis_SR_register(LPM0_bits + GIE);              // Enter LPM0 w/ interrupts
    
        return MasterMode;
    }
    
    void CopyArray(uint8_t *source, uint8_t *dest, uint8_t count)
    {
        uint8_t copyIndex = 0;
        for (copyIndex = 0; copyIndex < count; copyIndex++)
        {
            dest[copyIndex] = source[copyIndex];
        }
    }
    
    
    //******************************************************************************
    // Device Initialization *******************************************************
    //******************************************************************************
    
    
    void initGPIO()
    {
        // Configure GPIO
        LED_OUT &= ~(LED0_PIN | LED1_PIN); // P1 setup for LED & reset output
        LED_DIR |= (LED0_PIN | LED1_PIN);
    
        // I2C pins
        P1SEL0 |= BIT2 | BIT3;
        P1SEL1 &= ~(BIT2 | BIT3);
    
        // Disable the GPIO power-on default high-impedance mode to activate
        // previously configured port settings
        PM5CTL0 &= ~LOCKLPM5;
    }
    
    void initClockTo16MHz()
    {
        // Configure one FRAM waitstate as required by the device datasheet for MCLK
        // operation beyond 8MHz _before_ configuring the clock system.
        FRCTL0 = FRCTLPW | NWAITS_1;
    
        // Clock System Setup
        __bis_SR_register(SCG0);                           // disable FLL
        CSCTL3 |= SELREF__REFOCLK;                         // Set REFO as FLL reference source
        CSCTL0 = 0;                                        // clear DCO and MOD registers
        CSCTL1 &= ~(DCORSEL_7);                            // Clear DCO frequency select bits first
        CSCTL1 |= DCORSEL_5;                               // Set DCO = 16MHz
        CSCTL2 = FLLD_0 + 487;                             // DCOCLKDIV = 16MHz
        __delay_cycles(3);
        __bic_SR_register(SCG0);                           // enable FLL
        while(CSCTL7 & (FLLUNLOCK0 | FLLUNLOCK1));         // FLL locked
    }
    
    void initI2C()
    {
        UCB0CTLW0 = UCSWRST;                      // Enable SW reset
        UCB0CTLW0 |= UCMODE_3 | UCMST | UCSSEL__SMCLK | UCSYNC; // I2C master mode, SMCLK
        UCB0BRW = 160;                            // fSCL = SMCLK/160 = ~100kHz
        UCB0I2CSA = SLAVE_ADDR;                   // Slave Address
        UCB0CTLW0 &= ~UCSWRST;                    // Clear SW reset, resume operation
        UCB0IE |= UCNACKIE;
    }
    
    
    //******************************************************************************
    // Main ************************************************************************
    // Send and receive three messages containing the example commands *************
    //******************************************************************************
    
    int main(void) {
        WDTCTL = WDTPW | WDTHOLD;	// Stop watchdog timer
        initClockTo16MHz();
        initGPIO();
        initI2C();
    
        I2C_Master_WriteReg(SLAVE_ADDR, CMD_TYPE_0_MASTER, MasterType0, TYPE_0_LENGTH);
        I2C_Master_WriteReg(SLAVE_ADDR, CMD_TYPE_1_MASTER, MasterType1, TYPE_1_LENGTH);
        I2C_Master_WriteReg(SLAVE_ADDR, CMD_TYPE_2_MASTER, MasterType2, TYPE_2_LENGTH);
    
        I2C_Master_ReadReg(SLAVE_ADDR, CMD_TYPE_0_SLAVE, TYPE_0_LENGTH);
        CopyArray(ReceiveBuffer, SlaveType0, TYPE_0_LENGTH);
    
        I2C_Master_ReadReg(SLAVE_ADDR, CMD_TYPE_1_SLAVE, TYPE_1_LENGTH);
        CopyArray(ReceiveBuffer, SlaveType1, TYPE_1_LENGTH);
    
        I2C_Master_ReadReg(SLAVE_ADDR, CMD_TYPE_2_SLAVE, TYPE_2_LENGTH);
        CopyArray(ReceiveBuffer, SlaveType2, TYPE_2_LENGTH);
    
        __bis_SR_register(LPM0_bits + GIE);
    	return 0;
    }
    
    
    //******************************************************************************
    // I2C Interrupt ***************************************************************
    //******************************************************************************
    
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector = USCI_B0_VECTOR
    __interrupt void USCI_B0_ISR(void)
    #elif defined(__GNUC__)
    void __attribute__ ((interrupt(USCI_B0_VECTOR))) USCI_B0_ISR (void)
    #else
    #error Compiler not supported!
    #endif
    {
      //Must read from UCB0RXBUF
      uint8_t rx_val = 0;
      switch(__even_in_range(UCB0IV, USCI_I2C_UCBIT9IFG))
      {
        case USCI_NONE:          break;         // Vector 0: No interrupts
        case USCI_I2C_UCALIFG:   break;         // Vector 2: ALIFG
        case USCI_I2C_UCNACKIFG:                // Vector 4: NACKIFG
          break;
        case USCI_I2C_UCSTTIFG:  break;         // Vector 6: STTIFG
        case USCI_I2C_UCSTPIFG:  break;         // Vector 8: STPIFG
        case USCI_I2C_UCRXIFG3:  break;         // Vector 10: RXIFG3
        case USCI_I2C_UCTXIFG3:  break;         // Vector 12: TXIFG3
        case USCI_I2C_UCRXIFG2:  break;         // Vector 14: RXIFG2
        case USCI_I2C_UCTXIFG2:  break;         // Vector 16: TXIFG2
        case USCI_I2C_UCRXIFG1:  break;         // Vector 18: RXIFG1
        case USCI_I2C_UCTXIFG1:  break;         // Vector 20: TXIFG1
        case USCI_I2C_UCRXIFG0:                 // Vector 22: RXIFG0
            rx_val = UCB0RXBUF;
            if (RXByteCtr)
            {
              ReceiveBuffer[ReceiveIndex++] = rx_val;
              RXByteCtr--;
            }
    
            if (RXByteCtr == 1)
            {
              UCB0CTLW0 |= UCTXSTP;
            }
            else if (RXByteCtr == 0)
            {
              UCB0IE &= ~UCRXIE;
              MasterMode = IDLE_MODE;
              __bic_SR_register_on_exit(CPUOFF);      // Exit LPM0
            }
            break;
        case USCI_I2C_UCTXIFG0:                 // Vector 24: TXIFG0
            switch (MasterMode)
            {
              case TX_REG_ADDRESS_MODE:
                  UCB0TXBUF = TransmitRegAddr;
                  if (RXByteCtr)
                      MasterMode = SWITCH_TO_RX_MODE;   // Need to start receiving now
                  else
                      MasterMode = TX_DATA_MODE;        // Continue to transmision with the data in Transmit Buffer
                  break;
    
              case SWITCH_TO_RX_MODE:
                  UCB0IE |= UCRXIE;              // Enable RX interrupt
                  UCB0IE &= ~UCTXIE;             // Disable TX interrupt
                  UCB0CTLW0 &= ~UCTR;            // Switch to receiver
                  MasterMode = RX_DATA_MODE;    // State state is to receive data
                  UCB0CTLW0 |= UCTXSTT;          // Send repeated start
                  if (RXByteCtr == 1)
                  {
                      //Must send stop since this is the N-1 byte
                      while((UCB0CTLW0 & UCTXSTT));
                      UCB0CTLW0 |= UCTXSTP;      // Send stop condition
                  }
                  break;
    
              case TX_DATA_MODE:
                  if (TXByteCtr)
                  {
                      UCB0TXBUF = TransmitBuffer[TransmitIndex++];
                      TXByteCtr--;
                  }
                  else
                  {
                      //Done with transmission
                      UCB0CTLW0 |= UCTXSTP;     // Send stop condition
                      MasterMode = IDLE_MODE;
                      UCB0IE &= ~UCTXIE;                       // disable TX interrupt
                      __bic_SR_register_on_exit(CPUOFF);      // Exit LPM0
                  }
                  break;
    
              default:
                  __no_operation();
                  break;
            }
            break;
        default: break;
      }
    }
    
    

    //******************************************************************************
    //   MSP430FR243x Demo - eUSCI_B0, I2C Slave multiple byte TX/RX
    //
    //   Description: I2C master communicates to I2C slave sending and receiving
    //   3 different messages of different length. (This is the slave code). The
    //   slave will be in LPM0 mode, waiting for the master to initiate the
    //   communication. The slave will send/receive bytes based on the master's
    //   request. The slave will handle I2C bytes sent/received using the
    //   I2C interrupt.
    //   ACLK = NA, MCLK = SMCLK = DCO 16MHz.
    //
    //                                     /|\ /|\
    //                   MSP430FR2633      4.7k |
    //                 -----------------    |  4.7k
    //            /|\ |             P1.3|---+---|-- I2C Clock (UCB0SCL)
    //             |  |                 |       |
    //             ---|RST          P1.2|-------+-- I2C Data (UCB0SDA)
    //                |                 |
    //                |                 |
    //                |                 |
    //                |                 |
    //                |                 |
    //                |                 |
    //
    //   Nima Eskandari and Ryan Meredith
    //   Texas Instruments Inc.
    //   November 2017
    //   Built with CCS V7.3
    //******************************************************************************
    
    #include <msp430.h>
    #include <stdint.h>
    
    //******************************************************************************
    // Example Commands ************************************************************
    //******************************************************************************
    
    #define LED_OUT     P1OUT
    #define LED_DIR     P1DIR
    #define LED0_PIN    BIT0
    #define LED1_PIN    BIT1
    
    #define SLAVE_ADDR  0x48
    
    /* CMD_TYPE_X_SLAVE are example commands the master sends to the slave.
     * The slave will send example SlaveTypeX buffers in response.
     *
     * CMD_TYPE_X_MASTER are example commands the master sends to the slave.
     * The slave will initialize itself to receive MasterTypeX example buffers.
     * */
    
    #define CMD_TYPE_0_SLAVE      0
    #define CMD_TYPE_1_SLAVE      1
    #define CMD_TYPE_2_SLAVE      2
    
    #define CMD_TYPE_0_MASTER      3
    #define CMD_TYPE_1_MASTER      4
    #define CMD_TYPE_2_MASTER      5
    
    #define TYPE_0_LENGTH   1
    #define TYPE_1_LENGTH   2
    #define TYPE_2_LENGTH   6
    
    #define MAX_BUFFER_SIZE     20
    
    /* MasterTypeX are example buffers initialized in the master, they will be
     * sent by the master to the slave.
     * SlaveTypeX are example buffers initialized in the slave, they will be
     * sent by the slave to the master.
     * */
    
    uint8_t MasterType2 [TYPE_2_LENGTH] = {0};
    uint8_t MasterType1 [TYPE_1_LENGTH] = { 0, 0};
    uint8_t MasterType0 [TYPE_0_LENGTH] = { 0};
    
    uint8_t SlaveType2 [TYPE_2_LENGTH] = {'A', 'B', 'C', 'D', '1', '2'};
    uint8_t SlaveType1 [TYPE_1_LENGTH] = {15, 16};
    uint8_t SlaveType0 [TYPE_0_LENGTH] = {12};
    
    //******************************************************************************
    // General I2C State Machine ***************************************************
    //******************************************************************************
    
    typedef enum I2C_ModeEnum{
        IDLE_MODE,
        NACK_MODE,
        TX_REG_ADDRESS_MODE,
        RX_REG_ADDRESS_MODE,
        TX_DATA_MODE,
        RX_DATA_MODE,
        SWITCH_TO_RX_MODE,
        SWITHC_TO_TX_MODE,
        TIMEOUT_MODE
    } I2C_Mode;
    
    /* Used to track the state of the software state machine*/
    I2C_Mode SlaveMode = RX_REG_ADDRESS_MODE;
    
    /* The Register Address/Command to use*/
    uint8_t ReceiveRegAddr = 0;
    
    /* ReceiveBuffer: Buffer used to receive data in the ISR
     * RXByteCtr: Number of bytes left to receive
     * ReceiveIndex: The index of the next byte to be received in ReceiveBuffer
     * TransmitBuffer: Buffer used to transmit data in the ISR
     * TXByteCtr: Number of bytes left to transfer
     * TransmitIndex: The index of the next byte to be transmitted in TransmitBuffer
     * */
    uint8_t ReceiveBuffer[MAX_BUFFER_SIZE] = {0};
    uint8_t RXByteCtr = 0;
    uint8_t ReceiveIndex = 0;
    uint8_t TransmitBuffer[MAX_BUFFER_SIZE] = {0};
    uint8_t TXByteCtr = 0;
    uint8_t TransmitIndex = 0;
    
    
    /* Initialized the software state machine according to the received cmd
     *
     * cmd: The command/register address received
     * */
    void I2C_Slave_ProcessCMD(uint8_t cmd);
    
    /* The transaction between the slave and master is completed. Uses cmd
     * to do post transaction operations. (Place data from ReceiveBuffer
     * to the corresponding buffer based in the last received cmd)
     *
     * cmd: The command/register address corresponding to the completed
     * transaction
     */
    void I2C_Slave_TransactionDone(uint8_t cmd);
    void CopyArray(uint8_t *source, uint8_t *dest, uint8_t count);
    
    void I2C_Slave_ProcessCMD(uint8_t cmd)
    {
        ReceiveIndex = 0;
        TransmitIndex = 0;
        RXByteCtr = 0;
        TXByteCtr = 0;
    
        switch (cmd)
        {
            case (CMD_TYPE_0_SLAVE):                        //Send slave device id (This device's id)
                SlaveMode = TX_DATA_MODE;
                TXByteCtr = TYPE_0_LENGTH;
                //Fill out the TransmitBuffer
                CopyArray(SlaveType0, TransmitBuffer, TYPE_0_LENGTH);
                UCB0IE &= ~UCRXIE;                       // Disable RX interrupt
                UCB0IE |= UCTXIE;                        // Enable TX interrupt
                break;
            case (CMD_TYPE_1_SLAVE):                      //Send slave device time (This device's time)
                SlaveMode = TX_DATA_MODE;
                TXByteCtr = TYPE_1_LENGTH;
                //Fill out the TransmitBuffer
                CopyArray(SlaveType1, TransmitBuffer, TYPE_1_LENGTH);
                UCB0IE &= ~UCRXIE;                       // Disable RX interrupt
                UCB0IE |= UCTXIE;                        // Enable TX interrupt
                break;
            case (CMD_TYPE_2_SLAVE):                  //Send slave device location (This device's location)
                SlaveMode = TX_DATA_MODE;
                TXByteCtr = TYPE_2_LENGTH;
                //Fill out the TransmitBuffer
                CopyArray(SlaveType2, TransmitBuffer, TYPE_2_LENGTH);
                UCB0IE &= ~UCRXIE;                       // Disable RX interrupt
                UCB0IE |= UCTXIE;                        // Enable TX interrupt
                break;
            case (CMD_TYPE_0_MASTER):
                SlaveMode = RX_DATA_MODE;
                RXByteCtr = TYPE_0_LENGTH;
                UCB0IE &= ~UCTXIE;                       // Disable RX interrupt
                UCB0IE |= UCRXIE;                        // Enable TX interrupt
                break;
            case (CMD_TYPE_1_MASTER):
                SlaveMode = RX_DATA_MODE;
                RXByteCtr = TYPE_1_LENGTH;
                UCB0IE &= ~UCTXIE;                       // Disable RX interrupt
                UCB0IE |= UCRXIE;                        // Enable TX interrupt
                break;
            case (CMD_TYPE_2_MASTER):
                SlaveMode = RX_DATA_MODE;
                RXByteCtr = TYPE_2_LENGTH;
                UCB0IE &= ~UCTXIE;                       // Disable RX interrupt
                UCB0IE |= UCRXIE;                        // Enable TX interrupt
                break;
            default:
                __no_operation();
                break;
        }
    }
    
    
    void I2C_Slave_TransactionDone(uint8_t cmd)
    {
        switch (cmd)
        {
            case (CMD_TYPE_0_SLAVE):                        //Slave device id was sent(This device's id)
                break;
            case (CMD_TYPE_1_SLAVE):                      //Slave device time was sent(This device's time)
                break;
            case (CMD_TYPE_2_SLAVE):                  //Send slave device location (This device's location)
                break;
            case (CMD_TYPE_0_MASTER):
                CopyArray(ReceiveBuffer, MasterType0, TYPE_0_LENGTH);
                break;
            case (CMD_TYPE_1_MASTER):
                CopyArray(ReceiveBuffer, MasterType1, TYPE_1_LENGTH);
                break;
            case (CMD_TYPE_2_MASTER):
                CopyArray(ReceiveBuffer, MasterType2, TYPE_2_LENGTH);
                break;
            default:
                __no_operation();
                break;
        }
    }
    
    void CopyArray(uint8_t *source, uint8_t *dest, uint8_t count)
    {
        uint8_t copyIndex = 0;
        for (copyIndex = 0; copyIndex < count; copyIndex++)
        {
            dest[copyIndex] = source[copyIndex];
        }
    }
    
    
    //******************************************************************************
    // Device Initialization *******************************************************
    //******************************************************************************
    
    void initGPIO()
    {
        // Configure GPIO
        LED_OUT &= ~(LED0_PIN | LED1_PIN); // P1 setup for LED & reset output
        LED_DIR |= (LED0_PIN | LED1_PIN);
    
        P1SEL0 |= BIT2 | BIT3;                    // I2C pins
        P1SEL1 &= ~(BIT2 | BIT3);
    
        // Disable the GPIO power-on default high-impedance mode to activate
        // previously configured port settings
        PM5CTL0 &= ~LOCKLPM5;
    }
    
    void initI2C()
    {
        UCB0CTLW0 = UCSWRST;                      // Software reset enabled
        UCB0CTLW0 |= UCMODE_3 | UCSYNC;           // I2C mode, sync mode
        UCB0I2COA0 = SLAVE_ADDR | UCOAEN;         // Own Address and enable
        UCB0CTLW0 &= ~UCSWRST;                    // clear reset register
        UCB0IE |= UCRXIE + UCSTPIE;
    }
    
    void initClockTo16MHz()
    {
        // Configure one FRAM waitstate as required by the device datasheet for MCLK
        // operation beyond 8MHz _before_ configuring the clock system.
        FRCTL0 = FRCTLPW | NWAITS_1;
    
        __bis_SR_register(SCG0);    // disable FLL
        CSCTL3 |= SELREF__REFOCLK;  // Set REFO as FLL reference source
        CSCTL0 = 0;                 // clear DCO and MOD registers
        CSCTL1 &= ~(DCORSEL_7);     // Clear DCO frequency select bits first
        CSCTL1 |= DCORSEL_5;        // Set DCO = 16MHz
        CSCTL2 = FLLD_0 + 487;      // set to fDCOCLKDIV = (FLLN + 1)*(fFLLREFCLK/n)
                                    //                   = (487 + 1)*(32.768 kHz/1)
                                    //                   = 16 MHz
    
        __delay_cycles(3);
        __bic_SR_register(SCG0);                        // enable FLL
        while(CSCTL7 & (FLLUNLOCK0 | FLLUNLOCK1));      // FLL locked
    
        CSCTL4 = SELMS__DCOCLKDIV | SELA__REFOCLK;
    }
    
    
    //******************************************************************************
    // Main ************************************************************************
    // Enters LPM0 and waits for I2C interrupts. The data sent from the master is  *
    // then interpreted and the device will respond accordingly                    *
    //******************************************************************************
    
    
    int main(void) {
        WDTCTL = WDTPW | WDTHOLD;	// Stop watchdog timer
    
        initClockTo16MHz();
        initGPIO();
        initI2C();
    
        __bis_SR_register(LPM0_bits + GIE);
    	return 0;
    }
    
    //******************************************************************************
    // I2C Interrupt ***************************************************************
    //******************************************************************************
    
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector = USCI_B0_VECTOR
    __interrupt void USCI_B0_ISR(void)
    #elif defined(__GNUC__)
    void __attribute__ ((interrupt(USCI_B0_VECTOR))) USCI_B0_ISR (void)
    #else
    #error Compiler not supported!
    #endif
    {
      //Must read from UCB0RXBUF
      uint8_t rx_val = 0;
      switch(__even_in_range(UCB0IV, USCI_I2C_UCBIT9IFG))
      {
        case USCI_NONE:          break;         // Vector 0: No interrupts
        case USCI_I2C_UCALIFG:   break;         // Vector 2: ALIFG
        case USCI_I2C_UCNACKIFG:                // Vector 4: NACKIFG
          break;
        case USCI_I2C_UCSTTIFG:  break;         // Vector 6: STTIFG
        case USCI_I2C_UCSTPIFG:
            UCB0IFG &= ~(UCTXIFG0);
            break;         // Vector 8: STPIFG
        case USCI_I2C_UCRXIFG3:  break;         // Vector 10: RXIFG3
        case USCI_I2C_UCTXIFG3:  break;         // Vector 12: TXIFG3
        case USCI_I2C_UCRXIFG2:  break;         // Vector 14: RXIFG2
        case USCI_I2C_UCTXIFG2:  break;         // Vector 16: TXIFG2
        case USCI_I2C_UCRXIFG1:  break;         // Vector 18: RXIFG1
        case USCI_I2C_UCTXIFG1:  break;         // Vector 20: TXIFG1
        case USCI_I2C_UCRXIFG0:                 // Vector 22: RXIFG0
            rx_val = UCB0RXBUF;
            switch (SlaveMode)
            {
              case (RX_REG_ADDRESS_MODE):
                  ReceiveRegAddr = rx_val;
                  I2C_Slave_ProcessCMD(ReceiveRegAddr);
                  break;
              case (RX_DATA_MODE):
                  ReceiveBuffer[ReceiveIndex++] = rx_val;
                  RXByteCtr--;
                  if (RXByteCtr == 0)
                  {
                      //Done Receiving MSG
                      SlaveMode = RX_REG_ADDRESS_MODE;
                      UCB0IE &= ~(UCTXIE);
                      UCB0IE |= UCRXIE;                          // Enable RX interrupt
                      I2C_Slave_TransactionDone(ReceiveRegAddr);
                  }
                  break;
              default:
                  __no_operation();
                  break;
            }
            break;
        case USCI_I2C_UCTXIFG0:                 // Vector 24: TXIFG0
            switch (SlaveMode)
            {
              case (TX_DATA_MODE):
                  UCB0TXBUF = TransmitBuffer[TransmitIndex++];
                  TXByteCtr--;
                  if (TXByteCtr == 0)
                  {
                      //Done Transmitting MSG
                      SlaveMode = RX_REG_ADDRESS_MODE;
                      UCB0IE &= ~(UCTXIE);
                      UCB0IE |= UCRXIE;                          // Enable RX interrupt
                      I2C_Slave_TransactionDone(ReceiveRegAddr);
                  }
                  break;
              default:
                  __no_operation();
                  break;
            }
            break;                      // Interrupt Vector: I2C Mode: UCTXIFG
        default: break;
      }
    }
    

    Regards,

    Ling

  • Yes, thanks, your example code exemplifies the "combined transaction" that I need. Where did you find that example code?

    And your example code does seem to work with a STMicro LIS3MDL compass chip, at least to read the ID register.

    But it doesn't answer the question, since I would prefer a DriverLib implementation (since it might be more portable among family members.) I might want to explore how the sequence of events from your example compares to the sequence of events from the DriverLib calls I gave in my original question.

    Also, for the LIS3MDL chip (and I suspect for many other I2C sensor chips), the register address byte has the upper two MSB bits set to indicate whether it is a read or write, and whether it is multiple byte transaction. It would be nice if that was in the API. As it is, I must modify the example, which is more of a template, a toy that can't be tested without modification with real hardware. A higher level API in DriverLib would be nice.
  • Hi lloyd,

    This is bare metal code example. And unfortunately we don't have driverlib version.

    Regards,
    Ling

**Attention** This is a public forum