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.

Detection of connected or disconnected I2C devices on MSP430G2553

Other Parts Discussed in Thread: MSP430G2553

Hi all,

I'd like to detect a new I2C device that is either connected or disconnected to a running code on MSP430G2553 (without program being stuck).

I have a list of just 16 addresses (devices) that can be connected to that I2C so it would be nice to have a scanner that detects what is connected and reads it. In case of disconnection, it would just go further without being stuck.

1. What would be the best way to do it?

2. Which register is the best to check to make sure I2C routine does not get stuck and it countinues in the main program avoiding reading I2C when device is not connected.

I'm using an I2C rutine (written by TI employee) and when you have a I2C device hooked to MSP430G2553 and you disconnected it while main program is running, the whole MSP430 get stuck.

Can I modify this routine to detect connection of disconnection of I2C device? I'm interested in any help, ideas.

Thanks, Daniel

Routine:

/*******************************************************************************
*       USCI_I2C.c Set's up the MSP430 uC USCI in I2C mode.                    *
*                                                                              *
*       Author:             William Goh                                        *
*                                                                              *
*       Revision Date:      May 2011                                           *
*                                                                              *
*       Revision Level:     00.01                                              *
*                                                                              *
*       For Support:        https://e2e.ti.com/support/development_tools/mavrk/default.aspx*
*                                                                              *
********************************************************************************
* Copyright © 2009-2010 Texas Instruments Incorporated - http://www.ti.com/    *
********************************************************************************

/*******************************************************************************
*                                 Included Headers                             *
*******************************************************************************/
#include "USCI_I2C.h"                       // Definition Files for USCI I2C module

/*******************************************************************************
*                                 Prototypes                                   *
*******************************************************************************/
// I2C write init
void I2CWriteInit(void);
// I2C read init
void I2CReadInit(void);

/*******************************************************************************
*                                 External Variables                           *
*******************************************************************************/

/*******************************************************************************
*                                 Global Variables                             *
*******************************************************************************/

// Transmit size pointter
int PtrTransmit;
// I2C transmit buffer array
unsigned char I2CBufferArray[5];
// I2C receive buffer
unsigned char I2CBuffer;

unsigned int I2C_Read (unsigned char Word_Byte_mode, unsigned char Slave_Address, unsigned char Register_Address)
{
    // Variable for transmitted data
    unsigned int MST_Data = 0;

    // Wait until I2C module has finished all operations
    while (UCB0STAT & UCBUSY);
	
	IE1 &= ~WDTIE; 														// DISABLE WDT FOR I2C COMMUNICATION
	P1IE &= ~BIT5;
    // Set slave address
    UCB0I2CSA  = Slave_Address;

    // Set register address
    I2CBufferArray[0] = Register_Address;
    // set I2CBufferArray Pointer
    PtrTransmit = 0;

    // Configure Write Address first
    I2CWriteInit();
    // Start condition generation => I2C communication is started
    UCB0CTL1 |= UCTXSTT;

    // Enable interrupts without entering LPM0.
    __bis_SR_register(GIE);
    // Wait till bus is free.
    while (UCB0STAT & UCBUSY);

    // Configure Read Data byte
    I2CReadInit();

    // I2C restart condition
    UCB0CTL1 |= UCTXSTT;
    // Wait till Start condition sent
    while(UCB0CTL1 & UCTXSTT);

    // If sending a byte, send stop bit immediately
    if (Word_Byte_mode == I2C_BYTE)
    {
        // I2C stop condition
        UCB0CTL1 |= UCTXSTP;
    }

    // Enter LPM0 w/ interrupts
    __bis_SR_register(LPM0_bits + GIE);
    // Receive the first byte
    MST_Data = I2CBuffer;

    // If word mode, receive another byte
    if (Word_Byte_mode == I2C_WORD)
    {
        // I2C stop condition
        UCB0CTL1 |= UCTXSTP;
        // Enter LPM0 w/ interrupts
        __bis_SR_register(LPM0_bits + GIE);
        // Shift Master data
        MST_Data <<= 8;
        // Load data byte
        MST_Data |= I2CBuffer;
    }

    // Ensure stop condition got sent
    while(UCB0CTL1 & UCTXSTP);


	IE1 |= WDTIE; 														
	P1IE |= BIT5;
    // Return word
    return MST_Data;
}

void I2C_Write (unsigned char Word_Byte_mode, unsigned char Slave_Address, unsigned char Register_Address, unsigned int Register_Data)
{
    // wait until I2C module has finished all operations.
    while (UCB0STAT & UCBUSY);
	IE1 &= ~WDTIE; 														// DISABLE WDT FOR I2C COMMUNICATION
	P1IE &= ~BIT5;
    // Set slave address
    UCB0I2CSA  = Slave_Address;

    if (Word_Byte_mode == I2C_BYTE)
    {
        // If byte mode, write only 1 byte

        // Set register address.
        I2CBufferArray[1] = Register_Address;
        // Data to be written
        I2CBufferArray[0] = (unsigned char) (Register_Data & 0x00FF);
        // set I2CBufferArray size
        PtrTransmit = 1;
    }
    else
    {
        // If word mode, write 2 bytes

        // Set register address.
        I2CBufferArray[2] = Register_Address;
        // High nibble data to be written
        I2CBufferArray[1] = Register_Data >> 8;
        // Lower nibble data to be written
        I2CBufferArray[0] = Register_Data & 0xFF;
        // set I2CBufferArray size
        PtrTransmit = 2;
    }

    // start condition generation
    I2CWriteInit();

    // => I2C communication is started
    UCB0CTL1 |= UCTXSTT;
	
    // Enter LPM0 w/ interrupts
    __bis_SR_register(LPM0_bits + GIE);

    // I2C stop condition
    UCB0CTL1 |= UCTXSTP;

    // Ensure stop condition got sents
    while(UCB0CTL1 & UCTXSTP);
    IE1 |= WDTIE; 														// DISABLE WDT FOR I2C COMMUNICATION
	P1IE |= BIT5;
    
}

/*----------------------------------------------------------------------------*/
// Description:
//   Initialization of the I2C Module for Write operation.
/*----------------------------------------------------------------------------*/
void I2CWriteInit(void)
{
    // UCTR=1 => Transmit Mode (R/W bit = 0)
    UCB0CTL1 |= UCTR;
    IFG2 &= ~UCB0TXIFG;
    // disable Receive ready interrupt
    IE2 &= ~UCB0RXIE;
    // enable Transmit ready interrupt
    IE2 |= UCB0TXIE;
}

/*----------------------------------------------------------------------------*/
// Description:
//   Initialization of the I2C Module for Read operation.
/*----------------------------------------------------------------------------*/
void I2CReadInit(void)
{
    // UCTR=0 => Receive Mode (R/W bit = 1)
    UCB0CTL1 &= ~UCTR;
    IFG2 &= ~UCB0RXIFG;
    // disable Transmit ready interrupt
    IE2 &= ~UCB0TXIE;
    // enable Receive ready interrupt
    IE2 |= UCB0RXIE;
}

/*******************************************************************************
*    USI interrupt service routine used for I2C State Macine                   *
*******************************************************************************/
/*
Transmit Register in Device

    Start       7 bit Address         W   ACK         Device Address                Register Address
      _    _______________________    _   _    ___________________________     ___________________________
_   _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ |
SCL  |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_|

Followed by either Transmit of Data to the Register:

           Device High Byte                Device Low Byte
       ___________________________     ___________________________
_   _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _
SCL  |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_|

or Receive Data from the Register:

 Re-Start       7 bit Address         R   ACK      Device High Byte                Device Low Byte
      _    _______________________    _   _    ___________________________     ___________________________
_   _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ |
SCL  |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_|

*/

/*
 *  USCI_B0 I2C Transmit ISR. Enable USCI_B0 I2C transmit interrupt and set USCIAB0TX_ISR as the interrupt handler.
 *  Then set the transmit interrupt to Manual on interrupt return.
 */
unsigned short USCIAB0TX_ISR (void)
{
    // Load TX buffer
    UCB0TXBUF = I2CBufferArray[PtrTransmit];

    // Decrement TX byte counter
    PtrTransmit--;

    if(PtrTransmit < 0)
    {
        // Wait until the last byte transmittted before exiting
        while(!(IFG2 & UCB0TXIFG));

        // disable interrupts.
        IE2 &= ~UCB0TXIE;

        // Clear USCI_B0 TX int flag
        IFG2 &= ~UCB0TXIFG;

        // Exit LPM0
        return (LPM0_bits);
    }
    return 0;
}

/*
 *  USCI_B0 I2C Receive ISR. Enable USCI_B0 I2C receive interrupt and set USCIAB0RX_ISR as the interrupt handler.
 *  Then set the receive interrupt to Active on interrupt return.
 */
void USCIAB0RX_ISR (void)
{
    // store received data in buffer
    I2CBuffer = UCB0RXBUF;
}

...just for a reference....here is the HEADER file USCI_I2C.h

/*******************************************************************************
*                                 Included Headers                             *
*******************************************************************************/

#include <msp430.h>                         // Definition Files for the MSP430

/*******************************************************************************
*                                 Definitions                                  *
*******************************************************************************/
/************************** I2C USIC Engine ***********************************/
#define I2C_SDA BIT7                        // SDA on P1.7
#define I2C_SCL BIT6                        // SCL on P1.6

#define I2C_WRITE 0x00
#define I2C_READ 0x01

#define I2C_BYTE 0x00
#define I2C_WORD 0x01

#define I2C_ACK  0x00
#define I2C_NAK  0x01

/*******************************************************************************
*                                 Prototypes                                   *
*******************************************************************************/
/************ System Configuration *************************/
void Set_I2C(void);

/************ I2C Interface *******************************/
unsigned int I2C_Read (unsigned char, unsigned char, unsigned char);
void I2C_Write (unsigned char, unsigned char, unsigned char, unsigned int);

...end.

  • What does "disconnect" mean precisely?

    If you disconnect only the device, the master will simply see a NACK when sending to that slave's address. If this happens mid-operation, the master will see an abrupt NACK and will terminate the operation based on that.

    However, if disconnecting the slave breaches the bus -- most notably disconnecting or interfering with the pullups -- then the master will probably malfunction.

    If e.g. the clock (SCL) line is permanently pulled low, this is indistinguishable from (slave) clock-stretching, and the master just has to wait for it to stop. I2C (in contrast to its first cousin SMBus) defines no timeouts, so this wait will be indefinitely long. The master could continue to do other things by using interrupts  rather than spin loops -- I see two such spin loops in this code -- or applying its own timeouts.

    Device presence (assuming the bus is intact) is indicated by an ACK on the first (address) byte. A bus probe could be accomplished (I've done this before) by issuing Read operations to each of the (128-reserved) available addresses, and seeing which addresses get ACKs back. I suppose there might be devices which NACK otherwise-correct  Read requests based on internal state, but I don't recall having seen any.

  • Hi Bruce,

    Many thanks for your quick reply and comments.

    By disconneting I mean physically unplugging the I2C slave device (daughter card) from master (main board with MSP430 that includes pull-ups).

    So at power-up of main board, I'd like to check what is connected and protect program from being stuck when you do a hot unplug of the card.

    The best way (from what I have read in your tips) would be to send a slave address and then trying to get an interrupt if the ACK is not coming from slave. I read in datasheet that UCNACKIFG is set in this case (and UCNACKIE needs to be allowed).

    Where exactly would you put it in the code that I posted. I guess it should not be that difficult. I think it should be better than watching STOP condition (UCTXSTP).

    What do you think?

    With kind regards,
    Daniel

     

  • Bruce:
    it's nice to define a timeout for I2C, but since there is no way to pull SCL high if someone pulls it low, it stays low, timeout or not. Sure, an SMBus device, if working properly, must not pull SCL low beyond the timeout limit. But the key here is 'if working properly'. If it doesn't, you're hosed.

    Daniel:
    The way the MSP driver library does it is to send a write operation without data to the slave and check for NACKIFG.
    Keep in mind that NACKIFG calls the other ISR on 2x family. Also, the ACK bit is only received if you either write something to TXBUF (which you won't in this case) or set UCTXSTP.
    However, you don't really need the ISR at ll. You'll have to wait for TXSTT to clear anyway (as you won't get an ACK interrupt if the slave is present) and you can check for NACKIFG directly in main, after TXSTT cleared. (you don't need to set UCNACKIE to get UCNACKIFG set - the two are independent. UCNACKIE only enables calling the ISR if UCNACKIFG is set.)

**Attention** This is a public forum