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.

I2C not transmitting correct address

Other Parts Discussed in Thread: MSP430F5308

I'm having problems trying to use the I2C module USCI_B1 of an MSP430F5308 as master

My code is here:

#include <legacymsp430.h>

// P4.1 is data, P4.2 is clock

void initi2c(void)
{
	UCB1CTL1|= UCSWRST;		// reset
	P4SEL |= BIT1|BIT2;		// USCI_B1 I2C
	P4REN |= BIT1|BIT2;		// Enable pullups
	// 7-bit address, single master, master mode, I2C mode, synchronous
	UCB1CTL0 = UCMST|UCMODE_3|UCSYNC;
	// smclk, transmitter, normal ack
	UCB1CTL1 |= UCSSEL__SMCLK|UCTR;
	UCB1BR0 = 0x7f;			// 33 KHz scl ??
	UCB1BR1 = 0;
	// enable arbitration lost, not-acknowledge, start condition, transmit interrupts.
	UCB1IE |= UCALIE|UCNACKIE|UCSTTIE|UCTXIE;
	UCB1CTL1 &= ~UCSWRST;	// release for operation
}
#define ERR_NOACK	1;		// not acknowledge received
#define ERR_ARBT	2;		// arbitration lost

int nbytes, done, error;
char *bufPtr;

interrupt (USCI_B1_VECTOR) i2cB1_Interrupt (void)
{
int iv = UCB1IV;
	switch (iv)
	{
		case 2:			// arbitration lost
			error = ERR_ARBT;
			done = 1;
			break;
		case 4:			// not acknowledge
			error = ERR_NOACK;
			done = 1;
			break;
		case 6:			// start condition received
			UCB1TXBUF = *bufPtr++;			// load first byte
			nbytes--;
			break;
		case 0xc:		// transmit buffer empty
			if (nbytes)
			{
				UCB1TXBUF = *bufPtr++;
				nbytes--;
			}
			else
			{
				UCB1CTL1 |= UCTXSTP;		// generate stop
				done = 1;
			}
			break;
		default:
			break;
	}
}

int i2cWrite(char address, char *datap, int ndata)
{
	nbytes = ndata;
	bufPtr = datap;
	done = 0;
	error = 0;
	UCB1CTL1|= UCSWRST;			// reset
	UCB1I2CSA = address;
	UCB1CTL1 &= ~UCSWRST;		// release for operation
	UCB1CTL1 |= UCTXSTT;		// generate START condition
	while (!done)
		;
	return error;
}


The device I'm transmitting data to has address 0x46. However the address that is transmitted is 0x86 as shown by the following:

Another problem is that the not-acknowledge interrupt is not generated.
 I'm sure I've done something wrong but I have no idea what. Your suggestions are appreciated.

  • That looks like START followed by 0x8C (then NACK). That's what I'd expect to see on the line when passing a slave address of 0x46 to the USCI module and triggering a start condition in write mode.

    I2C addressing can be a little confusing. The standard address length is 7 bits, but it is sent in the upper 7 bits of the byte sent after a START condition. The low bit is the R/W flag, which is set to 1 if the master wants to read from the slave.

    In your case the first byte of a transfer works out as ((0x46 << 1) | 0x0), which is 0x8C.

    What makes this confusing is that datasheets for I2C slaves and microcontrollers are inconsistent over whether the slave addresses are written as 7 bits (without R/W) or 8 (with R/W baked in already).

    It may be the case that your slave address is specified in the latter format. The USCI module expects a 7-bit address, so you'd need to set UCB1I2CSA to 0x23 instead of 0x46 to get a response.

    EDIT: Regarding the missing NACK interrupt, I've not spotted anything in your code that would definitely cause that. I assume you have GIE set, as the start condition doesn't happen until after UCB1TXBUF is written to. I assume you have GIE set, as the USCI holds SCL low after sending the address+R/W until UCB1TXBUF is written to. That clock stretch prevents (N)ACK from occurring until the USCI has the next byte of data ready to send.

    The scope trace shows that SCL goes high again after the R/W bit is sent. That means the USCI must think it has a byte ready to send. The linked code only sets UCB1TXBUF in the interrupt, so it must be getting in there somehow.

    Just to be safe I suggest declaring nbytes, done, error and bufPtr as volatile, since they're accessed in the main flow of execution as well as in the interrupt.

    By the way, I don't think that you need to set UCB1TXBUF in response to UCSTTIFG. Setting UCTXSTT causes UCTXIFG to be set to 1, and you already handle that by writing a byte of your output to UCB1TXBUF.

  • Robert,

    I finally realized what the problem was with the address, I changed the value to 0x23 and now I get the ACK but still no interrupt so no data is transmitted. I enable interrupts in main() and the program does process the watchdog and a timer interrupt.

    Thanks for the heads up re declaring nbytes, done, error and bufPtr as volatile. I get burned with that mistake quite often.

    I'll puzzle over this a bit more and will likely be back again.

    Many thanks,

    Gary

  • Gary Richardson said:
    I changed the value to 0x23 and now I get the ACK but still no interrupt so no data is transmitted.

    That's strange. Like I said in my (edited) reply above, the USCI blocks the bus until it has a byte ready to send. By the time it lets SCL rise so the slave can (N)ACK it should already have the next byte ready to send. That byte should start to transfer immediately after the ACK.

  • Setting bit UCSWRST clears also UCBxIE (disables I2C interrupts), remove these lines in your i2cWrite(), they are not necessary.

  • Leo,

    Yes! That was the problem.

    Thanks for the help.

  • Robert, that’ snot quite true. The USCI will pull SCL low, at which moment the slave can put its ACK on SDA. But the USCI won’t release SCL and recognize the ACK until either TXBUF is written to or TXSTP is set.
    So on a logic analyzer, you’ll see the ACK, but the clock cycle won’t be complete, so the ACK is not recognized (yet), which will happen on the rising edge of SCL after the USCI known how to continue.

**Attention** This is a public forum