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.

MSP430FR5522 I2C slave problem

Other Parts Discussed in Thread: MSP430F5438A

Howdy All,

I've been writing some code using a semi-unique feature of the FRAM MSPs I2C bus in slave mode, the ability to interrupt on any (and all) 7bit device IDs.  I cannot go into details as to what the product is or does (for NDA reasons).  I can say that I'm trying to mimic the behavior of some now obsolete EEPROMs that were not 100% I2C compliant (the bus timing is I2C compliant, but they did not have a device ID)

My code works perfectly when used using I2C tools like an aardvark or when controlled by some of my own code written on another MSP.  However, when it is attached to the product it was designed to work with, it fails.  The problem is that the device it is being attached to is somewhat ill behaved and doesn't completely conform to the I2C standard itself, and I cannot change the behavior of this product. 

When the product is first turned on, it searches for this now obsolete EEPROM by polling the bus and looking for something to ACK it, which my code does.  The product then sends what appears to be garbage, followed by 9 perfectly valid start conditions followed by what should be  STOP.  Even though these are actually valid START conditions (and at a relatively slow rate too), I believe that the MSP only sees the first 1, and thinks the others are actually the device ID being clocked in, and the last pulse it is actually sending an ACK (the MSP holds the line low I can only assume waiting for the master to send the next clock)

I've somewhat verified this by sticking some debug code in to count the number of starts and stops received.  In this particular instance it claims to have received only 1 start, even though several were sent sequentially.  I don't see anything mentioned in the errata about anything like this, but all I can think of is that this MSP doesn't support repeated start under these instances.  The data sheet is a bit vague about the min timing as it does say anything about master vs slave mode.  If the min timing is truly 4us, then I'm violating that as the start timing I have is around 3.6us, but why then does the aardvark work with sub 1us timing difference between the falling edges of SDA & SCL? I have a few screen shots of my logic analyzer I can upload, but it appears that you can only upload 1 at a time?  so hopefully I can upload the other in a follow up shortly.

The picture attached here shows the garbage followed by the 9 start pulses, and the MSP holding the SDA line low.

  • This screen shot shows the timing of the starts.  note that all of the starts are basically the same (within 0.1us differences)

  • Hi Nelson,

    Let me make sure I am on the same page - the MSP430 is the slave device, pretending to be a EEPROM - you don't control the master side at all and the MSP needs to just work with whatever it was already sending to the EEPROM - correct?

    You mentioned that the host doesn't completely conform to the I2C standard - can you elaborate a little bit more? Is it the timing that does not conform?

    Nelson Chadderdon said:
    The data sheet is a bit vague about the min timing as it does say anything about master vs slave mode.  If the min timing is truly 4us, then I'm violating that as the start timing I have is around 3.6us, but why then does the aardvark work with sub 1us timing difference between the falling edges of SDA & SCL?

    Does your aardvark (which you're using to simulate the host right?) does it also send this garbage data followed by 9 starts when you do your test?

    Nelson Chadderdon said:
    The product then sends what appears to be garbage, followed by 9 perfectly valid start conditions followed by what should be  STOP.  Even though these are actually valid START conditions (and at a relatively slow rate too), I believe that the MSP only sees the first 1, and thinks the others are actually the device ID being clocked in, and the last pulse it is actually sending an ACK (the MSP holds the line low I can only assume waiting for the master to send the next clock)

    ...

    I've somewhat verified this by sticking some debug code in to count the number of starts and stops received.  In this particular instance it claims to have received only 1 start, even though several were sent sequentially.

    How are you counting the MSP seeing the starts? According to the user's guide www.ti.com/lit/pdf/slau367 table 23-2 I2C State Change Interrupt Flags, you'll see that it says UCSTTIFG is only set when the module detects a START together with its own address. So if you are simply counting how many times the ISR is entered for UCSTTIFG, and your address setup is such that the following repeated starts could be interpreted as one of the addresses you have set the slave to respond to, then I could see this. However I would have maybe more expected UCSTTIFG to never be set if you aren't ever sending the correct address (though maybe you have this address masked in such a way it will respond to everything?).

    What are you needing the MSP to do with these 9 start conditions with no address ever being sent? It seems like a strange setup if the address is never sent at all but data or action is expected back from the slave at that point?

    Regards,

    Katie

  • Hi Nelson,

    Hardest driver I ever built. However, it was because I was making it too difficult. I found slaa368a and slaa382 very helpful along with an oscillascope that had an I2C bus (very helpful!!!).7536.slaa382.zip7384.slaa368a.zip

    I have the i2c.c and i2c.h driver files if you would like me to upload them to you.

    Hardy

     

  • Hey again Nelson,

    I forgot to attach the pdf's as well. 8741.slaa368a.pdf5153.slaa382.pdf

    Forgot to mention too that I am using Code Composer Studio Version: 5.5.0.00077 and MSP430F5438a. Should be similar but I am not sure ok...

  • slau208m, MSP430x5xx and MSP430x6xx Family User's Guide states that the i2c implementation is in compliance to the Philips Semiconductor I2C specification v2.1. MSP430x5xx also uses a "state machine" so that is why I say it is similar. I just had one more attachment that may be useful, the i2c specification.

    Philips Semiconductors which is (now NXP Semiconductors) developed a simple bidirectional 2-wire bus for efficient inter-IC control.

    Here it is...7411.I2C_protocol.pdf

     

    Philips Semiconductors (now NXP Semiconductors) developed a simple

    bidirectional 2-wire bus for efficient inter-IC control

  • Hi Katie

    Katie Pier said:
    Let me make sure I am on the same page - the MSP430 is the slave device, pretending to be a EEPROM - you don't control the master side at all and the MSP needs to jsut work with whatever it was already sending to the EEPROM - correct?

    I think the answer is yes.... I'm trying to use the MSP to simulate a non standard and now obsolete EEPROM in a product that I cannot change.

    Katie Pier said:
    You mentioned that they host doesn't completely conform to the I2C standard - can you elaborate a little bit more;? Is it the timing that does not conform?

    That's a hard one.  It is partly because I didn't design the product (though I have seen the source, we just cannot change it... if I could change it, I'd just change it to use a non-obsolete EEPROM), and partly because of experience and looking at what it does on the bus.

    First, the EEPROM itself I am simulating isn't fully I2C compliant... it uses the slave ID as the index into its memory (ie, depending on your view point, it either has no slave ID, or has 128 of them), thus why I liked the feature of the FRAM MSPs that basically allow _any_ slave address (by setting the mask to zero).  As far as I know, no other MSP allows this.

    THe other thing is that I believe the I2C spec states that to clear the bus, issue up to 9 clock pulses looking for the data line to float high, when it does float high, issue a stop, and now the bus should be synchronized and allow a start to be issued.  The master in this product doesn't do that, it issues 9 start pulses followed by a stop.  technically, this should matter, but in this case it seems to.  The MSP seems to ack the 9th pulse (by holding the data line low), and thus the bus is dead until another pulse comes along to clear it.

    Katie Pier said:
    Does your aardvark (which you're using to simulate the host right?) does it also send this garbage data followed by 9 starts when you do your test?

    no, and while I didn't contact total phase, I don't think it is capable of sending garbage... though that would be a nice feature to do robustness testing with.

    Katie Pier said:
    How are you counting the MSP seeing the starts? According to the user's guide www.ti.com/lit/pdf/slau367 table 23-2 I2C State Change Interrupt Flags, you'll see that it says UCSTTIFG is only set when the module detects a START together with its own address. So if you are simply counting how many times the ISR is entered for UCSTTIFG, and your address setup is such that the following repeated starts could be interpreted as one of the addresses you have set the slave to respond to, then I could see this. However I would have maybe more expected UCSTTIFG to never be set if you aren't ever sending the correct address

    true... so I guess that 'test' was flawed

    Katie Pier said:
    (though maybe you have this address masked in such a way it will respond to everything?).

    bingo!  As I mentioned, unless I'm missing something, this features seems to be quite unique to the FRAM based MSPs (and every other small micro I know of).

    Katie Pier said:
    What are you needing the MSP to do with these 9 start conditions with no address ever being sent? It seems like a strange setup if the address is never sent at all but data or action is expected back from the slave at that point?

    strange... yes, thus my post.  I was hoping that the MSP would reset its internal state machine every time it saw the start, which is how most slave devices work that I've ever encountered... I've never tried to purposely torture a device like this product is, but I've also never had an I2C slave device hang a bus like this either.

    Having said all this and listening to some of the ways you phrased things, it sounds like the MSP cannot handle these multiple starts... if that's a mis-interpretation, please let me know, as right now I'm in the middle of writing (on an MSP) a slave I2C bus using timer-captures... I actually just finished writing it, but I'm off on a business trip and didn't being the HW with me to test it out.  If I go this route, I'm lucky in that the product this is being used with has a slow (sub 20k) clock driving the I2C bus.

  • Thanks for the responses William, but the FRAM based MSPs have a _VERY_ different I2C bus than all the other MSPs... and that actually exactly why I chose it.  It has the unique feature that in slave mode it can answer to any slave ID all the time.  If I've been misinterpreting the data sheets for the other MSPs, I'd love to hear about it and see your code, though I have done dozens of I2C products in slave and master mode using MSPs and never had such a problem as this... but I've never had the need to answer to all 128 possible slave addresses at the same time either.

  • When checking the timing, I stumbled over the same thing as you, the 3.6µs vs. 4µs. But then, the USCI works with 1µs timing on >100kHz I2C clock. And before the  start condition, it cannot know the frequency of the I2C clock at all. So these values are highly suspicious to me. Without further explanation, I don’t know how to interpret them (and this will tell a lot).

    If you know the sequence, you can try a trick: detect this awkward - err. aardwark sequence by using port pin interrupt or port pin polling. Then you switch the pin to I2C mode when the preceding and disturbing start conditions have passed. A dirty hack, but maybe it does the trick.

    BTW: the USI module also allows acting on any address, as it simple doesn't process the start byte at all and leaves it to the software :)

  • Hi Nelson,

    Thanks for the further explanation. Also meant to ask which part you are using - I saw MSP430FR5522 but I don't recognize that as a part number - maybe a typo?

    I have a feeling there will be some way to work around this in the way you write your MSP software.

    Here is a guess of what is happening - the MSP is interpreting the starts as an address I think and ACK'ing it because you have it set to accept all addresses. This happens on the 9th pulse because: 1 bit for Start, 7 bits for address, 1 bit for R/W = 9 bits. A 1 for R/W means slave transmitter - the MSP thinks it is supposed to send data back to the master in this case. As you can see in Figure 23-9 which shows the flow in Slave Transmitter mode, the MSP will stall the Bus by holding SCL low until you write data to UCBxTXBUF. I think this explains the behavior you are seeing.

    Do you know what is the master expecting to happen after the 9 start conditions, if the MSP did not stall the bus by holding SCL low?

    I think your software might be able to workaround the issue if we get creative - unfortunately this is a little bit hard for me to test here since the behavior is so specific to your setup, but I'll try to recommend some avenues you could try to explore:

    • An interesting feature you might be able to use in your code is to set UCETXINT - the early transmit interrupt feature. This will cause the UCTXIFG0 bit to be set after each start condition, instead of only after the matching address is received - this would let you use the UCTXIFG0 ISR to count the starts being sent.
    • Maybe you could try just resetting the whole module after you get this 9th start condition, using UCSWRST to quickly disable/re-enable it? I would guess this might release the lines?
    • I'm not sure about all of your I2C settings, but you may want to look into the options for ACK/NAK to try to clear the clock stretching. In section 23.3.9.2 of www.ti.com/lit/pdf/slau367 it discusses the ability to set UCSWACK = 1 and then manually in software you can ACK/NAK the address (using UCTXNACK/UCTXACK in UCBxCTLW0 register) from the ISR for UCSTTIFG. You might also need to try to clear UCTXIFG. You could see if this will end the clock stretching (and how the master might react if you try to send a NAK). I think normally (with UCSWACK = 0 for automatic mode) the ACK/NAK won't be sent until you load TXIFG - but you'd have to test this.
    • As a note, if for some reason you need to determine from your MSP-side software if SCL is getting held low, you can check the UCSCLLOW bit in USBxSTATW register.
    • If all of these fail, Jens-Michael's suggestion using GPIO might be an option. But hopefully using the early transmit interrupt setting UCEXTINT might be an option you could use instead.

    Hopefully one of these things might spark an idea for you.

    Regards,

    Katie

  • Your welcome Nelson. It appears that I am much less experienced as you since the driver I wrote is geared towards a master mode implementation and is not required to respond to multiple I2C devices as is yours. However I would be glad to have you look at the code and I have included the devices layer that use the i2c driver as well. Hope you figure out your implementation. Katie and Jens-Michael have both been a big help to me as well.

    8831.i2c.c
    /***************************************************************************************************
     *	Project: Modular Instrumentation
     *	i2c.c
     *	Description: Drivers for the MSP430F5438A I2C module.
     *
     *	Author: Scott Wheeler
     *	Date  : September 2011
     *	Author: Hardy Williams
     *	Date  : April 2014
     *
     *	Revisions:
     *
     *
     ***************************************************************************************************/
    #include "i2c.h"
    
    #include "ProcLib\MISL_Sys.h"
    
    // Local Prototypes
    unsigned char usciI2CTimeout(void);
    void usciI2CInit(unsigned char slaveAddress, int i2cDivisor);
    void usciI2CSelectSlaveAddress(unsigned char address);
    unsigned char usciI2CNotready();
    void usciI2CReceive(char byteCount, unsigned char *buffer);
    void usciI2CTransmit(char byteCount, unsigned char *buffer);
    unsigned char usciI2CSlaveNotPresent(unsigned char slaveAddress);
    
    //I2C globals
    volatile long gI2CTimeout;
    volatile unsigned char gI2CTimerExpired = 0;
    volatile unsigned char gI2CTimeoutStarted = 0;
    
    volatile signed char RXByteCtr;
    volatile unsigned char RxBuffer[16] = {0};	// receive buffer
    volatile unsigned char *ptrReceiveBuffer;
    
    volatile signed char TXByteCtr;
    volatile unsigned char TxBuffer[16] = {0};	// transmit buffer
    volatile unsigned char *ptrTransmitBuffer;
    
    /*******************************************************************************
     * Function Name	: usciI2CTimeout
     * Description		: Check thermometer(s) are available
     * Input			: None
     * Return			: 0 - timer expiredl
     *                  : 1 - timer running
     *******************************************************************************/
    unsigned char usciI2CTimeout(void)
    {
    
    	if (gI2CTimeoutStarted)
    	{
    		gI2CTimeout--;
    		if (gI2CTimeout == 0)
    		{
    			gI2CTimeoutStarted = 0;
    			gI2CTimerExpired = 1;
    			return 0;	// timer expired
    		}
    		else
    		{
    			return 1;	// timer running
    		}
    	}
    	else
    	{
    		gI2CTimeoutStarted = 1;
    		gI2CTimerExpired = 0;
    #if MSP430_CLK_1MHz
    		// 1ms delay @ ~1MHz
    		gI2CTimeout = 1000;
    #endif
    #if MSP430_CLK_4MHz
    		// 1ms delay @ ~4MHz
    		gI2CTimeout = 4000;
    #endif
    #if MSP430_CLK_8MHz
    		// 1ms delay @ ~8MHz
    		gI2CTimeout = 8000;
    #endif
    #if MSP430_CLK_12MHz
    		// 1ms delay @ ~12MHz
    		gI2CTimeout = 12000;
    #endif
    #if MSP430_CLK_20MHz
    		// 1ms delay @ ~20MHz
    		gI2CTimeout = 20000;
    #endif
    #if MSP430_CLK_25MHz
    		// 1ms delay @ ~25MHz
    		gI2CTimeout = 25000;
    #endif
    		return 1;	// timer running
    	}
    }
    
    //------------------------------------------------------------------------------
    // void usciI2CReset()
    //
    // This function initializes the USCI module for master operation.
    //
    // IN:   unsigned char slaveAddress   =>  Slave Address
    //       unsigned char i2cDivisor     =>  SCL clock adjustment, clock speed to
    //                                        standard 100kHz based on system clock
    //------------------------------------------------------------------------------
    void usciI2CReset()
    {
    	P3OUT |= BIT7;							// Pull high output
    	P5OUT |= BIT4;							// Pull high output
    	Delay_ms(35);							// delay 35ms
    	P3OUT &= ~BIT7;							// Pull low output
    	P5OUT &= ~BIT4;							// Pull low output
    	Delay_ms(35);							// delay 35ms
    //	usciI2CInit(TMP275_I2C_TMP_0, getDivisorUnrounded(SYSTEM_CLOCK, I2C_BAUD_RATE));
    }
    
    //------------------------------------------------------------------------------
    // void usciI2CInit(unsigned char slaveAddress, int i2cDivisor)
    //
    // This function initializes the USCI module for master operation.
    //
    // IN:   unsigned char slaveAddress   =>  Slave Address
    //       unsigned char i2cDivisor     =>  SCL clock adjustment, clock speed to
    //                                        standard 100kHz based on system clock
    //------------------------------------------------------------------------------
    void usciI2CInit(unsigned char slaveAddress, int i2cDivisor)
    {
    	//  I2C_SDA Serial Data Line
    	P3SEL |= BIT7;							// Enable USCI_B1 secondary I2C SDA function
    	P3REN |= BIT7;							// Enable internal pull-up resistor for the USCI_B1 I2C SDA pin
    	P3OUT |= BIT7;							// Initialize as a high output
    
    	//  I2C_SCL Serial Clock Line
    	P5SEL |= BIT4;							// Enable USCI_B1 secondary I2C SCL function
    	P5REN |= BIT4;							// Enable internal pull-up resistor for the USCI_B1 I2C SCL pin
    	P5OUT |= BIT4;							// Initialize as a high output
    
    	// When UCA10 = 0, 7-bit addressing	is selected.
    	// The UCGCEN bit selects if the USCI module responds to a general call.
    	UCB1CTL1 |= UCSWRST;					// Enable SW reset
    	UCB1CTL0 = UCMST + UCMODE_3 + UCSYNC;	// 7-bit addresses (default), single master mode,
    	                                        // I2C, synch I2C Master, synchronous mode
    	UCB1CTL1 |= UCSSEL_2 + UCSWRST;			// Use SMCLK
    	UCB1BR0 = i2cDivisor;					// SMCLK/i2cDivisor = ~100kHz
    	UCB1BR1 = 0;
    	UCB1I2COA = 0;							// Ignore general call; own addr = 0
    	UCB1I2CSA = slaveAddress;				// Slave Address
    	UCB1CTL1 &= ~UCSWRST;					// Clear SW reset, resume operation
    
    	UCB1IE |= (UCNACKIE + UCRXIE + UCTXIE);			// Enable NACK, TX and RX interrupts
    }
    
    //------------------------------------------------------------------------------
    // unsigned char usciI2CSelectSlaveAddress()
    //
    // This function is used to address an external slave device to be used by the
    // USCI_Bx module.
    //
    // IN:   unsigned char address  =>  address of external slave device
    // OUT:  none
    //------------------------------------------------------------------------------
    void usciI2CSelectSlaveAddress(unsigned char address)
    {
    	UCB1I2CSA = address;
    }
    
    //------------------------------------------------------------------------------
    // unsigned char usciI2CSlaveNotPresent(unsigned char slaveAddress)
    //
    // This function is used to look for a slave address on the I2C bus. This
    // function detects the presence of a slave by addressing it and determining if
    // the slave responds with an ACK/NACK.
    //
    // This function may also be used to address an I2C device and poll while waiting
    // for it to become ready to acknowledge. Useful when first addressing an external
    // I2C device for first time.
    //
    // IN:   unsigned char address  =>  address of external slave device
    // OUT:  unsigned char          =>  0: if the slave is present and has acknowledged
    //                                  1: if the slave is absent or not ready to acknowledge
    //                                  2: if timeout occurred
    //------------------------------------------------------------------------------
    unsigned char usciI2CSlaveNotPresent(unsigned char address)
    {
    
    	unsigned char nackStatus;
    
    	usciI2CSelectSlaveAddress(address);		// select slaveAddress
    
    	__disable_interrupt();					// disable interrupts for polling
    	UCB1CTL1 |= (UCTR + UCTXSTT + UCTXSTP);		// Set I2C TX, Set START/STOP condition
    	gI2CTimeoutStarted = 0;
    	while( (UCB1CTL1 & UCTXSTP) && usciI2CTimeout() ); // Poll for STOP condition
    	nackStatus = (UCB1IFG & UCNACKIFG);		// retrieve NACK status
    	UCB1IFG &= ~UCTXIFG;					// Clear USCI_B1 Transmit Interrupt Flag set by state machine
    	__enable_interrupt();					// enable interrupts
    
    	if (gI2CTimerExpired)
    	{
    		nackStatus = 2;
    	}
    
    	return nackStatus;						// return NACK status
    }
    
    //------------------------------------------------------------------------------
    // unsigned char usciI2CNotready()
    //
    // This function is used to check if there is communication in progress. The bus
    // busy bit, UCBBUSY, is set after a START and cleared after a STOP. This allows
    // a determination of completion of a transaction.
    //
    // NOTE: The USCI module checks if the bus is available, generates the START
    //       condition, and transmits the slave address so it is not necessary to
    //       call this service before starting a transaction...
    //
    // OUT:  unsigned char  =>  0: I2C bus is idle,
    //                          1: communication is in progress
    //------------------------------------------------------------------------------
    unsigned char usciI2CNotready()
    {
      return (UCB1STAT & UCBBUSY);
    }
    
    //------------------------------------------------------------------------------
    // void usciI2CReceive(char byteCount, unsigned char *buffer)
    //
    // This function is used to start an I2C communication in master-receiver mode.
    //
    // After initialization, master receiver mode is initiated by writing the desired slave address to the
    // UCBxI2CSA register, selecting the size of the slave address with the UCSLA10 bit (defaults to 7bit),
    // clearing UCTR for receiver mode, and setting UCTXSTT to generate a START condition.
    //
    // The USCI module checks if the bus is available, generates the START condition, and transmits the slave
    // address. As soon as the slave acknowledges the address, the UCTXSTT bit is cleared.
    //
    // After the acknowledge of the address from the slave, the first data byte from the slave is received and
    // acknowledged and the UCRXIFG flag is set. (see USCI_B1_ISR())
    //
    // IN:   unsigned char byteCount  =>  number of bytes that should be read
    //       unsigned char *buffer    =>  array variable used to store received data
    //------------------------------------------------------------------------------
    void usciI2CReceive(char byteCount, unsigned char *buffer)
    {
    	int i;
    
    	ptrReceiveBuffer = (unsigned char *)&RxBuffer[0];	// reset Start of RX buffer
    	UCB1CTL1 &= ~UCTR;								// Clear UCTR to set Master into receiver mode
    
    	// If a master wants to receive a single byte only, the UCTXSTP bit must be set while
    	// the byte is being received. For this case, the UCTXSTT must be polled to determine
    	// when it is cleared: The USCI module checks if the bus is available, generates the
    	// START condition, and transmits the slave address. UCTXSTT is automatically cleared
    	// after START condition and address information is	transmitted. So really all we have
    	// to do is check that the UCTXSTT bit is cleared, then send STOP condition (set UCTXSTP).
    	if ( byteCount == 1 ){
    		RXByteCtr = 0 ;					// use global variable count for ISR use
    		__disable_interrupt();			// disable interrupts since we need to poll
    		UCB1CTL1 |= UCTXSTT;			// Send I2C start condition
    		while(UCB1CTL1 & UCTXSTT);		// Poll for UCTXSTT cleared
    		UCB1CTL1 |= UCTXSTP;			// Send I2C stop condition
    		__enable_interrupt();			// enable interrupts
    	}
    	else
    	{
    		RXByteCtr = byteCount-1;		// use global variable count for ISR use
    		UCB1CTL1 |= UCTXSTT;			// Send I2C start condition
    	}
    
    	// wait for transaction to complete (Poll for STOP condition)
    	gI2CTimeoutStarted = 0;
    	while( usciI2CNotready() && usciI2CTimeout() );
    
    	// move RX buffer data to buffer
    	for(i = 0; i < byteCount; i++)
    	{
    	    buffer[i] = RxBuffer[i];
    	}
    }
    
    //------------------------------------------------------------------------------
    // void usciI2CTransmit(char byteCount, unsigned char *buffer)
    //
    // This function is used to start an I2C communication in master-transmit mode.
    //
    // After initialization, master transmitter mode is initiated by writing the
    // desired slave address to the UCBxI2CSA register, selecting the size of the
    // slave address with the UCSLA10 bit (7bit by default), setting UCTR for
    // transmitter mode, and setting UCTXSTT to generate a START condition.
    //
    // The USCI module checks if the bus is available, generates the START condition,
    // and transmits the slave address. The UCTXIFG bit is set when the START
    // condition is generated and the first data to be transmitted can be written
    // into UCBxTXBUF. As soon as the slave acknowledges the address, the UCTXSTT bit
    // is cleared. (see USCI_B1_ISR())
    //
    // MOTE: Checking the UCTXSTT bit to verify if the START condition has been
    //       sent ensures that the TXIFG is being serviced correctly.
    
    // IN:   unsigned char byteCount  =>  number of bytes that should be transmitted
    //       unsigned char *buffer     =>  array variable. Its content will be sent.
    //------------------------------------------------------------------------------
    void usciI2CTransmit(char byteCount, unsigned char *buffer)
    {
    	int i;
    
    	ptrTransmitBuffer = (unsigned char *)&TxBuffer[0];	// reset Start of RX buffer
    
    	for (i = 0; i<byteCount; i++)
        {
    		TxBuffer[i] = buffer[i];					// move buffer data to TX buffer
        }
    	TXByteCtr = byteCount;							// use global variable count for ISR use
    	UCB1CTL1 |= (UCTR + UCTXSTT);						// I2C TX, Send START condition
    }
    
    // ******************************************************************************************************************************
    // Function: USCI_B1_ISR()
    // Parameters: None
    // Purpose: I2C interrupt routine.
    //
    // UCTXIFG: The UCBxTXBUF is discarded and the UCTXIFG bit is set when the START condition is generated
    //          (see usciI2CTransmit()) and the first data to be transmitted can be written into UCBxTXBUF.
    //          Data written into UCBxTXBUF is transmitted and UCTXIFG is set again as soon as the data is
    //          transferred from UCBxTXBUF into the shift register.
    //
    //          When transmitting a single byte of data, the UCTXSTP bit must be set while the byte is
    //          being transmitted or anytime after transmission begins, without writing new data into
    //          UCBxTXBUF. Otherwise, only the address is transmitted. When the data is transferred from
    //          the buffer to the shift register, UCTXIFG is set, indicating data transmission has begun,
    //          and the UCTXSTP bit may be set.
    //
    // UCNACKIFG: If the slave does not acknowledge the transmitted data, the not-acknowledge interrupt flag
    //            UCNACKIFG is set. The master must react with a STOP condition. If data was already written
    //            into UCBxTXBUF, it is discarded.
    //
    // UCRXIFG: the UCRXIFG flag is set when the START condition is generated (see usciI2CReceive()) and the
    //          first data byte from the slave is received and acknowledged. Data is received from the slave,
    //          as long as UCTXSTP or UCTXSTT is not set. If UCBxRXBUF is not read, the master holds the bus
    //          during reception of the last data bit and until the UCBxRXBUF is read.
    //
    //          Setting the UCTXSTP bit generates a STOP condition. After setting UCTXSTP, a NACK followed by
    //          a STOP condition is generated after reception of the data from the slave, or immediately if
    //          the USCI module is currently waiting for UCBxRXBUF to be read.
    //
    //
    // Return: None
    //
    // ******************************************************************************************************************************
    #pragma vector = USCI_B1_VECTOR
    __interrupt void USCI_B1_ISR(void)
    {
    	switch(__even_in_range(UCB1IV,12))
    	{
    	case  0: break;								// Vector  0: No interrupts
    	case  2: break;								// Vector  2: ALIFG
    	case  4:									// ==>>Vector  4: NACKIFG<<==
    		UCB1CTL1 |= UCTXSTP;					// Send I2C stop condition
    		UCB1IFG &= ~UCNACKIFG;					// Clear USCI_B1 Transmit Interrupt Flag
    		break;
    	case  6: break;								// Vector  6: STTIFG
    	case  8: break;								// Vector  8: STPIFG
    	case 10:									// ==>>Vector 10: RXIFG<<==
    		if (RXByteCtr > 0)
    		{
    			if (RXByteCtr == 1)	UCB1CTL1 |= UCTXSTP; // Only one byte left? Generate I2C stop condition
    			*ptrReceiveBuffer = UCB1RXBUF;		// Move RX I2C data to receive buffer
    			ptrReceiveBuffer++;					// Increment receive buffer pointer
    		}
    		else
    		{
    			*ptrReceiveBuffer = UCB1RXBUF;		// Move RX I2C data to receive buffer
    		}
    		RXByteCtr--;							// Decrement RX byte counter
    		break;
    	case 12:// ==>>Vector 12: TXIFG<<==
    		if (TXByteCtr == 0)
    		{
    			UCB1CTL1 |= UCTXSTP;				// Send I2C stop condition
    			UCB1IFG &= ~UCTXIFG;				// Clear USCI_B1 Transmit Interrupt Flag
    		}
    		else
    		{
    			UCB1TXBUF = *ptrTransmitBuffer;		// Load TX register from next byte in transmit buffer
    			ptrTransmitBuffer++;				// Increment receive buffer pointer
    			TXByteCtr--;						// Decrement TX byte counter
    		}
    	default:									// No: Should never happen!
    		I2CStatus = I2CIdle;					// Reset I2C USCI Module
    		break;
    	}
    }
    

    7510.i2c.h

    8053.I2C_Thermometer_TMP275.c
    // *****************************************************************************************************************************
    // I2C_Thermometer_TMP275.c
    // *****************************************************************************************************************************
    // TMP275_I2C_ADDRESS range from 0x48-0x49
    #include "ProcLib\MISL_Sys.h"
    
    #include "I2C_Thermometer_TMP275.h"
    
    #include "i2c.h"
    
    #ifdef __DEF_I2C_DBG__
    #include "IOLib\ti_printf.h"
    #endif
    
    volatile long gTMP275Timeout;
    volatile unsigned char gTMP275TimeoutStarted = 0;
    
    TMP275_DEV_T temperatureDevices[TEMPERATURE_SENSOR_COUNT];
    
    // Local Prototypes
    unsigned char I2C_Thermometer_TMP275_Timeout(void);
    unsigned char I2C_Thermometer_TMP275_InitDevices(void);
    unsigned char I2C_Thermometer_TMP275_CheckDevice(unsigned char addr);
    void I2C_Thermometer_TMP275_ConfigDevice(unsigned char addr);
    unsigned char I2C_Thermometer_TMP275_ReadConfig(unsigned char addr);
    unsigned int I2C_Thermometer_TMP275_ReadTemp(unsigned char addr);
    void I2C_Thermometer_TMP275_WriteCfg(unsigned char addr, unsigned char ptrReg, unsigned char value);
    void I2C_Thermometer_TMP275_SetReadPtr(unsigned char addr, unsigned char value);
    
    
    /*******************************************************************************
     * Function Name	: I2C_Thermometer_TMP275_Timeout
     * Description		: Check thermometer(s) are available
     * Input			: None
     * Return			: 0 - timer expiredl
     *                  : 1 - timer running
     *******************************************************************************/
    unsigned char I2C_Thermometer_TMP275_Timeout(void)
    {
    
    	if (gTMP275TimeoutStarted)
    	{
    		gTMP275Timeout--;
    		if (gTMP275Timeout == 0)
    		{
    			gTMP275TimeoutStarted = 0;
    			return 0;	// timer expired
    		}
    		else
    		{
    			return 1;	// timer running
    		}
    	}
    	else
    	{
    		gTMP275TimeoutStarted = 1;
    #if MSP430_CLK_1MHz
    		// 1ms delay @ ~1MHz
    		gTMP275Timeout = 1000;
    #endif
    #if MSP430_CLK_4MHz
    		// 1ms delay @ ~4MHz
    		gTMP275Timeout = 4000;
    #endif
    #if MSP430_CLK_8MHz
    		// 1ms delay @ ~8MHz
    		gTMP275Timeout = 8000;
    #endif
    #if MSP430_CLK_12MHz
    		// 1ms delay @ ~12MHz
    		gTMP275Timeout = 12000;
    #endif
    #if MSP430_CLK_20MHz
    		// 1ms delay @ ~20MHz
    		gTMP275Timeout = 20000;
    #endif
    #if MSP430_CLK_25MHz
    		// 1ms delay @ ~25MHz
    		gTMP275Timeout = 25000;
    #endif
    		return 1;	// timer running
    	}
    }
    
    /*******************************************************************************
     * Function Name	: I2C_Thermometer_TMP275_InitDevices
     * Description		: Check thermometer(s) are available
     * Input			: None
     * Return			: 0 - initialization successful
     *                  : 1 - initialization error(s)
     *                  : 2 - i2c module needs reinitialized
     *******************************************************************************/
    unsigned char I2C_Thermometer_TMP275_InitDevices(void)
    {
    	unsigned char addr;
    	unsigned char index;
    	unsigned char retStatus;
    	unsigned char cfgCount;
    	volatile unsigned char devConfig;
    	unsigned char initStatus = 0;	// start out optimistically
    
    	// initialize device configured count
    	// if a device is successfully configured, increment count
    	cfgCount = 0;
    
    	// for number of temperature devices
    	for(addr = TMP275_I2C_TMP_0, index=0; index<TEMPERATURE_SENSOR_COUNT; addr++, index++)
    	{
    		// initialize device address
    		temperatureDevices[index].address = addr;
    
    		// initialize device status indicators pessimistically
    		temperatureDevices[index].available = false;
    		temperatureDevices[index].configured = false;
    		temperatureDevices[index].error = true;
    
    		// check if device address is available
    		retStatus = I2C_Thermometer_TMP275_CheckDevice(addr);
    		if (retStatus == 0)
    		{
    			// if device available, mark it available
    			temperatureDevices[index].available = true;
    
    			// Configure to read from configuration register
    			I2C_Thermometer_TMP275_SetReadPtr(addr, CONFIGURATION_REGISTER);
    
    			// configure temperature device
    			I2C_Thermometer_TMP275_ConfigDevice(addr);
    
    			// check that configuration was successful
    			devConfig = I2C_Thermometer_TMP275_ReadConfig(addr);
    			if (devConfig == CONVERTER_RESOLUTION_12)
    			{
    				// if device configured, mark it configured
    				temperatureDevices[index].configured = true;
    				// temperature device is available and configured, mark it error free
    				temperatureDevices[index].error = false;
    				// increment device configured count
    				cfgCount++;
    				// Configure to read from temperature register
    				I2C_Thermometer_TMP275_SetReadPtr(addr, TEMPERATURE_REGISTER);
    			}
    		}
    		else
    		{
    			switch(retStatus)
    			{
    			case 1:
    				// pump device not available
    				break;
    			case 2:
    				// i2c module needs reinitialized
    				initStatus = 2;
    				break;
    			}
    		}
    	} // for number of temperature devices
    
    	// check error status
    	if (cfgCount < TEMPERATURE_SENSOR_COUNT)
    	{
    		switch(initStatus)
    		{
    		case 0:
    			initStatus = 1;	// set initStatus to error
    			break;
    		case 2:
    			initStatus = 2;	// i2c module needs reinitialized
    			break;
    		}
    	}
    
    	return (initStatus);
    }
    
    /*******************************************************************************
     * Function Name	: I2C_Thermometer_TMP275_ChkDevice
     * Description		: Check thermometer is available
     * Input			: thermometer address
     * Return			: 0 - address found
     *                  : 1 - address not found
     *******************************************************************************/
    unsigned char I2C_Thermometer_TMP275_CheckDevice(unsigned char addr)
    {
    	return (usciI2CSlaveNotPresent(addr));
    }
    
    /*******************************************************************************
     * Function Name	: I2C_Thermometer_TMP275_CfgDevice
     * Description		: Configure thermometer
     * Input			: thermometer address
     * Return			: None
     *******************************************************************************/
    void I2C_Thermometer_TMP275_ConfigDevice(unsigned char addr)
    {
    	// Configure for continuous conversions with 12 bit resolution
    	I2C_Thermometer_TMP275_WriteCfg(addr, CONFIGURATION_REGISTER, CONVERTER_RESOLUTION_12);
    }
    
    /*******************************************************************************
     * Function Name	: I2C_Thermometer_TMP275_ReadConfig
     * Description		: Read the configuration
     * Input			: thermometer address
     * Return			: thermometer configuration data
     *******************************************************************************/
    unsigned char I2C_Thermometer_TMP275_ReadConfig(unsigned char addr)
    {
    	unsigned char config[1];
    
    	usciI2CSelectSlaveAddress(addr);
    	usciI2CReceive(1, &config[0]);
    
    	return (config[0]);
    }
    
    /*******************************************************************************
     * Function Name	: I2C_Thermometer_TMP275_ReadTemp
     * Description		: Read the temperature sensor
     * Input			: thermometer address
     * Return			: thermometer temperature data
     *******************************************************************************/
    unsigned int I2C_Thermometer_TMP275_ReadTemp(unsigned char addr)
    {
    	unsigned char buffer[2];
    	unsigned int temperature = 0;
    
    	buffer[0] = 0;
    	buffer[1] = 0;
    	usciI2CSelectSlaveAddress(addr);
    	usciI2CReceive(2, &buffer[0]);
    	temperature = (buffer[0]<<8 | buffer[1])>>4;
    #ifdef __DEF_I2C_DBG__
    	ti_printf("Temperature--> ADDR: 0x%02X Raw: 0x%03X Actual: %d.%d C\r\n",
    			  addr, temperature, (signed char)(temperature>>4),
    			  (temperature&0x3)*25);
    #endif
    	return temperature;
    }
    
    /*******************************************************************************
     * Function Name	: I2C_Thermometer_TMP275_WriteCfg
     * Description		: Write configuration to the thermometer
     * Input			: addr: thermometer address
     *					: ptrReg: address of thermometer data register
     *					: value: cfg data to write to thermometer data register
     * Return			: None
     *******************************************************************************/
    void I2C_Thermometer_TMP275_WriteCfg(unsigned char addr, unsigned char ptrReg, unsigned char value)
    {
    	unsigned char xmtBuffer[2];
    
    	xmtBuffer[0] = ptrReg;
    	xmtBuffer[1] = value;
    	usciI2CSelectSlaveAddress(addr);
    	usciI2CTransmit(2, &xmtBuffer[0]);
    	// wait for transaction to complete
    	gTMP275TimeoutStarted = 0;
    	while( usciI2CNotready() && I2C_Thermometer_TMP275_Timeout() );
    }
    
    /*******************************************************************************
     * Function Name	: I2C_Thermometer_TMP275_SetReadPtr
     * Description		: Change pointer of sensor
     * Input			: addr: thermometer address
     *                  : value: thermometer configuration value
     * Return			: None
     *******************************************************************************/
    void I2C_Thermometer_TMP275_SetReadPtr(unsigned char addr, unsigned char value)
    {
    	unsigned char xmtBuffer[1];
    
    	xmtBuffer[0] = value;
    	usciI2CSelectSlaveAddress(addr);
    	usciI2CTransmit(1, &xmtBuffer[0]);
    	// wait for transaction to complete
    	gTMP275TimeoutStarted = 0;
    	while( usciI2CNotready() && I2C_Thermometer_TMP275_Timeout() );
    }
    

    1106.I2C_Thermometer_TMP275.h

    2620.I2C_DAC_AD5622.c
    // *****************************************************************************************************************************
    // I2C_DAC_AD5622.c
    // *****************************************************************************************************************************
    #include "ProcLib\MISL_Sys.h"
    
    // AD5622_I2C_ADDRESS range from 0x0F
    #include "I2C_DAC_AD5622.h"
    
    #include "i2c.h"
    
    #ifdef __DEF_I2C_DBG__
    #include "IOLib\ti_printf.h"
    #endif
    #ifdef __DEF_I2C_TIMEO_DBG__
    #include "IOLib\ti_printf.h"
    #endif
    volatile long gAD5622Timeout;
    volatile unsigned char gAD5622TimeoutStarted = 0;
    
    AD5622_DEV_T pumpDevice;
    
    // Local Prototypes
    unsigned char I2C_DAC_AD5622_Timeout(void);
    unsigned char I2C_DAC_AD5622_InitDevice(unsigned char addr);
    unsigned char I2C_DAC_AD5622_CheckDevice(unsigned char addr);
    unsigned int I2C_DAC_AD5622_ReadAnalogOut(unsigned char addr);
    void I2C_DAC_AD5622_WriteAnalogOut(unsigned char addr, unsigned short value);
    
    /*******************************************************************************
     * Function Name	: I2C_DAC_AD5622_Timeout
     * Description		: Check thermometer(s) are available
     * Input			: None
     * Return			: 0 - timer expiredl
     *                  : 1 - timer running
     *******************************************************************************/
    unsigned char I2C_DAC_AD5622_Timeout(void)
    {
    
    	if (gAD5622TimeoutStarted)
    	{
    		gAD5622Timeout--;
    		if (gAD5622Timeout == 0)
    		{
    			gAD5622TimeoutStarted = 0;
    			return 0;	// timer expired
    		}
    		else
    		{
    			return 1;	// timer running
    		}
    	}
    	else
    	{
    		gAD5622TimeoutStarted = 1;
    #if MSP430_CLK_1MHz
    		// 1ms delay @ ~1MHz
    		gAD5622Timeout = 1000;
    #endif
    #if MSP430_CLK_4MHz
    		// 1ms delay @ ~4MHz
    		gAD5622Timeout = 4000;
    #endif
    #if MSP430_CLK_8MHz
    		// 1ms delay @ ~8MHz
    		gAD5622Timeout = 8000;
    #endif
    #if MSP430_CLK_12MHz
    		// 1ms delay @ ~12MHz
    		gAD5622Timeout = 12000;
    #endif
    #if MSP430_CLK_20MHz
    		// 1ms delay @ ~20MHz
    		gAD5622Timeout = 20000;
    #endif
    #if MSP430_CLK_25MHz
    		// 1ms delay @ ~25MHz
    		gAD5622Timeout = 25000;
    #endif
    		return 1;	// timer running
    	}
    }
    
    /*******************************************************************************
     * Function Name	: I2C_DAC_AD5622_InitDevice
     * Description		: Check pump voltage DAC is available
     * Input			: None
     * Return			: 0 - initialization successful
     *                  : 1 - initialization error(s)
     *                  : 2 - i2c module needs reinitialized
     *******************************************************************************/
    unsigned char I2C_DAC_AD5622_InitDevice(unsigned char addr)
    {
    	unsigned char retStatus;
    	unsigned char initStatus = 0;	// start out optimistically
    
    	// initialize device address
    	pumpDevice.address = addr;
    
    	// initialize device status indicators pessimistically
    	pumpDevice.available = false;
    	pumpDevice.configured = false;
    	pumpDevice.error = true;
    
    	// check if device address is available
    	retStatus = I2C_DAC_AD5622_CheckDevice(addr);
    	if (retStatus == 0)
    	{
    		// if device available, mark it available
    		pumpDevice.available = true;
    		// if device configured, mark it configured
    		pumpDevice.configured = true;
    		// pump voltage device is available and configured, mark it error free
    		pumpDevice.error = false;
    	}
    	else
    	{
    		switch(retStatus)
    		{
    		case 1:
    			// pump device not available
    			initStatus = 1;
    			break;
    		case 2:
    			// i2c module needs reinitialized
    			initStatus = 2;
    			break;
    		}
    	}
    
    	return (initStatus);
    }
    
    /*******************************************************************************
     * Function Name	: I2C_DAC_AD5622_ChkDevice
     * Description		: Check if pump voltage device is available
     * Input			: The I2C address of the AD5622
     * Return			: 0 - address found
     *                  : 1 - address not found
     *******************************************************************************/
    unsigned char I2C_DAC_AD5622_CheckDevice(unsigned char addr)
    {
    	return (usciI2CSlaveNotPresent(addr));
    }
    
    /*******************************************************************************
     * Function Name	: I2C_DAC_AD5622_ReadAnalogOut
     * Description		: This function reads the DAC input register to
     *                    retrieve the analog output value
     * Input			: The I2C address of the AD5622
     * Return			: the value back from the analog output register
     *******************************************************************************/
    unsigned int I2C_DAC_AD5622_ReadAnalogOut(unsigned char addr)
    {
    	unsigned char buffer[2];
    	unsigned int value = 0;
    
    	buffer[0] = 0;
    	buffer[1] = 0;
    	usciI2CSelectSlaveAddress(addr);
    	usciI2CReceive(2, &buffer[0]);
    	// the AD5622 sends the high byte first
    	// when reading the value is left-justified 2 bits (see AD5622 manual)
    	value = (buffer[0]<<8 | buffer[1])>>2;
    #ifdef __DEF_I2C_DBG__
    	ti_printf("Pump Analog Out--> ADDR: 0x%02X Value: 0x%03X\r\n", addr, value);
    #endif
    	return value;
    }
    
    /*******************************************************************************
     * Function Name	: I2C_DAC_AD5622_WriteAnalogOut
     * Description		: This function writes the analog output value to
     *                    the DAC input register
     * Input			: addr: The I2C address of the AD5622
     *					: value: output value (0..4095 => 0..5V)
     * Return			: None
     *******************************************************************************/
    void I2C_DAC_AD5622_WriteAnalogOut(unsigned char addr, unsigned short value)
    {
    	unsigned char xmtBuffer[2];
    
    	// The first two bits are reserved bits that must be set to zero,
    	// the next two bits are control bits that select the mode of operation
    	// of the device (00 = normal mode).
    	xmtBuffer[0] = (unsigned char)((((unsigned short)value) & 0x0F00) >> 8);	// set the high byte
    	xmtBuffer[1] = (unsigned char)(((unsigned short)value) & 0x00FF);			// set the low byte
    
    	usciI2CSelectSlaveAddress(addr);
    	usciI2CTransmit(2, &xmtBuffer[0]);
    	// wait for transaction to complete
    	gAD5622TimeoutStarted = 0;
    	while( usciI2CNotready() && I2C_DAC_AD5622_Timeout() );
    #ifdef __DEF_I2C_TIMEO_DBG__
    	if (gAD5622Timeout == 0)
    	{
    	ti_printf("Write Pump Analog Out--> TIMED OUT!!!\r\n");
    	}
    #endif
    }
    

    1016.I2C_DAC_AD5622.h

    0876.I2C_PWM_DS1050.c
    // *****************************************************************************************************************************
    // I2C_PWM_DS1050.c
    // *****************************************************************************************************************************
    #include "ProcLib\MISL_Sys.h"
    
    // DS1050_I2C_ADDRESS range from 0x0F
    #include "I2C_PWM_DS1050.h"
    
    #include "i2c.h"
    
    #ifdef __DEF_I2C_DBG__
    #include "IOLib\ti_printf.h"
    #endif
    
    volatile long gDS1050Timeout;
    volatile unsigned char gDS1050TimeoutStarted = 0;
    
    DS1050_DEV_T pwmDevices[PWM_DEVICE_COUNT];
    
    // Local Prototypes
    unsigned char I2C_PWM_DS1050_Timeout(void);
    unsigned char I2C_PWM_DS1050_InitDevices(void);
    unsigned char I2C_PWM_DS1050_CheckDevice(unsigned char addr);
    unsigned char I2C_PWM_DS1050_ReadPWMDutyCycle(unsigned char addr);
    void I2C_PWM_DS1050_WritePWMDutyCycle(unsigned char addr, unsigned char dutyCycle);
    
    /*******************************************************************************
     * Function Name	: I2C_PWM_DS1050_Timeout
     * Description		: Check thermometer(s) are available
     * Input			: None
     * Return			: 0 - timer expiredl
     *                  : 1 - timer running
     *******************************************************************************/
    unsigned char I2C_PWM_DS1050_Timeout(void)
    {
    
    	if (gDS1050TimeoutStarted)
    	{
    		gDS1050Timeout--;
    		if (gDS1050Timeout == 0)
    		{
    			gDS1050TimeoutStarted = 0;
    			return 0;	// timer expired
    		}
    		else
    		{
    			return 1;	// timer running
    		}
    	}
    	else
    	{
    		gDS1050TimeoutStarted = 1;
    #if MSP430_CLK_1MHz
    		// 1ms delay @ ~1MHz
    		gDS1050Timeout = 1000;
    #endif
    #if MSP430_CLK_4MHz
    		// 1ms delay @ ~4MHz
    		gDS1050Timeout = 4000;
    #endif
    #if MSP430_CLK_8MHz
    		// 1ms delay @ ~8MHz
    		gDS1050Timeout = 8000;
    #endif
    #if MSP430_CLK_12MHz
    		// 1ms delay @ ~12MHz
    		gDS1050Timeout = 12000;
    #endif
    #if MSP430_CLK_20MHz
    		// 1ms delay @ ~20MHz
    		gDS1050Timeout = 20000;
    #endif
    #if MSP430_CLK_25MHz
    		// 1ms delay @ ~25MHz
    		gDS1050Timeout = 25000;
    #endif
    		return 1;	// timer running
    	}
    }
    
    /*******************************************************************************
     * Function Name	: I2C_PWM_DS1050_InitDevices
     * Description		: Check PWM(s) are available
     * Input			: None
     * Return			: 0 - initialization successful
     *                  : 1 - initialization error(s)
     *                  : 2 - i2c module needs reinitialized
     *******************************************************************************/
    unsigned char I2C_PWM_DS1050_InitDevices(void)
    {
    	unsigned char addr;
    	unsigned char index;
    	unsigned char retStatus;
    	unsigned char cfgCount;
    	volatile unsigned char devConfig;
    	unsigned char initStatus = 0;	// start out optimistically
    
    	// initialize device configured count
    	// if a device is successfully configured, increment count
    	cfgCount = 0;
    
    	// for number of temperature devices
    	for(addr = DS1050_I2C_PWM_U30, index=0; index<PWM_DEVICE_COUNT; addr++, index++)
    	{
    		if (addr == DS1050_I2C_PWM_U5 ||
    				addr == DS1050_I2C_PWM_U24 ||
    				addr == DS1050_I2C_PWM_U30 ||
    				addr == DS1050_I2C_PWM_U36)
    		{
    			// initialize device address
    			pwmDevices[index].address = addr;
    
    			// initialize device status indicators pessimistically
    			pwmDevices[index].available = false;
    			pwmDevices[index].configured = false;
    			pwmDevices[index].error = true;
    
    			// check if device address is available
    			retStatus = I2C_PWM_DS1050_CheckDevice(addr);
    			if (retStatus == 0)
    			{
    				// if device available, mark it available
    				pwmDevices[index].available = true;
    				// if device configured, mark it configured
    				pwmDevices[index].configured = true;
    				// temperature device is available and configured, mark it error free
    				pwmDevices[index].error = false;
    				// increment device configured count
    				cfgCount++;
    			}
    			else
    			{
    				switch(retStatus)
    				{
    				case 1:
    					// PWM device not available
    					break;
    				case 2:
    					// i2c module needs reinitialized
    					initStatus = 2;
    					break;
    				}
    			}
    		} // if valid PWM device address
    		else
    		{
    			index--;
    		}
    	} // for number of PWM devices
    
    	// check error status
    	if (cfgCount < PWM_DEVICE_COUNT)
    	{
    		switch(initStatus)
    		{
    		case 0:
    			initStatus = 1;	// set initStatus to error
    			break;
    		case 2:
    			initStatus = 2;	// i2c module needs reinitialized
    			break;
    		}
    	}
    
    	return (initStatus);
    }
    
    /*******************************************************************************
     * Function Name	: I2C_PWM_DS1050_ChkDevice
     * Description		: Check if PWM device is available
     * Input			: The I2C address of the DS1050
     * Return			: 0 - address found
     *                  : 1 - address not found
     *******************************************************************************/
    unsigned char I2C_PWM_DS1050_CheckDevice(unsigned char addr)
    {
    	return (usciI2CSlaveNotPresent(addr));
    }
    
    /*******************************************************************************
     * Function Name	: I2C_PWM_DS1050_ReadPWMDutyCycle
     * Description		: This function reads the PWM configuration register
     * Input			: The I2C address of the DS1050
     * Return			: the value back from the PWM configuration register
     *******************************************************************************/
    unsigned char I2C_PWM_DS1050_ReadPWMDutyCycle(unsigned char addr)
    {
    
    	unsigned char dutyCycle[1];
    
    	usciI2CSelectSlaveAddress(addr);
    	usciI2CReceive(1, &dutyCycle[0]);
    #ifdef __DEF_I2C_DBG__
    	ti_printf("PWM Out--> ADDR: 0x%02X Value: 0x%02X\r\n", addr, dutyCycle);
    #endif
    
    	// Is Set PWM Duty Cycle 100% ?
    	if (dutyCycle[0] & SET_PWM_100_PERCENT)
    	{
    		// The first three bits must be set to 001B, the next five bits are don't care.
    		dutyCycle[0] = dutyCycle[0] & SET_PWM_100_PERCENT;
    	}
    
    	return (dutyCycle[0]);
    }
    
    /*******************************************************************************
     * Function Name	: I2C_PWM_DS1050_WritePWMDutyCycle
     * Description		: This function writes the configuration value to
     *                    the PWM configuration register
     * Input			: addr: The I2C address of the DS1050
     *					: value: The duty cycle value (0..11111B => 0..96.88%)
     * Return			: None
     *******************************************************************************/
    void I2C_PWM_DS1050_WritePWMDutyCycle(unsigned char addr, unsigned char dutyCycle)
    {
    	unsigned char xmtBuffer[1];
    
    	// Set PWM Duty Cycle 100% ?
    	if (dutyCycle & SET_PWM_100_PERCENT)
    	{
    		// The first three bits must be set to 001B, the next five bits are don't care.
    		xmtBuffer[0] = dutyCycle & SET_PWM_100_PERCENT;
    	}
    	else
    	{
    		// The first three bits must be set to 000B, the next five bits are PWM Duty Cycle Data.
    		xmtBuffer[0] = dutyCycle & 0x1F;
    	}
    	usciI2CSelectSlaveAddress(addr);
    	usciI2CTransmit(1, &xmtBuffer[0]);
    	// wait for transaction to complete
    	gDS1050TimeoutStarted = 0;
    	while( usciI2CNotready() && I2C_PWM_DS1050_Timeout() );
    }
    

    4010.I2C_PWM_DS1050.h

    3021.I2C_PMON_LTC4151.c
    // *****************************************************************************************************************************
    // I2C_PMON_LTC4151.c
    // *****************************************************************************************************************************
    // LTC4151_I2C_ADDRESS range from 0x48-0x49
    #include "ProcLib\MISL_Sys.h"
    
    #include "I2C_PMON_LTC4151.h"
    
    #include "i2c.h"
    
    #ifdef __DEF_I2C_DBG__
    #include "IOLib\ti_printf.h"
    #endif
    
    volatile long gLTC4151Timeout;
    volatile unsigned char gLTC4151TimeoutStarted = 0;
    
    LTC4151_DEV_T gPmonDevices[PMON_SENSOR_COUNT];
    unsigned char gPmonRegisters[PMON_REGISTER_COUNT];
    
    // Local Prototypes
    unsigned char I2C_PMON_LTC4151_Timeout(void);
    unsigned char I2C_PMON_LTC4151_InitDevices(void);
    unsigned char I2C_PMON_LTC4151_CheckDevice(unsigned char addr);
    void I2C_PMON_LTC4151_ConfigDevice(unsigned char addr);
    void I2C_PMON_LTC4151_ReadRegisters(unsigned char addr, unsigned char *recvBuffer);
    void I2C_PMON_LTC4151_WriteCfg(unsigned char addr, unsigned char ptrReg, unsigned char value);
    void I2C_PMON_LTC4151_ReadPowerOut(unsigned char addr);
    void I2C_PMON_LTC4151_SetReadPtr(unsigned char addr, unsigned char ptrReg);
    
    /*******************************************************************************
     * Function Name	: I2C_PMON_LTC4151_Timeout
     * Description		: Check power monitor(s) are available
     * Input			: None
     * Return			: 0 - timer expired
     *                  : 1 - timer running
     *******************************************************************************/
    unsigned char I2C_PMON_LTC4151_Timeout(void)
    {
    
    	if (gLTC4151TimeoutStarted)
    	{
    		gLTC4151Timeout--;
    		if (gLTC4151Timeout == 0)
    		{
    			gLTC4151TimeoutStarted = 0;
    			return 0;	// timer expired
    		}
    		else
    		{
    			return 1;	// timer running
    		}
    	}
    	else
    	{
    		gLTC4151TimeoutStarted = 1;
    #if MSP430_CLK_1MHz
    		// 1ms delay @ ~1MHz
    		gLTC4151Timeout = 1000;
    #endif
    #if MSP430_CLK_4MHz
    		// 1ms delay @ ~4MHz
    		gLTC4151Timeout = 4000;
    #endif
    #if MSP430_CLK_8MHz
    		// 1ms delay @ ~8MHz
    		gLTC4151Timeout = 8000;
    #endif
    #if MSP430_CLK_12MHz
    		// 1ms delay @ ~12MHz
    		gLTC4151Timeout = 12000;
    #endif
    #if MSP430_CLK_20MHz
    		// 1ms delay @ ~20MHz
    		gLTC4151Timeout = 20000;
    #endif
    #if MSP430_CLK_25MHz
    		// 1ms delay @ ~25MHz
    		gLTC4151Timeout = 25000;
    #endif
    		return 1;	// timer running
    	}
    }
    
    /*******************************************************************************
     * Function Name	: I2C_PMON_LTC4151_InitDevices
     * Description		: Check power monitor(s) are available
     * Input			: None
     * Return			: 0 - initialization successful
     *                  : 1 - initialization error(s)
     *                  : 2 - i2c module needs reinitialized
     *******************************************************************************/
    unsigned char I2C_PMON_LTC4151_InitDevices(void)
    {
    	unsigned char addr;
    	unsigned char index;
    	unsigned char retStatus;
    	unsigned char cfgCount;
    	volatile unsigned char devConfig;
    	unsigned char initStatus = 0;	// start out optimistically
    
    	// initialize device configured count
    	// if a device is successfully configured, increment count
    	cfgCount = 0;
    
    	// for number of power monitor devices
    	for(addr = LTC4151_I2C_PMON_U39, index=0; index<PMON_SENSOR_COUNT; addr++, index++)
    	{
    		// initialize device address
    		gPmonDevices[index].address = addr;
    
    		// initialize device status indicators pessimistically
    		gPmonDevices[index].available = false;
    		gPmonDevices[index].configured = false;
    		gPmonDevices[index].error = true;
    
    		// check if device address is available
    		retStatus = I2C_PMON_LTC4151_CheckDevice(addr);
    		if (retStatus == 0)
    		{
    			// if device available, mark it available
    			gPmonDevices[index].available = true;
    
    			// configure power monitor device
    			I2C_PMON_LTC4151_ConfigDevice(addr);
    
    			// check that configuration was successful
    			I2C_PMON_LTC4151_ReadRegisters(addr, &gPmonRegisters[0]);
    			if (gPmonRegisters[PMON_CONTROL_REGISTER] == PAGE_R_W_ENABLE)
    			{
    				// if device configured, mark it configured
    				gPmonDevices[index].configured = true;
    				// power monitor device is available and configured, mark it error free
    				gPmonDevices[index].error = false;
    				// increment device configured count
    				cfgCount++;
    				I2C_PMON_LTC4151_SetReadPtr(addr, PMON_SENSE_A_REGISTER);
    			}
    		}
    		else
    		{
    			switch(retStatus)
    			{
    			case 1:
    				// pump device not available
    				break;
    			case 2:
    				// i2c module needs reinitialized
    				initStatus = 2;
    				break;
    			}
    		}
    	} // for number of power monitor devices
    
    	// check error status
    	if (cfgCount < PMON_SENSOR_COUNT)
    	{
    		switch(initStatus)
    		{
    		case 0:
    			initStatus = 1;	// set initStatus to error
    			break;
    		case 2:
    			initStatus = 2;	// i2c module needs reinitialized
    			break;
    		}
    	}
    
    	return (initStatus);
    }
    
    /*******************************************************************************
     * Function Name	: I2C_PMON_LTC4151_ChkDevice
     * Description		: Check power monitor is available
     * Input			: thermometer address
     * Return			: 0 - address found
     *                  : 1 - address not found
     *******************************************************************************/
    unsigned char I2C_PMON_LTC4151_CheckDevice(unsigned char addr)
    {
    	return (usciI2CSlaveNotPresent(addr));
    }
    
    /*******************************************************************************
     * Function Name	: I2C_PMON_LTC4151_CfgDevice
     * Description		: Configure power monitor
     * Input			: thermometer address
     * Return			: None
     *******************************************************************************/
    void I2C_PMON_LTC4151_ConfigDevice(unsigned char addr)
    {
    	// Configure for page r/w and continuous conversions with 12 bit resolution
    	I2C_PMON_LTC4151_WriteCfg(addr, CONTROL_REGISTER, PAGE_R_W_ENABLE);
    }
    
    /*******************************************************************************
     * Function Name	: I2C_PMON_LTC4151_ReadRegisters
     * Description		: Read the power monitor control register
     * Input			: addr: power monitor address
     *					: recvBuffer: power monitor register data retrieved
     * Return			: none
     *******************************************************************************/
    void I2C_PMON_LTC4151_ReadRegisters(unsigned char addr, unsigned char *recvBuffer)
    {
    #ifdef __DEF_I2C_DBG__
    	unsigned int rawData = 0;
    #endif
    
    	usciI2CSelectSlaveAddress(addr);
    	usciI2CReceive(7, recvBuffer);
    #ifdef __DEF_I2C_DBG__
    	rawData = (recvBuffer[0]<<8 | recvBuffer[1])>>4;
    	ti_printf("SENSE--> ADDR: 0x%02X Raw: 0x%03X\r\n", addr, rawData);
    	rawData = (recvBuffer[2]<<8 | recvBuffer[3])>>4;
    	ti_printf("Vin--> ADDR: 0x%02X Raw: 0x%03X\r\n", addr, rawData);
    	rawData = (recvBuffer[4]<<8 | recvBuffer[5])>>4;
    	ti_printf("ADin--> ADDR: 0x%02X Raw: 0x%03X\r\n", addr, rawData);
    	ti_printf("Control--> ADDR: 0x%02X Raw: 0x%03X\r\n", addr, recvBuffer[6]);
    #endif
    }
    
    /*******************************************************************************
     * Function Name	: I2C_PMON_LTC4151_WriteCfg
     * Description		: Write configuration to the power monitor
     * Input			: addr: power monitor address
     *					: ptrReg: address of power monitor register to write to
     *					: value: cfg data to write to power monitor control register
     * Return			: None
     *******************************************************************************/
    void I2C_PMON_LTC4151_WriteCfg(unsigned char addr, unsigned char ptrReg, unsigned char value)
    {
    	unsigned char xmtBuffer[2];
    
    	xmtBuffer[0] = ptrReg;
    	xmtBuffer[1] = value;
    	usciI2CSelectSlaveAddress(addr);
    	usciI2CTransmit(2, &xmtBuffer[0]);
    	// wait for transaction to complete
    	gLTC4151TimeoutStarted = 0;
    	while( usciI2CNotready() && I2C_PMON_LTC4151_Timeout() );
    }
    
    /*******************************************************************************
     * Function Name	: I2C_PMON_LTC4151_SetReadPtr
     * Description		: Change internal pointer of power monitor register
     * Input			: addr: power monitor device address
     *                  : ptrReg: Power Monitor SENSE Register address
     * Return			: None
     *******************************************************************************/
    void I2C_PMON_LTC4151_SetReadPtr(unsigned char addr, unsigned char ptrReg)
    {
    	unsigned char xmtBuffer[1];
    
    	xmtBuffer[0] = ptrReg;
    	usciI2CSelectSlaveAddress(addr);
    	usciI2CTransmit(1, &xmtBuffer[0]);
    	// wait for transaction to complete
    	gLTC4151TimeoutStarted = 0;
    	while( usciI2CNotready() && I2C_PMON_LTC4151_Timeout() );
    }
    
    /*******************************************************************************
     * Function Name	: I2C_PMON_LTC4151_ReadPowerOut
     * Description		: Read and populate the power monitor register buffer (gPmonRegisters)
     * Input			: addr: power monitor address
     * Return			: none:
     *******************************************************************************/
    void I2C_PMON_LTC4151_ReadPowerOut(unsigned char addr)
    {
    	// read the power monitor registers
    	I2C_PMON_LTC4151_ReadRegisters(addr, &gPmonRegisters[0]);
    }
    

    2335.I2C_PMON_LTC4151.h

  • Thanks Katie & Jens-Michael,

    First... Katie, yeah... typo, its a 5722.

    I'm a little worried about switching between GPIO & I2C modes as this needs to be VERY reliable (and by very reliable, I mean it needs to work 100% of the time with this ill behaved bus).  THe product polls the I2C bus roughly every 500ms, and if it doesn't respond in its expected way, it stops until the EEPROM is disconnected and reconnected.  Luckily, this 'bad behavior only happens about 2 or 3 seconds after the EEPROM is attached.

    What might be an interesting experiment is to wire the I2C bus to both the I2C bus and a separate set of GPIO interrupts, and if this silliness is detected, I could start counting the starts and issue s SWRST as Katie suggested at the appropriate time.... though I will try the UCEXTINT idea first

    As for the USI module... is there an easy way to figure out what parts have them?  All I've been able to figure out is that _some_ of the MSP430G2xxx parts have them.  I'll search more when I get home (right now I have a pretty slow hotel-room connection)

  • The problem with the early start interrupt is, that the USCI apparently accepts the first start as start and interprets the next starts as data bits. SO it won’t generate 9 start interrupts, even with the early start interrupt enabled. You’d have to detect the interrupt, then reset the USCI, so it will detect and report the next start. Almost the same as counting the starts with port pin interrupt and then clearing SWRST after the 9th.

  • I finally had some time to work on this again. 

    My capture-compare solution works quite nicely with an aardvark (up to about 25KHz SCL), but is very unreliable (like less than 10%) with the 3.6uS high clock pulses coming from the product.  I'm sure I could hand optimize it, but I seriously doubt enough optimized to make it reliable.  I also think that if I used 2 different capture timer ISRs (rather than a single ISR) might cure it, as the problem is that I cannot reliably see both the rising and falling edge of that 3.6us clock.

    I went back and started to play with the FRAM's USCI address mask.  As we all know, setting the address mask to zero (to accept everything) appears not to work at all because of the garbage.  I tried to set 4 of the slave addresses (0, 1 , 2, 3), and put the address mask back to the default.  It got past the 'garbage' previous described, and the first 4 bytes work beautifully, and then nack'd byte 5 as expected, but released the bus (another odd ball behavior I didn't mention the first time.. this product only reads or writes a single byte at a time).  I then set the address mask to '3' and something very strange happened.  It worked for 5 bytes (address 0-4), and then stopped.  Strangely, byte #6 was loaded into the TX register but the SCL & SDA were held low until the MSP was reset.... I forgot to count how many clock pulses there were in the last transfer, but I suspect the MSP was at fault for holding the clock low since resetting the MSP cured the latch up.

    I also tried the same experiment by only setting the address mask to '1' and using address match of 0 and 1... it works for 3 bytes, and loads the value of byte 4 into TX, but then the bus lacthes up with both clock & data low.

    My next plan is to try some of the other devices (UCS?) suggested by Jens-Michael, but I don't have any of those parts and need to order some.

  • The timer capture registers can trigger on 'any' edge, not just rising or falling, like the port pin interrupts.

    When the MSP holds SCL low, then it is probably waiting for an action. Like reading the last byte from RXBUF or writing to TXBUF or whatever. Maybe your code still has a racing condition.

    As an alternative, I was talking about the USI. Which is a “poor man’s USCI”: no UART and barely more than a shift register with a clock generator. Only the older F2x, G2x or F4x devices have one. However, in your special case, it could be a solution.

**Attention** This is a public forum