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.

MSP430 g2553 as SPI slave. RX ok with even at 2.3MHz, TX starts failing at 500KHz

Other Parts Discussed in Thread: MSP430G2553

Hi,

I've started working on launchpad and MSP430 (g2553) quite recently, so I'm still learning the basics, and I appreciate any tips you might have.
I've connected my launchpad to a Freescale i.mx233 linux host, and working on a communication protocol between the devices now, and would like to get some advice. 
imx only has half-duplex HW SPI, so my plan is to always initiate connections from imx side, using RX interrupt on MSP. If MSP needs to initiate a conversation to the main CPU, it will pull a separate pin up towards imx, to raise an interrupt.

Currently, I am able to send 1 message from [imx] master to MSP, and also read the message back. But only if I use speed no higher than 490KHz. Actually MSP will successfully read the incoming data (though, I haven't tried longer than 20 bytes) even at 2.3MHz (I checked memory content with debugger), but TX won't be able to shift the bytes quick enough  after 500KHz.. In the code below while (!(IFG2 & UCA0TXIFG)) //is this wrong?

Can I ask you to take a look at my code, and point out possible stupidities I am doing, which might cause this ? I'm still not entirely clear on how and which clocks I'm supposed to setup in the beginning. My current understanding is that when in slave mode, MSP shouldn't need any other clocks set up than DCOCTL and BCSCTL1.
- But I can't understand why 16MHz processing is not able to handle even 500KHz TX..
- Is it stupid to use USCIAB0RX_VECTOR for also TX stuff ?
- Isn't USCIAB0RX_VECTOR triggered at every clock pulse ? If I would enable TX interrupts, same clock would trigger 2 interrupts ? I was thinking I should try put everything to 1 interrupt handler, as not to burden the uC
- How about __bis_SR_register(GIE); is that a good mode for the uC to be in ?  

My code:
#include "msp430g2553.h"
#include <string.h>
#include "stdarg.h"

char cmdbuf[20];
char cmd_index=0;
int size_received=0;
int incoming_data_size=0;
uint8_t upper_byte= 0;
uint8_t lower_byte= 0;
int loop = 0;
int sending = 0;

void main(void)

{
WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer
DCOCTL = CALDCO_16MHZ;    // DCO frequency set to 16 MHz
BCSCTL1 = CALBC1_16MHZ;   // DCO range set to 16 MHz

P1DIR |= BIT6; //p1.6 output (green led)
P1DIR |= BIT0; //p1.0 output (red led)
P1SEL = BIT1 + BIT2 + BIT4;
P1SEL2 = BIT1 + BIT2 + BIT4;

//Turn off pullups from P1, needed?
P1REN = 0;

while (P1IN & BIT4);    // If clock sig from mstr stays low,
                        // it is not yet in SPI mode
flash_spi_detected();   // just blink leds

UCA0CTL1 = UCSWRST;     // **Put state machine in reset**
UCA0CTL0 |= UCSYNC;     //Sync mode = SPI
UCA0CTL0 |= UCMSB;      //MSB first
UCA0CTL1 &= ~UCSWRST;   // **Initialize USCI state machine**
IE2 |= UCA0RXIE;        // Enable USCI0 RX interrupt
UCA0TXBUF = 0x00;
__bis_SR_register(GIE); // Enable interrupts
}

#pragma vector=USCIAB0RX_VECTOR
__interrupt void USCI0RX_ISR(void)
{
  if (!sending) {

    //First 2 bytes are the incoming transfer size
    if (size_received != 2) {
      if (size_received == 1) {
        lower_byte = UCA0RXBUF;
        size_received++;
        incoming_data_size = (upper_byte << 8) | lower_byte;
      } else {
        upper_byte = UCA0RXBUF;
        size_received++;
      }
    } else {
      cmdbuf[cmd_index] = UCA0RXBUF;
      cmd_index++;

      //Transfer complete
      if (cmd_index == incoming_data_size) {
        sending = 1;
        indidate_msg_read(2); //blink leds

        //FIXME temporary increment (otherwise first byte will be 0x00 set in main)
        while (!(IFG2 & UCA0TXIFG));
        UCA0TXBUF = cmdbuf[0];
        loop++;
     }
   } //receiving part
   //now every clock push new byte to TXBUF
  } else {
    while (!(IFG2 & UCA0TXIFG));
    UCA0TXBUF = cmdbuf[loop];
    loop++;
  }
} //RX interrupt 

Please don't hesitate to point out any stupidities I might have conjured here :)

  • I didn't look at your code, but let's do some simple math first....

    500KHz clock rate at 16MHz CPU speed is only 32 CPU clock cycles per bit. 8 bits are in a byte, so 32 * 8 = 256 CPU clock cycles per byte.

    MSP430 instructions do not execute in  single clock cycle. Depending on variable storage location (stack vs register vs static memory location) will make some C instructions take longer as the pointers are de-referenced and loaded into registers.

    I would recommend looking at using the DMA engine to feed the UART TX buffer. Likewise for the RX buffer.

    Ok... just glanced at your code....you shouldn't be looping to fill the TX buffer inside your RX ISR for starters.....

  • Brian Boorman said:
    .you shouldn't be looping to fill the TX buffer inside your RX ISR for starters.....

    I agree. busy-waiting inside an ISR is one of the worst things one can do. Unfortunately, one of the TI demo codes (for UART) does it. and people adopt this.
    However, it isn't even necessary (in the said UART demo code and in this SPi code).
    Since both sides run at the same baudrate, you can be sure that when you received a new byte, the sending of the previous is done.
    And if not, for some reason (usually slow code compared to baudrate) there's nothing you can do against it: the input bytes keep coming and if you wait for the output to be ready, the input overflows.

    The correct way would be to use input and output buffers and separate receiving from sending.

  • Hi Brian and Jens-Michael,

    Thanks for your comments!
    Brian, don't take this the wrong way, but with your math (256CPU cycles for each byte) also 490KHz shouldn't work then, shouldn't it ? This is indeed what I mean that I'm a newbie in this arena, and that math for example now doesn't align well in my head :)

    Thank you for pointing out the mistake of looping TX buffer in RX ISR.
    Can you share some thoughts on how you would implement this kind of transfer ? 
    I mean specifically, my transfer always has to be initiated by master, and it can only be half duplex. So when MSP wants to send something, master always has to tell it "ok, let's go", something like this:
    Message initiated by slave:
    master                       slave(MSP)
           <-------------- indicate msg waiting (30 bytes of data waiting, MSP prepares 2 byte size package)
    [isr handling]
    READ 2 bytes  -------------->
    [prepare buffer] 
    READ 30 bytes -------------->
    [process]

    Message initiated by master (15 bytes waiting for transfer: 
    master                       slave(MSP)
    SEND 2 bytes -------------->  
                              [prepare buffer]
    SEND 15 bytes ------------->
                      [process]

    This logic was the reason I thought I'll do everything in RX ISR, since the only message actually "initiated by MSP" is a separate line that is an GPIO on master end, and ISR will initiate another message sequence..

    Can you guys also point out me some information about how can I use DMA engine to feed TX and RX buffers ? Currently I have been allocating everything to the stack, would you advise not to do it ? By static memory location, do you mean some area in the flash ? Is that a smart solution for this kind of temporary use ?

    Is there some good example on using these input & output buffers ?
    In future I might need to use as much as 1KB of memory for these messages, so I realize that the current solution of g2553 might not work, but for now I would like to learn the basics with this chip..

    Thanks for your advice. 

  • Juha Lumme said:
    Brian, don't take this the wrong way, but with your math (256CPU cycles for each byte) also 490KHz shouldn't work then, shouldn't it ?

    Each byte, not each bit. 16MHz/256 = 62500 bytes per second = 500k bit per second.

    Being an SPi slave is a real pain. "When the call comes, be ready" is the main rule. Else you'll have to implement some sort of data handshake. Liek SD cards do: after pullign the SD cards chip select, you may send an instruciton and you'll either get an 'ok' or a 'busy' answer. In which case you repeat the instruction. This way, a busy slave may pre-load its TX buffer by a busy message as soon as it detects the chip select.

    What about this protocol:

    CS low.
    Master sends dummy byte and receives slave state (data waiting/busy/whatever).
    Master sends command (read data/write data). Slave answers with status byte.
    If slave is to be send data, master keeps sending dummy bytes and checking the status until it reads 'data is coming'.

    etc.

    This way, the slave knows beforehand what to send (its status) and can stuff TXBUF in time as soon as communication is initiated by chip select (which can even trigger a port pin interrupt which is then setting up the status byte). It can even delay the master until the data is ready.
    More, once you wrote a byte to TXBUF, it will be sent over and over again if you don't writ eanything new to TXBUF. So you don't need to constantly write 'busy' to TXBUF.

**Attention** This is a public forum