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 mode for USART0 using a master and slave

Other Parts Discussed in Thread: MSP430F149

I am using 2 MSP430F149 processors and they are communicating to each other via SPI USART1.  I am having troubles sycronizing the communication without adding delays.  See the dalay added below to avoid receiving duplicate data.  Do I have to enable the receive interrupt on the master then poll (IFG2 & URXIFG1) ?

Also what is the difference between "(U0TCTL & TXEPT)" and "(U0IFG & UTXIFG0)"?  When do you use one vs the other?

Thanks for any help...

Tom

--- #1 Master Processor
void usart0_init()
{
  P3SEL &= ~0x30;
  P3SEL |=0x0E;   //P3.1 (SIMO), P3.2 (SOMI), P3.3 (UCLK)
  P3DIR |=0x0B;   //P3.0 , P3.1 (SIMO), and P3.3 (UCLK) are outputs
  P3DIR &= ~0x04;  //P3.2 (SOMI) is an input

  U0CTL = SWRST;  //Must set SWRST bit 1st before changing
  U0CTL = CHAR + SYNC + SWRST; //8-bit char length, SPI mode
  U0TCTL = 0;   //reset U0TCTL
  U0CTL |= MM;   //USART is Master
  U0TCTL = SSEL1;  //Select SMCLK as BRCLK
  U0TCTL |= STC;  //3-pin SPI mode
  P3OUT |= 0x01;  //make sure cs line starts high
  U0MCTL = 0;
  ME1 = USPIE0;   //enable the SPI mode for USART0
  U0BR0=0x16;   //baudrate
  U0BR1=0;   //baudrate
  U0CTL &= ~SWRST;  //Reset SWRST bit to activate USART
}

--- #1 Master Processor start RECEIVE ---
  if(AUX_PROC_RX_HIGH)   //Port Interrupt occurred
  {
    SLAVE_ENABLE;
    for(i=0;i<length;i++)
    {
      usart0Putch(DUMMY);
      while(!(U0TCTL & TXEPT));
      brief_pause(100);   //WHY is a delay needed?
      if(i == 0)
        cmd = U0RXBUF;
      else
        rec[i - 1]=U0RXBUF;
      if(!AUX_PROC_RX_HIGH)
      {
 usart0Putch(DUMMY);
        while(!(U0TCTL & TXEPT));
        rec[i]=U0RXBUF;
        break;
      }
    }
    SLAVE_DISABLE;
    *count = i;
  }
--- #1 Master Processor end RECEIVE ---
 
--- #1 Master Processor
 int usart0Putch(char ch)
 {
   while (!(U0IFG & UTXIFG0))            // wait for TX buffer to empty
     continue;                           // also either WDOG() or swap()
 
   TXBUF0 = ch;
 
   return (uint8_t)ch;
 }

 

--- #2 Slave Processor
void qspi0_init()
{
  P3SEL |=0x0E;   //P3.1 (SIMO), P3.2 (SOMI), P3.3 (UCLK)
  P3DIR |=0x0B;   //P3.0 , P3.1 (SIMO), and P3.3 (UCLK) are outputs
  P3DIR &= ~0x01;  //Make STE0 an input
  P3SEL |= 0x01;  //Activate STE0
  P3DIR &= ~0x04;  //P3.2 (SOMI) is an input
  U0CTL = SWRST;  //Must set SWRST bit 1st before changing
  U0CTL = CHAR + SYNC + SWRST; //8-bit char length, SPI mode
  U0MCTL = 0;
  ME1 = USPIE0;   //enable the SPI mode for USART0
  U0BR0=0x02;   //baudrate
  U0BR1=0;   //baudrate
  U0CTL &= ~SWRST;  //Reset SWRST bit to activate USART
  IE1 |= URXIE0;
}


--- #2 Slave Processor start SEND ---
  timerX(1000);  //setup timeout
  t = usart0Putch(cmd); //send the cmd first

  READY_TO_TX;      //wake up the main processor with port interrupt
  while(timerXDelay)
  {
    timer = 0;
    do
    {
     t = usart0Getch();
    }while((t == -1) && (timer++ < 4000));
    if(t != -1)
     break;
  }
  for(i=0;i<length;i++)
  {
    usart0Putch(send[i]);
    timer = 0;
    do
    {
      t = usart0Getch();
    }while((t == -1) && (timer++ < 4000));
  }
  TX_DONE;
--- #2 Processor end SEND ---


--- #2 Slave Processor
int usart0Putch(char ch)
{
  long timer;
  timer = 0;
  while (!(U0IFG & UTXIFG0) && (timer < 10000))            // wait for TX buffer to empty
  {
     timer++;
  }
  TXBUF0 = ch;
  if(timer == 10000)
     return -1;
    
  return (uint8_t)ch;
}

--- #2 Slave Processor
int usart0Getch(void)
{
  uint8_t ch;

  if (usart0_rx_insert_idx == usart0_rx_extract_idx) // check if character is available
    return -1;

  ch = usart0_rx_buffer[usart0_rx_extract_idx++]; // get character, bump pointer
  usart0_rx_extract_idx %= USART0_RX_BUFFER_SIZE; // limit the pointer
  return ch;
}

  • Hi Tom,

    The USART transmitter uses a one-byte buffer and a one-byte shift register.  After data moves from the buffer into the shift register, the USART sets TXIFG.  If no data is in the buffer when it's time to move something into the shift register, the USART sets TXEPT.  The USART also clears these flags at appropriate times, such as when you put data into the buffer or when the USART moves data from the buffer into the shift register.

    Effectively:

    TXIFG = buffer empty

    TXEPT = shift register empty (entire transmitter empty)

    The reason you need the delays in your code seems to be that the slave is waiting to receive each character before it puts the next character into the transmitter.  When the master starts the next byte transfer before the slave has something new in TXBUF, then the old byte in TXBUF just gets sent again -- from the "empty" TXBUF.

    I can't tell from your code whether you are trying to have meaningful full-duplex exchanges.  If you don't care about the data coming from the master while the slave is sending meaningful data, then the slave ought to just watch TXIFG to keep the transmit buffer stuffed.

    Jeff

  • Thank you for your explanation of the different flags.  It made pefect sence but for some reason I could not get the transmit buffer stuffing to work.  I do not need full-duplex exchanges but the two processors need to send date to each other at different times.  So I removed the delay and wait for TXEPT and added a check for the RXIFG flag.  Does this look right?

    Thanks

    Tom

     

    --- #1 Master Processor start RECEIVE ---
      if(AUX_PROC_RX_HIGH)   //Port Interrupt occurred
      {
        SLAVE_ENABLE;
        for(i=0;i<length;i++)
        {
          usart0Putch(DUMMY);
          //while(!(U0TCTL & TXEPT)); -- REMOVED, rely on usart0Putch() to wait for buffer to empty
          //brief_pause(100);   //WHY is a delay needed?  -- REMOVED
           while(!(U0IFG & URXIFG0));  --  Added to wait for new byte

     

          if(i == 0)
            cmd = U0RXBUF;
          else
            rec[i - 1]=U0RXBUF;
          if(!AUX_PROC_RX_HIGH)
          {
            usart0Putch(DUMMY);
            //while(!(U0TCTL & TXEPT));  -- REMOVED, rely on usart0Putch() to wait for buffer to empty

           while(!(U0IFG & URXIFG0));  --  Added to wait for new byte

     

           rec[i]=U0RXBUF;
            break;
          }
        }
        SLAVE_DISABLE;
        *count = i;
      }
    --- #1 Master Processor end RECEIVE ---
     
    --- #1 Master Processor
     int usart0Putch(char ch)
     {
       while (!(U0IFG & UTXIFG0))            // wait for TX buffer to empty
         continue;                           // also either WDOG() or swap()
     
       TXBUF0 = ch;
     
       return (uint8_t)ch;
     }

  • Tom,

    It looks like your changes won't really fix the problem.  Is that your experience?

    I think you need to change your slave code to keep up with the master.  Your application seems to use the SPI bus in a half-duplex way.  (That's pretty common.)  Your slave's job during slave->master transfers is to keep the TXBUF stuffed.  The master can go as fast or as slow as it wants, so the slave simply must keep up.

    When the master transmits a byte, it also receives a byte.  Whether the slave sent one or not!  In your case, your slave MSP430 will always send one, even if there's "nothing" to send.  It will just send whatever is in the slave's TXBUF.

    The current problem with your code seems to be that the slave is waiting for receive events during the transmit sequence.  Instead, the slave should be waiting for the TXBUF to become available again during the transmit sequence.

    Jeff

**Attention** This is a public forum