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); }