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.

MSP430FR2676: SPI Slave Issues

Part Number: MSP430FR2676
Other Parts Discussed in Thread: CAPTIVATE-FR2676

The Goal

Having an issue with getting this processor to act appropriately as an SPI slave. Basically, the goal is to make this MCU into a transceiver that gets sent a request from the master, then responds accordingly depending on the request.

The Problem

But when I try to transmit to the master, I get intermittent and erroneous results. Either the wrong bytes will be sent entirely, or the timing will be off, or the order that the bytes are sent to the master gets out of sync, etc. 

What I've Tried:

- I've quadruple-checked my wiring.

- Made sure my SPI mode settings (clock polarity, phase, LSB/MSB, etc) are correct on both MCU's.

- Tried every clock speed I could think of on the master.

- Scoured the MSP430FR4xx and MSP430FR2xx family user guide and MSP430FR2676 datasheet.

- Tried both using the DriverLibrary & also tried writing directly to registers.

My code is based on the example code TI provides which is shown below. At the moment all I am trying to get working is: slave receives 3 bytes from the master, then slave responds with 3 bytes accordingly. Any help getting this to work is much appreciated. 

Note: I am using the CAPTIVATE-FR2676 dev board as the slave. and a simple Arduino as master.

//******************************************************************************
//   MSP430FR2676x Demo - eUSCI_A0, SPI 4-Wire Slave multiple byte RX/TX
//
//   Description: SPI master communicates to SPI slave sending and receiving
//   3 different messages of different length. SPI slave will enter LPM0
//   while waiting for the messages to be sent/receiving using SPI interrupt.
//   ACLK = NA, MCLK = SMCLK = DCO 16MHz.
//
//
//                   MSP430FR2676
//                 -----------------
//                |                 |
//                |                 |
//                |             P5.2|<- Data In (UCA0SIMO)
//                |                 |
//                |             P5.1|-> Data Out (UCA0SOMI)
//                |                 |
//                |             P5.0|<- Serial Clock In (UCA0CLK)
//                |                 |
//                |             P4.7|<- Master's GPIO (UCA0STE)
//
//******************************************************************************

#include <msp430.h>
#include <stdint.h>
#include <stdbool.h>
#include "Board.h"
#include "driverlib.h"

//******************************************************************************
// Pin Config ******************************************************************
//******************************************************************************

#define LED_OUT     P5OUT
#define LED_DIR     P5DIR
#define LED0_PIN    BIT7


//******************************************************************************
// General SPI State Machine ***************************************************
//******************************************************************************

typedef enum SPI_ModeEnum{
    IDLE_MODE,
    RX_PREAMBLE_MODE,
    TX_DATA_MODE,
    RX_DATA_MODE,
    TIMEOUT_MODE
} SPI_Mode;

/* Used to track the state of the software state machine*/
SPI_Mode SlaveMode = RX_PREAMBLE_MODE;

#define MAX_BUFFER_SIZE     20
uint8_t ReceiveBuffer[MAX_BUFFER_SIZE] = {0};
uint8_t RXByteCtr = 0;
uint8_t ReceiveIndex = 0;
uint8_t TransmitBuffer[MAX_BUFFER_SIZE] = {0};
uint8_t TXByteCtr = 0;
uint8_t TransmitIndex = 0;

uint8_t ReceiveNumBytes = 0;
uint8_t ReceiveRW = 0;
uint8_t ReceiveRegAddr = 0;
uint8_t ReceiveHeader = 0;
uint8_t frame = 0;
static const uint8_t RX = 1;
static const uint8_t TX = 2;
uint8_t AckByteCtr = 0;

uint8_t RegisterMap[0xFF];

void SendUCA0Data(uint8_t val);
void SPI_Slave_ProcessCMD(uint8_t regaddr, uint8_t header);
void CopyArray(uint8_t *source, uint8_t *dest, uint8_t count, uint8_t startAddr);
void initSPI();
void initSystem();
void initClockTo16MHz();



void SendUCA0Data(uint8_t val)
{
    while (!(UCA0IFG & UCTXIFG));              // USCI_A0 TX buffer ready?
    UCA0TXBUF = val;
}

void SPI_Slave_ProcessCMD(uint8_t regaddr, uint8_t header)
{
        ReceiveIndex = 0;
        TransmitIndex = 0;
        RXByteCtr = 0;
        TXByteCtr = 0;
        ReceiveRegAddr = regaddr;
        ReceiveRW = (header & 0b10000000) >> 7;
        ReceiveNumBytes = (header & 0b01111111);

         if(frame == RX)
         {
          SlaveMode = RX_DATA_MODE;
          RXByteCtr = ReceiveNumBytes;
         }
         else if(frame == TX)
         {
           SlaveMode = TX_DATA_MODE;
           TXByteCtr = ReceiveNumBytes;
           CopyArray(RegisterMap, TransmitBuffer, ReceiveNumBytes, ReceiveRegAddr);
           SendUCA0Data(header);
           SendUCA0Data(ReceiveRegAddr);
         }

}

void CopyArray(uint8_t *source, uint8_t *dest, uint8_t count, uint8_t startAddr)
{
    unsigned int copyIndex = 0;
    for (copyIndex = 0; copyIndex < count; copyIndex++)
    {
        dest[copyIndex] = source[copyIndex+startAddr];
    }
}

//******************************************************************************
// Device Initialization *******************************************************
//******************************************************************************

void initSPI()
{
    SYSCFG3|=USCIA0RMP;                       //Set the SPI remapping source
    P5SEL0 |= BIT0 | BIT1 | BIT2;             // set SPI MOSI, MISO, & CLK pins as second function
    //Initialize slave to LSB first, clock polarity of 0, clock phase of 1 and 4 wire SPI
    EUSCI_A_SPI_initSlaveParam param = {0};
    param.msbFirst = EUSCI_A_SPI_MSB_FIRST;
    param.clockPhase = EUSCI_A_SPI_PHASE_DATA_CAPTURED_ONFIRST_CHANGED_ON_NEXT;
    param.clockPolarity = EUSCI_A_SPI_CLOCKPOLARITY_INACTIVITY_LOW;
    param.spiMode = EUSCI_A_SPI_4PIN_UCxSTE_ACTIVE_LOW; //UCMODE_2
    EUSCI_A_SPI_initSlave(EUSCI_A0_BASE, &param);

    //Enable SPI Module
    EUSCI_A_SPI_enable(EUSCI_A0_BASE);
    //Clear receive interrupt flag
    EUSCI_A_SPI_clearInterrupt(EUSCI_A0_BASE, EUSCI_A_SPI_RECEIVE_INTERRUPT);
    //Enable Receive interrupt
    EUSCI_A_SPI_enableInterrupt(EUSCI_A0_BASE, EUSCI_A_SPI_RECEIVE_INTERRUPT);
}

void initSystem()
{
    WDTCTL = WDTPW | WDTHOLD;   // Stop watchdog timer
    initClockTo16MHz();
    //Stop watchdog timer
    WDT_A_hold(WDT_A_BASE);

    // Configure Pins for XIN
    //Set P2.0 and P2.1 as Module Function Input.
    GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P2, GPIO_PIN1 + GPIO_PIN0, GPIO_PRIMARY_MODULE_FUNCTION);

    // Configure SPI Pins for UCA0CLK, UCA0TXD/UCA0SIMO and UCA0RXD/UCA0SOMI
    GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P5, GPIO_PIN0 + GPIO_PIN1 + GPIO_PIN2, GPIO_PRIMARY_MODULE_FUNCTION);

    // Disable the GPIO power-on default high-impedance mode to activate previously configured port settings
    PMM_unlockLPM5();

    initSPI();

    __bis_SR_register(LPM0_bits + GIE);       // Enter LPM0, enable interrupts
}

void initClockTo16MHz()
{
    // Configure one FRAM waitstate as required by the device datasheet for MCLK
    // operation beyond 8MHz _before_ configuring the clock system.
    FRCTL0 = FRCTLPW | NWAITS_1;

    __bis_SR_register(SCG0);    // disable FLL
    CSCTL3 |= SELREF__REFOCLK;  // Set REFO as FLL reference source
    CSCTL0 = 0;                 // clear DCO and MOD registers
    CSCTL1 &= ~(DCORSEL_7);     // Clear DCO frequency select bits first
    CSCTL1 |= DCORSEL_5;        // Set DCO = 16MHz
    CSCTL2 = FLLD_0 + 487;      // set to fDCOCLKDIV = (FLLN + 1)*(fFLLREFCLK/n)
                                //                   = (487 + 1)*(32.768 kHz/1)
                                //                   = 16 MHz

    __delay_cycles(3);
    __bic_SR_register(SCG0);                        // enable FLL
    while(CSCTL7 & (FLLUNLOCK0 | FLLUNLOCK1));      // FLL locked
}


//******************************************************************************
// Main ************************************************************************
// Enters LPM0 and waits for SPI interrupts. The data sent from the master is  *
// then interpreted and the device will respond accordingly                    *
//******************************************************************************
void main(void) 
{
    initSystem();
    __no_operation();
    RegisterMap[128] = 1;
}

//******************************************************************************
// SPI Interrupt ***************************************************************
//******************************************************************************
#pragma vector=USCI_A0_VECTOR
__interrupt void USCI_A0_ISR(void)
{
    uint8_t uca0_rx_val = 0;
    switch(__even_in_range(UCA0IV, USCI_SPI_UCTXIFG))
    {
        case USCI_NONE: break;
        case USCI_SPI_UCRXIFG:
            uca0_rx_val = UCA0RXBUF;
            UCA0IFG &= ~UCRXIFG;
            switch (SlaveMode)
            {
                case (RX_PREAMBLE_MODE):
                    frame = RX;
                    if(AckByteCtr == 0)
                    {
                      ReceiveHeader = uca0_rx_val;
                      AckByteCtr = 1;
                    }
                    else if(AckByteCtr == 1)
                    {
                        ReceiveRegAddr = uca0_rx_val;
                        SPI_Slave_ProcessCMD(ReceiveRegAddr, ReceiveHeader);
                        AckByteCtr = 0;
                    }
                    break;
                case (RX_DATA_MODE):
                    ReceiveBuffer[ReceiveIndex++] = uca0_rx_val;
                    RXByteCtr--;
                    if (RXByteCtr == 0)
                    {
                        frame = TX;
                        SPI_Slave_ProcessCMD(ReceiveRegAddr, ReceiveHeader);
                    }
                    break;
                case (TX_DATA_MODE):
                    if (TXByteCtr > 0)
                    {
                        SendUCA0Data(1);//TransmitBuffer[TransmitIndex++]);
                        TXByteCtr--;
                    }
                    if (TXByteCtr == 0)
                    {
                        //Done Transmitting MSG
                        SlaveMode = RX_PREAMBLE_MODE;
                    }
                    break;
                default:
                    __no_operation();
                    break;
                }
            break;
        case USCI_SPI_UCTXIFG:
            break;
        default: break;
    }
}

  • Hi Max,

    Looking at this code, it is apparent that when the master sends 8 clocks and it's first byte to the slave (MSP430), the SPI receive interrupt is generated and  ISR calls function SendUCA0Data(1).  In that function, the value 1 is written to the TXBUF.  So far so good.  The master must then send 8 more clock pulses in order to clock out the value in the TXBUF, which is = 1.  So I suspect what you are seeing during the master's first 8 clock pulses is random, and on the second set of 8 clock pulses you should see the value 1. Make sense?  What do you see?

  • Hi Dennis, 

    So now what I am getting is this - the very first transaction goes as expected. But every following transaction is wrong (slave only sends 0x01's)

    First Transaction: Master sends  0x01, 0x80, 0x02 to slave. Then slave responds with 0x01, 0x80, 0x01. 

    Every transaction after the first transaction: Master sends  0x01, 0x80, 0x02 to slave. Then slave responds with 0x01, 0x01, 0x01. 

    First Transaction In Logic Analyzer (Correct):

    Each Following Transaction In Logic Analyzer (Wrong):

  • Dennis here is the latest code (mostly just more comments to help understand the logic).

    //******************************************************************************
    //   MSP430FR2676x Demo - eUSCI_A0, SPI 4-Wire Slave multiple byte RX/TX
    //
    //   Description: SPI master communicates to SPI slave sending and receiving
    //   3 different messages of different length. SPI slave will enter LPM0
    //   while waiting for the messages to be sent/receiving using SPI interrupt.
    //   ACLK = NA, MCLK = SMCLK = DCO 16MHz.
    //
    //
    //                   MSP430FR2676
    //                 -----------------
    //                |                 |
    //                |             P5.2|<- Data In (UCA0SIMO)
    //                |                 |
    //                |             P5.1|-> Data Out (UCA0SOMI)
    //                |                 |
    //                |             P5.0|<- Serial Clock In (UCA0CLK)
    //                |                 |
    //                |             P4.7|<- Master's GPIO (UCA0STE)
    //
    //******************************************************************************
    
    #include <msp430.h>
    #include <stdint.h>
    #include <stdbool.h>
    #include "Board.h"
    #include "driverlib.h"
    
    //******************************************************************************
    // Defines *********************************************************************
    //******************************************************************************
    
    #define LED_OUT     P5OUT
    #define LED_DIR     P5DIR
    #define LED0_PIN    BIT7
    
    
    #define MAX_BUFFER_SIZE     20
    
    
    //******************************************************************************
    // Globals *********************************************************************
    //******************************************************************************
    
    uint8_t ReceiveBuffer[MAX_BUFFER_SIZE] = {0};
    uint8_t RXByteCtr = 0;
    uint8_t ReceiveIndex = 0;
    uint8_t TransmitBuffer[MAX_BUFFER_SIZE] = {0};
    uint8_t TXByteCtr = 0;
    uint8_t TransmitIndex = 0;
    
    uint8_t ReceiveNumBytes = 0;
    uint8_t ReceiveRW = 0;
    uint8_t ReceiveRegAddr = 0;
    uint8_t ReceiveHeader = 0;
    uint8_t frame = 0;
    const uint8_t RX = 1;
    const uint8_t TX = 2;
    uint8_t AckByteCtr = 0;
    
    uint8_t RegisterMap[0xFF];
    
    
    //******************************************************************************
    // SPI State Machine ***********************************************************
    //******************************************************************************
    
    typedef enum SPI_ModeEnum{
        IDLE_MODE,
        RX_PREAMBLE_MODE,
        TX_DATA_MODE,
        RX_DATA_MODE,
        TIMEOUT_MODE
    } SPI_Mode;
    
    /* Used to track the state of the software state machine*/
    SPI_Mode SlaveMode = RX_PREAMBLE_MODE;
    
    
    
    void SendUCA0Data(uint8_t val);
    void SPI_Slave_ProcessCMD(uint8_t regaddr, uint8_t header);
    void CopyArray(uint8_t *source, uint8_t *dest, uint8_t count, uint8_t startAddr);
    void initSPI();
    void initSystem();
    void initClockTo16MHz();
    
    
    
    void SendUCA0Data(uint8_t val)
    {
        while (!(UCA0IFG & UCTXIFG));              // USCI_A0 TX buffer ready?
        UCA0TXBUF = val;
    }
    
    void SPI_Slave_ProcessCMD(uint8_t regaddr, uint8_t header)
    {
            ReceiveIndex = 0; //Clear Rx Index
            TransmitIndex = 0; //Clear Tx Index
            RXByteCtr = 0; //Clear Rx Byte Counter
            TXByteCtr = 0; //Clear Tx Byte Counter
            ReceiveRegAddr = regaddr; //Get Register Address for processing
            ReceiveRW = (header & 0b10000000) >> 7; //Get RW Bit from 1st bit of Header Byte
            ReceiveNumBytes = (header & 0b01111111); //Get Num Regs from last 7 bits of Header Byte
    
             if(frame == RX)
             {
              SlaveMode = RX_DATA_MODE; //Enter RX_Data Mode inside interrupt
              RXByteCtr = ReceiveNumBytes; //Set Rx Counter to # of data bytes to be received
             }
             else if(frame == TX)
             {
               SlaveMode = TX_DATA_MODE; //Enter TX_Data Mode inside interrupt
               TXByteCtr = ReceiveNumBytes; //Set Tx Counter to # of data bytes to be sent
               CopyArray(RegisterMap, TransmitBuffer, ReceiveNumBytes, ReceiveRegAddr); //Gather data from requested registers and add it to the transmit buffer.
               SendUCA0Data(header); //Send header byte
               SendUCA0Data(ReceiveRegAddr); //Send Register Address byte
             }
    
    }
    
    void CopyArray(uint8_t *source, uint8_t *dest, uint8_t count, uint8_t startAddr)
    {
        unsigned int copyIndex = 0;
        for (copyIndex = 0; copyIndex < count; copyIndex++)
        {
            dest[copyIndex] = source[copyIndex+startAddr];
        }
    }
    
    //******************************************************************************
    // Device Initialization *******************************************************
    //******************************************************************************
    
    void initSPI()
    {
        SYSCFG3|=USCIA0RMP;                       //Set the SPI remapping source
        P5SEL0 |= BIT0 | BIT1 | BIT2;             // set SPI MOSI, MISO, & CLK pins as second function
    
        //Initialize slave to MSB first, clock polarity of 0, clock phase of 1 and 4 wire SPI
        EUSCI_A_SPI_initSlaveParam param = {0};
        param.msbFirst = EUSCI_A_SPI_MSB_FIRST;
        param.clockPhase = EUSCI_A_SPI_PHASE_DATA_CAPTURED_ONFIRST_CHANGED_ON_NEXT;
        param.clockPolarity = EUSCI_A_SPI_CLOCKPOLARITY_INACTIVITY_LOW;
        param.spiMode = EUSCI_A_SPI_4PIN_UCxSTE_ACTIVE_LOW; //UCMODE_2
        EUSCI_A_SPI_initSlave(EUSCI_A0_BASE, &param);
    
        EUSCI_A_SPI_enable(EUSCI_A0_BASE); //Enable SPI Module
        EUSCI_A_SPI_clearInterrupt(EUSCI_A0_BASE, EUSCI_A_SPI_RECEIVE_INTERRUPT); //Clear receive interrupt flag
        EUSCI_A_SPI_enableInterrupt(EUSCI_A0_BASE, EUSCI_A_SPI_RECEIVE_INTERRUPT);//Enable Receive interrupt
    }
    
    void initSystem()
    {
        WDTCTL = WDTPW | WDTHOLD;   // Stop watchdog timer
        initClockTo16MHz(); //init clock
        WDT_A_hold(WDT_A_BASE); //Stop watchdog timer
    
        GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P2, GPIO_PIN1 + GPIO_PIN0, GPIO_PRIMARY_MODULE_FUNCTION); //Configure Pins for XIN - Set P2.0 and P2.1 as Module Function Input.
        GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P5, GPIO_PIN0 + GPIO_PIN1 + GPIO_PIN2, GPIO_PRIMARY_MODULE_FUNCTION); // Configure SPI Pins for UCA0CLK, UCA0TXD/UCA0SIMO and UCA0RXD/UCA0SOMI
    
        PMM_unlockLPM5(); // Disable the GPIO power-on default high-impedance mode to activate previously configured port settings
        initSPI(); //init SPI
        __bis_SR_register(LPM0_bits + GIE);       // Enter LPM0, enable interrupts
    }
    
    void initClockTo16MHz()
    {
        FRCTL0 = FRCTLPW | NWAITS_1; // Configure one FRAM waitstate as required by the device datasheet for MCLK operation beyond 8MHz _before_ configuring the clock system.
        __bis_SR_register(SCG0);    // disable FLL
        CSCTL3 |= SELREF__REFOCLK;  // Set REFO as FLL reference source
        CSCTL0 = 0;                 // clear DCO and MOD registers
        CSCTL1 &= ~(DCORSEL_7);     // Clear DCO frequency select bits first
        CSCTL1 |= DCORSEL_5;        // Set DCO = 16MHz
        CSCTL2 = FLLD_0 + 487;      // set to fDCOCLKDIV = (FLLN + 1)*(fFLLREFCLK/n) = (487 + 1)*(32.768 kHz/1) = 16 MHz
    
        __delay_cycles(3);
        __bic_SR_register(SCG0);                        // enable FLL
        while(CSCTL7 & (FLLUNLOCK0 | FLLUNLOCK1));      // FLL locked
    }
    
    
    //******************************************************************************
    // Main ************************************************************************
    // Enters LPM0 and waits for SPI interrupts. The data sent from the master is  *
    // then interpreted and the device will respond accordingly                    *
    //******************************************************************************
    void main(void)
    {
        initSystem();
        __no_operation();
    }
    
    //******************************************************************************
    // SPI Interrupt ***************************************************************
    //******************************************************************************
    #pragma vector=USCI_A0_VECTOR
    __interrupt void USCI_A0_ISR(void)
    {
        RegisterMap[128] = 1;
        uint8_t uca0_rx_val = 0;
        switch(__even_in_range(UCA0IV, USCI_SPI_UCTXIFG))
        {
            case USCI_NONE: break;
            case USCI_SPI_UCRXIFG:
                uca0_rx_val = UCA0RXBUF;
                UCA0IFG &= ~UCRXIFG;
                switch (SlaveMode)
                {
                    case (RX_PREAMBLE_MODE):
                        frame = RX;
                        if(AckByteCtr == 0)
                        {
                          ReceiveHeader = uca0_rx_val; //Grab Header Byte
                          AckByteCtr = 1; //Get ready to grab next byte
                        }
                        else if(AckByteCtr == 1)
                        {
                            ReceiveRegAddr = uca0_rx_val; //Grab Register Address Byte
                            SPI_Slave_ProcessCMD(ReceiveRegAddr, ReceiveHeader); //Process receiving part of transaction
                            AckByteCtr = 0;
                        }
                        break;
                    case (RX_DATA_MODE):
                        ReceiveBuffer[ReceiveIndex++] = uca0_rx_val; //Receive # of Data Bytes Requested
                        RXByteCtr--;
                        if (RXByteCtr == 0)
                        {
                            frame = TX; //Get ready to send response
                            SPI_Slave_ProcessCMD(ReceiveRegAddr, ReceiveHeader); //Process transmitting part of transaction
                        }
                        break;
                    case (TX_DATA_MODE):
                        if (TXByteCtr > 0)
                        {
                            SendUCA0Data(TransmitBuffer[TransmitIndex++]); //Transmit # of Data Bytes Requested
                            TXByteCtr--;
                        }
                        if (TXByteCtr == 0)
                        {
                            //Done Transmitting MSG
                            SlaveMode = RX_PREAMBLE_MODE; // Transaction complete, get ready for next transaction
                        }
                        break;
                    default:
                        __no_operation();
                        break;
                    }
                break;
            case USCI_SPI_UCTXIFG:
                break;
            default: break;
        }
    }
    

  • Here is the board.h file I am using as well (couldn't find one available for this processor/dev kit so had to make this from scratch)

    /* --COPYRIGHT--,BSD
     * Copyright (c) 2017, Texas Instruments Incorporated
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     *
     * *  Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     *
     * *  Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     *
     * *  Neither the name of Texas Instruments Incorporated nor the names of
     *    its contributors may be used to endorse or promote products derived
     *    from this software without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
     * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     * --/COPYRIGHT--*/
    /**
     * ===
     * Board.h
     * ===
     * This file is meant to provide generic GPIO selection definitions that allows
     * DriverLib to be used portably across different LaunchPads. The same module
     * pin might not be populated on the same physical port and pin on different
     * LaunchPads.
     *
     * This file is intended to be modified and updated by individual users based
     * on application need. It will be initially populated according to common
     * peripherals available on a LaunchPad board, but by all means not complete.
     *
     * GPIO_PRIMARY_MODULE_FUNCTION indicates Port SEL bits are 01
     * GPIO_SECONDARY_MODULE_FUNCTION indicates Port SEL bits are 10
     * GPIO_TERNARY_MODULE_FUNCTION indicates Port SEL bits are 11
     * If the Port SEL bits are 00, the FUNCTION bits should be ignored
     * Please consult device datasheet for more info, under "Input/Output Diagrams"
     *
     **/
    
    #ifndef __BOARD_H__
    #define __BOARD_H__
    
    #ifdef __MSP430FR2676__
    
    
    #define GPIO_PORT_LED1          GPIO_PORT_P1
    #define GPIO_PIN_LED1           GPIO_PIN7
    
    #define GPIO_PORT_LED2          GPIO_PORT_P1
    #define GPIO_PIN_LED2           GPIO_PIN6
    
    #define GPIO_PORT_LED3          GPIO_PORT_P5
    #define GPIO_PIN_LED3           GPIO_PIN7
    
    #define GPIO_PORT_S1            GPIO_PORT_P5
    #define GPIO_PIN_S1             GPIO_PIN3
    
    #define GPIO_PORT_ADC7          GPIO_PORT_P1
    #define GPIO_PIN_ADC7           GPIO_PIN7
    #define GPIO_FUNCTION_ADC7      GPIO_PRIMARY_MODULE_FUNCTION
    
    #define GPIO_PORT_MCLK          GPIO_PORT_P1
    #define GPIO_PIN_MCLK           GPIO_PIN3
    #define GPIO_FUNCTION_MCLK      GPIO_PRIMARY_MODULE_FUNCTION
    
    #define GPIO_PORT_SMCLK         GPIO_PORT_P1
    #define GPIO_PIN_SMCLK          GPIO_PIN7
    #define GPIO_FUNCTION_SMCLK     GPIO_SECONDARY_MODULE_FUNCTION
    
    #define GPIO_PORT_ACLK          GPIO_PORT_P2
    #define GPIO_PIN_ACLK           GPIO_PIN2
    #define GPIO_FUNCTION_ACLK      GPIO_SECONDARY_MODULE_FUNCTION
    
    #define GPIO_PORT_UCA0TXD       GPIO_PORT_P1
    #define GPIO_PIN_UCA0TXD        GPIO_PIN4
    #define GPIO_FUNCTION_UCA0TXD   GPIO_PRIMARY_MODULE_FUNCTION
    
    #define GPIO_PORT_UCA0RXD       GPIO_PORT_P1
    #define GPIO_PIN_UCA0RXD        GPIO_PIN5
    #define GPIO_FUNCTION_UCA0RXD   GPIO_PRIMARY_MODULE_FUNCTION
    
    #define GPIO_PORT_UCB0SCL       GPIO_PORT_P1
    #define GPIO_PIN_UCB0SCL        GPIO_PIN3
    #define GPIO_FUNCTION_UCB0SCL   GPIO_PRIMARY_MODULE_FUNCTION
    
    #define GPIO_PORT_UCB0SDA       GPIO_PORT_P1
    #define GPIO_PIN_UCB0SDA        GPIO_PIN2
    #define GPIO_FUNCTION_UCB0SDA   GPIO_PRIMARY_MODULE_FUNCTION
    
    #endif // __MSP430FR2676__
    
    
    #ifdef __MSP430FR4133__
    
    #define GPIO_PORT_LED1          GPIO_PORT_P1
    #define GPIO_PIN_LED1           GPIO_PIN0
    #define GPIO_PORT_LED2          GPIO_PORT_P4
    #define GPIO_PIN_LED2           GPIO_PIN0
    #define GPIO_PORT_S1            GPIO_PORT_P1
    #define GPIO_PIN_S1             GPIO_PIN2
    
    #define GPIO_PORT_ADC7          GPIO_PORT_P1
    #define GPIO_PIN_ADC7           GPIO_PIN7
    #define GPIO_FUNCTION_ADC7      GPIO_PRIMARY_MODULE_FUNCTION
    
    #define GPIO_PORT_MCLK          GPIO_PORT_P1
    #define GPIO_PIN_MCLK           GPIO_PIN4
    #define GPIO_FUNCTION_MCLK      GPIO_PRIMARY_MODULE_FUNCTION
    #define GPIO_PORT_SMCLK         GPIO_PORT_P8
    #define GPIO_PIN_SMCLK          GPIO_PIN0
    #define GPIO_FUNCTION_SMCLK     GPIO_PRIMARY_MODULE_FUNCTION
    #define GPIO_PORT_ACLK          GPIO_PORT_P8
    #define GPIO_PIN_ACLK           GPIO_PIN1
    #define GPIO_FUNCTION_ACLK      GPIO_PRIMARY_MODULE_FUNCTION
    
    #define GPIO_PORT_UCA0TXD       GPIO_PORT_P1
    #define GPIO_PIN_UCA0TXD        GPIO_PIN0
    #define GPIO_FUNCTION_UCA0TXD   GPIO_PRIMARY_MODULE_FUNCTION
    #define GPIO_PORT_UCA0RXD       GPIO_PORT_P1
    #define GPIO_PIN_UCA0RXD        GPIO_PIN1
    #define GPIO_FUNCTION_UCA0RXD   GPIO_PRIMARY_MODULE_FUNCTION
    
    #define GPIO_PORT_UCB0SCL       GPIO_PORT_P5
    #define GPIO_PIN_UCB0SCL        GPIO_PIN3
    #define GPIO_FUNCTION_UCB0SCL   GPIO_PRIMARY_MODULE_FUNCTION
    #define GPIO_PORT_UCB0SDA       GPIO_PORT_P5
    #define GPIO_PIN_UCB0SDA        GPIO_PIN2
    #define GPIO_FUNCTION_UCB0SDA   GPIO_PRIMARY_MODULE_FUNCTION
    
    #endif // __MSP430FR4133__
    
    
    #endif // __BOARD_H__

  • Dennis,

    Hate to keep messaging but this is an urgent work project and we are currently at a standstill. Any help is much appreciated.

    Thanks

  • Hi Max,

    Sorry for the delay.  Let me digest your most recent posts.

  • I sense a race problem. After you receive that command to send data the master keeps pumping out clocks. So which will happen first: you putting data into TXBUF or TXBUF being transferred to the shift register?

    I don't see anything in the documentation even hinting at when that transfer happens.

    But since it appears to work OK for the first transaction but not the second, the problem is somewhere in your state machine. For some reason it doesn't put any data in TXBUF so the last value written keeps getting sent.

    But consider the timing. The ISR puts its final byte into TXBUF and switches the state to preamble mode. But there are still clocks remaining in this transaction (data in transmit shift register and TXBUF) and two receive interrupts. Which means that the receive state machine, which doesn't seem to care what value is received, sends another set of data to be processed. That can't end well.

  • Hi David,

    You were right on the money. 

    Before switching back to PREAMBLE MODE all I had to do was insert this:

    while(EUSCI_A_SPI_isBusy(EUSCI_A0_BASE) == EUSCI_A_SPI_BUSY);

    to wait for the SPI transaction to finish before switching.

  • Hi Max,

    Following the logic of your code, I think this might the issue.  Change it and let me know.

    case (TX_DATA_MODE):


    if (TXByteCtr > 0)
    {

    // TXByteCtr = 1, master just clocked in 0x00 in, slave clocked out 0x01, TXBUF (0x80) moves to shift register, TXIFG is set

    SendUCA0Data(TransmitBuffer[TransmitIndex++]); //Transmit # of Data Bytes Requested // move transmitBuffer[0] (this is copy of registerMap[]) into TXBUF

    TXByteCtr--; // TXByteCtr = 0 at this point
    }
    if (TXByteCtr == 0) <----- I THINK THIS IS THE PROBLEM - SHOULD BE -> if else(TXByteCtr==0)
    {

    //Done Transmitting MSG
    SlaveMode = RX_PREAMBLE_MODE; // Transaction complete, get ready for next transaction
    }
    break;
    default:

  • Dennis,

    thanks for your response. David's answer fixed the problem.

    Any ideas on how to access the values in the "RegisterMap[ ]" array from both inside and outside the interrupt routine?

    Is this even possible?

  • RegisterMap[] is a global and accessible everywhere.

    Another thing. Many SPI slaves frame transactions with a CS signal but all you do here is enable the SPI slave device. It would be nice if you could use a transition on the STE signal to reset the state machine. It appears, but isn't completely clear, that the PxSEL bits disable the port interrupt hardware so that isn't an option.

  • Hi Max,

    Kudos to David :)

    I don't see an issue with how you are using RegisterMap[ ].  It is a global so you can access it from with the ISR and main code.  I would declare it 'volatile' though and here is why:

    The volatile keyword is intended to prevent the compiler from applying any optimizations on objects that can change in ways that cannot be determined by the compiler. 

    Objects declared as volatile are omitted from optimization because their values can be changed by code outside the scope of current code at any time. The system always reads the current value of a volatile object from the memory location rather than keeping its value in temporary register at the point it is requested, even if a previous instruction asked for a value from the same object. So the simple question is, how can value of a variable change in such a way that compiler cannot predict. Consider the following cases for answer to this question. 

    Global variables modified by an interrupt service routine outside the scope: For example, a global variable can represent a data port (usually global pointer referred as memory mapped IO) which will be updated dynamically. The code reading data port must be declared as volatile in order to fetch latest data available at the port. Failing to declare variable as volatile, the compiler will optimize the code in such a way that it will read the port only once and keeps using the same value in a temporary register to speed up the program (speed optimization). In general, an ISR used to update these data port when there is an interrupt due to availability of new data 

  • Dennis, 

    Regardless of whether or not I declare it volatile, none of the stored values from main() can be accessed from within the Interrupt. For example, if I have to update the data within register 0x28 to the value of 2 (ie. RegisterMap[0x28] = 2;) from within the main loop because a button was pressed all is well. But then when I return to the interrupt, that value is not given. How would I go about doing this so the data stored in each register is accessible from both sides of the fence?

  • Max,

    Sure, volatile was not intended to address the scope of the variable.  Let me make sure I understand.

    You show in your code, at the top, under "Globals' 

    uint8_t RegisterMap[0xFF];

    From my experience, as long as the variable is in the same file, and is not declared in a function(unless it is declared static), it can be accessed by any function in that same file, including the ISR or in main().

    Do I have this right?

  • David, 

    Thanks again, this interrupt is triggered by a low signal on the CS pin. If you look back up at my first reply to Dennis, the logic analyzer clips show the line going low for a transaction, then going high again once it ends.

  • Dennis, 

    I believe the difference is because it is a hardware interrupt not just a software interrupt. The only way I can get the register values to change and still be accessible in the interrupt, is if I update them from within the interrupt routine... But of course, if I am updating register data based on a sensor that I am monitoring outside of the interrupt routine, then how would I send these updated values to the interrupt routine and vice versa?

    I'd love to be wrong on this, but this is what I am experiencing.

  • The CS signal does not cause an interrupt. It enables the SPI slave and RXIFG triggers the interrupt. It has zero effect on your state machine.

  • David, 

    I see. So the CS line only allows for the possibility of one of the interrupt flags to be set, which in turn, triggers the interrupt. 

    Thank you all for your help. Still looking for a solution to the interrupt problem if anyone has suggestions.

  • Dennis, 

    I figured out that the problem was because the MCU was entering Low Power Mode! because of that, changing any variables in the main loop were'nt even being reached, and therefore showing up empty in the interrupt. 

    So, this:

        __bis_SR_register(LPM0_bits + GIE);       // Enter LPM0, enable interrupts

    needed to be changed to:

        __bis_SR_register(GIE);       // Enable interrupts

    This is the post that made me realize the issue: https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/119681/loop-on-a-global-variable-that-is-changed-in-interrupt

  • While those things appear in the MSP430 header files I never use them. The headers also define some slightly more readable macros such as "LPM0;" and "LPM0_EXIT;". For control of the global interrupt enable I use "__eint()".

    Keeping those operations quite separate. I usually enable interrupts before entering the main (infinite) loop and enter a low power mode within the loop.

**Attention** This is a public forum