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.

LP-MSP430FR2476: SPI polling receive

Part Number: LP-MSP430FR2476

I have the SPI set up in polling mode as discussed in my recent thread.

LP-MSP430FR2476: SPI transmit and receive interrupts - MSP low-power microcontroller forum - MSP low-power microcontrollers - TI E2E support forums

From the MSP430 user guide slau445i, transmit and receive operations happen concurrently. If a response is expected from the slave, when will this receive byte show up on MISO? In the scope view, I can see all transmitted bytes, but no activity on the MISO line.

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

#include <driverlib.h>
#include <msp430.h> 
#include "clock~.h"                 // Clock configurations


static uint8_t TXData = 0;           //transaction count
static uint8_t RXData = 0;           //SPI receive byte


typedef struct {
    uint8_t    Len;
    uint8_t    Data[5];
    uint8_t    RxFlag;
} spi_MaximTrans_t;

static spi_MaximTrans_t DCInit_transactions[15];


uint8_t SPI_TX_index = 0;            //byte count in transaction



/**
 *  Initialize system clocks
 */
static void init_clock(void) {
    // Configure one FRAM waitstate as required by the device datasheet for MCLK
    // operation beyond 8MHz _before_ configuring the clock system.
    FRAMCtl_configureWaitStateControl(FRAMCTL_ACCESS_TIME_CYCLES_1);

    //Set DCO FLL reference = REFO
    CS_initClockSignal(CS_FLLREF, CS_REFOCLK_SELECT,   CS_CLOCK_DIVIDER_1);
    //Set ACLK = REFO
    CS_initClockSignal(CS_ACLK,   CS_REFOCLK_SELECT,   CS_CLOCK_DIVIDER_1);

    CS_initFLLParam param = {0};
    //Set Ratio/Desired MCLK Frequency, initialize DCO, save trim values
    CS_initFLLCalculateTrim(CS_MCLK_DESIRED_FREQUENCY_IN_KHZ, CS_MCLK_FLLREF_RATIO, &param);

    //Set MCLK = REFO
    CS_initClockSignal(CS_MCLK,   CS_REFOCLK_SELECT,   CS_CLOCK_DIVIDER_1);
    //Set SMCLK = DCO
    CS_initClockSignal(CS_SMCLK,  CS_DCOCLKDIV_SELECT, CS_CLOCK_DIVIDER_1);

    //Clear all OSC fault flag
    CS_clearAllOscFlagsWithTimeout(1000);
}

/**
 *  Initialize all of the IO pins per their configuration
 */
static void init_gpio(void) {
    // Set all GPIO pins to output low to prevent floating input and reduce power consumption
    GPIO_setOutputLowOnPin(GPIO_PORT_P1, GPIO_PIN_ALL8);
    GPIO_setOutputLowOnPin(GPIO_PORT_P2, GPIO_PIN_ALL8);
    GPIO_setOutputLowOnPin(GPIO_PORT_P3, GPIO_PIN_ALL8);
    GPIO_setOutputLowOnPin(GPIO_PORT_P4, GPIO_PIN_ALL8);
    GPIO_setOutputLowOnPin(GPIO_PORT_P5, GPIO_PIN_ALL8);
    GPIO_setOutputLowOnPin(GPIO_PORT_P6, GPIO_PIN0 | GPIO_PIN1 | GPIO_PIN2);

    GPIO_setAsOutputPin(   GPIO_PORT_P1, GPIO_PIN_ALL8);
    GPIO_setAsOutputPin(   GPIO_PORT_P2, GPIO_PIN_ALL8);
    GPIO_setAsOutputPin(   GPIO_PORT_P3, GPIO_PIN_ALL8);
    GPIO_setAsOutputPin(   GPIO_PORT_P4, GPIO_PIN_ALL8);
    GPIO_setAsOutputPin(   GPIO_PORT_P5, GPIO_PIN_ALL8);
    GPIO_setAsOutputPin(   GPIO_PORT_P6, GPIO_PIN0 | GPIO_PIN1 | GPIO_PIN2);

}

/*
 * Initialize the SPI peripheral on EUSCI A1
 */
void init_spi_peripheral()
{
    //Initialize Master
        EUSCI_A_SPI_initMasterParam param = {0};
        param.selectClockSource = EUSCI_A_SPI_CLOCKSOURCE_SMCLK;
        param.clockSourceFrequency = CS_getSMCLK();
        param.desiredSpiClock = 1000000;
        param.msbFirst = UCMSB;
        param.clockPhase = 0;
        param.clockPolarity = EUSCI_A_SPI_CLOCKPOLARITY_INACTIVITY_LOW;
        param.spiMode = EUSCI_A_SPI_3PIN;
        EUSCI_A_SPI_initMaster(EUSCI_A1_BASE, &param);

        EUSCI_A_SPI_enable(EUSCI_A1_BASE);

}


void SetUpTransactions(void){       //All transactions padded to 4 bytes
    //Enable keep alive mode
    DCInit_transactions[0].Len = 4;
    DCInit_transactions[0].Data[0] = 0x10;
    DCInit_transactions[0].Data[1] = 0x5;
    DCInit_transactions[0].Data[2] = 0x11;
    DCInit_transactions[0].Data[3] = 0x0;
    DCInit_transactions[0].RxFlag = 0x0;        //No Receive

    //Enable Rx Interrupt flags
    DCInit_transactions[1].Len = 4;
    DCInit_transactions[1].Data[0] = 0x4;
    DCInit_transactions[1].Data[1] = 0x88;
    DCInit_transactions[1].Data[2] = 0x0;
    DCInit_transactions[1].Data[3] = 0x0;
    DCInit_transactions[1].RxFlag = 0x0;        //No Receive

    //Clear receive buffer
    DCInit_transactions[2].Len = 4;
    DCInit_transactions[2].Data[0] = 0xe0;
    DCInit_transactions[2].Data[1] = 0x0;
    DCInit_transactions[2].Data[2] = 0x0;
    DCInit_transactions[2].Data[3] = 0x0;
    DCInit_transactions[2].RxFlag = 0x0;        //No Receive


    //Wakeup UART slave devices
    DCInit_transactions[3].Len = 4;
    DCInit_transactions[3].Data[0] = 0xe0;
    DCInit_transactions[3].Data[1] = 0x30;
    DCInit_transactions[3].Data[2] = 0x0;
    DCInit_transactions[3].Data[3] = 0x0;
    DCInit_transactions[3].RxFlag = 0x0;        //No Receive
    //2ms delay for each slave to wake up

    //Wait for all UART slave devices to wake up
    DCInit_transactions[4].Len = 4;
    DCInit_transactions[4].Data[0] = 0x01;
    DCInit_transactions[4].Data[1] = 0x0;
    DCInit_transactions[4].Data[2] = 0x0;
    DCInit_transactions[4].Data[3] = 0x91;
    DCInit_transactions[4].RxFlag = 0x1;        //Receive of 0x21 expected

    //Read message command. This was added to see a SPI receive byte on the MSP430
    //Without this, there was no change in the SPI receive buffer.


    //End of UART slave device wake-up period
    DCInit_transactions[5].Len = 4;
    DCInit_transactions[5].Data[0] = 0x0e;
    DCInit_transactions[5].Data[1] = 0x10;
    DCInit_transactions[5].Data[2] = 0x0;
    DCInit_transactions[5].Data[3] = 0x0;
    DCInit_transactions[5].RxFlag = 0x0;        //No Receive
    //2ms delay for each slave to report null message

    //Wait for null message to be received
    DCInit_transactions[6].Len = 4;
    DCInit_transactions[6].Data[0] = 0x01;
    DCInit_transactions[6].Data[1] = 0x0;
    DCInit_transactions[6].Data[2] = 0x0;
    DCInit_transactions[6].Data[3] = 0x0;
    DCInit_transactions[6].RxFlag = 0x1;        //Receive 0x10 or 0x12

    //Clear transmit buffer
    DCInit_transactions[7].Len = 4;
    DCInit_transactions[7].Data[0] = 0x20;
    DCInit_transactions[7].Data[1] = 0x0;
    DCInit_transactions[7].Data[2] = 0x0;
    DCInit_transactions[7].Data[3] = 0x0;
    DCInit_transactions[7].RxFlag = 0x0;        //No Receive

    //Clear receive buffer
    DCInit_transactions[8].Len = 4;
    DCInit_transactions[8].Data[0] = 0xe0;
    DCInit_transactions[8].Data[1] = 0x0;
    DCInit_transactions[8].Data[2] = 0x0;
    DCInit_transactions[8].Data[3] = 0x0;
    DCInit_transactions[8].RxFlag = 0x0;        //No Receive

}


/**
 * main.c
 */
int main(void)
{

    uint32_t i;
    WDTCTL = WDTPW | WDTHOLD;   // stop watchdog timer


    init_clock();
    init_gpio();                                    // Set up IO pins

    GPIO_setOutputHighOnPin(GPIO_PORT_P3, GPIO_PIN1);

    // Configure SPI Pins for UCA1CLK, UCA1TXD/UCA1SIMO and UCA1RXD/UCA1SOMI
    /*
    * Select Port 2
    * Set Pin 4, Pin 5 and Pin 6 to input Secondary Module Function
    */
    GPIO_setAsPeripheralModuleFunctionInputPin(
        GPIO_PORT_P2,
        GPIO_PIN4 + GPIO_PIN5 + GPIO_PIN6,
        GPIO_PRIMARY_MODULE_FUNCTION
    );

    // Set P1.0 to output direction

    GPIO_setAsOutputPin (GPIO_PORT_P1, GPIO_PIN0);
    GPIO_setAsInputPinWithPullUpResistor (GPIO_PORT_P4, GPIO_PIN2);
    GPIO_enableInterrupt (GPIO_PORT_P4, GPIO_PIN2);
    GPIO_selectInterruptEdge (GPIO_PORT_P4, GPIO_PIN2, GPIO_HIGH_TO_LOW_TRANSITION);
    GPIO_clearInterrupt (GPIO_PORT_P4, GPIO_PIN2);

    PMM_unlockLPM5();


    SetUpTransactions();
    TXData = 0x0;                             // Holds transaction number


    // Setup peripheral(s) now that gpio and clocks are setup
    init_spi_peripheral();                      // Init Maxim spi peripheral
    GPIO_setOutputLowOnPin(GPIO_PORT_P3, GPIO_PIN1);        //Maxim chip select low
    for(i=10000; i>0; i--);                                  //idle time between SPI transactions

        for(SPI_TX_index = 0; SPI_TX_index < DCInit_transactions[TXData].Len; SPI_TX_index++){
            while(!(UCA1IFG & UCTXIFG));
            UCA1TXBUF = DCInit_transactions[TXData].Data[SPI_TX_index];
            while (!(UCA1IFG & UCRXIFG));
               RXData = UCA1RXBUF;
        }
        TXData = 1;
        for(SPI_TX_index = 0; SPI_TX_index < DCInit_transactions[TXData].Len; SPI_TX_index++){
            while(!(UCA1IFG & UCTXIFG));
            UCA1TXBUF = DCInit_transactions[TXData].Data[SPI_TX_index];
            while (!(UCA1IFG & UCRXIFG));
            RXData = UCA1RXBUF;
        }
        TXData = 2;
        for(SPI_TX_index = 0; SPI_TX_index < DCInit_transactions[TXData].Len; SPI_TX_index++){
           while(!(UCA1IFG & UCTXIFG));
           UCA1TXBUF = DCInit_transactions[TXData].Data[SPI_TX_index];
           while (!(UCA1IFG & UCRXIFG));
           RXData = UCA1RXBUF;
        }
        TXData = 3;
        for(SPI_TX_index = 0; SPI_TX_index < DCInit_transactions[TXData].Len; SPI_TX_index++){
           while(!(UCA1IFG & UCTXIFG));
           UCA1TXBUF = DCInit_transactions[TXData].Data[SPI_TX_index];
           while (!(UCA1IFG & UCRXIFG));
           RXData = UCA1RXBUF;
        }

        __delay_cycles(64000);

        TXData = 4;
        for(SPI_TX_index = 0; SPI_TX_index < DCInit_transactions[TXData].Len; SPI_TX_index++){
           while(!(UCA1IFG & UCTXIFG));
           UCA1TXBUF = DCInit_transactions[TXData].Data[SPI_TX_index];
           while (!(UCA1IFG & UCRXIFG));
           RXData = UCA1RXBUF;
        }
        if ((TXData == 4) && (RXData == 0x21)){
            DCInit_transactions[TXData].RxFlag = 0;
            TXData = 5;
        }

//    GPIO_setOutputHighOnPin(GPIO_PORT_P3, GPIO_PIN1);        //Maxim chip select high
}

//******************************************************************************
//
//This is the PORT2_VECTOR interrupt vector service routine
//
//******************************************************************************
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=PORT4_VECTOR
__interrupt
#elif defined(__GNUC__)
__attribute__((interrupt(PORT2_VECTOR)))
#endif
void P4_ISR (void)
{
 //   GPIO_setOutputHighOnPin(GPIO_PORT_P3, GPIO_PIN1);
    GPIO_clearInterrupt (GPIO_PORT_P4, GPIO_PIN2);
    // Toggle P1.0 output
    GPIO_toggleOutputOnPin (GPIO_PORT_P1, GPIO_PIN0);

    GPIO_disableInterrupt (GPIO_PORT_P4, GPIO_PIN2);
}


  • This code looks fine at first glance. 

    When the (meaningful) data is sent by the slave is something that is described in the slave datasheet. What slave device are you working with?

  • Is is the MAX17841 chip connected to two MAX17853 chips in daisy chain. I am trying the wakeup sequence. When the SPI transmit/receive is synchronous, what does this mean to the MOSI/MISO line? For eg., a response is expected after transaction 5. Will the response byte happen the same time as Transaction 5 (DCInit_transactions[4]) or the byte after?

    If there is no response expected do I expect to see the Tx and Rx bytes both on the scope at the same time (synchronous SPI)

  • The Tx takes 8 bit-times to be sent over MOSI. During that time the SPI is simultaneously clocking in MISO (Rx) bits. When that's done there is a simultaneous Rx-done event (RXIFG) and a Tx-done event (related, but not the same as TXIFG). Once the slave starts transmitting, you should see both MISO and MOSI on the scope.

    When the first byte is sent over MOSI, the slave has nothing to say, since it doesn't know what the master wants, so there's nothing meaningful on MISO. This may be expressed as 0, 1 or hi-Z (the datasheet will tell you). After 1 (or more) Tx bytes, the slave has something to say, so it drives MISO.

  • It looks like you're running the sequence in data sheet  (19-6790; Rev 3) Table 10. As I read Transaction 5 (your [4]), the status byte (0x21, we hope) will appear as the 2nd byte, not the 4th byte. The description implies it might not be 0x21 immediately, so you have to keep asking.

    Even before that, I would expect to see 0x05 on MISO for the 4th byte in Transaction 1 (your [0]), since it's reading back what was just written.

    If you're never seeing any activity on MISO, that suggests that something else is wrong. The text for Table 10 mentions /SHDN. Is that set appropriately?

    You may want to "start small": Construct a while(1) loop that just does Transaction 1 repeatedly (maybe with a human-time delay between) until you're getting consistent results.

    ------------------

    This may or may not be your problem, but it should be fixed:

    >  param.clockPhase = 0;

    This sets UCCKPH=0. Data sheet Table 2 prescribes CPHA=0, which corresponds to UCCKPH=1. For driverlib this would be:

    >  param.clockPhase = EUSCI_A_SPI_PHASE_DATA_CAPTURED_ONFIRST_CHANGED_ON_NEXT;

  • I want to see a timing diagram of Tx and Rx happening simultaneously.

    Arbitrary example: Tx 0xA must get Rx 0x5 for a response. Will both these bytes be seen in the MOSI/MISO lines in the clock cycles of the same transaction byte? What happens to Rx 0xA?

    Tx 0xB receives no response. Will Rx 0xB be visible on the MOSI and MISO lines in the clock cycles of the same byte?

    I know the Maxim slaves respond just right with the evaluation GUI. I don't see any setting for the SHDN in their GUI. I will see if I need to program this register.

  • Data sheet Figure 1 shows a sample SPI waveform. (Alas not a very good one -- would showing 2 full bytes have been that hard?) The "crosshatched" section is intended to denote "driven with indeterminate value", i.e. 0 or 1 but not Hi-Z.

    /SHDN is a pin [Ref data sheet p. 7]. It is active low, with a (very weak) internal pulldown, so if it's not connected the chip is stopped. You need to drive/pull it high. Do you have a wire to this pin?

    [Edit: To your specific example, you should see the first 8 SCLKs paired with MOSI=0x0A and MISO=<nonsense>. The second 8 clocks would show MOSI=<nonsense> and MISO=0x05.]

  • Thank you for your reply and pointers. I connected the GPIO from the MSP 430 and set it high for SHDL. This is connected to the MAX17841. I also connected the SHDL of the MAX17853 to 5V. I have the MSP 430 SPI signals connected to connecter J1 of the EV kit. I hope the MAX 17841 is receiving the SPI signals at its GPIOs. I have attached the EV kit schematic. Let me know otherwise. I also corrected the SPI clock phase. There is a bit of noise on the MISO line, following the SPI clock. It is just high during SPI transactions. I wish there was a simpler SPI peripheral I can connect to to see the MSP430 SPI receive in action. I don't have ideas for sanity check.MAX17841EVMINIQU.pdf

  • How are you powering the MAX17841? I'm supposing you're providing 3.3V to DCIN on J1, populating JU1 (to feed 3.3V to VAA), and leaving JU5 open? You want to make sure that the SPI is running at 3.3V.

    I expect that the MAX17853s are connected at P1 and P2, on the other side of the isolators, so I think they don't figure into this.

  • I have JU5 set to the 3.3V setting. I am taking a 3.3V jumper from the LP-MSP430 and plugging it into the DCIN test point on the MAX17841. SHDL, CS', SCLK, MOSI, MISO are all connections hooked to header J1. I have the 3.3V out of the LP also jumpered to the SHDL pins of both the MAX17853 eval boards. Vaa and DCin are both measuring 3.3V on J1 header in the MAX17841. SHNL on both the MAX17853 measures 3.3V. I see one high to low transition on the MISO pin, I assume when SHDL is asserted high by the MSP430. Following this, I see all the MOSI bytes, but only noise like ripples on MISO which is staying high. 

    Shunt position on one pin only is equivalent to header being left open (Table 1 of the MAX17841 EV kit).

    Thanks

  • After reading the section on "Register Transactions" [DS p. 9] a few more times, I now interpret it to mean: Each transaction (punctuated by /CS assert-de-assert) is either a read or a write, based on the first byte. Your transaction [0] is a write to registers 0x10, 0x12, 0x14, rather than a write and a read(-back) of register 0x10. This would suggest:

    1) Break your transaction [0] into two: the first (write) with 0x10, 0x05 and the second (read-back) with 0x11,0x00.

    2) For each of the two resulting transactions, assert /CS (low) before and de-assert it after.

    3) Repeat (as appropriate) for the other transactions.

    ----------------

    Tstartup [DS p.4], from /SHDN high to truly-active, can be up to 2ms. It's probably prudent to insert something like "__delay_cycles(2000);" [assuming MCLK=1MHz] between setting /SHDN high and starting the first transaction. (I don't recommend a for() loop as a delay, since it's hard to know what the compiler will do with it.)

  • That works!! Thank you SO much. I have one successful write followed by a precious read.