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.

SPI master does not re-trigger transmit interrupt on USCI_B0

Other Parts Discussed in Thread: MSP430FR5969

I'm using the MSP430FR5969 device as a SPI master and have an issue with the UCTXIFG using the following code:

#pragma vector=USCI_B0_VECTOR
__interrupt void HW_SpiServiceInterrupt(void)
{
	uint8_t c;

	// FOR DEBUG USE ONLY
	P1OUT |= BIT3;

	switch(__even_in_range(UCB0IV, USCI_SPI_UCTXIFG))
	{
		// No RX/TX interrupt pending
		case USCI_NONE:

			// Only here for optimization purposes
			break;

		// Receive interrupt pending
		case USCI_SPI_UCRXIFG:

			// Buffer the received SPI byte
			fbInsert(&spi_rx_buf, UCB0RXBUF);
			break;

		// Transmit interrupt pending
		case USCI_SPI_UCTXIFG:

			// Write next available out-bound byte to SPI transmit register
			if ( fbRemove(&spi_tx_buf, &c) )
			{
				// FOR DEBUG USE ONLY
				P1OUT |= (BIT5);

				UCB0TXBUF = c; 

				// FOR DEBUG USE ONLY
				P1OUT &= ~(BIT5);
			}

			// Clear the SPI transmit register if nothing to send and
			// disable the transmit interrupt to prevent wasted cycles
			else
			{
				// FOR DEBUG USE ONLY
				P1OUT |= (BIT4);

				UCB0TXBUF = 0; // ONLY WORKS IF THIS LINE INCLUDED

				UCB0IE &= ~UCTXIE;

				// FOR DEBUG USE ONLY
				P1OUT &= ~(BIT4);
			}
			break;

		default:
			break;
	}

	// FOR DEBUG USE ONLY
	P1OUT &= ~BIT3;

}

The code works sending the desired data contained in the spi_tx_buf (a circular buffer) and a null byte (0x00) after the buffer has emptied. The following diagram shows the DEBUG CODE stimulus for one byte (a 0x00 in this case) being sent plus the null byte. This cycle repeats when another byte is inserted into the spi_tx_buf and the UCTXIE is turned on again.

The problem occurs when the line UCB0TXBUF = 0; is removed from the ISR. Only the initial byte in the spi_tx_buf is transmitted, but nothing ever again. I've verified the UCTXIE is re-enabled (see signal P1.2 in figure).
 Below is the updated capture when the line UCB0TXBUF = 0; is removed:

Notice the extra pulses on the P1.2 probe which indicate the UCTXIE was re-enabled, yet the ISR is never invoked.

  • When reading UCB0IV, you clear the IFG bit of the reported interrupt, so no more interrupt is pending, and setting the IE bit again later has no effect. With writing 0 to TXBUF, you force another TX interrupt (which isn’t immediately handled due to the cleared IE bit). However, it causes another (superfluous) transfer.

     You should restructure your code as follow:
    You need to clear UCTXIE right before writing the last byte to TXBUF.
    So fbRemove should return false if there are no more bytes in the buffer after extracting the one it returns right now. (so ‘false’ means ‘the buffer is empty now’ rather than ‘the buffer was already empty and I don’t have a byte to return’)

    if(!fbRemove(…)) UCB0IE &=~UCTXIE;
    UCB0TXBUF = c;

    This way, you spare one unnecessary interrupt, TXIE gets cleared when there is nothing more in the buffer but the TXIFG of the last byte remains set then until TXIE is set again for new byte sin the buffer.

    Some more suggestion: you should integrate the fbRemove function directly into the ISR. Calling functions inside an ISR is inefficient - even for short functions, it leads to additional stack usage and perhaps unnecessarily saved and restored registers and hinders optimization.

  • Thank you Jens-Michael for your response. I found it to be clear and fully explains my issue.


    Your comment about my ISR containing a function call is also accurate and appreciated, but already known. I posted the non macro code version just for ease of understanding from the community. Of course you didn't know that and so another point for you!


    Thanks to you, I now have working code.

**Attention** This is a public forum