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.

MSP430F2274 Master Receive No Interrupts

I have an application which uses SPI on USCIA and I2C on USCIB. Right now, I am only exercising the I2C functionality. There are five devices on the I2C which I need to poll. I have set an interrupt using Timer B to start the I2C transactions. The first three of the five devices aren't on the current board, and should thus respond with NACK interrupts, which I have enabled. The latter two should transfer data as they are sensors on the same board. I have put the SDA and SCL pins on an oscilloscope. Here's what I see...

For the devices which aren't on the board, I see a start, a NACK, and then an immediate start, NACK, etc., forever, immediately. What I mean by immediately is that it just runs forever. I don't ever see an interrupt in my code. Pausing the code, I see that the NACK IE and IFG are both set, but the ISR I declared using the pragma doesn't fire.

For the devices which are on the board, I see a start, a single byte transferred, a NACK, followed immediately by another start, single byte, NACK, START, etc. ad infinitum. I never receive an interrupt here either. RXIE is set. This behavior sounds very similar to USCI30 from the Errata, except that the interrupt vector never fires giving me a chance to clear RXBUF.

I desperately need to use interrupts to minimize the amount of time spent on I2C transactions.

  • Jonathan,

    Are you able to share any source code for review? Are you sure that UCNACKIE and UCB0RXIE are enabled in the UCB0I2CIE and RXIE registers, respectively, as well as global interrupts? Also make sure that your ISR vectors are correct.

    Regards,
    Ryan
  • Sorry, was off Friday. I'm fairly certain I'm initializing everything correctly, but I hope not. Below are the relevant snippets...

    I initialize all peripherals in main.c:

    I2c_init( &i2c, i2c_buf );
    Spi_init( &spi, spi_buf );
    TimerB_init();
    __bis_SR_register( GIE );
    

    TimerB prompts the peripheral polling every 100ms:

    #pragma vector = TIMERB1_VECTOR
    __interrupt void TimerB_isr(void)
    {
    	extern struct I2c_t i2c;
    	extern uint8_t cI2c;
    	cI2c++;
    	if ( cI2c == 3 ) {
    		i2c.ix = 0;
    		i2c.cb = 0;
    		UCB0I2CSA = I2C_SLAVE_ADDRESS[i2c.ix];
    		UCB0CTL1 |= UCTXSTT;
    		cI2c = 0;
    	}
    }

    I have set a breakpoint at the beginning of this ISR:

    // ISR for receive
    // UCBxTXIFG and UCBxRXIFG from USCI_Bx and UCAxTXIFG from USCI_Ax share a vector
    #pragma vector = USCIAB0TX_VECTOR
    __interrupt void I2c_irqRx()
    {
    	extern struct I2c_t i2c;
    	// Put the byte in the I2C buffer
    	*(i2c.pBuf + I2C_BUFFER_OFFSET[ i2c.ix ] + i2c.cb) = UCB0RXBUF;
    	i2c.cb++;
    
    	// If this is the last byte, generate a stop condition, reset the counter, go to the next index
    	if ( i2c.cb == I2C_TRANSACTION_BYTES[ i2c.ix ] ) {
    		i2c.cb = 0;
    		i2c.ix++;
    		// Check to see if we need to start the next transaction
    		if ( i2c.ix != I2C_NUM_SLAVES ) {
    			UCB0I2CSA = I2C_SLAVE_ADDRESS[ i2c.ix ];
    			UCB0CTL1 |= UCTXSTT;
    		// If we're done, generate a stop, and copy all data to the destination buffer
    		} else {
    			UCB0CTL1 |= UCTXSTP;
    		}
    	}
    }

    And this one:

    #pragma vector=USCIAB0RX_VECTOR
    __interrupt void Spi_irq()
    {
    	extern struct Spi_t spi;
    	extern struct I2c_t i2c;
    
    	if ( IFG2 & UCA0RXIFG ) {
    		spi.ix = UCA0RXBUF;
    
    		// If this is the first byte, copy all data from the I2C buffer
    		if ( spi.ix == 0 ) {
    			Spi_getMessage( &spi, &i2c );
    			spi.ix++;
    
    		// If this is one of the message bytes, copy that to the I2C buffer
    		} else if ( spi.ix < SPI_NUM_MSG_BYTES-1 ) {
    			UCA0TXBUF = *(spi.pBuf + spi.ix);
    			spi.ix++;
    
    		// otherwise, send the default byte
    		} else {
    			spi.ix = 0;
    			UCA0TXBUF = SPI_DEFAULT_BYTE;
    		}
    	}
    
    	if ( UCB0STAT & UCNACKIFG ) {
    		// We can only get here by receiving a NACK, in which case we'll just move on to the next transaction
    		// (assuming we're not on the last slave)
    		i2c.ix++;
    		i2c.cb = 0;
    		if ( i2c.ix != I2C_NUM_SLAVES ) {
    			UCB0I2CSA = I2C_SLAVE_ADDRESS[ i2c.ix ];
    			UCB0CTL1 |= UCTXSTT;
    		} else {
    			UCB0CTL1 |= UCTXSTP;
    		}
    	}
    
    }

    Neither breakpoint fires, despite NACKIFG being set, as the attached word document shows.

    I2C Interrupt Screen Shots.docx

    #pragma vector=USCIAB0RX_VECTOR

    __interrupt void Spi_irq()

    {

    extern struct Spi_t spi;

    extern struct I2c_t i2c;

    if ( IFG2 & UCA0RXIFG ) {

    spi.ix = UCA0RXBUF;

    // If this is the first byte, copy all data from the I2C buffer

    if ( spi.ix == 0 ) {

    Spi_getMessage( &spi, &i2c );

    spi.ix++;

    // If this is one of the message bytes, copy that to the I2C buffer

    } else if ( spi.ix < SPI_NUM_MSG_BYTES-1 ) {

    UCA0TXBUF = *(spi.pBuf + spi.ix);

    spi.ix++;

    // otherwise, send the default byte

    } else {

    spi.ix = 0;

    UCA0TXBUF = SPI_DEFAULT_BYTE;

    }

    }

    if ( UCB0STAT & UCNACKIFG ) {

    // We can only get here by receiving a NACK, in which case we'll just move on to the next transaction

    // (assuming we're not on the last slave)

    i2c.ix++;

    i2c.cb = 0;

    if ( i2c.ix != I2C_NUM_SLAVES ) {

    UCB0I2CSA = I2C_SLAVE_ADDRESS[ i2c.ix ];

    UCB0CTL1 |= UCTXSTT;

    } else {

    UCB0CTL1 |= UCTXSTP;

    }

    }

    }

  • Ignore everything after the word document. Didn't show in my post...
  • Jonathan,

    Thank you for the code. Could you also post your I2c_init and Spi_init functions?

    Regards,
    Ryan

  • void I2c_init( struct I2c_t* const p, uint8_t* pBuf ) { p->ix = 0; p->cb = 0; p->pBuf = pBuf; // The recommended USCI initialization or reconfiguration process is: // 1. Set UCSWRST (BIS.B #UCSWRST,&UCxCTL1) // 2. Initialize all USCI registers with UCSWRST=1 (including UCxCTL1) // 3. Configure ports. // 4. Clear UCSWRST via software (BIC.B #UCSWRST,&UCxCTL1) // 5. Enable interrupts (optional) via UCxRXIE and/or UCxTXIE // 1. Set UCSWRST (BIS.B #UCSWRST,&UCxCTL1) UCB0CTL1 |= UCSWRST; // Enable SW reset // 2. Initialize all USCI registers with UCSWRST=1 (including UCxCTL1) UCB0CTL0 |= UCMST + UCMODE_3 + UCSYNC; // I2C Master, synchronous mode UCB0CTL1 |= UCSSEL_2; // Use SMCLK, keep SW reset UCB0BR1 = 1; UCB0BR0 = 0; // 3. Configure ports. P3SEL |= 0x06; // 4. Clear UCSWRST via software (BIC.B #UCSWRST,&UCxCTL1) UCB0CTL1 &= ~UCSWRST; // 5. Enable interrupts (optional) via UCxRXIE and/or UCxTXIE UCB0I2CIE |= UCNACKIE; IE2 |= UCB0RXIE + UCB0TXIE; } void Spi_init( struct Spi_t* const p, uint8_t* const pBuf ) { p->ix = 0; p->pBuf = pBuf; /* * Steps to initialize SPI are * 1. Set UCWRST * 2. Initialize all USCI registers with UCSWRST=1 (includeing UCxCTL1) * 3. Configure ports * 4. Clear UCSWRST via software * 5. Enable interrupts (optional) via UCxRXIE and/or UCxTXIE */ // 1. Set UCWRST UCA0CTL1 |= UCSWRST; // **Put state machine in reset** // 2. Initialize all USCI registers with UCSWRST=1 (including UCxCTL1) UCA0CTL0 |= UCCKPL + UCMSB + UCMODE_2 + UCSYNC; // 3. Configure ports // Set select bits to secondary functions P3SEL |= 0x39; ADC10AE0 &= ~BIT5; // 4. Clear UCSWRST via software // Clock polarity high, MSB first UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine** // 5. Enable interrupts (optional) via UCxRXIE and/or UCxTXIE IE2 |= UCA0RXIE + UCA0TXIE; // Enable USCI_A0 RX interrupt // Place default byte in Tx register UCA0TXBUF = SPI_DEFAULT_BYTE; }

    I think I might have had fewer interrupts enabled at one point and then enabled more, trying to get something promoted to the vector.

  • I incorrectly configured TimerB, which was issuing interrupts at a rate faster than the SCL period, which apparently makes it difficult to service any other interrupts.

**Attention** This is a public forum