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/EK-TM4C123GXL: Why SPI does not work with timer

Part Number: EK-TM4C123GXL
Other Parts Discussed in Thread: SYSBIOS

Tool/software: TI-RTOS

Dear TI team,

I tried using SPI with Timer4A periodic timer in TM4C123GXL evaluation kit. When i ran SPI and timer individually, it works. It fails when i combined it together. There's a strange behavior that when i ran,

SPI0_init();
Timer4_init();

It works. When i ran,

Timer4_init();
SPI0_init();

It fails. It fails in line - SPI_transfer(...). This is its output

System provider is set to SysMin. Halt the target to view any SysMin contents in ROV.
init timer
done
open spi [OK]
e: 0x20000400.
Task stack size: 0x400.
R0 = 0x00000000  R8  = 0x0000842c
R1 = 0x400ff028  R9  = 0xffffffff
R2 = 0x00000001  R10 = 0xffffffff
R3 = 0x00000008  R11 = 0xffffffff
R4 = 0x00008634  R12 = 0x000000a0
R5 = 0x00000000  SP(R13) = 0x20000748
R6 = 0x200007a8  LR(R14) = 0x00003189
R7 = 0x200007c0  PC(R15) = 0x00003188
PSR = 0x01000000
ICSR = 0x00417817
MMFSR = 0x00
BFSR = 0x00
UFSR = 0x0000
HFSR = 0x00000000
DFSR = 0x00000001
MMAR = 0xe000ed34
BFAR = 0xe000ed38
AFSR = 0x00000000
Terminating execution...

In ROV debugging, no BIOS error but there's a decoded exception in HWI, Undefined Hwi: 23. More detail in dubugging will be shown below.

I used tirtos for TivaC version 2.16.1.14, TI compiler version 5.2.6, CCS version 6.2.0.00050
I created project using tirtos empty project template

My question are
1) Why i cannot make SPI and timer to works at the same time?
2) What cause it fails when i ran Timer4_init() and then SPI0_init()

This is my full code and debug. Please help.

Thank you,

Sarawin

// ============= empty.c ===============/

/* XDCtools Header files */
#include <xdc/std.h>
#include <xdc/runtime/System.h>

/* BIOS Header files */
#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Task.h>
#include <ti/sysbios/knl/Clock.h>

/* TI-RTOS Header files */
#include <ti/drivers/GPIO.h>
#include <ti/drivers/I2C.h>
// #include <ti/drivers/SDSPI.h>
#include <ti/drivers/SPI.h>
#include <ti/drivers/UART.h>
// #include <ti/drivers/Watchdog.h>
// #include <ti/drivers/WiFi.h>

#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "driverlib/interrupt.h"
#include "driverlib/rom.h"
#include "driverlib/timer.h"
#include "driverlib/sysctl.h"

/* Board Header file */
#include "BoardConfig.h"

#define TASKSTACKSIZE   1024

Task_Struct task0_Struct;
Char task0_Stack[TASKSTACKSIZE];

static SPI_Handle spiHandle = NULL;

static void Timer4_init(void);
static void Timer4_intHandler(void);
static void SPI0_init(void);

static void Timer4_init()
{
  System_printf("init timer\r\n");
  System_flush();

  SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER4);
  TimerConfigure(TIMER4_BASE, TIMER_CFG_A_PERIODIC);

  // IntPrioritySet(INT_TIMER4A,4<<5);
  TimerIntRegister(TIMER4_BASE,TIMER_A,Timer4_intHandler);

  uint32_t const freq = SysCtlClockGet(); // freq = 80 000 000 = 80MHz
  uint32_t const period = freq >> 2; // Overflow period = 1 sec
  TimerLoadSet(TIMER4_BASE, TIMER_A, period);

  IntMasterEnable();
  TimerIntEnable(TIMER4_BASE, TIMER_TIMA_TIMEOUT);
  IntEnable(INT_TIMER4A);
  TimerEnable(TIMER4_BASE, TIMER_A);

  System_printf("done\r\n");
  System_flush();
}

static void Timer4_intHandler(void){
  TimerIntClear(TIMER4_BASE, TIMER_TIMA_TIMEOUT);
  GPIO_toggle(Board_LED1);
}

static void SPI0_init(void)
{
  SPI_Params spiParams;
  SPI_Params_init(&spiParams);
  spiParams.bitRate = 8000000;
  spiParams.frameFormat = SPI_POL0_PHA0;

  spiHandle = SPI_open(Board_SPI0,&spiParams);
  if(spiHandle == NULL){
    System_printf("open spi [FAIL]\r\n");
    System_flush();
    while(true);
  }

  System_printf("open spi [OK]\r\n");
  System_flush();

  SPI_Transaction transaction;
  uint8_t txBuf[4];
  uint8_t rxBuf[4];
  transaction.count = 4;
  transaction.txBuf = txBuf;
  transaction.rxBuf = rxBuf;

  uint8_t addr = 0x00;
  uint32_t val = 0x000000;

  txBuf[0] = addr;
  txBuf[1] = (uint8_t)((val>>16)&0xFF);
  txBuf[2] = (uint8_t)((val>>8)&0xFF);
  txBuf[3] = (uint8_t)((val>>0)&0xFF);

  SPI_transfer(spiHandle,&transaction);

  SPI_close(spiHandle);
}

static void task0_init(void)
{
  Timer4_init();
  SPI0_init();
}

void task0_fxn(UArg arg0, UArg arg1)
{
  task0_init();

  System_printf("Task start\r\n");
  System_flush();

  while (1) {
    Task_sleep((UInt)arg0);
    GPIO_toggle(Board_LED0);
  }
}

/*
 *  ======== main ========
 */
int main(void)
{
    Task_Params taskParams;

    /* Call board init functions */
    Board_initGeneral();
    Board_initGPIO();
    // Board_initI2C();
    // Board_initSDSPI();
    Board_initSPI();
    // Board_initUART();
    // Board_initUSB(Board_USBDEVICE);
    // Board_initWatchdog();
    // Board_initWiFi();


    /* Construct heartBeat Task  thread */
    Task_Params_init(&taskParams);
    taskParams.arg0 = 1000;
    taskParams.stackSize = TASKSTACKSIZE;
    taskParams.stack = &task0_Stack;
    Task_construct(&task0_Struct, (Task_FuncPtr)task0_fxn, &taskParams, NULL);

    /* Turn on user LED */
    GPIO_write(Board_LED0, Board_LED_ON);

    System_printf("Starting the example\nSystem provider is set to SysMin. "
                  "Halt the target to view any SysMin contents in ROV.\n");
    /* SysMin will only print to the console when you call flush or exit */
    System_flush();

    /* Start BIOS */
    BIOS_start();

    return (0);
}


// ====== SPI section in BoardConfig.c  ======= //

#if defined(__TI_COMPILER_VERSION__)
#pragma DATA_SECTION(SPI_config, ".const:SPI_config")
#pragma DATA_SECTION(spiTivaDMAHWAttrs, ".const:spiTivaDMAHWAttrs")
#endif

#include <ti/drivers/SPI.h>
#include <ti/drivers/spi/SPITivaDMA.h>

SPITivaDMA_Object spiTivaDMAObjects[BOARD_SPICOUNT];

#if defined(__TI_COMPILER_VERSION__)
#pragma DATA_ALIGN(spiTivaDMAscratchBuf, 32)
#elif defined(__IAR_SYSTEMS_ICC__)
#pragma data_alignment=32
#elif defined(__GNUC__)
__attribute__ ((aligned (32)))
#endif
uint32_t spiTivaDMAscratchBuf[BOARD_SPICOUNT];

const SPITivaDMA_HWAttrs spiTivaDMAHWAttrs[BOARD_SPICOUNT] = {
    {
        .baseAddr = SSI0_BASE,
        .intNum = INT_SSI0,
        .intPriority = (~0),
        .scratchBufPtr = &spiTivaDMAscratchBuf[0],
        .defaultTxBufValue = 0,
        .rxChannelIndex = UDMA_CHANNEL_SSI0RX,
        .txChannelIndex = UDMA_CHANNEL_SSI0TX,
        .channelMappingFxn = uDMAChannelAssign,
        .rxChannelMappingFxnArg = UDMA_CH10_SSI0RX,
        .txChannelMappingFxnArg = UDMA_CH11_SSI0TX
    },
};

const SPI_Config SPI_config[] = {
    {
        .fxnTablePtr = &SPITivaDMA_fxnTable,
        .object = &spiTivaDMAObjects[0],
        .hwAttrs = &spiTivaDMAHWAttrs[0]
    },
    {NULL, NULL, NULL},
};

void Board_initSPI(void)
{
    /* SPI0 */
    SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0);

    /* Need to unlock PF0 */
    GPIOPinConfigure(GPIO_PA2_SSI0CLK);
    GPIOPinConfigure(GPIO_PA3_SSI0FSS);
    GPIOPinConfigure(GPIO_PA4_SSI0RX);
    GPIOPinConfigure(GPIO_PA5_SSI0TX);

    GPIOPinTypeSSI(GPIO_PORTA_BASE, GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5);

    Board_initDMA();
    SPI_init();
}

// ===== SPI section in BoardConfig.h ===== //

typedef enum Board_SPIName {
    BOARD_SPI0 = 0,

    BOARD_SPICOUNT
} Board_SPIName;

  • Hi Sarawin,

    The problem is that you are calling TimerIntRegister. Take a look at the last post on this thread: e2e.ti.com/.../2347913

    It discusses why you cannot use driverlib's IntRegister to plug a vector in a TI-RTOS application. Note: TimerIntRegister calls IntRegister. You should not be calling IntMasterEnable either in a TI-RTOS based application.

    Todd
  • This diagnosis seems most reasonable - but for the fact that poster claims it is the "calling order" -  (and only one such order) which distresses.

    Follows his direct quote:  "There's a strange behavior that when i ran,

    SPI0_init();
    Timer4_init();

    It works. When i ran,

    Timer4_init();
    SPI0_init();

    It fails"

    Would not the disallowed, "IntRegister" - along w/equally disallowed, "IntMasterEnable" - both be called - independent of,  "calling order?"    (thus should not "either order" fail?)