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.
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.
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;
}
}
}
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