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.

RTOS/CC1310: SPI (SSI) does not work when LF XOSC selected

Part Number: CC1310

Tool/software: TI-RTOS

There is a project using SPI (SSI). It uses its own function to initialize the interface to ensure maximum speed (for working with the display). The rest is from the SDK.
Everything works adequately. Radio, PWM, and everything else. In addition to regimes with reduced consumption in conjunction with SSI.
The current consumption in Standby is about 1mA.
Used TI-RTOS, simplelink_cc13x0_sdk_2_10_00_36.

By experiments it turned out that the problem is in the settings of the clock generators.
The ccfg.c file specifies the type of generator used.
With the parameter
#define SET_CCFG_MODE_CONF_SCLK_LF_OPTION 0x0 // LF clock derived from High Frequency XOSC

All works adequately, but increased current consumption. About 1ma. It's understandable - the generator works at 24MHz, which for some reason does not stop when switching to standby.
SSI adequately operates at a bit rate of up to 24MHz, falls asleep (decreases consumption when turned off) and wakes up adequately.
Those. consumption, most likely, only because of the generator.

Next....

#define SET_CCFG_MODE_CONF_SCLK_LF_OPTION 0x2 // LF XOSC

SSI does not work. Those. Access to the registers passes, modes are configured, but after writing to the buffer the transfer does not start. And after recording the 8th packet (the depth of the hardware buffer 8) - everything stops on the line (in the regular function SSIDataPut):
    // Wait until there is space.
    while (! (HWREG (ui32Base + SSI_O_SR) & SSI_SR_TNF))
    {
    }
Waiting for transmission. Nothing is naturally withdrawn. At all. Including the CS line does not transfer to a low level.

With these settings of the generator and the use of software SPI (everything is configured programmatically, without the use of hardware SSI), everything works adequately, the radio operates, at the right frequencies, etc. Consumption is minimal - my device can not be measured, <0.001ma.
Those. everything is normal. But slowly. Since  SSI is used for display and software is very slow.

With these same generator settings and using the SPI driver supplied in the SDK - everything works, it goes to sleep, but the SPI works even slower than software, because very large pauses between individual parcels. DMA to use is not real, because There are no resources for the full buffer and the data alternates with the commands, which requires constant updating of what is being transferred.

Actually questions.
1. How to put the HF_XOSC generator into sleep mode when setting SET_CCFG_MODE_CONF_SCLK_LF_OPTION 0x0. And to include it when you leave sleep.

2. Why with SET_CCFG_MODE_CONF_SCLK_LF_OPTION 0x2 // LF XOSC
does not work adequately with SSI, whereas with clause 1. - everything works fine. What I miss when it is initialized?

3. How to switch the generator "on the fly" before going into hibernation mode and when exiting it.
Recommended construction type
// if (OSCClockSourceGet (OSC_SRC_CLK_LF)! = OSC_XOSC_LF)
// {
// / * Request to switch to the crystal to enable radio operation. * /
// OSCClockSourceSet (OSC_SRC_CLK_LF, OSC_XOSC_LF);
// // OSCClockSourceSet (OSC_SRC_CLK_LF | OSC_SRC_CLK_MF | OSC_SRC_CLK_HF, OSC_XOSC_HF);
// / * Switch the HF source to XTAL - Done via ROM API * /
// while (OSCClockSourceGet (OSC_SRC_CLK_LF)! = OSC_XOSC_LF);
//}
// OSCHF_SwitchToRcOscTurnOffXosc ();
// if (OSCClockSourceGet (OSC_SRC_CLK_HF)! = OSC_RCOSC_HF)
// {
// / * Request to switch to the crystal to enable radio operation. * /
// OSCClockSourceSet (OSC_SRC_CLK_MF | OSC_SRC_CLK_HF, OSC_RCOSC_HF);
// // OSCClockSourceSet (OSC_SRC_CLK_LF | OSC_SRC_CLK_MF | OSC_SRC_CLK_HF, OSC_XOSC_HF);
// / * Switch the HF source to XTAL - Done via ROM API * /
// OSCHfSourceSwitch ();
// OSCClockSourceSet (OSC_SRC_CLK_LF, OSC_RCOSC_LF);
//}
The effect is not given.

int main(void)
{
    Task_Params taskParams;
    Error_init(&eb);

    Semaphore_Params semParams;

    Power_init();

// if(OSCClockSourceGet(OSC_SRC_CLK_HF) != OSC_XOSC_HF) 
// {
// /* Request to switch to the crystal to enable radio operation. */
// 
// OSCClockSourceSet(OSC_SRC_CLK_LF | OSC_SRC_CLK_MF | OSC_SRC_CLK_HF, OSC_XOSC_HF);
// /* Switch the HF source to XTAL - Done via ROM API*/
// OSCHfSourceSwitch();
// }


    PIN_init(BoardGpioTable); 
    PIN_init(ButtonTable); 

    /* Configure task. */
    Task_Params_init(&taskParams);
    taskParams.stack = RemoteTaskStack;
    taskParams.stackSize = sizeof(RemoteTaskStack);
    Task_construct(&RemoteTask, taskFxn, &taskParams, NULL);

    /* Configure shutdown semaphore. */
    Semaphore_Params_init(&semParams);
    semParams.mode = Semaphore_Mode_BINARY;
    Semaphore_construct(&shutdownSem, 0, &semParams);

    Error_init(&eb);
    Clock_Params_init(&clk0Params);
    clk0Params.period = (10*(1000 / Clock_tickPeriod)); //10ms
    clk0Params.startFlag = TRUE;
    clk0Params.arg = 0;
    clk0Clock = Clock_create(clk0Fxn, (10*(1000 / Clock_tickPeriod)), &clk0Params, &eb);

    /* Start kernel. */
    BIOS_start();

    return 0;
}
static void taskFxn(UArg a0, UArg a1)
{
  uint8_t i=0, key=0;

  boardPinHandle = PIN_open(&GpioPinState, BoardGpioTable);
  buttonPinHandle = PIN_open(&buttonPinState, ButtonTable);
  PIN_registerIntCb(buttonPinHandle, &buttonCallbackFxn);

#ifdef USE_FSPI
  SSI_handle_init(&tft_spi, 0);
  tft_spi.dataSize = 9;
  tft_spi.bitRate = 12000000;
  tft_spi.mosiPin = TFT_MOSI;
  tft_spi.clkPin = TFT_CLK;
  tft_spi.csnPin = TFT_CS;
  fspiinit(&tft_spi);
#endif

  TFT_display_init();

  PWM_init();
  PWM_Params_init(&tft_led_pwm_params);
  tft_led_pwm_params.dutyUnits = PWM_DUTY_US;
  tft_led_pwm_params.dutyValue = TFT_PWM_MIN_DUTY;
  tft_led_pwm_params.periodUnits = PWM_PERIOD_US;
  tft_led_pwm_params.periodValue = pwmPeriod;
  tft_led_pwm = PWM_open(CC1310_PWM0, &tft_led_pwm_params);
  PWM_start(tft_led_pwm);
  PWM_setDuty(tft_led_pwm, duty);

  delay(3000);
  display_clear();

  rf_init(0); 
}

Hibernate and exit:

      Clock_stop(clk0Clock);
      PWM_stop(tft_led_pwm);

      disp_spi_transfer_cmd(ST7789_10_SLPIN);
      fspi_disable(&tft_spi);
      RF_close(rfHandle);
      Semaphore_pend(Semaphore_handle(&shutdownSem), BIOS_WAIT_FOREVER);

My functions for initialization of SPI (SSI)
typedef struct SSI_HANDLE_ 
{
  uint32_t        baseAddr; /*! SPI Peripheral's base address */
  SPI_Mode        mode;     /*!< Master or Slave mode */
  unsigned int    bitRate;
  unsigned int    dataSize;            /*!< SPI data frame size in bits */
  SPI_FrameFormat frameFormat;         /*!< SPI frame format */

  PowerCC26XX_Resource   powerMngrId; /*! SPI Peripheral's power manager ID */
  
  PIN_State      pinState;
  PIN_Handle     pinHandle;
  
  PIN_Id         mosiPin;
  PIN_Id         misoPin;
  PIN_Id         clkPin;
  PIN_Id         csnPin;
  
}SSI_HANDLE;

/* Default SSI handle structure */
const SSI_HANDLE SSI_default_handle = {
  NULL,  
  SPI_MASTER,  
  4000000,   
  8,         
  SPI_POL0_PHA0,
  NULL, //PowerCC26XX_PERIPH_SSI0,
  {
    NULL,
    NULL,
    NULL
  },
  NULL,
  PIN_UNASSIGNED,
  PIN_UNASSIGNED,
  PIN_UNASSIGNED,
  PIN_UNASSIGNED
};


/* Mapping SPI frame format from generic driver to CC26XX driverlib */
const uint32_t fsframeFormat[] = {
    SSI_FRF_MOTO_MODE_0,    /* SPI_POLO_PHA0 */
    SSI_FRF_MOTO_MODE_1,    /* SPI_POLO_PHA1 */
    SSI_FRF_MOTO_MODE_2,    /* SPI_POL1_PHA0 */
    SSI_FRF_MOTO_MODE_3,    /* SPI_POL1_PHA1 */
    SSI_FRF_TI,             /* SPI_TI */
    SSI_FRF_NMW             /* SPI_MW */
};

/* Mapping SPI mode from generic driver to CC26XX driverlib */
const uint32_t fsmode[] = {
    SSI_MODE_MASTER,    /* SPI_MASTER */
    SSI_MODE_SLAVE      /* SPI_SLAVE */
};

void SSI_handle_init(SSI_HANDLE *hndl, uint8_t SSI_index)
{
    *hndl = SSI_default_handle;
    if ( SSI_index )
    {
      hndl->baseAddr = SSI1_BASE;
      hndl->powerMngrId = PowerCC26XX_PERIPH_SSI1;
    } else
    {
      hndl->baseAddr = SSI0_BASE;
      hndl->powerMngrId = PowerCC26XX_PERIPH_SSI0;
    }
}


void fspiinit(SSI_HANDLE *hndl)
{

  ClockP_FreqHz               freq;
  PIN_Config                  spiPinTable[5];
  uint32_t i = 0;

  /* Register power dependency - i.e. power up and enable clock for SPI. */
  Power_setDependency(hndl->powerMngrId);
  /*
  PRCMPowerDomainOn(PRCM_DOMAIN_SERIAL);
  PRCMPeripheralRunEnable(PRCM_PERIPH_SSI0);
  PRCMLoadSet();
  PRCMPeripheralSleepDisable(PRCM_PERIPH_SSI0);
  PRCMLoadSet();
  PRCMPeripheralDeepSleepDisable(PRCM_PERIPH_SSI0);
  PRCMLoadSet();
  
  SSIDMADisable(hndl->baseAddr, SSI_DMA_RX | SSI_DMA_TX );
  */
  
  /* Configure the hardware module */
  SSIDisable(hndl->baseAddr);
  /* Disable SPI module interrupts */
  SSIIntDisable(hndl->baseAddr, SSI_RXOR | SSI_RXFF | SSI_RXTO | SSI_TXFF);
  SSIIntClear(hndl->baseAddr, SSI_RXOR | SSI_RXFF | SSI_RXTO | SSI_TXFF);

  /* Set the SPI configuration */
  ClockP_getCpuFreq(&freq);
  SSIConfigSetExpClk(hndl->baseAddr, freq.lo, fsframeFormat[hndl->frameFormat],
                     fsmode[hndl->mode], hndl->bitRate, hndl->dataSize);

  spiPinTable[i++] = hndl->mosiPin | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_INPUT_DIS | PIN_DRVSTR_MED;
  spiPinTable[i++] = hndl->misoPin | PIN_INPUT_EN | PIN_PULLDOWN;
  /* Output low signal on SCLK until SPI module drives signal if clock polarity is configured to '0' */
  /* Output high signal on SCLK until SPI module drives signal if clock polarity is configured to '1' */
  if ( hndl->frameFormat == SPI_POL0_PHA0 || hndl->frameFormat == SPI_POL0_PHA1 ) {
      spiPinTable[i++] = hndl->clkPin | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_INPUT_DIS | PIN_DRVSTR_MED;
  }
  else {
      spiPinTable[i++] = hndl->clkPin | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH | PIN_PUSHPULL | PIN_INPUT_DIS | PIN_DRVSTR_MED;
  }
  /* If CSN isn't SW controlled, drive it high until SPI module drives signal to avoid glitches */
  if(hndl->csnPin != PIN_UNASSIGNED) {
      spiPinTable[i++] = hndl->csnPin | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH | PIN_PUSHPULL | PIN_INPUT_DIS | PIN_DRVSTR_MED;
  }
  spiPinTable[i++] = PIN_TERMINATE;
  
  /* Open and assign pins through pin driver */
  if (!(hndl->pinHandle = PIN_open(&(hndl->pinState), spiPinTable))) {
      return;
  }

  /* Set IO muxing for the SPI pins */
  if (fsmode[hndl->mode] == SSI_MODE_SLAVE) {
      /* Configure IOs for slave mode */
      PINCC26XX_setMux(hndl->pinHandle, hndl->mosiPin, (hndl->baseAddr == SSI0_BASE ? IOC_PORT_MCU_SSI0_RX  : IOC_PORT_MCU_SSI1_RX));
      PINCC26XX_setMux(hndl->pinHandle, hndl->misoPin, (hndl->baseAddr == SSI0_BASE ? IOC_PORT_MCU_SSI0_TX  : IOC_PORT_MCU_SSI1_TX));
      PINCC26XX_setMux(hndl->pinHandle, hndl->clkPin,  (hndl->baseAddr == SSI0_BASE ? IOC_PORT_MCU_SSI0_CLK : IOC_PORT_MCU_SSI1_CLK));
      PINCC26XX_setMux(hndl->pinHandle, hndl->csnPin,   (hndl->baseAddr == SSI0_BASE ? IOC_PORT_MCU_SSI0_FSS : IOC_PORT_MCU_SSI1_FSS));
  }
  else {
      /* Configure IOs for master mode */
      PINCC26XX_setMux(hndl->pinHandle, hndl->mosiPin, (hndl->baseAddr == SSI0_BASE ? IOC_PORT_MCU_SSI0_TX  : IOC_PORT_MCU_SSI1_TX));
      PINCC26XX_setMux(hndl->pinHandle, hndl->misoPin, (hndl->baseAddr == SSI0_BASE ? IOC_PORT_MCU_SSI0_RX  : IOC_PORT_MCU_SSI1_RX));
      PINCC26XX_setMux(hndl->pinHandle, hndl->clkPin,  (hndl->baseAddr == SSI0_BASE ? IOC_PORT_MCU_SSI0_CLK : IOC_PORT_MCU_SSI1_CLK));
      if(hndl->csnPin != PIN_UNASSIGNED) {
          PINCC26XX_setMux(hndl->pinHandle, hndl->csnPin, (hndl->baseAddr == SSI0_BASE ? IOC_PORT_MCU_SSI0_FSS  : IOC_PORT_MCU_SSI1_FSS));
      }
  }
      
  
  /* Enable the SPI module */
  SSIEnable(hndl->baseAddr);
}

void fspi_enable(SSI_HANDLE *hndl)
{
 /* Enable the SPI module */
//  Power_setDependency(hndl->powerMngrId);
  SSIEnable(hndl->baseAddr);
}

void fspi_disable(SSI_HANDLE *hndl)
{
  SSIDisable(hndl->baseAddr);
//  Power_releaseDependency(hndl->powerMngrId);
  
}

  • Hi Andrey,

    Starting from the top,  there should be no reason for the SSI to stop working when setting:

     #define SET_CCFG_MODE_CONF_SCLK_LF_OPTION               0x2        // LF XOSC

    This is even the default setting used in our SPI examples. I can see any obvious configuration misses, the power driver should cause the behavior you describe. You probably want to use Power_setConstraints to prevent the Power driver from putting you into a mode you do not expect while doing transfers. 

    To test this I ran the TI-RTOS SPI driver in polling mode (utilizing SSIDataPut) using the LF XOSC source without any problems.

    Regarding using the SDK SPI Driver; you say it is slow, how are you setting up the SPI driver, what is your definition of slow in this case? I would expect the driver to do simple polling transfers if the DMA transfer threshold is set high enough and the mode is blocking. If configured in callback it would always do DMA transfers which of course would give a big overhead on small transfers.

    Regarding the questions, I would not recommend that you do clock switching yourself as it will most likely end up conflicting with the TI-RTOS power driver. The power driver will typically control clocks and power domains to put the device in to the lowest power mode possible based on the constraints.