Document created by martynhunt Employee on May 9, 2014Last modified by ebiz_ws_prod on Dec 13, 2017
Version 5Show Document
  • View in full screen mode

The SPI bus has the capability of addressing multiple slave devices by a single master. The Kinetis L series of devices feature either an 8-bit or 16-bit capable SPI module; however, there is only one dedicated CS/SS signal per instance of the module. Of course this signal is muxed to a few pin locations on the device. Unfortunately, there are not that many pins with the CS/SS muxing and they are most likely they are not near to each other physically.


A solution to this issue is to use GPIO as CS/SS lines. This way you can take advantage of the SPI bus protocol and the Kinetis L series IOPORT interface (also known as FGPIO on Kinetis L). The Cortex-M0+ allows accesses to the IOPORT to occur in parallel with any instruction fetches; therefore, these accesses will complete in a single cycle.


Core vs. SPI


I'm sure many who have tried to use GPIO as CS/SS have written code similar to this pseudo code, I know I have:


while(1) {      set_cs_low;      send_byte;      set_cs_high; }   


Logically this makes sense, but on an oscilloscope you will see the GPIO CS/SS line toggling at irregular intervals and out of sync with the SPI transfers. This is due to the nature of the 'send_byte' function or instruction. Simply transmitting a data packet will not prevent the core from waiting for the transmission to complete. The core will move on from writing data to the SPI data register, and execute the next instruction. If you have a core operating at 48 MHz and you are performing, at most depending on instance, 24 MHz SPI transfers the core will always move onto the next instruction before the data has left the module. The code must either implement a delay or wait for the transmission to complete. Incorporating an accurate delay can be tricky and can be interrupted by any interrupts occurring during the delay process. A more robust solution is to wait for the transmission to complete. However, there appears to be no Transmit Complete Flag (TCF) in the L-Series SPI module.


The Solution


Fortunately, there is a way to wait for transmit complete. Software must wait for the SPI read buffer full flag (SPRF) to be set in the SPI status register (SPIx_S) after writing data to the SPI data register (SPIx_D) . When the SPRF bit is set, software must read the SPIx_D. This procedure will ensure that the core does not move onto GPIO toggling, or other instructions, until the data has left the SPI module.


The following function demonstrates how to write the above procedure in C using SPI0 and PTD0 as the CS/SS line:


uint8_t SPI_send(uint8_t spiWrite) {       uint8_t spiRead;                        //Variable for storing SPI data       FGPIOD_PCOR |= (1 << 0);                //Toggle CS/SS line low       while(!(SPI0_S & SPI_S_SPTEF_MASK))     {         __asm("NOP");     }                                       //Wait for SPI transmit empty flag to set       SPI0_D = spiWrite;                            //Write data to SPI       while(!(SPI0_S & SPI_S_SPRF_MASK))     {         __asm("NOP");     }                                       //Wait for receive flag to set       spiRead = SPI0_D;                       //Read the SPI data register       FGPIOD_PSOR |= (1 << 0);                //Toggle CS/SS line high       return spiRead;  }   


Please note that the GPIO CS/SS toggling need not be in the function. It should work just as well if the GPIO CS/SS toggles occur before and after the function is call, just remove the FGPIO instructions from the function and place them outside.


I hope this document proves useful to those of you designing multiple slave SPI buses around Kinetis L series parts.

Original Attachment has been moved to: spi_gpio_cs_ss.c.zip