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 code for I2C communication using interrupts

Part Number: MSP430FR2433


To whoever may be struggling with eUSCI I2C implementation using interrupts....

I have spent countless hours fruitlessly combing through the net looking for working examples, or at least pointers to how to implement I2C communication using eUSCI module.

There are already plenty of comments on TI's toy examples, so I do not need to say anything more about it.  Documentation contains almost everything but it is so dense, it seems

even punctuation marks matter.  I still wasn't able to find very fine details about how to implement "repeated start" with auto-stop bit (not sure if it is even possible). For example,

in order to modify UCASTPx (from sending 1 byte reg address to reading desired nr of bytes), I have to reset the module, UCSWRST = 1, but doing so will mess up (yet) unsent STOP bit.

I could not get the "repeated start" to work even with manually sending stop bits.  The next best thing seems sending start bit immediately after the stop bit and this code does it.

Not sure if this will be of any use to anyone, but this code is working for me on MSP430FR2433 (clock running at 8 MHz and I2C at 100kHz), running as a master, in single

master mode.

Any comments on how to improve it, made it more robust or efficient are welcome (no claims to perfection, just working).

Dispatch routine, used for both reading and writing to a slave.

I2c_Fault_t i2c_dispatch( pi2c_Stream_t pI2c,      // struct containing I2C base address, rx/tx buffer and some auxiliary data
                                 uint8_t slave_address,          // I2C slave address
                                 uint8_t reg_address,             // slave reg address
                                 uint8_t * pdata,                     // pointer to data to be sent (rx data to be written to)
                                 uint8_t tx_size,                     // tx_size if nr of data bytes, excluding slave reg adddress
                                 uint8_t rx_size)                     // nr of rx bytes if reading
{
    // check if module is not busy
    I2c_Fault_t ret_value = i2c_is_ready( pI2c );
    if ( ret_value )
    {
        return ret_value;
    }

    pI2c->rx_size   = rx_size;       
    pI2c->tx_size   = tx_size + 1;       // one extra byte for reg address
    pI2c->buff[0]   = reg_address;     // put reg address as the first byte of the buffer to be sent out
    uint8_t i = 1;
    while ( tx_size > 0 )                      // write other data (no data in case of read)
    {
        pI2c->buff[i]   = *pdata;
        pdata ++;
        tx_size --;
    }
    pI2c->ptr       = 0;                        // initialize pointer of the buffer
    pI2c->busy      = 1;                      // set I2C structure to busy

    i2c_disable_module( pI2c );              // put in reset condition while changing parameters
    i2c_set_slave_address( pI2c, slave_address );
    i2c_set_mode( pI2c, UCTR );              // both RD and WR start with WR

    // setup auto-stop bit if transmit only (will not be followed by repeated start)
        HWREG16( pI2c->base_address + OFS_UCBxTBCNT ) = pI2c->tx_size;
        HWREG16( pI2c->base_address + OFS_UCBxCTLW1 ) &= ~( UCASTP3_L ); // clear before setting bits
        HWREG16( pI2c->base_address + OFS_UCBxCTLW1 ) |= UCASTP1_L;    // automatic stop bit generation after all bytes are sent

    i2c_send_start( pI2c );
    i2c_enable_module( pI2c );

    i2c_clear_interrupt( pI2c, UCTXIFG  | UCNACKIFG | UCCLTOIFG | UCALIFG | UCSTPIFG);
    i2c_enable_interrupt( pI2c, UCTXIE0 | UCNACKIE  | UCCLTOIE  | UCALIE  | UCSTPIE );

    return I2C_FAULT_NONE;
}

Most of the actual work is done in the ISR (i2c_list[0] is just a pointer to the structure that was initialized in the dispatch routine (relevant parts of the

structure are only ptr and buff (ptr points to the current data to be send or received in buff array).

#pragma vector=USCI_B0_VECTOR
__interrupt void USCI_B0_ISR( void )
{
    switch( __even_in_range( UCB0IV, 0x1e ) )
    {
    case USCI_I2C_UCALIFG:
        // set AL fault
        i2c_list[0].fault = I2C_FAULT_AL;
        break;

    case USCI_I2C_UCNACKIFG:
        // set NACK fault and send STOP bit
        i2c_list[0].fault = I2C_FAULT_NACK;
        break;

    case USCI_I2C_UCSTTIFG:
    case USCI_I2C_UCRXIFG3:
    case USCI_I2C_UCTXIFG3:
    case USCI_I2C_UCRXIFG2:
    case USCI_I2C_UCTXIFG2:
    case USCI_I2C_UCRXIFG1:
    case USCI_I2C_UCTXIFG1:
        i2c_list[0].fault = I2C_FAULT_UNDEF;
        break;

    case USCI_I2C_UCRXIFG0:
        // Read data from the RXBUF
        i2c_list[0].buff[i2c_list[0].ptr++] = HWREG16( EUSCI_B0_BASE + OFS_UCBxRXBUF );

        // if the last byte received, disable interrupt RX interrupt and enable STOP bit interrupt
        if( i2c_list[0].ptr == i2c_list[0].rx_size )
        {
            i2c_list[0].rx_size = 0;                    // to make sure USCI_I2C_UCSTPIFG goes to exit path, another flag can be used for just this purpose
            i2c_disable_interrupt(  &i2c_list[0],  UCRXIE0  | UCBCNTIE );
            HWREG16( EUSCI_B0_BASE + OFS_UCBxIE ) |= ( UCSTPIE );
        }
        break;

    case USCI_I2C_UCTXIFG0:
        // write bytes to TXBUF
        HWREG16( EUSCI_B0_BASE + OFS_UCBxTXBUF ) = i2c_list[0].buff[i2c_list[0].ptr];
        i2c_list[0].ptr++;

        // if the last byte transmitted, disable TX interrupt,
        if( i2c_list[0].ptr == i2c_list[0].tx_size )
        {
            HWREG16( EUSCI_B0_BASE + OFS_UCBxIE ) &= ~( UCTXIE0 );
        }
        break;

    case USCI_I2C_UCSTPIFG:
        // if reading slave, sent stop and then start bits
        if( i2c_list[0].rx_size )
        {
            // prepare for repeat reception
            i2c_list[0].ptr = 0;
            HWREG16(EUSCI_B0_BASE + OFS_UCBxCTLW0) |= UCSWRST;
            HWREG16( EUSCI_B0_BASE + OFS_UCBxCTLW0 ) &= ~EUSCI_B_I2C_TRANSMIT_MODE;
            HWREG16( EUSCI_B0_BASE + OFS_UCBxTBCNT ) = i2c_list[0].rx_size ;
            HWREG16( EUSCI_B0_BASE + OFS_UCBxCTLW1 ) &= ~UCASTP_3;
            HWREG16( EUSCI_B0_BASE + OFS_UCBxCTLW1 ) |= UCASTP1_L;    // automatic stop bit generation after all bytes are received
            HWREG16( EUSCI_B0_BASE + OFS_UCBxCTLW0 ) |= UCTXSTT;
            HWREG16(EUSCI_B0_BASE + OFS_UCBxCTLW0) &= ~UCSWRST;
            HWREG16( EUSCI_B0_BASE + OFS_UCBxIFG ) &= ~( UCRXIFG0 | UCNACKIFG | UCCLTOIFG | UCBCNTIFG );
            HWREG16( EUSCI_B0_BASE + OFS_UCBxIE ) |= ( UCRXIE0 | UCNACKIE  | UCCLTOIE  | UCBCNTIE );
        } else {
            // write mode - after all the data bytes are sent out, disable interrupts and the eUSCI module
            i2c_list[0].busy = 0;
            HWREG16( EUSCI_B0_BASE + OFS_UCBxIE ) &= ~( UCSTPIE | UCNACKIE  | UCCLTOIE );
            HWREG16(EUSCI_B0_BASE + OFS_UCBxCTLW0) |= UCSWRST;
            __low_power_mode_off_on_exit( );   // end of writing data
        }
        break;

    case USCI_I2C_UCBCNTIFG:
        i2c_list[0].cnt_flag = 1;
        break;

    case USCI_I2C_UCCLTOIFG:
        i2c_list[0].fault = I2C_FAULT_CLTO;
        break;

    case USCI_I2C_UCBIT9IFG:
        break;
    }

    // handle I2C faults
    if( i2c_list[0].fault )
    {
        HWREG16( EUSCI_B0_BASE + OFS_UCBxCTLW0 ) |= UCTXSTP;
        HWREG16( EUSCI_B0_BASE + OFS_UCBxIE ) = 0x0;
        __low_power_mode_off_on_exit( );  // exit with a fault
    }
}

Cheers

 

**Attention** This is a public forum