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.

MSP430FR2355: SPI Master TX and RX to second MSP430 SPI Slave works for the first transmit receive and fails subsequent ones with an erroneous byte I believe due to USCI42 errata?

Part Number: MSP430FR2355


Tool/software:

Hi Forum,

I have two launch pads configured to run at 24Mhz connected via SPIsending a random 32 Byte packet from the master to the slave using half duplex transmit polling method, I think the code is falling victim to the USCI42 errata but not sure.  I have tried a bunch of different ways to resolve it with no luck.  

The master is configured as follows

#include "spi_master_config.h"
#include "common.h"
#include "intrinsics.h"
#include "utils.h"
#include <stdio.h>
#include <string.h>
#include <stdbool.h>


// Defines
void spi_master_Init() {

    P1SEL0 |= BIT1 | BIT2 | BIT3; // set 4-SPI pin as second function

    //Set P6.3 and P6.6 to output direction
    P6DIR |= BIT6;
    P6OUT |= BIT6;
  
    // For this example, CS is assumed to be on P3.1.
    P3DIR |= BIT1; // Set P3.1 as output
    P3OUT |= BIT1; // Deassert CS (active low)
  
    // Disable the GPIO power-on default high-impedance mode to activate
    // previously configured port settings.
    PM5CTL0 &= ~LOCKLPM5;
  
    UCB0CTLW0 |= UCSWRST; // **Put state machine in reset**
                          // 4-pin, 8-bit SPI master
    UCB0CTLW0 |= UCMST | UCSYNC | UCCKPL;
    // Clock polarity high, LSB
    
  
    UCB0CTLW0 |= UCSSEL__SMCLK; // Use SMCLK as clock source
    UCB0BR0 = 0x0C;  // Divide SMCLK by 24 1MBits
    UCB0BR1 = 0;
    UCB0CTLW0 &= ~UCSWRST; // **Initialize USCI state machine**
                            
}

uint8_t spi_master_Transfer(uint8_t *txbuff, uint8_t length)
{
  int i = 0;

  // Assert CS
  P3OUT &= ~BIT1;
  __delay_cycles(50);
  for (i = 0; i < length; i++) {
    while (!(UCB0IFG & UCTXIFG));      // Wait until TX buffer is ready
    UCB0TXBUF = txbuff[i];             // Transmit data

    // (Optional) If you want to flush the RX buffer:
    while (!(UCB0IFG & UCRXIFG));
    volatile uint8_t dummy = UCB0RXBUF;
  }

  // Wait until the SPI peripheral is no longer busy
  while (UCB0STAT & UCBUSY);

  __delay_cycles(50);
  // Deassert CS
  P3OUT |= BIT1;
  
  return i;
}

uint8_t spi_master_Receive(uint8_t *rxbuff, uint8_t length)
{
    int i = 0;

    // Assert CS
    P3OUT &= ~BIT1;
    __delay_cycles(50);

    // Ensure SPI is idle before starting
    while (UCB0STAT & UCBUSY);

    for (i = 0; i < length; i++) 
    {
        // Wait until the TX buffer is ready
        while (!(UCB0IFG & UCTXIFG));

        // Transmit a dummy byte to generate clock for receiving data.
        UCB0TXBUF = 0xFF;

        // Wait until a byte has been received.
        while (!(UCB0IFG & UCRXIFG));
        
        // Store the received byte into the buffer.
        rxbuff[i] = UCB0RXBUF;
      
    }

    // Wait until the SPI module is no longer busy (flush TX shift register)
    while (UCB0STAT & UCBUSY);

    __delay_cycles(50);  // Allow for a clean CS transition

    // Deassert CS
    P3OUT |= BIT1;

    return i;  // Return the number of bytes received
}

The Transmit and receive is like this:

      memset((void *)txbuff, 0, PACKET_BUFFER_MAX_SIZE);
      memset((void *)rxbuff, 0, PACKET_BUFFER_MAX_SIZE);

      randomizeData((void *)txbuff, PACKET_BUFFER_MAX_SIZE);

      spi_master_Transfer((void *)txbuff, PACKET_BUFFER_MAX_SIZE);
      __delay_cycles(120000);
      spi_master_Receive((void *)rxbuff, PACKET_BUFFER_MAX_SIZE);

      printf("\r\nTX Buffer:\r\n");
      printBuffer((void *)txbuff, PACKET_BUFFER_MAX_SIZE);
      printf("\r\nRX Buffer:\r\n");
      printBuffer((void *)rxbuff, PACKET_BUFFER_MAX_SIZE);

      send_receive_slave = false;

The SPI receive on the other board is setup using interrupts like this:

/* spi_slave.c
 *
 *  Created on: Dec 3, 2023
 *      Author: gvigelet
 */

#include "common.h"
#include "sys/cdefs.h"
#include "spi_slave_config.h"
#include <stdio.h>
#include <string.h>

volatile uint8_t spiBuffer[PACKET_BUFFER_MAX_SIZE];
volatile unsigned int pSpiSend;

void spi_slave_Init()
{

  P1SEL0 |= BIT4 | BIT5 | BIT6 | BIT7;      // set 4-SPI pin as second function

  UCA0CTLW0 |= UCSWRST;                     // **Put state machine in reset**
                                            // 4-pin, 8-bit SPI slave
  UCA0CTLW0 |= UCSYNC | UCCKPL | UCSTEM; // Synchronous mode, CPOL=1, CPHA=0
                                            // Clock polarity high, MSB

  UCA0CTLW0 |= UCSSEL__SMCLK; // Use SMCLK as clock source
  UCA0CTLW0 &= ~UCMST;        // Set as SPI slave
  
  PM5CTL0 &= ~LOCKLPM5;                     // Disable the GPIO power-on default high-impedance mode
                                            // to activate previously configured port settings
  UCA0TXBUF = 0x00;                         // set to ready (use start packet byte)
  pSpiSend = 0;
  memset((void *)spiBuffer, 0, PACKET_BUFFER_MAX_SIZE);
  UCA0CTLW0 &= ~UCSWRST;                    // **Initialize USCI state machine**
  UCA0IE |= UCRXIE;                         // Enable USCI_A0 RX interrupt

}


#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=USCI_A0_VECTOR
__interrupt void USCI_A0_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(USCI_A0_VECTOR))) USCI_A0_ISR (void)
#else
#error Compiler not supported!
#endif
{
    switch (__even_in_range(UCA0IV, 4))
    {
        case USCI_NONE:
            break; // No interrupt
        case USCI_SPI_UCRXIFG:
            spiBuffer[pSpiSend++] = UCA0RXBUF;
            if (pSpiSend >= PACKET_BUFFER_MAX_SIZE) {
                UCA0IE &= ~UCRXIE;  // Disable further RX interrupts.
                // Fill in temperature data into the last two bytes.
                //spiBuffer[PACKET_BUFFER_MAX_SIZE-2] = (uint8_t)(rawTemp >> 8);  // High byte
                //spiBuffer[PACKET_BUFFER_MAX_SIZE-1] = (uint8_t)(rawTemp & 0xFF);  // Low byte
                pSpiSend = 0;
                UCA0IE |= UCTXIE;   // Enable TX interrupt for transmission.
            }
            break;
        case USCI_SPI_UCTXIFG:
            if (pSpiSend < PACKET_BUFFER_MAX_SIZE) {
                UCA0TXBUF = spiBuffer[pSpiSend++];
            } else {
                UCA0IE &= ~UCTXIE;  // Disable TX interrupts.
                memset((void *)spiBuffer, 0, PACKET_BUFFER_MAX_SIZE);
                pSpiSend = 0;
                // Preload TXBUF with a default value for the next transaction.
                UCA0TXBUF = 0x00;  // Set this to whatever default is appropriate.
                UCA0IE |= UCRXIE;   // Re-enable RX interrupts for the next packet.
            }
            break;
        default:
            break;
    }
}

The first transfer works correctly then all subsequent transfers error with an erroneous first byte as shown in the image below, I have the push button initiating the transfer.

After the first transfer the UCB0TXBUF has 0xFF in there and the next transfer gets that as the first byte.  I haven't found a way to clear it, before the next transfer, resetting the peripheral didn't clear it? Is there a way to work around this issue or am I doing something incorrectly prior to subsequent transmissions?  Any help would be greatly appreciated.

--- George 

  • I have been able to get it to work correctly if i add the USCI reset in the ISR but seems like a bad way to do it 

            case USCI_SPI_UCTXIFG:
                if (pSpiSend < PACKET_BUFFER_MAX_SIZE) {
                    UCA0TXBUF = spiBuffer[pSpiSend++];
                } else {
                    UCA0IE &= ~UCTXIE;  // Disable TX interrupts.
                    memset((void *)spiBuffer, 0, PACKET_BUFFER_MAX_SIZE);
                    pSpiSend = 0;
                    // Preload TXBUF with a default value for the next transaction.
                    UCA0TXBUF = 0x00;  // Set this to whatever default is appropriate.
                    
                    UCA0CTLW0 |= UCSWRST;
                    UCA0CTLW0 &= ~UCSWRST;
    
                    UCA0IE |= UCRXIE;   // Re-enable RX interrupts for the next packet.
                }
                break;

  • In your slave code you set UCSTEM but that bit only applies in master mode. For 4 wire slave operation, which is what you seem to want, you have to use the UCMODEx bits.

    The USCI42 problem only crops up if you use that interrupt, but you don't.

  • >  UCA0IE |= UCRXIE; // Re-enable RX interrupts for the next packet.

    At this moment you have a stale UCRXIFG from the last dummy byte (0xFF) the master sent. That will trigger immediately, and you'll capture that 0xFF as the first Rx byte of the new transaction. I suggest you precede this with:

    > UCA0IFG &= ~UCRXIFG;  // Clear stale

    That's what your UCSWRST sequence is doing, but this way is a little less heavy-handed.

    -------

    Also, everything David said.

  • Thank you David, I was thing UCSTEM was enabling the slave select line for 4 wire, my mistake will definitely remove that.  Thanks for the information.

  • Thanks Bruce, I did try the clear the interrupt flag in a few different spots but most likely was doing it in the wrong spot, I will make that change and see how it goes.  

**Attention** This is a public forum