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 start and stop sequence problems

Other Parts Discussed in Thread: MSP430G2553

I am having trouble initializing and reading from an ADC that uses I2C to talk to my MSP430G2553. I have set breakpoints while debugging and have found that the problem occurs right after I send a start sequence. It seems that the start bit is not cleared after I send the start sequence even though that is what the data sheet told me would happen. The two functions that should be of interest are:

void initI2C_USCIB(void)

and

void sendASIADC(unsigned char location, unsigned char data);

//***************************************************************************************
//  MSP430 Blink the LED Demo - Software Toggle P1.0
//
//  Description; Toggle P1.0 by xor'ing P1.0 inside of a software loop.
//  ACLK = n/a, MCLK = SMCLK = default DCO
//
//                MSP430x5xx
//             -----------------
//         /|\|              XIN|-
//          | |                 |
//          --|RST          XOUT|-
//            |                 |
//            |             P1.0|-->LED
//
//  J. Stevenson
//  Texas Instruments, Inc
//  July 2011
//  Built with Code Composer Studio v5
//***************************************************************************************

#include <msp430.h>				

void transmitByteUART(unsigned char data);
void initSPI_USCIA(void);							//initializes SPI on USCIA0 for the sd card reader/writer
void initI2C_USCIB(void);							//initializes I2C on USCIB0 for the ADC from ASI
void initLED(void);									//initializes led so we can toggle it
__interrupt void USCIAB0RX_ISR(void);				//interrupt for usci
void initASIadc(void);								//inits our external adc using i2c
void initUART_USCIA(void);							//use this for checking the adc
void delay(unsigned long time);						//simple delay function
void transmitDecimal(unsigned char data);			//this sends the decimal representaton of an 8 bit number to terminal
void sendASIADC(unsigned char location, unsigned char data); //sends data to specified register to ASI ADC
void askForASIData(unsigned char channel);			//input channel and should receive rx interrupt on uscib0
void readASIData(unsigned char channel);			//read only used w/in askfordata

void main(void) {
	WDTCTL = WDTPW | WDTHOLD;							//Stop watchdog timer

	//initSPI_USCIA();
	//while (!(IFG2 & UCA0TXIFG)){
		//IFG2 =0x00;				//clear flags after waiting for tx flag
	//}
	//UCA0TXBUF = 0x11;									//writes to spi data register
	__bis_SR_register(GIE);								//interrupts enabled
	initLED();
	initI2C_USCIB();									//inits with rx interrupt
	initUART_USCIA();									//use this to check the adc with tera term
	initASIadc();




	while(1){
		delay(1000);
		askForASIData(0x00);						//should cause an interrupt that sends adc stuff to teraterm
		//wait for interrupt
	}

}

void delay(unsigned long time){
	unsigned char i = 0;
	while(i<time){						//pretty simple delay function
		i++;
	}

}

void initSPI_USCIA(void){
	P1SEL = (BIT1 | BIT2 | BIT4);					//configures pins for three pin SPI. MISO P1.1, MOSI P1.2, CLK P1.4
	P1SEL2 = (BIT1 | BIT2 | BIT4);					//configures pins for their peripheral setting

	UCA0CTL1 |= UCSWRST; 							//set software reset to 1 so we can configure SPI
	//UCA0STAT |= UCLISTEN;							//the launchpad listens to itself (use for testing)
	UCA0CTL0 |= UCCKPH + UCMSB + UCMST + UCSYNC;	//clkphase,clkpolarity,MSBfirst,8-bit,master mode, 3-pin, synchronous SPI
	UCA0CTL1 |= UCSSEL_2;                   		//choose SMCLK as our clock. SMCLK is 1.1 MHz. We divise it with the baud register
	UCA0BR0 |= 0x02;                       			//Baud rate is 1.1MHZ/(2+1) = 366 KHz
	UCA0BR1 = 0x00;                            		//upper byte of br register. set to zero
	UCA0MCTL = 0;                           		//No modulation
	UCA0CTL1 &= ~UCSWRST;                   		//enables the SPI to work

	IE2 |= UCA0RXIE;                        		// Enable USCI0 RX interrupt
}

void initLED(void){
		 P1DIR |= BIT0;                     //sets red led to output and turns it on
		 P1OUT &= ~BIT0;				    //turn red led off
}

void initI2C_USCIB(void){							//initializes I2C on USCIB0 for the ADC from ASI

	  P1SEL |= BIT6 + BIT7;              		// Assign I2C pins to USCI_B0
	  P1SEL2|= BIT6 + BIT7;              		// Assign I2C pins to USCI_B0

	  UCB0CTL1 |= UCSWRST;                      // Enable SW reset
	  UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC;     // I2C Master, synchronous mode
	  UCB0CTL1 |= UCSSEL_2 + UCTR + UCSWRST;    // Use SMCLK which is 1.1 MHZ, keep SW reset, receiver mode
	  UCB0BR0 = 60;                             // fSCL = SMCLK/60 = ~18.3kHz
	  UCB0BR1 = 0;								//leave upper bits 0
	  UCB0I2CSA = 0x12;                         // Slave Address is 0x12
	  UCB0CTL1 &= ~UCSWRST;                     // Clear SW reset, resume operation

	  IE2 |= UCB0RXIE;                        	//Enable USCI0 RX interrupt
}

void initASIadc(void){
	P2DIR |= BIT0;                     	//sets p2.0 output
	P2OUT |= BIT0;						//sets p2.0 HIGH. This resets the ADC
	delay(100);						 	//waits for a few clock cycles
	P2OUT &= ~BIT0;						//sets p2.0 LOW

	sendASIADC(0x04, 0x00);				//clear the ADC_CHENAB register
	sendASIADC(0x00, 0x00);				//sends 0x04 to the control register of Asi's ADC
	sendASIADC(0x01, 0x00);				//ensures that CSEND is low by clearing
	sendASIADC(0x02, 0x06);				//divides the 1 MHZ internal oscillator by 64 by setting ADC_CLKPRE
	sendASIADC(0x03, 0x02);				// ADC_CLKDV register divides the internal oscillator by 2
}

void initUART_USCIA(void){
	//set clock to 1 MHZ
	BCSCTL1 = CALBC1_1MHZ; // Set Digitally Controlled Oscilaltor to 1MHz
	DCOCTL = CALDCO_1MHZ;  // Set DCO to 1MHz

	//configure hardware UART
	P1SEL = (BIT1 | BIT2);				//configures pins for Uart. MISO P1.1, MOSI P1.2, CLK P1.4
	P1SEL2 = (BIT1 | BIT2);				//configures pins for their peripheral setting

	UCA0CTL1 |= UCSWRST; 							//set software reset to 1 so we can configure SPI
	UCA0CTL0 = 0x00;								//Basic 8-bit UART mode
	UCA0CTL1 |= UCSSEL_2;                   		//choose SMCLK as our clock. SMCLK is 1.1 MHz. We divise it with the baud register
	UCA0BR0 |= 104;                       			//lower baud rate register
	UCA0BR1 = 0;                            		//Baud rate is 1.1MHZ/(108+1) = 9600 (approximately)
	UCA0MCTL = 0;                           		//No modulation
	UCA0CTL1 &= ~UCSWRST;                   		//enables the UART to work

	//enable RX interrupt
	IE2 |= UCA0RXIE;                        		// Enable USCI0 RX interrupt
}

void transmitByteUART(unsigned char data){
	while (!(IFG2 & UCA0TXIFG)){}			//waits until ready to transmit
				IFG2 =0x00;					//clear flags after waiting for tx flag
				UCA0TXBUF = data;			//send 7 to terminal
}

void transmitDecimal(unsigned char data){
	unsigned char first;
	unsigned char second;
	unsigned char third;

	first = data % 10;						//this gives us the first digit /lsb of decimal
	second = data/10;
	second = second %10;					//gives us middle digit
	third = data / 100;						//gives last digit

	first |= 0x30;							//turns it into ascii character
	second |= 0x30;							//turns it into ascii character
	third |= 0x30;							//turns it into ascii character


	transmitByteUART(third);
	transmitByteUART(second);
	transmitByteUART(first);
	transmitByteUART(0x0A);					//new line
	transmitByteUART(0x08);					//carriage return
	transmitByteUART(0x08);					//carriage return
	transmitByteUART(0x08);					//carriage return
}

void sendASIADC(unsigned char location, unsigned char data){			//sends data via spi to asi's adc
	//UCB0CTL1 |= UCTXNACK;						//precede start condition with NACK
	UCB0CTL1 |= UCTXSTT; 				//send start sequence
	while(UCB0CTL1 & UCTXSTT){
		//wait for start sequence to be sent
	}

	UCB0TXBUF =	0x24;							//send i2c address of slave w/ lsb as 0 for write and 1 for read
	UCB0TXBUF =	location;						//send internal register number
	UCB0TXBUF = data;							//send data byte to selected register in adc

	UCB0CTL1 |= UCTXSTP; 						//send stop sequence
	while(UCB0CTL1 & UCTXSTP){
		//wait for stop sequence to be sent
	}
}

void askForASIData(unsigned char channel){
unsigned char bitNum;
unsigned char controlSettings;
bitNum = 0x01 << channel;						//sets the bit to 1 that is the channel number
sendASIADC(0x04, bitNum);						//puts the selected bit high in the ADC_CHENAB register
controlSettings = channel << 2;					//shifts channel by two so it's in the correct position for CTRL register
controlSettings |= 0x01;						//or it with 1 to start the conversion
sendASIADC(0x00, controlSettings);				//selects the desired channel and starts the conversion
delay(10000);									//just wait for the conversion to complete
readASIData(channel);							//should try to read from the selected channel
sendASIADC(0x04, 0x00);							//clear the ADC_CHENAB register in preparation for the next read

}

void readASIData(unsigned char channel){
	UCB0CTL1 |= UCTXSTT; 						//send start sequence
		while(UCB0CTL1 & UCTXSTT){
			//wait for start sequence to be sent
		}

		UCB0TXBUF =	0x24;						//send i2c address of slave w/ lsb as 0 for write
		channel += 0x08;							//dac register
		UCB0TXBUF =	channel;					//send internal register number(for DAC reg)

		UCB0CTL1 |= UCTXSTT; 					//Send a start sequence again (repeated start)
				while(UCB0CTL1 & UCTXSTT){
					//wait for start sequence to be sent
				}

			UCB0TXBUF =	0x25;						//send i2c address of slave w/ lsb as 1 for read

			UCB0CTL1 |= UCTXSTP; 						//send stop sequence
				while(UCB0CTL1 & UCTXSTP){
					//wait for stop sequence to be sent
				}
}
#pragma vector = USCIAB0RX_VECTOR
__interrupt void USCIAB0RX_ISR(void){
	unsigned char data;
	if(IFG2 & UCA0RXIFG){
	data = UCA0RXBUF;							//reading should clear the RX int flag
	}
	else if(IFG2 & UCB0RXIFG){
		data = UCB0RXBUF;
	}
	transmitDecimal(data);					//sends data to terminal
	P1OUT |= BIT0;							//turns led on

	//UCA0TXBUF = UCB0RXBUF;						//sends adc data to terminal
	//IFG2 = 0x00;									//clears flags

}

  • Ignore the LED demo at the top of the code
  • There are several quirks in your code.
    1) When you want to send, you need to set the UCTR bit together with UCTXSTT. Else you tell the slave that you want to receive.
    2) after setting UCTR and UICTXSTT, you need to write your first byte you want to send to TXBUF. UCTXSTT won't clear (the ACK cycle is not completed) if UCTXIFG is set, so you eithe rhave to write something to TXBUF or set UCTXSTP (in case you only want to see whether the slave is there but do not want to send anything)
    3) before writing to TXBUF, you need to check whether TXIFG is set. sendASIADC() simply writes three times to TXBUF, each time overwriting the previous still unsent byte. SO in your code, move line 177 before 173. And when TXSTT has cleared, check for NACKIFG (in which case the slave didn't answer and you have to set TXSTP and exit). Then wait for TXIFG before writing the next byte to TXBUF or before setting UCTXSTP (else the stop is sent after the byte that is currently sending and not after the byte you just wrote to TXBUF and stil wait sin the buffer)
    4) in readASIData, before setting UCTXSTT in line 211, clear UCTR (and in line 202, add UCTR). And move line 207 before 203 (and check for NACKIFG and for TXIFG etc..
  • Your delay function will never return if time > 255.  The type of i in delay should match the input argument (unsigned char --> unsigned long).

  • Wow I really appreciate you taking the time to go through my code! Thanks so much!
  • Well, I guess that's why you posted it? :)
    You're welcome.

**Attention** This is a public forum