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, LPM3, slave mode.

Other Parts Discussed in Thread: MSP430F5438, MSP430F5529, MSP430WARE

Hello,

I'm using the MSP430F5438. The peripheral in question is the SPI bus when configured as Slave and the uC is in LPM3 mode. When that happens, I cannot receive data from the master uC although the uC is waken by the ISR.

If the uC is in LPM1, then there is no problem. The uC is supposed to go into LPM, then wake up when a byte is received. Below is the configuration, ISR and a couple of processing routines.

 

//-------------------------------------------------------------------------
// Spi::Init() -- Initialize serial interface to MCU3.
//-------------------------------------------------------------------------

void Spi::Init()
{
   
    rxIdx=0; txIdx=0; usingPing=true; InTransit=false; appBuffPtr=0;
    bufferReady=false; state=sIdle;

    // Indicate "ready to use"
    pingBuffer[0] = 0;
    pongBuffer[0] = 0;
   
    // uC interface.
    P9SEL |=  BIT1 | BIT2 | BIT3;        // Select P9 for SPI
    P9DIR |=  BIT2;                         // SOMI (output)  
    P9DIR &= ~BIT1;                            // SIMO (input)
    P9DIR &= ~BIT3;                            // CLK_IN
 
    UCB2CTL1 |= UCSWRST;                    // Put state machine in reset
    UCB2CTL0 |= UCMSB + UCSYNC + UCCKPL;    // 3-pin, 8-bit SPI slave
                                            // Clock polarity high, MSB
   
    UCB2CTL1 |= UCSSEL_2;                   // SMCLK
    UCB2BR0 = 0x02;                         // /2
    UCB2BR1 = 0;                            //
    UCA2MCTL = 0;                           // No modulation
     
    UCB2CTL1 &= ~UCSWRST;                   // **Initialize USCI state machine**
    UCB2IE |= UCRXIE;                       // Enable USCI_B2 RX interrupt 
    UCB2IFG &= ~UCRXIFG;
    UCB2TXBUF= 0x00;                        // Idle.
}

 

//-------------------------------------------------------------------------
// USCI_B2 Interrupt Service Routing
//-------------------------------------------------------------------------

#pragma vector=USCI_B2_VECTOR
__interrupt void USCI_B2_ISR(void)
{               
    UserInterfaceMcu & PI = UserInterfaceMcu::Instance();   

    int code = __even_in_range(UCB2IV,4);
    switch(code)
        {
        case 0:                                 // Transmit ISR
            break;
        case 2:                                 // Receive ISR
            switch(PI.McuComm().state)
                {            
                case Spi::sIdle:
                    PI.McuComm().StartTxRxProcess();
                    break;
                case Spi::sTransmit: 
                    PI.McuComm().TransmitByte();
                    UCB2IFG &= ~UCRXIFG;        // To clear interrupt.
                    break;         
                case Spi::sReceive:
                    PI.McuComm().ReceiveByte();
                    break;
                default:
                    break;
                }
            break;
        case 4:
        default:
            break;
        }
   __low_power_mode_off_on_exit();
}

 

//-------------------------------------------------------------------------
// void StartReceiveProcess()
//-------------------------------------------------------------------------

void Spi::StartTxRxProcess()
{
    rxIdx = 0;
    txIdx = 0;

    unsigned char rxByte = UCB2RXBUF;

    if (rxByte == 0x00)            
        {
        if (InTransit)                      // Change variable name !!!
            {   
            Mutex m;
            state = sTransmit;
            TransmitByte();
            }
        }
    else
        {
        if (rxByte == cRqstToSend)
            {           
            Mutex m;
            state = sReceive;
            }
        }
}

//-------------------------------------------------------------------------
// TransmitByte()
//-------------------------------------------------------------------------

void Spi::TransmitByte()
{
    static unsigned char bytesRemaining = 0;
    switch (txIdx++)
        {
        case 0:
            while(!(UCB2IFG & UCTXIFG)) ;
            UCB2TXBUF = cRqstToSend;           
            break;
        case 1:
            while(!(UCB2IFG & UCTXIFG)) ;
            UCB2TXBUF = cPreamble;
            break;
        case 2:
            // ID
            while(!(UCB2IFG & UCTXIFG)) ;
            UCB2TXBUF = TxBuffer[txIdx - 3 ];           // 2 = rqst + preamble.
            break;
        case 3:
            // Length
            bytesRemaining = TxBuffer[txIdx - 3 ]-2;
            while(!(UCB2IFG & UCTXIFG)) ;
            UCB2TXBUF    = TxBuffer[txIdx - 3 ] + 2;    // Include header bytes. 
            break;
        default:
            if (bytesRemaining == 0)
                { 
                while(!(UCB2IFG & UCTXIFG)) ;
                UCB2TXBUF = 0x00;     
                Mutex m;
                state = sIdle;
                InTransit = false;
                }
            else
                {        
                bytesRemaining--;
                while(!(UCB2IFG & UCTXIFG)) ;
                UCB2TXBUF = TxBuffer[txIdx - 3 ]; 
                }
            break;
        }
}

//-------------------------------------------------------------------------
// ReceiveByte()
//-------------------------------------------------------------------------

void Spi::ReceiveByte()
{   //P11OUT ^= BIT0;
    static unsigned char bytesRemaining = 0;
    unsigned char rxByte = UCB2RXBUF;
    // At a minimum we already have the rqstToSend.
    switch (rxIdx++)
        {
        case 0:
            // Preamble
            if (rxByte!= cPreamble)
                {
                Mutex m;
                rxIdx = 0;
                state = sIdle;
                }
            break;
        case 1:
            //  ID
            if (usingPing)
                pingBuffer[rxIdx-2] = rxByte;
            else
                pongBuffer[rxIdx-2] = rxByte;
            break;
        case 2:
            // Pkt. Length
            if (usingPing)
                pingBuffer[rxIdx-2] = rxByte-2; // Strip the header contents.
            else
                pongBuffer[rxIdx-2] = rxByte-2;
            bytesRemaining = rxByte - 4;        // 4 is the already processed bytes.
            if (bytesRemaining > MaxSize)
                {
                // Something went wrong. Abort this transmission.
                ProcessBufferReady(false);
                }
            if (bytesRemaining == 0)            // This is a pkt w/o paylod.
                {
                ProcessBufferReady(true);
                }
            break;
        default:
            // Payload
            if (usingPing)
                pingBuffer[rxIdx-2] = rxByte;
            else
                pongBuffer[rxIdx-2] = rxByte;
            bytesRemaining --;

            if (bytesRemaining == 0)
                {
                // Post test.
                ProcessBufferReady(true);
                }
            break;
            } 
    UCB2TXBUF = 0x00;
}

 

 

 

 

Any ideas ?, I have seen some code where the TxIF flag is checked prior to moving a byte in, I am not sure how that can be related to my application, but most of the code samples have that feature.

Thanks. God bless,

 

Saul

  • It looks like you are clocking the USCI with SMCLK. What is sourcing SMCLK? One factor to consider is that SMCLK is turned off in LPM3.

    Essentially we need to establish a very basic case for this problem: Can you take 2 USCI SPI code examples from our web site and run them on 2 MSPs and show the issue? The 5xx codes are here: http://www.ti.com/lit/zip/slac166

  • Hi Brandon,

    1) Yes, SMCLK is the clock to the SPI. DCOCLKDIV is the source to SMCLK, which is the default option and I think is the same case for the code you provided in the link.

    2) I used the coded  provided by you, with the only exception that I am using port B0 on the Master and B2 on the Slave, due to hardware limitations; as opposed to Port A0 on both sides.

    3) I still have the same problem. If the Slave is in LPM2, 3,or 4, then I get comm. errors. If the uC is not in LPM, then the communication is ok (I did not try LPM, I suspect it'll work).

    4) Is it the case the Port B takes more time to wake up than Port A ?  ..... 

    Thanks.

     

    Saul

  • Quote "I have a question about the statement in your original email: "NOTE: If the debugger (MSP-FETU430IF) is plugged-in, then I have no problems" Does this mean you do not see any issue using LPM3 if you are using the MSP-FET430UIF? If so what is your setup otherwise?

     

    Hello Brandon,

    Correct, if the MSP-FET430UIF debugger is plugged in, then I do not see issues in LMP3 nor LMP2 ( I have not verified LPM4), the reason is because (according to what I understand), the debugger provides signals that keep "necessary" peripherals running.

     

    Now, in the sample code you provided, the Slave uC is only in LPM4 for a brief period of time, i.e., until it receives the first character, then I do not see how it would go back to LMP4, unless one has to explicitly modify the status register. Here is how your sample code looks like in the last instructions of Main():

     

    void main(void)

    {

     ......

    __bis_SR_register(LPM4_bits + GIE);

    }

     

     

     

    Here is how mine looks (complete code)

    SLAVE:


    #include "msp430x54x.h"
    void SpiInit();
    void InitDigitalIo();
    //-------------------------------------------------------------------------
    //
    //-------------------------------------------------------------------------
    void main(void)
    {
        WDTCTL = WDTPW+WDTHOLD;                   // Stop watchdog timer
        InitDigitalIo();
        SpiInit();

        while(1)

                {

                 __bis_SR_register(LPM4_bits + GIE);       // Enter LPM4, enable interrupts;

                __no_operation();

                }
    }

    //-------------------------------------------------------------------------
    // Echo character
    //-------------------------------------------------------------------------

    #pragma vector=USCI_B2_VECTOR
    __interrupt void USCI_B2_ISR(void)
    {
      switch(__even_in_range(UCB2IV,4))
      {
        case 0:break;                             // Vector 0 - no interrupt
        case 2:                                   // Vector 2 - RXIFG
          while (!(UCB2IFG&UCTXIFG));             // USCI_A0 TX buffer ready?
          UCB2TXBUF = UCB2RXBUF;
          break;
        case 4:break;                             // Vector 4 - TXIFG
        default: break;
      }
    }

    //-------------------------------------------------------------------------
    // Spi::Init() -- Initialize serial interface to MCU3.
    //-------------------------------------------------------------------------

    void SpiInit()
    {
      
        // uC interface.
        P9SEL |=  BIT1 | BIT2 | BIT3;        // Select P9 for SPI
        P9DIR |=  BIT2;                         // SOMI (output)  
        P9DIR &= ~BIT1;                            // SIMO (input)
        P9DIR &= ~BIT3;                            // CLK_IN
     
        UCB2CTL1 |= UCSWRST;                    // Put state machine in reset
        UCB2CTL0 |= UCMSB + UCSYNC + UCCKPL;    // 3-pin, 8-bit SPI slave
                                                // Clock polarity high, MSB
       
        UCB2CTL1 |= UCSSEL_2;                   // SMCLK
        UCB2BR0 = 0x02;                         // /2
        UCB2BR1 = 0;                            //
        UCA2MCTL = 0;                           // No modulation
         
        UCB2CTL1 &= ~UCSWRST;                   // **Initialize USCI state machine**
        UCB2IE |= UCRXIE;                       // Enable USCI_B2 RX interrupt 
        UCB2IFG &= ~UCRXIFG;
        UCB2TXBUF= 0x00;                        // Idle.
    }

    //-------------------------------------------------------------------------
    // InitDigitalIo() -- Default settings for I/O ports.
    //-------------------------------------------------------------------------

    void InitDigitalIo()
    {
      // 1, 2 
      PAOUT = 0;
      PADIR = 0xC040;
      PASEL = 0;

      // 3, 4
      PBOUT = 0; 
      PBDIR = 0xFFFF;
      PBOUT = 0X0004;     // LCD in reset and not selected.
      PBSEL  = 0;

      // 5, 6
      PCOUT  = 0;   
      PCDIR  = 0xFFFF;
      PCSEL  = 0; 

      // 7, 8
      PDOUT  = 0;         // UCS disabled by default.
      PDDIR  = 0xFFFF;
      PDSEL  = 0; 

      // 9, 10
      PEOUT  = 0; 
      PEDIR  = 0xFFFF;    // SPI to ARM processor disabled by default.                                                                
      PESEL  = 0; 

      P11OUT = 0;
      P11DIR = 0xFF;      // Test points are disabled by default.
      P11SEL = 0x0; 
     
      PJOUT  = 0;         // JTAG ?
      PJDIR  = 0xFF;


        // Latch PowerON.   
        P1OUT |= 0x40;   // P1.6
    }

     

    ==========================================

     

    MASTER CODE:

     

     

    #include "msp430x54x.h"

    unsigned char MST_Data,SLV_Data;
    void InitSpiComm();
    void BoardInit(void);
    //-------------------------------------------------------------------------
    //
    //-------------------------------------------------------------------------

    void main()
    {
        WDTCTL = WDTPW+WDTHOLD;                   // Stop watchdog timer
        BoardInit();
        P1OUT |= 0x03;                            // Set P1.0,1 for LED
        P1DIR |= 0x03;                            // Set P1.0,1 to output direction
        InitSpiComm();

        __delay_cycles(100);                      // Wait for slave to initialize
       
        MST_Data = 0x01;                          // Initialize data values
        SLV_Data = 0x00;                          //
       
        while (!(UCB0IFG&UCTXIFG))                // USCI_B0 TX buffer ready?
            ;              
        UCB0TXBUF = MST_Data;                     // Transmit first character
       
        __bis_SR_register(LPM0_bits + GIE);       // CPU off, enable interrupts
       
        while(1);
    }

    //-------------------------------------------------------------------------
    //
    //-------------------------------------------------------------------------

    void InitSpiComm()
    {
        // Set up pins used by peripheral unit
        P3SEL |= BIT1 + BIT2 + BIT3;                // MOSI, MISO, CLK.   

        P3DIR &= ~BIT2;                             // MISO (input).

        UCB0CTL1 = UCSWRST;                         // Disable USCI
      
        // Master 3-pin, 8-bit, MSB first, SMCLK.
        UCB0CTL0 |= UCMST + UCSYNC + UCCKPL + UCMSB;
        UCB0CTL1 |= UCSSEL_2;

        // Ignore clockrate argument for now, just use clock source/2
        UCB0BR0  = 0x02;
        UCB0BR1  = 0x00;

        // Release for operation
        UCB0CTL1 &= ~UCSWRST;
        UCB0IFG &= ~UCRXIFG; 
        UCB0IE |= UCRXIE;
    }

    //-------------------------------------------------------------------------
    //
    //-------------------------------------------------------------------------

    void BoardInit(void)

        //Tie unused ports
        PAOUT  = 0;
        PADIR  = 0xFFFF; 
        PASEL  = 0;
        PBOUT  = 0; 
        PBDIR  = 0xFFFF;
        PBSEL  = 0;
        PCOUT  = 0;   
        PCDIR  = 0xFFFF;
        PCSEL  = 0; 
        PDOUT  = 0; 
        PDDIR  = 0xFFFF;
        PDSEL  = 0; 
        PEOUT  = 0; 
        PEDIR  = 0xFEFF;                            // P10.0 to USB RST pin,
                                                    // ...if enabled with J5
        PESEL  = 0; 
        P11OUT = 0;
        P11DIR = 0xFF;
        PJOUT  = 0;   
        PJDIR  = 0xFF;
        P11SEL = 0;
    }

    //-------------------------------------------------------------------------
    //
    //-------------------------------------------------------------------------

    #pragma vector=USCI_B0_VECTOR
    __interrupt void USCI_B0_ISR(void)
    {
        switch(__even_in_range(UCB0IV,4))
            {
            case 0: break;                          // Vector 0 - no interrupt
            case 2:                                 // Vector 2 - RXIFG
              while (!(UCB0IFG&UCTXIFG));           // USCI_A0 TX buffer ready?
           
              if (UCB0RXBUF==SLV_Data)              // Test for correct character RX'd
                P1OUT |= 0x01;                      // If correct, light LED
              else
                P1OUT &= ~0x01;                     // If incorrect, clear LED
           
              MST_Data++;                           // Increment data
              SLV_Data++;
              UCB0TXBUF = MST_Data;                 // Send next value
           
              __delay_cycles(40);                   // Add time between transmissions to
                                                    // make sure slave can process information
              break;
            case 4: break;                          // Vector 4 - TXIFG
            default: break;
            }
    }

     

     

     

     

    This is what I observed on the oscilloscope:

     

                                                   |-------------|

    Master SCLK: _________|                 |-----------------------

     

                                                   |<-------DT-------->|--------------------|

    SlaveSMCLK _______________________|                          |____________________

     

    The Delta Time is as follows:

    When the debugger is plugged in DT = 1.31uS and communications are fine. When the debugger is not connected, then DT = 150uS and I missed several bytes depending on the inter character time, which right now is around 30uS.

    The question is: Is this timing normal or does 150uS wake-up time seems to be excesive ?

     

    Please let me know.

    Thanks.

    God bless,

     

    Saul

     

     

     

     


  • Quote "When the debugger is plugged in DT = 1.31uS and communications are fine. When the debugger is not connected, then DT = 150uS and I missed several bytes depending on the inter character time"

    Hi! Did Anyone resolve this issue?. I`ve discovered that I have the same problem using msp430f5529. I´ve tried modifying  some PMM registers in order to get FastWake and and Full performance, the the problem remains.


    Any tip will be apreciated.

    Thanks in advance

  • OK, I hade followed the table from 2.2.9.1 from slau208 and I had many bits that setted my uC in slow wake up mode. I forced the configuration in full performance, fast wakeup mode .

    msp430ware drive libraries doesn´t seem to have support for some functions and configurations so I added some extra code to force my uC into a fast wakeup mode.

    I think that PMM module documentation is somehow confusing. Also the MSP430F5529 datasheet where shows the wakeup times in page 62

**Attention** This is a public forum