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.
While I have SPI working correctly on the MSP430FR5969, there are a few intervals that seem much larger than expected. The following logic analyzer capture is for 3 wire SPI with a software controlled CS pin. It is transmitting 0x4F, 0x00, and 0x25 (correctly). For this test, MISO is left floating. SMCLK = 1 MHz.
I believe the issue is two-fold. First, after the CS pin is toggled and UCA1TXBUF is written to, there is a very long delay before data transmission starts. I know that I am not using DMA, but should the delay really be this large? Second, the delay between bytes also seems unusually large.
// Init CS pin (active low) P4DIR |= BIT3; P4OUT |= BIT3; // Configure SPI pins P2SEL1 |= BIT4; // USCI_A1 operation P2SEL1 |= BIT5 + BIT6; // USCI_A1 operation // Configure USCI_A1 for SPI operation UCA1CTLW0 = UCSWRST; // **Put state machine in reset** UCA1CTLW0 |= UCMST + UCSYNC + UCMSB; // 3-pin, 8-bit SPI master, MSB UCA1CTLW0 |= UCSSEL__SMCLK; // SMCLK UCA1BR0 = 0x02; // /2 UCA1BR1 = 0; // UCA1MCTLW = 0; // No modulation UCA1CTLW0 &= ~UCSWRST; // **Initialize USCI state machine** UCA1IE |= UCRXIE; // Enable USCI_A1 RX interrupt
The above code initializes the eUSCI module.
*SPI_CS_PORT &= ~SPI_CS_PIN; while (!(UCA1IFG & UCTXIFG)); // USCI_A1 TX buffer ready? UCA1TXBUF = buffer_read(&SPI_tx_buffer);
The above code is used to start SPI transmission.
#pragma vector=USCI_A1_VECTOR __interrupt void USCI_A1_ISR(void) { switch(__even_in_range(UCA1IV, USCI_SPI_UCTXIFG)) { case USCI_NONE: break; case USCI_SPI_UCRXIFG: if (buffer_count(&SPI_tx_buffer)) // Check TX byte counter { UCA1TXBUF = buffer_read(&SPI_tx_buffer); } else { *SPI_CS_PORT |= SPI_CS_PIN; SPI_busy = 0; __bic_SR_register_on_exit(LPM0_bits); // Wake-up CPU } SPI_rx_byte = UCA1RXBUF; UCA1IFG &= ~UCRXIFG; break; case USCI_SPI_UCTXIFG: break; default: break; } }
The above code is the interrupt routine.
The delay seems much longer than what it would take to execute the code in the service routines and transfer the data to and from the eUSCI buffer registers. Does anyone have any suggestions? Have I set up the eUSCI incorrectly? Even if it did take that long between bytes, why does it take so long for the first byte to be sent after writing to UCA1TXBUF?
Thank you for your help!
Thank you both for your help. Both answers helped me understand what was causing the delay. When only sending fixed values and simplifying the ISR, things speed up significantly. I do have two followup questions.
1. Is there a more efficient way to do the following?
while (!(UCA1IFG & UCTXIFG)); // USCI_A1 TX buffer ready?
2. When I have only the TX interrupt enabled, the delay is significantly less than when I have both TX and RX interrupts enabled. The first image is when only the TX interrupt is enabled.
The next image is when both TX and RX interrupts are enabled.
Is this increase really due to the interrupt being called twice per byte sent? It seems strange that the delay is increased every second byte. I had thought that since I am sending the next byte in the TX interrupt, the RX interrupt would not greatly slow things down. I am not doing anything in the RX interrupt (see code below). Is this because of the switch statement? If so, would it be better to use two if statements since I only care about two interrupts (RX and TX)? Is there anything else that can be done to remove this extra delay when an RX interrupt is needed? I know that I am nit-picking here, but I would definitely like to understand what is going on.
While I understand not using interrupts is the fastest way to go, my application requires many things to happen "at once", so I will be relying on interrupts to handle all communications. I will definitely be looking into using DMA to make things happen as fast as possible.
Thank you again for helping me clarify things!
Also, for anyone else who may stumble across this, here is an example of code with minimal delay between transmissions.
while (!(UCA1IFG & UCTXIFG)); // USCI_A1 TX buffer ready? UCA1IE |= UCTXIE; UCA1TXBUF = 0x55; // Send fixed data
Sending a fixed values removes any additional instructions that need to be run.
#pragma vector=USCI_A1_VECTOR __interrupt void USCI_A1_ISR(void) { switch(__even_in_range(UCA1IV, USCI_SPI_UCTXIFG)) { case USCI_NONE: break; case USCI_SPI_UCRXIFG: break; case USCI_SPI_UCTXIFG: UCA1TXBUF = 0x52; break; default: break; } }
The above interrupt routine does nothing except feed in another value to the TX buffer. This code is only for demo purposes, it doesn't do anything useful.
Yes, the increased delay between pairs of bytes does appear to be caused by the RXIFG being processed.
What's happening is that the first byte is loaded, then TXIFG fires almost immediately and the ISR loads the next byte. When the first byte has been sent/received you get RXIFG and the ISR starts handling that. While the ISR is running TXIFG goes high again, but is left pending until the ISR has finished from the last RXIFG. Unfortunately, before you get to the line where the ISR reads UCA1IV another byte is received. RXIFG has higher priority than TXIFG, so it gets processed first and delays the next transmit.
In other words, your interrupts are forced into a pattern of: "TX, TX, rx, rx, TX, TX, rx, rx..." rather than: "TX, TX, rx, TX, rx, TX, rx, TX, rx...". Having two rx in a row means the TXBUF is already empty, so the USCI needs to stall until the second rx and the subsequent TX complete before it can transmit again.
Just to give you some idea of where the time goes, here's a fragment of the disassembly with cycle counts:
naken430util - by Michael Kohn Web: http://www.mikekohn.net/ 6 (interrupt entry) 0x441a: 0x92a2 cmp.w #4, &0x05fe 4 0x441c: 0x05fe 0x441e: 0x2003 jne 0x4426 (offset: 6) 2 0x4420: 0x40b2 mov.w #0x0052, &0x05ee 5 0x4422: 0x0052 0x4424: 0x05ee 0x4426: 0x1300 reti 5 (interrupt exit)
That works out as 22 MCLK cycles per TX interrupt and 17 per RX interrupt. In each case there are 11 cycles of interrupt handling overhead. With the /2 divider selected the USCI transfers a byte every 16 MCLK cycles, so the CPU can't keep up.
Also note that when clearing SWRST, TXIFG is instantly set., So when setting TXIE, an interrupt will be triggered (if GIE is set too). So your write to TXBUF after setting TXIE will happen after the ISR has already written to TXBUF. Either have GIE clear , or let the ISR do all the writes to TXBUF, or clear TXIFG right before setting TXIE (in this case, a 'starting' write to TXBUF is required to get TXIFG ever set again for subsequent transfers)
Do you need a delay after you asserts nCS and before you start sending data, yes.
The SPI slave datasheet will specify, but for example the cc3000 needs 50uS
It now comes down on how to write your SPI driver.
I do NOT write the first byte to TX to start it, I let the IRQ write byte[0]..[n]
I instead I just enable its IRQ permission (line3 below)
At boot up I {bis.b #UCA0TXIFG,&IFG2 ; set IRQ flag}, but leave permission off so it's ready to go.
bic.b #nCSpin,&nCSport ; chip select low delay 50, R15 ; 50uS delay macro use r15 (plain dec.b R15 loop) bis.b #UCA0TXIE,&IE2 ; Enable USCI_A0 TX interrupt, IFG is already on. ...
IRQ mov.b @SpiPnt+,&UCA0TXBUF ; SpiPnt =R8, a write to BUF clears UCA0TXIFG dec.w &spiLen ; subtract the numbers of bytes to send if_z bic.b #UCA0TXIE,&IE2 ; Disable interrupt, IFG flag will still be set but on hold reti
**Attention** This is a public forum