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.

TM4C1294NCPDT: SPI Master from bootloader to SD card

Part Number: TM4C1294NCPDT

Trying to get bootloader from SD card working. Sorry, to post code with direct register writes, but all the Tivaware functions don't seem to be available or work from bootloader.

I'm not able to get any data going out the SPI port to the SD card. My hardware is good and my main application will read directories, files, etc. I've read through the datasheet and followed the steps there, but still no data coming out the SPI port. Debugger doesn't work and equivalent Tivaware calls crash the bootloader.

Below is my initialization in the bootloader and the SPI. I ripped the ConfigureSSI() code from Tivaware lib source. Maybe I'm missing something obvious, any help or suggestions would be greatly appreciated.

Regards,

Bob Starr

void
ConfigureDevice(void)
{
#ifdef CRYSTAL_FREQ
    //
    // Since the crystal frequency was specified, enable the main oscillator
    // and clock the processor from it.
    //
#if defined(TARGET_IS_TM4C129_RA0) ||                                         \
    defined(TARGET_IS_TM4C129_RA1) ||                                         \
    defined(TARGET_IS_TM4C129_RA2)
    //
    // Since the crystal frequency was specified, enable the main oscillator
    // and clock the processor from it. Check for whether the Oscillator range
    // has to be set and wait states need to be updated
    //
    if(CRYSTAL_FREQ >= 10000000)
    {
        HWREG(SYSCTL_MOSCCTL) |= (SYSCTL_MOSCCTL_OSCRNG);
        HWREG(SYSCTL_MOSCCTL) &= ~(SYSCTL_MOSCCTL_PWRDN |
                                   SYSCTL_MOSCCTL_NOXTAL);
    }
    else
    {
        HWREG(SYSCTL_MOSCCTL) &= ~(SYSCTL_MOSCCTL_PWRDN |
                                   SYSCTL_MOSCCTL_NOXTAL);
    }

    //
    // Wait for the Oscillator to Stabilize
    //
    Delay(524288);

    if(CRYSTAL_FREQ > 16000000)
    {
        HWREG(SYSCTL_MEMTIM0)  = (SYSCTL_MEMTIM0_FBCHT_1_5 |
                                  (1 << SYSCTL_MEMTIM0_FWS_S) |
                                  SYSCTL_MEMTIM0_EBCHT_1_5 |
                                  (1 << SYSCTL_MEMTIM0_EWS_S) |
                                  SYSCTL_MEMTIM0_MB1);
        HWREG(SYSCTL_RSCLKCFG) = (SYSCTL_RSCLKCFG_MEMTIMU |
                                  SYSCTL_RSCLKCFG_OSCSRC_MOSC);
    }
    else
    {
        HWREG(SYSCTL_RSCLKCFG) = (SYSCTL_RSCLKCFG_OSCSRC_MOSC);
    }
#else
    HWREG(SYSCTL_RCC) &= ~(SYSCTL_RCC_MOSCDIS);
    Delay(524288);
    HWREG(SYSCTL_RCC) = ((HWREG(SYSCTL_RCC) & ~(SYSCTL_RCC_OSCSRC_M)) |
                         SYSCTL_RCC_OSCSRC_MAIN);
#endif
#endif

    //
    // Enable SSI peripheral (SSI1 => SDCard)
    //
    HWREG(SYSCTL_RCGCSSI) |= SSI_CLOCK_ENABLE;

    //
    // Enable the clocks to the SSI and GPIO modules.
    //
    HWREG(SYSCTL_RCGCGPIO) |= (SSI_CLKPIN_CLOCK_ENABLE |
                               SSI_FSSPIN_CLOCK_ENABLE |
                               SSI_MISOPIN_CLOCK_ENABLE |
                               SSI_MOSIPIN_CLOCK_ENABLE);
    //
    // Make the pin be peripheral controlled.
    //
    HWREG(SSI_CLKPIN_BASE + GPIO_O_AFSEL) |= SSI_CLK;           // SPI SCK PIN (PB5)
    HWREG(SSI_CLKPIN_BASE + GPIO_O_PCTL) |= SSI_CLK_PCTL;
    HWREG(SSI_CLKPIN_BASE + GPIO_O_DEN) |= SSI_CLK;
    HWREG(SSI_CLKPIN_BASE + GPIO_O_DR4R) |= SSI_CLK;            // GPIO 4-mA Drive Select

    HWREG(SSI_FSSPIN_BASE + GPIO_O_AFSEL) |= SSI_CS;            // SPI FSS PIN (PK7)
    HWREG(SSI_FSSPIN_BASE + GPIO_O_PCTL) |= SSI_CS_PCTL;
    HWREG(SSI_FSSPIN_BASE + GPIO_O_DEN) |= SSI_CS;
    HWREG(SSI_FSSPIN_BASE + GPIO_O_DR4R) |= SSI_CS;             // GPIO 4-mA Drive Select

    HWREG(SSI_MISOPIN_BASE + GPIO_O_AFSEL) |= SSI_TX;           // SPI MISO PIN (PE5)
    HWREG(SSI_MISOPIN_BASE + GPIO_O_PCTL) |= SSI_TX_PCTL;
    HWREG(SSI_MISOPIN_BASE + GPIO_O_DEN) |= SSI_TX;
    HWREG(SSI_MISOPIN_BASE + GPIO_O_PUR) |= SSI_TX;             // GPIO Pull up Select

    HWREG(SSI_MOSIPIN_BASE + GPIO_O_AFSEL) |= SSI_RX;           // SPI MOSI PIN (PE4)
    HWREG(SSI_MOSIPIN_BASE + GPIO_O_PCTL) |= SSI_RX_PCTL;
    HWREG(SSI_MOSIPIN_BASE + GPIO_O_DEN) |= SSI_RX;
    HWREG(SSI_MOSIPIN_BASE + GPIO_O_DR4R) |= SSI_RX;            // GPIO 4-mA Drive Select

    //
    // We're initializing out of reset, so disable the SSI before making any changes.
    //
    HWREG(SSIx_BASE + SSI_O_CR1) &= ~(SSI_CR1_SSE);

    //
    // Set SSI protocol to Motorola mode 0 with default clock high and data valid
    // on the rising edge. SD card needs Motorola Mode 0 (CPOL=0 and CPHA=0)
    // and default clock speed of 400kHz per SD card specifications.
    //
    //HWREG(SSIx_BASE + SSI_O_CR0) = (SSI_CR0_SPH | SSI_CR0_SPO | (DATA_BITS_SSI - 1));

    ConfigureSSI(SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, 400000, 8);

    // Enable the synchronous serial interface in master mode.
    HWREG(SSIx_BASE + SSI_O_CR1) |= SSI_CR1_SSE;
}

//*****************************************************************************
//
//! Configure the SSI port the SPI for the SD interface in SPI mode.
//
//*****************************************************************************

void ConfigureSSI(uint32_t ui32Protocol, uint32_t ui32Mode,
                  uint32_t ui32BitRate, uint32_t ui32DataWidth)
{
    uint32_t ui32MaxBitRate;
    uint32_t ui32RegVal;
    uint32_t ui32PreDiv;
    uint32_t ui32SCR;
    uint32_t ui32SPH_SPO;

    uint32_t ui32SSIClk = 120000000;

    // Set the mode.
    ui32RegVal = (ui32Mode == SSI_MODE_MASTER) ? 0 : SSI_CR1_MS;
    HWREG(SSIx_BASE + SSI_O_CR1) = ui32RegVal;

    // Set the clock predivider.
    ui32MaxBitRate = ui32SSIClk / ui32BitRate;
    ui32PreDiv = 0;
    do
    {
        ui32PreDiv += 2;
        ui32SCR = (ui32MaxBitRate / ui32PreDiv) - 1;
    }
    while(ui32SCR > 255);
    // Set the prescaler value
    HWREG(SSIx_BASE + SSI_O_CPSR) = ui32PreDiv;

    // Set protocol and clock rate.
    ui32SPH_SPO = (ui32Protocol & 3) << 6;
    ui32Protocol &= SSI_CR0_FRF_M;

    ui32RegVal = (ui32SCR << 8) | ui32SPH_SPO | ui32Protocol | (ui32DataWidth - 1);

    HWREG(SSIx_BASE + SSI_O_CR0) = ui32RegVal;

    // Configure the QSSI clock from system clock source
    HWREG(SSIx_BASE + SSI_O_CC) = SSI_CC_CS_SYSPLL;
}

  • Hi Robert,

      As you know, writing in DRM style will be very prone to mistakes and hard to debug for both you and me. 

      This is what I will suggest. I believe you may have run some SSI examples. If not, you can find a simple SSI module example in C:\ti\TivaWare_C_Series-2.2.0.295\examples\peripherals\ssi\spi_master.c file. In this example, you will find how the SSI is configured in terms of the pinmuxing. Below is a snippet. Please run this example and get it to work first in terms seeing data on the MOSI pin. When you get it working, examine the GPIO and SSI registers in the CCS register window to see how the modules are configured. Then compare the register values when running with your own code. This way I hope you can find out which register is not configured correctly in your code. 

        //
        // The SSI0 peripheral must be enabled for use.
        //
        SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0);
    
        //
        // For this example SSI0 is used with PortA[5:2].  The actual port and pins
        // used may be different on your part, consult the data sheet for more
        // information.  GPIO port A needs to be enabled so these pins can be used.
        // TODO: change this to whichever GPIO port you are using.
        //
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    
        //
        // Configure the pin muxing for SSI0 functions on port A2, A3, A4, and A5.
        // This step is not necessary if your part does not support pin muxing.
        // TODO: change this to select the port/pin you are using.
        //
        GPIOPinConfigure(GPIO_PA2_SSI0CLK);
        GPIOPinConfigure(GPIO_PA3_SSI0FSS);
        GPIOPinConfigure(GPIO_PA4_SSI0XDAT0);
        GPIOPinConfigure(GPIO_PA5_SSI0XDAT1);
    
    
        //
        // Configure the GPIO settings for the SSI pins.  This function also gives
        // control of these pins to the SSI hardware.  Consult the data sheet to
        // see which functions are allocated per pin.
        // The pins are assigned as follows:
        //      PA5 - SSI0Tx (TM4C123x) / SSI0XDAT1 (TM4C129x)
        //      PA4 - SSI0Rx (TM4C123x) / SSI0XDAT0 (TM4C129x)
        //      PA3 - SSI0Fss
        //      PA2 - SSI0CLK
        // TODO: change this to select the port/pin you are using.
        //
        GPIOPinTypeSSI(GPIO_PORTA_BASE, GPIO_PIN_5 | GPIO_PIN_4 | GPIO_PIN_3 |
                       GPIO_PIN_2);
    
        //
        // Configure and enable the SSI port for SPI master mode.  Use SSI0,
        // system clock supply, idle clock level low and active low clock in
        // freescale SPI mode, master mode, 1MHz SSI frequency, and 8-bit data.
        // For SPI mode, you can set the polarity of the SSI clock when the SSI
        // unit is idle.  You can also configure what clock edge you want to
        // capture data on.  Please reference the datasheet for more information on
        // the different SPI modes.
        //
        SSIConfigSetExpClk(SSI0_BASE, ui32SysClock, SSI_FRF_MOTO_MODE_0,
                           SSI_MODE_MASTER, 1000000, 8);
    
    
        //
        // Enable the SSI0 module.
        //
        SSIEnable(SSI0_BASE);

  • Thanks, I'll go back to default Tivaware level app and see what I can figure out there. Was hoping it was something global that I'm overlooking like a master clock gate or something, but I've been stuck at this point with nothing coming out the SPI for a while and can't get past this so far. My boot loader is working on the trigger pin and eventually gives up trying to mount the SD and restarts running the existing code. There my main TI-RTOS based application reads the SD fine.

    FWIW, originally I tried Tivaware calls in the bootloader and this seemed to work to a point. But, it appeared to be crashing in the SSICOnfigSetExpClk() function best I could determine, without access to a debugger..

    I'll try the Tivaware example and will get back in a few days to let you know what I find. 

    Thanks!

    Bob

  • I've updated my code to use Tivaware calls in the bootloader. I'm using the same init sequence that I use in my main app (which works) as shown below.

    Everything works up to the point I call ROM_SSIConfigSetExpClk. When I call this function everything hangs. Digging into the Tivaware driver lib code, I don't see anything there that should cause it to hang unless it's stuck in the divide loop. Maybe SysCtlClockGet() is returning bad value. Trying to debug these calls now...

    Bob

        //
        // The SSI1 peripheral must be enabled for use.
        //
        ROM_SysCtlPeripheralEnable(SSIx_BASE);
    
    
        /* Enable SD SSI peripherals */
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI1);
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOK);
    
        /* SSI-1 Configure Pins */
    
        // Enable pin PE5 for SSI1 SSI1XDAT1
        ROM_GPIOPinConfigure(GPIO_PE5_SSI1XDAT1);
        ROM_GPIOPinTypeSSI(GPIO_PORTE_BASE, GPIO_PIN_5);
    
        // Enable pin PE4 for SSI1 SSI1XDAT0
        ROM_GPIOPinConfigure(GPIO_PE4_SSI1XDAT0);
        ROM_GPIOPinTypeSSI(GPIO_PORTE_BASE, GPIO_PIN_4);
    
        // Enable pin PB5 for SSI1 SSI1CLK
        ROM_GPIOPinConfigure(GPIO_PB5_SSI1CLK);
        ROM_GPIOPinTypeSSI(GPIO_PORTB_BASE, GPIO_PIN_5);
    
        // Enable pin PB4 for SSI1 SSI1FSS
        //GPIOPinConfigure(GPIO_PB4_SSI1FSS);
        //GPIOPinTypeSSI(GPIO_PORTB_BASE, GPIO_PIN_4);
    
        // Enable pin PK7 for GPIOOutput (SSI1FSS_SD)
        ROM_GPIOPinTypeGPIOOutput(GPIO_PORTK_BASE, GPIO_PIN_7);
    
        /* Configure pad settings */
    
        /* SCK (PB5) */
        MAP_GPIOPadConfigSet(GPIO_PORTB_BASE,
                         GPIO_PIN_5,
                         GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD);
        /* MOSI (PE4) */
        MAP_GPIOPadConfigSet(GPIO_PORTE_BASE,
                         GPIO_PIN_4,
                         GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD);
        /* MISO (PE5) */
        MAP_GPIOPadConfigSet(GPIO_PORTE_BASE,
                         GPIO_PIN_5,
                         GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD_WPU);
        /* CS (PK7) */
        MAP_GPIOPadConfigSet(GPIO_PORTK_BASE,
                         GPIO_PIN_7,
                         GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD);
    
        uint32_t sysclock = MAP_SysCtlClockGet();
    
    #if 1
    
        //
        // Configure and enable the SSI port for SPI master mode.  Use SSI1,
        // system clock supply, idle clock level low and active low clock in
        // Freescale SPI mode, master mode, 1MHz SSI frequency, and 8-bit data.
        // For SPI mode, you can set the polarity of the SSI clock when the SSI
        // unit is idle.  You can also configure what clock edge you want to
        // capture data on.  Please reference the datasheet for more information on
        // the different SPI modes.
        //
        ROM_SSIConfigSetExpClk(SSIx_BASE, sysclock, SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, 400000, 8);
    #endif
    
        //
        // Enable the SSI1 module.
        //
        ROM_SSIEnable(SSIx_BASE);
    

  • Hi Robert,

      Glad you are making some progress. I hope this is the reason for the issue. You cannot use SysCtlClockGet() on TM4C129 device. You need to use the return value from SysCtlClockFreqSet() instead for TM4C129 device. 

     

  • Yes, this seems to be the problem. I hard coded the cpu freq and it's now passing the SPI initialization. Now to see if the PetitFS system and flash code works, will let you know. Many thanks again!

    Bob

  • Wanted to report back that SD bootloader is now working for the TM4C1294 with PetitFS. I've posted all the code on github in case anyone else needs this feature and wants to get going quickly. Very happy to have this working as it makes updating firmware in the field very easy now.

    You can get it here:  GitHub - rtzmicro/X24015_boot_loader_sd: X24015 Bootloader for SD Drive

    Most of the code can be configured through the bl_config.h file and custom.c functions and should port to other TM4C1294 I/O configurations easily. Hopefully others will find this useful. 

  • Hi Robert,

      Thank you for your generosity to share your code. I'm sure it will benefit others in the community.