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.

MSP432P401R: MSP432 SPI 3 wire increment issue

Part Number: MSP432P401R

Hello,

I am trying to modify the SPI 3 wire increment example provided with the MSP DriverLib to control a signal generator (SparkFun MiniGen, basically an AD9837). The MiniGen came with some functions for setting up the signal generator that were useful, and it seems I only need to send a few control bits through SPI to properly configure the generator.

However, I am running into an issue, as it seems the SPI data is actually not transmitting. I made quite a few changes to the example code, and my current project looks below:

/******************************************************************************
 * MSP432 SPI - 3-wire Master Incremented Data
 *
 * This example shows how SPI master talks to SPI slave using 3-wire mode.
 * Incrementing data is sent by the master starting at 0x01. Received data is
 * expected to be same as the previous transmission.  eUSCI RX ISR is used to
 * handle communication with the CPU, normally in LPM0. Because all execution 
 * after LPM0 is in ISRs, initialization waits for DCO to stabilize against 
 * ACLK.
 *
 * Note that in this example, EUSCIB0 is used for the SPI port. If the user
 * wants to use EUSCIA for SPI operation, they are able to with the same APIs
 * with the EUSCI_AX parameters.
 *
 * ACLK = ~32.768kHz, MCLK = SMCLK = DCO 3MHz
 *
 * Use with SPI Slave Data Echo code example.
 *
 *                MSP432P401
 *              -----------------
 *             |                 |
 *             |                 |
 *             |                 |
 *             |             P1.6|-> Data Out (UCB0SIMO)
 *             |                 |
 *             |             P1.7|<- Data In (UCB0SOMI)
 *             |                 |
 *             |             P1.5|-> Serial Clock Out (UCB0CLK)
 *******************************************************************************/
/* DriverLib Includes */
#include <ti/devices/msp432p4xx/driverlib/driverlib.h>
#include <ti/drivers/GPIO.h>
#include <ti/drivers/SPI.h>
#include "minigen.h"

/* Standard Includes */
#include <stdint.h>
#include <stdbool.h>

/* Statics
static volatile uint8_t RXData = 0;
static uint8_t TXData = 0;*/

//![Simple SPI Config]
/* SPI Master Configuration Parameter */
const eUSCI_SPI_MasterConfig spiMasterConfig =
{
        EUSCI_B_SPI_CLOCKSOURCE_SMCLK,             // SMCLK Clock Source
        3000000,                                   // SMCLK = DCO = 3MHZ
        500000,                                    // SPICLK = 500khz
        EUSCI_B_SPI_MSB_FIRST,                     // MSB First
        EUSCI_B_SPI_PHASE_DATA_CHANGED_ONFIRST_CAPTURED_ON_NEXT,    // Phase
        EUSCI_B_SPI_CLOCKPOLARITY_INACTIVITY_HIGH, // High polarity
        EUSCI_B_SPI_3PIN                           // 3Wire SPI Mode
};
//![Simple SPI Config]

int main(void)
{
    /* Halting WDT  */
    WDT_A_holdTimer();

    //![Simple SPI Example]
    /* Selecting P1.5 P1.6 and P1.7 in SPI mode */
    GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P1,
            GPIO_PIN5 | GPIO_PIN6 | GPIO_PIN7, GPIO_PRIMARY_MODULE_FUNCTION);
    //primary module functions in MSP432 datasheet; P1.5 = BSLCLK, P1.6 SIMO, P1.7 SOMI

    /* Configuring SPI in 3wire master mode */
    SPI_initMaster(EUSCI_B0_BASE, &spiMasterConfig);

    /* Enable SPI module */
    SPI_enableModule(EUSCI_B0_BASE);

    /* Enabling interrupts */
    //SPI_enableInterrupt(EUSCI_B0_BASE, EUSCI_B_SPI_RECEIVE_INTERRUPT);
    //Interrupt_enableInterrupt(INT_EUSCIB0);
    //Interrupt_enableSleepOnIsrExit();
    //![Simple SPI Example]

    /* Polling to see if the TX buffer is ready */
    //while (!(SPI_getInterruptStatus(EUSCI_B0_BASE,EUSCI_B_SPI_TRANSMIT_INTERRUPT)));

    /* Transmitting data to slave */
    //minigen_reset();
    minigen_setMode(SQUARE);
    uint32_t newFreq = minigen_freqCalc(140000.0);
    minigen_adjustFreq(FREQ0, FULL, newFreq);
    //SPI_transmitData(EUSCI_B0_BASE, TXData);

    PCM_gotoLPM0();
    __no_operation();
}

//******************************************************************************
//
//This is the EUSCI_B0 interrupt vector service routine.
//
//******************************************************************************
void EUSCIB0_IRQHandler(void)
{/*
    uint32_t status = SPI_getEnabledInterruptStatus(EUSCI_B0_BASE);
    uint32_t jj;

    SPI_clearInterruptFlag(EUSCI_B0_BASE, status);

    if(status & EUSCI_B_SPI_RECEIVE_INTERRUPT)
    {
        /* USCI_B0 TX buffer ready?
        while (!(SPI_getInterruptStatus(EUSCI_B0_BASE, EUSCI_B_SPI_TRANSMIT_INTERRUPT)));

        RXData = SPI_receiveData(EUSCI_B0_BASE);

        /* Send the next data packet
        SPI_transmitData(EUSCI_B0_BASE, ++TXData);

        /* Delay between transmissions for slave to process information
        for(jj=50;jj<50;jj++);
    }
*/
}

I removed the ISR as it was causing the program to interrupt and sleep whenever any modifications to the SPI register were made. Was this ISR crucial in any way if I am not using SPI RX? That's the only thing I can think of that might have affected it in the above piece of code.

Below is the code for the MiniGen functions. As I mentioned, it is pretty simple on the face of it in that it only needs to send a few bit changes for each configuration. The only thing that I could think to be wrong here is that I am somehow using the SPI_transmitData function incorrectly. Can anyone point out what the issue might be here?

#include <ti/devices/msp432p4xx/driverlib/driverlib.h>
#include <ti/drivers/GPIO.h>
#include <ti/drivers/SPI.h>
#include "minigen.h"

void minigen_reset(){
    uint32_t defaultFreq = minigen_freqCalc(100.0);
    minigen_adjustFreq(FREQ0, FULL, defaultFreq);
    minigen_adjustFreq(FREQ1, FULL, defaultFreq);
    minigen_adjustPhaseShift(PHASE0, 0x0000);
    SPI_transmitData(EUSCI_B0_BASE, 0x0100);
    SPI_transmitData(EUSCI_B0_BASE, 0x0000);
}

// Set the mode of the part. The mode (trinagle, sine, or square) is set by
//  three bits in the status register: D5 (OPBITEN), D3 (DIV2), and D1 (MODE).
//  Here's a nice truth table for those settings:
//  D5 D1 D3
//  0  0  x   Sine wave output
//  0  1  x   Triangle wave output
//  1  0  0   Square wave @ 1/2 frequency
//  1  0  1   Square wave @ frequency
//  1  1  x   Not allowed
void minigen_setMode(MODE newMode){
    // We want to adjust the three bits in the config register that we're
    //  interested in without screwing up anything else. Unfortunately, this
    //  part is write-only, so we need to maintain a local shadow, adjust that,
    //  then write it.
    configReg &= ~0x002A; // Clear D5, D3, and D1.
    // This switch statement sets the appropriate bit in the config register.
    switch(newMode)
    {
        case TRIANGLE:
            configReg |= 0x0002;
        break;
        case SQUARE_2:
            configReg |=0x0020;
        break;
        case SQUARE:
            configReg |=0x0028;
        break;
        case SINE:
            configReg |=0x0000;
        break;
    }
    SPI_transmitData(EUSCI_B0_BASE, configReg); // Now write our shadow copy to the part.
}

// The AD9837 has two frequency registers that can be independently adjusted.
//  This allows us to fiddle with the value in one without affecting the output
//  of the device. The register used for calculating the output is selected by
//  toggling bit 11 of the config register.
void minigen_selectFreqReg(FREQREG reg)
{
    // For register FREQ0, we want to clear bit 11.
    if (reg == FREQ0) configReg &= ~0x0800;
    // Otherwise, set bit 11.
    else              configReg |= 0x0800;
    SPI_transmitData(EUSCI_B0_BASE, configReg);
}


// Similarly, there are two phase registers, selected by bit 10 of the config
//  register.
void  minigen_selectPhaseReg(PHASEREG reg)
{
  if (reg == PHASE0) configReg &= ~0x0400;
  else               configReg |= 0x0400;
  SPI_transmitData(EUSCI_B0_BASE, configReg);
}


// The frequency registers are 28 bits in size (combining the lower 14 bits of
//  two 16 bit writes; the upper 2 bits are the register address to write).
//  Bits 13 and 12 of the config register select how these writes are handled:
//  13 12
//  0  0   Any write to a frequency register is treated as a write to the lower
//          14 bits; this allows for fast fine adjustment.
//  0  1   Writes are send to upper 14 bits, allowing for fast coarse adjust.
//  1  x   First write of a pair goes to LSBs, second to MSBs. Note that the
//          user must, in this case, be certain to write in pairs, to avoid
//          unexpected results!
void minigen_setFreqAdjustMode(FREQADJUSTMODE newMode)
{
    // Start by clearing the bits in question.
    configReg &= ~0x3000;
    // Now, adjust the bits to match the truth table above.
    switch(newMode)
    {
        case COARSE:  // D13:12 = 01
            configReg |= 0x1000;
            break;
        case FINE:    // D13:12 = 00
            break;
        case FULL:    // D13:12 = 1x (we use 10)
            configReg |= 0x2000;
            break;
    }
    SPI_transmitData(EUSCI_B0_BASE, configReg);
}

// The phase shift value is 12 bits long; it gets routed to the proper phase
//  register based on the value of the 3 MSBs (4th MSB is ignored).
void minigen_adjustPhaseShift(PHASEREG reg, uint16_t newPhase)
{
    // First, let's blank the top four bits. Just because it's the right thing
    //  to do, you know?
    newPhase &= ~0xF000;
    // Now, we need to set the top three bits to properly route the data.
    //  D15:D13 = 110 for PHASE0...
    if (reg == PHASE0) newPhase |= 0xC000;
    // ... and D15:D13 = 111 for PHASE1.
    else               newPhase |= 0xE000;
    SPI_transmitData(EUSCI_B0_BASE, configReg);
}

// Okay, now we're going to handle frequency adjustments. This is a little
//  trickier than a phase adjust, because in addition to properly routing the
//  data, we need to know whether we're writing all 32 bits or just 16. I've
//  overloaded this function call for three cases: write with a mode change (if
//  one is needed), and write with the existing mode.

// Adjust the contents of the given register, and, if necessary, switch mode
//  to do so. This is probably the slowest method of updating a register.
void minigen_adjustFreq(FREQREG reg, FREQADJUSTMODE mode, uint32_t newFreq)
{
    minigen_setFreqAdjustMode(mode);

    // We need to split the 32-bit input into two 16-bit values, blank the top
    //  two bits of those values, and set the top two bits according to the
    //  value of reg.
    // Start by acquiring the low 16-bits...
    uint16_t temp = (uint16_t)newFreq;
    // ...and blanking the first two bits.
    temp &= ~0xC000;
    // Now, set the top two bits according to the reg parameter.
    if (reg==FREQ0) temp |= 0x4000;
    else            temp |= 0x8000;
    // Now, we can write temp out to the device.
    SPI_transmitData(EUSCI_B0_BASE, temp);
    // Okay, that's the lower 14 bits. Now let's grab the upper 14.
    temp = (uint16_t)(newFreq>>14);
    // ...and now, we can just repeat the process.
    temp &= ~0xC000;
    // Now, set the top two bits according to the reg parameter.
    if (reg==FREQ0) temp |= 0x4000;
    else            temp |= 0x8000;
    // Now, we can write temp out to the device.
    SPI_transmitData(EUSCI_B0_BASE, temp);
}

// Helper function, used to calculate the integer value to be written to a
//  freq register for a desired output frequency.
// The output frequency is fclk/2^28 * FREQREG. For us, fclk is 16MHz. We can
//  save processor time by specifying a constant for fclk/2^28- .0596. That is,
//  in Hz, the smallest step size for adjusting the output frequency.
uint32_t minigen_freqCalc(float desiredFrequency)
{
    return (uint32_t) (desiredFrequency/.0596);
}

  • You can use the ISR to either service the RX or TX or both, and the API Interrupt_enableSleepOnIsrExit will return the CPU to sleep (LPM0) after the ISR is complete. The Receive ISR would be entered after a byte is transmitted, and this would ensure that the TX buffer is available to be written to. Alternatively, the TX interrupt could be used and the buffer loaded with what needed to be sent. You see this in the polling piece of code that you commented out.

    Do you have a GPIO to function as the FSYNC pin?

    The SPI_transmitData API has two parameters, the EUSCI identifier and the byte to be transmitted. You should get a warning when you try to pass values of the wrong type.

    dev.ti.com/.../group__spi__api.html

    Hope that helps,
    Chris
  • Hi Chris,

    Thanks for your quick reply. I don't understand the role the ISR plays. Is it necessary for SPI to function properly?

    I have tied the FSYNC pin directly to ground, as I am not using any other chip and don't need to worry about selecting between any alternate chips (I'm looking for an "always on" kind of functionality). Was this not correct?

    Finally, am I passing the wrong type? I am not receiving any warnings related to those values being incorrect with SPI_TransmitData.
  • In this instance the ISR is incrementing the TXData (byte) variable and transmitting after a byte is received. It is not necessary.

    I am not familiar with the device, but I was curious to see how the FSYNC was handled.

    Yes, you are passing the wrong type. This should not prohibit the lower half from being sent.

    Chris
  • Let me know if you are still having trouble.

    Chris

**Attention** This is a public forum