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: speed problem

Part Number: MSP432P401R

This is my first project with MSP432, and I'm using a custom board.

External 2-channel ADC is connected to eUSCI_B0, and also external 1-channel DAC connected to eUSCI_B1. I'm using external 32MHz HFXT for MCLK and SMCLK.

I should take ADC samples from both channels, do some processing and write the result to DAC. The code below is simplified version - just ADC read and write value of one channel to DAC(no processing still).

The sampling is controlled by timers, TIMER_A0 - interrupt in 32kHz - setting ConversionStart signal for ADC, and running TIMER_A1 to enter CCR0 isr in ~9us (max conversion time of the ADC).

The rest is just ADC result read (4 bytes via SPI) and DAC write (3 bytes via SPI).

This 2 actions last for 80us! I can not reach even sampling frequency speed, not to mention any processing. 

I tried even just a bit set/reset in TIMER_A1 routine (no SPI at all) and that single instruction lasts for 4us!

I would appreciate any help.

Here is the code with SPI:

#include "msp.h"
#include <driverlib.h>
#include <stdio.h>
#include <arm_math.h>
#include "arm_const_structs.h"

#define LFXT_FREQUENCY          32768
#define HFXT_FREQUENCY          32000000
#define SMCLK_FREQUENCY         HFXT_FREQUENCY
#define SPI_CLK_FREQUENCY       SMCLK_FREQUENCY/2


const eUSCI_SPI_MasterConfig spiMasterConfig =
{
  EUSCI_B_SPI_CLOCKSOURCE_SMCLK,                // SMCLK Clock Source
  SMCLK_FREQUENCY,                              // SMCLK = HFXT = 32MHz
  SPI_CLK_FREQUENCY,                            // SPICLK = SMCLK/2 = 16MHz
  EUSCI_B_SPI_MSB_FIRST,                        // MSB First
  EUSCI_B_SPI_PHASE_DATA_CHANGED_ONFIRST_CAPTURED_ON_NEXT,
  EUSCI_B_SPI_CLOCKPOLARITY_INACTIVITY_LOW,     // Low clock polarity
  EUSCI_B_SPI_3PIN                              // 3-wire SPI mode
};


uint16_t X, Y;
uint8_t ConvOver = 0;


void InitADC (void);
void ReadADCResults (void);
void InitDAC (void);
void uWriteDAC (uint16_t value);
void TimerA0_IRQHandler(void);
void TimerA1_IRQHandler(void);

void main(void)
{
    /* Halting WDT and disabling master interrupts */
    MAP_WDT_A_holdTimer();
    MAP_Interrupt_disableMaster();

    /* Set the core voltage level to VCORE1 */
    MAP_PCM_setCoreVoltageLevel(PCM_VCORE1);
    MAP_FlashCtl_setWaitState(FLASH_BANK0, 2);
    MAP_FlashCtl_setWaitState(FLASH_BANK1, 2);
    
    /* Initializes Clock System */
    MAP_GPIO_setAsPeripheralModuleFunctionOutputPin(GPIO_PORT_PJ,
          GPIO_PIN3 | GPIO_PIN2, GPIO_PRIMARY_MODULE_FUNCTION);
    MAP_CS_setExternalClockSourceFrequency(LFXT_FREQUENCY,HFXT_FREQUENCY);
    MAP_CS_startHFXT(false);
    MAP_CS_initClockSignal(CS_MCLK, CS_HFXTCLK_SELECT, CS_CLOCK_DIVIDER_1 );
    MAP_CS_initClockSignal(CS_HSMCLK, CS_HFXTCLK_SELECT,CS_CLOCK_DIVIDER_1 );
    MAP_CS_initClockSignal(CS_SMCLK, CS_HFXTCLK_SELECT,CS_CLOCK_DIVIDER_1 );
    MAP_CS_initClockSignal(CS_ACLK, CS_REFOCLK_SELECT, CS_CLOCK_DIVIDER_1);
    
    

    /* Initialize Peripherals */
    InitADC();
    InitDAC();
    
    
    /* Initialize TimerA0 and map interrupt */   
    TIMER_A0->CCTL[0] &= ~TIMER_A_CCTLN_CCIFG;
    TIMER_A0->CCTL[0] = TIMER_A_CCTLN_CCIE; // TACCR0 interrupt enabled
    TIMER_A0->CCR[0] = 1000 - 1;
    TIMER_A0->CTL = TIMER_A_CTL_SSEL__SMCLK | // SMCLK, UP mode
            TIMER_A_CTL_MC__UP;
    MAP_Interrupt_enableInterrupt(INT_TA0_0);
    
    /* Initialize TimerA1 and map interrupt */   
    TIMER_A1->CCTL[0] &= ~TIMER_A_CCTLN_CCIFG;
    TIMER_A1->CCTL[0] = TIMER_A_CCTLN_CCIE; // TACCR1 interrupt enabled
    TIMER_A1->CCR[0] = 0;
    TIMER_A1->CTL = TIMER_A_CTL_SSEL__SMCLK | // SMCLK, UP mode
            TIMER_A_CTL_MC__UP;
    MAP_Interrupt_enableInterrupt(INT_TA1_0);
    
  
    /* General Interrupt Enable */
    MAP_Interrupt_enableMaster();
        
    while(1)
    {
      while(!ConvOver);
      ConvOver = 0;     
     
      ReadADCResults();          // Will be written to X, Y

        P2->OUT &= ~BIT1;         // CONVST = 0

      uWriteDAC(X);
    }
}

void InitADC (void)
{
  // * Port init for ADC SPI communication
    // P1.5 - SCLK - SPI output
    GPIO_setAsPeripheralModuleFunctionOutputPin(GPIO_PORT_P1,GPIO_PIN5, GPIO_PRIMARY_MODULE_FUNCTION);
    // P1.7 - SDATA - SOMI - SPI input
    GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P1,GPIO_PIN7, GPIO_PRIMARY_MODULE_FUNCTION);    
    // P2.1 - CONVST - GPIO output
    GPIO_setAsOutputPin(GPIO_PORT_P2, GPIO_PIN1);
    GPIO_setOutputLowOnPin(GPIO_PORT_P2, GPIO_PIN1);
  
  // * Initialize USCI_B0 for SPI master operation
    SPI_initMaster(EUSCI_B0_BASE, &spiMasterConfig);
    SPI_enableModule(EUSCI_B0_BASE);
    SPI_clearInterruptFlag(EUSCI_B0_BASE, EUSCI_SPI_RECEIVE_INTERRUPT);
}

void ReadADCResults (void)
{
  X = 0;
  Y = 0;
  
  Interrupt_disableMaster();
  
  SPI_clearInterruptFlag(EUSCI_B0_BASE, EUSCI_SPI_RECEIVE_INTERRUPT);
  
  while (!(EUSCI_B0->IFG & EUSCI_B_IFG_TXIFG));
  SPI_transmitData(EUSCI_B0_BASE, 0xFF);
  while (!(EUSCI_B0->IFG & EUSCI_B_IFG_TXIFG));
  Y = SPI_receiveData(EUSCI_B0_BASE);
  Y = Y<<8;
  while (!(EUSCI_B0->IFG & EUSCI_B_IFG_TXIFG));
  SPI_transmitData(EUSCI_B0_BASE, 0xFF);
  while (!(EUSCI_B0->IFG & EUSCI_B_IFG_TXIFG));
  Y += SPI_receiveData(EUSCI_B0_BASE);
  
  while (!(EUSCI_B0->IFG & EUSCI_B_IFG_TXIFG));
  SPI_transmitData(EUSCI_B0_BASE, 0xFF);
  while (!(EUSCI_B0->IFG & EUSCI_B_IFG_TXIFG));
  X = SPI_receiveData(EUSCI_B0_BASE);
  X = X<<8;
  while (!(EUSCI_B0->IFG & EUSCI_B_IFG_TXIFG));
  SPI_transmitData(EUSCI_B0_BASE, 0xFF);
  while (!(EUSCI_B0->IFG & EUSCI_B_IFG_TXIFG));
  X += SPI_receiveData(EUSCI_B0_BASE);
  
  Interrupt_enableMaster();
}

void InitDAC (void)
{
  // * Port init for DAC SPI communication
    // P6.3 - SCLK - SPI output
    GPIO_setAsPeripheralModuleFunctionOutputPin(GPIO_PORT_P6, GPIO_PIN3, GPIO_PRIMARY_MODULE_FUNCTION);
    // P6.4 - DATA - SIMO - SPI output
    GPIO_setAsPeripheralModuleFunctionOutputPin(GPIO_PORT_P6, GPIO_PIN4, GPIO_PRIMARY_MODULE_FUNCTION);
    // P5.7 - SYNC - GPIO output
    GPIO_setAsOutputPin(GPIO_PORT_P5, GPIO_PIN7);
    GPIO_setOutputHighOnPin(GPIO_PORT_P5, GPIO_PIN7);
  
  // * Initialize USCI_B1 for SPI master operation
    SPI_initMaster(EUSCI_B1_BASE, &spiMasterConfig);
    SPI_enableModule(EUSCI_B1_BASE);
    SPI_clearInterruptFlag(EUSCI_B1_BASE, EUSCI_SPI_RECEIVE_INTERRUPT);
}

void uWriteDAC (uint16_t value)
{
  Interrupt_disableMaster();
  
  GPIO_setOutputLowOnPin(GPIO_PORT_P5, GPIO_PIN7);
  
  while (!(EUSCI_B1->IFG & EUSCI_B_IFG_TXIFG));
  SPI_transmitData(EUSCI_B1_BASE, (value>>10)&0x3F);
  while (!(EUSCI_B1->IFG & EUSCI_B_IFG_TXIFG));
  SPI_transmitData(EUSCI_B1_BASE, value>>2);
  while (!(EUSCI_B1->IFG & EUSCI_B_IFG_TXIFG));
  SPI_transmitData(EUSCI_B1_BASE, value<<6);
  while (!(EUSCI_B1->IFG & EUSCI_B_IFG_TXIFG));
  
  GPIO_setOutputHighOnPin(GPIO_PORT_P5, GPIO_PIN7);
  
  Interrupt_enableMaster();
}


/* ISR for TimerA0 */
void TimerA0_IRQHandler(void)
{
  TIMER_A0->CCTL[0] &= ~TIMER_A_CCTLN_CCIFG;

  // Start conversion here
  P2->OUT |= BIT1;              // CONVST = 1
  
  TA1CCR0 = 280;                // Set TimerA1 to max conversion time
}

/* ISR for TimerA1 */
void TimerA1_IRQHandler(void)
{
  TIMER_A1->CCTL[0] &= ~TIMER_A_CCTLN_CCIFG;
  ConvOver = 1;                 // Set Conversion Over Flag
  TA1CCR0 = 0;                  // Halt TimerA1
}

  • I managed to measure SPI signals timing by scope, the bit-send frequency is good (0.5us for 1 byte is correct, frequency is 16MHz), but the timing between 2 bytes is almost 4us!

    That is the line
    while (!(EUSCI_B1->IFG & EUSCI_B_IFG_TXIFG));
    between byte writes.

    Am I waiting for a wrong flag to clear?
  • Hello,
    A couple of quick observations:
    (1) Flash wait states can be set to 1 for frequencies up to 48Mhz, set to 0 for frequencies up to 24Mhz.
    (2) The eUSCI transmit is buffered, so the TXIFG represents that the value loaded has ‘fallen’ into the shift register while the RXIFG represents when the data is actually transmitted (and received). You should be able to write to the TX buffer two times before checking for the TXIFG.
    (3) You can use bit-banding to access the registers more quickly.

    Chris
  • Yes Chris, thank you, I already switched to register level and got a significant improvement, but with your suggestion about flash wait states DAC write works perfectly, 0.5us per byte send and 240ns between bytes. So this cleared the SPI speed issue, and I will check the "resolved" box.

    But, I still struggle with complete DAC write function speed in 2 different things:

    1) 3bytes sent + 4 wait states  = 2.46us 

        But with chip select high-to-low before data send (P5->OUT &= ~BIT7;) and low-to-high after (P5->OUT |= BIT7;), when I measure the pulse on P5.7 it is 3.3us long. Is it possible that a GPIO pin reset/set takes so so much time?

    2) And with a single disable/enable interrupt before and after this, the function duration rises to 4.5us (measured by control pin set at the begining of the function and reset at the end):

    void uWriteDAC (uint16_t value)
    {
    P5->OUT |= BIT2;                                                            //------------------------------------------------------------
    __disable_irq();                                                                //
    // Set output low on pin P5.7                                            //
    P5->OUT &= ~BIT7;                                                        //---------------------------------
                                                                                            //--------                           |
    while (!(EUSCI_B1->IFG & EUSCI_B_IFG_TXIFG));     //          |                          |
    EUSCI_B1->TXBUF = (value>>10)&0x3F;                     //          |                         |
    while (!(EUSCI_B1->IFG & EUSCI_B_IFG_TXIFG));     //          
    EUSCI_B1->TXBUF = value>>2;                                    // 2.46us                   3.3us                        4.5us 
    while (!(EUSCI_B1->IFG & EUSCI_B_IFG_TXIFG));     //          
    EUSCI_B1->TXBUF = value<<6;                                    //          |                         |
    while (!(EUSCI_B1->IFG & EUSCI_B_IFG_TXIFG));     //          |                         |
                                                                                            //--------                          |
    // Set output high on pin P5.7                                         //----------------------------------
    P5->OUT |= BIT7;                                                          //
    __enable_irq();                                                               //
    P5->OUT &= ~BIT2;                                                      //------------------------------------------------------------------

    }

    Looks like a single pin set or reset lasts for ~400ns.

    Is there a way to improve performance?

**Attention** This is a public forum