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.

MSP430F2618 SPI slave not syncing with master clock

Hi, I'm trying to have two duplicate MCUs communicate via SPI, with one as a master and the other as a slave. The master pulls the chip select line low, and then starts sending bytes (counts up to 127) on MOSI with 50 us intervals between bytes. Meanwhile, the Slave (shown below) waits for CS to go low before enabling SPI, at which point it starts sending bytes on MISO (it also counts up to 127 with 50 us intervals between bytes), but while the bytes coming from the master are correct, the bytes being sent by the slave seem to skip bytes in the sequence. Sometimes the intervals between bytes are regular, e.g., it sends 0x02, 0x04, 0x06, etc., but sometimes there doesn't seem to be any logical progression. The code below is for the slave, but I can also include the code for the master if that would be helpful. I've tried both with and without a 50 us delay between bytes on the slave side, but neither one works

First, could someone tell me if I'm implementing this correctly? Should CS enable SPI, or should I use some other method to sync master and slave? Should I include the delay between bytes on the slave side, or should I rely on the absence of a clock signal to dictate the delays between bytes? Perhaps using an edge trigger for the CS line would be better? Any help would be greatly appreciated. Thank you

#include "io430.h"

void initialize_pins();
void initialize_clocks();
void initialize_SPI();
unsigned char write_byte0(unsigned char byte);

int j = 0;
unsigned char RX[4] = {0x00}, AFC_data[128] = {0x00};

void main( void )
{
WDTCTL = WDTPW + WDTHOLD;
__bis_SR_register(GIE); // General interrupt enable

initialize_pins();
initialize_clocks();
initialize_SPI();

while(1)
{
if (P2IN & 0x02) // Wait for Chip select line to go low
{
UCB0CTL1 |= UCSWRST;
}
else
{
UCB0CTL1 &= ~UCSWRST; // Enable SPI
//__delay_cycles (50);
write_byte0(j);
j++;
if (j == 127)
j = 0;
}
}
}

void initialize_clocks()
{
// Turning on XTAL2
BCSCTL3 |= XT2S1; // Allow external 3-16 MHz crystal
BCSCTL1 &= ~XT2OFF; // Turn on XT2

// Displaying SMCLK (from XTAL2) on P1.4:
BCSCTL2 |= SELS; // Source SMCLK from XT2
BCSCTL2 |= DIVS1; // Divide SMCLK by 4 = 4MHz
}

void initialize_SPI()
{
UCB0CTL1 |= UCSWRST; // Hold MCU in reset state during config
UCB0CTL0 |= UCCKPH; // Rising edge
UCB0CTL0 |= UCMSB; // MSB first
UCB0CTL0 &= ~UCMST; // Slave
UCB0CTL0 |= UCSYNC; // Synchronous
P3SEL |= 0x0E; // P3.1,2,3 Select MOSI, MISO, SCLK
UCB0CTL1 &= ~UCSWRST; // Release MCU reset state

}

unsigned char write_byte0(unsigned char byte) // B0: SPI to EBM
{
while (IFG2 & UCB0TXIFG == 0); // Wait for TX buffer to clear
UCB0TXBUF = byte; // Send byte of CMD0
while (IFG2 & UCB0RXIFG == 0); // No hanging w/SPI b/c MCU waits for 8 clock pulses, not data
return (UCB0RXBUF); // Read in MISO data
}

  • You could connect the CS to the slave's STE and use four-pin mode, then you don't have to enable/disable it manually.

    With SPI, transmitting and receiving happens at the same time, whenever the master runs the clock. You have to put a byte into TXBUF before the transfer. TXIFG tells you when TXBUF is empty.

    You should wait for TXIFG and RXIFG concurrently (or use an actual interrupt handler) so that the order of these events does not matter.
  • Thanks for the reply, but last night I was able to get the code sorta working- part of my problem was that the slave wasn't latching to the falling edge of the CS line- you can see in my slave code that inside the while(1) loop, it effectively says if (!CS), send 128 bytes. I had hoped that this line:

    while (IFG2 & UCB0TXIFG == 0); // Wait for TX buffer to clear

    would cause the MCU to wait for a new set of 8 clock pulses from the master before incrementing "j" and sending the next byte, but apparently the TX buffer can be cleared even if the byte has not been clocked out of the shift register, I guess.

    So I changed the code to set a "ready" flag high if CS is high. If CS is low AND the flag is high, the code sets the flag low and transmits a single byte.

    This method works, but two things- first, I'm still occasionally getting byte errors- once every 100 or so bytes, a byte will be duplicated, (e.g. ...89, 90, 91, 91, 92...) and second, this kind of defeats the purpose of using a clock signal to trigger transmission because I have to cycle the CS line every time I want to send a single byte. I tried sending the entire 128 bytes after a latch, but I got the same garbled mess that I was getting previously. Here is the relevant part of the updated slave code followed by the relevant part of the master code. Thanks again

    Slave:

    while(1)
    {
    if (P2IN & 0x02) // Wait for Chip select
    {
    UCB0CTL1 |= UCSWRST;
    ready = 1;
    }
    else
    {
    if (ready)
    {
    ready = 0;
    UCB0CTL1 &= ~UCSWRST; // Enable SPI
    write_byte0(j); // Write a single byte
    j++;
    if (j == 127)
    j = 0;
    }
    }
    }

    Master:

    void main( void )
    {
    WDTCTL = WDTPW + WDTHOLD;
    __bis_SR_register(GIE); // General interrupt enable

    initialize_clocks();
    initialize_SPI();
    initialize_pins();

    while(1)
    {
    for (int j = 0; j < 128; j++)
    {
    P4OUT |= 0x01; // Deselect slave
    __delay_cycles (50); // Delay 1 ms
    P4OUT &= ~0x01; // Select slave
    __delay_cycles (50); // Delay 1 ms
    AFC_data[j] = write_byte1(j); // Send a byte of data
    __delay_cycles (50); // Delay 1 ms
    }
    }
    }

  • The tramsmit shift register and TXBUF indeed are two separate registers (see figure 16-1 in the User's Guide).

    The problem with SPI is that all the timing is controlled by the master, so the slave has to fill TXBUF at the right time, before the master does the transfer. (SPI really is designed for hardware implementations.)

    You could try to add a separate signal line for the slave to tell the master that it is ready, but then it might be a better idea to use I²C instead.

**Attention** This is a public forum