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.
martynhunt Thank you for the time to put this together! I wish I would have found this a couple of days ago after coming to the same conclusion by trial and error.
Note that the c file has the following error:
spiRead = ch; rather than the correct above listed call of spiRead = SPI0_D;
I am new to kinetis and codewarrior. Can you explain what inlcludes are need to run the terminal for your your attached code? Is it virtual debug terminal built into codewarrior?
Thanks again.
Hi MJ,
The file should work with the projects in the KL25_SC download. If you have those projects installed on your machine, navigate to ..\KL25_SC\kl25_sc_rev10\klxx-sc-baremetal\build\cw. In this directory you will see a .exe file called 'make_new_cw_project.exe', Run this program and name your new project spi_gpio_cs_ss. Then navigate to ..\KL25_SC\kl25_sc_rev10\klxx-sc-baremetal\src\projects and create a new folder called 'spi_gpio_cs_ss'. Copy into this folder 'spi_gpio_cs_ss.c' from this document and 'isr.h' from the 'platinum' folder located in KL25_SC\kl25_sc_rev10\klxx-sc-baremetal\src\projects.
I have edited the file to fix the build issues.
Best regards,
Martyn