Software SPI for use in SdFat and interrupts

Status
Not open for further replies.

Bill Greiman

Well-known member
Sharing the SPI bus with SdFat can be a problem for applications that use SPI in interrupts. Paul has developed SPI transactions which will help in many cases.

I will add SPI transaction to SdFat but this will be a limited solution since SdFat will still transfer an entire 512 byte block. The SPI bus can be released in a transaction while the SD is busy but you can not reliably breakup the actual data transfer.

This is from Elm Chan http://elm-chan.org/docs/mmc/mmc_e.html, the author of FatFS, the most used open source FAT file system for embedded systems.
In principle in SPI mode, the CS signal must be kept asserted during a transaction. However there is an exception to this rule. When the card is busy, the host controller can deassert CS to release SPI bus for any other SPI devices.

Another solution is to use software SPI for SdFat or in the interrupt routine.

I am developing a general software SPI library. Here are some results.

A scope trace for the transfer of the byte 0XAA is attached. Yellow is clock and green is data. The timing is not as good as hardware SPI but works in devices I have tested. A single macro is used for each clock period but compiler optimization make the timing for each bit different. I may try to improve it. The clock rate is something like 4-5 MHz.

Here is a test with the SdFat bench example comparing hardware SPI with software SPI on Teensy 3.1.

Software SPI:
File size 5 MB
Buffer size 512 bytes

write speed and latency
speed,max,min,avg
KB/Sec,usec,usec,usec
237.35,62658,1851,2155

read speed and latency
speed,max,min,avg
KB/Sec,usec,usec,usec
539.92,1992,933,947

Hardware SPI:
File size 5 MB
Buffer size 512 bytes

write speed and latency
speed,max,min,avg
KB/Sec,usec,usec,usec
398.41,60649,941,1284

read speed and latency
speed,max,min,avg
KB/Sec,usec,usec,usec
1351.26,1418,363,378

Here is the SdFat PrintBenchmark.

Software SPI:
Test of println(uint16_t)
Time 0.55 sec
File size 128.89 KB
Write 234.77 KB/sec
Maximum latency: 8882 usec, Minimum Latency: 9 usec, Avg Latency: 26 usec

Test of println(double)
Time 1.04 sec
File size 149.00 KB
Write 142.58 KB/sec
Maximum latency: 1178 usec, Minimum Latency: 29 usec, Avg Latency: 51 usec

Hardware SPI:
Test of println(uint16_t)
Time 0.34 sec
File size 128.89 KB
Write 382.46 KB/sec
Maximum latency: 3969 usec, Minimum Latency: 10 usec, Avg Latency: 15 usec

Test of println(double)
Time 0.94 sec
File size 149.00 KB
Write 158.85 KB/sec
Maximum latency: 55905 usec, Minimum Latency: 29 usec, Avg Latency: 46 usec
Software SPI will be fast enough for many applications. You could use hardware SPI for non-interrupt code and use software SPI for interrupts to get quick access to an SPI device at about 4 MHz.

I will post the software SPI library. The library supports all three SPI modes. There is no SPI speed parameter, the speed is fixed at about 4-5 MHz.

The library is a template class.
Code:
/**
 * @class SoftSPI
 * @brief Fast software SPI.
 */
template<uint8_t MisoPin, uint8_t MosiPin, uint8_t SckPin, uint8_t Mode = 0>
class SoftSPI {
 

Attachments

  • soft_spi.png
    soft_spi.png
    21.6 KB · Views: 241
Last edited:
Very interesting Bill. So I've been trying to follow the various Teensy 3.X SPI related discussions and some of it goes over my head, but I'm trying to figure it out. Question - does anyone know which SPI devices (and their libraries) play well together on the Teensy 3.X? My current project uses an SPI SD card and an SPI interface OLED display (NHD-2.7), and am trying to figure out which Wifi solution to used, but it will most likely be SPI based. Will the SD card and OLED play together on the single SPI bus? How about a SPI Wifi interface? Should I use SoftSPI or SoftwareSerial for one or more of those devices?
 
Some devices such as the wireless low speed radios (as in RadioHead) have an interrupt handler that needs low latency (like 100uSec or so). The nature of these radios is that packets are small, like 66 bytes or so max. SPI transfers for radio-transmit occur at the non-ISR level where the radio's FIFO is filled rapidly then later it interrupts with transmit complete status and that takes just one SPI command and one read response in the ISR.

For receive, the interrupt occurs, usually, when a packet's bytes have been received and placed in a FIFO in the radio. At interrupt, the ISR needs to at least use SPI to read the interrupt cause status. The ISR could then either read the FIFO via SPI (say, one command and 60 reads), or set a flag to cause non-ISR code to bid for an SPI port and do the transfer later. But the receive message in the FIFO needs to be copied soon, like a few mSec, in order to not miss another incoming packet. Today's libraries do the FIFO copy in the receive ISR but do so assuming that there's no need to bid/contend for the SPI port. Maybe this is were software SPI makes more sense. The popular HopeRF radios (Semtech chips) can use SCK at 10MHz, so an ISR using software SPI should strive for that speed to minimize latency.

Some radio types use no interrupts but rather all-polling. This is much simpler case, but vulnerable to getting enough CPU time to poll and work fast enough for the packet arrival rates.
 
Some devices such as the wireless low speed radios (as in RadioHead) have an interrupt handler that needs low latency (like 100uSec or so).

Steve, whether you like it or not, you're simply going to have to get used to the reality that having a SD card on the same SPI bus is necessarily going to result in approx 200 us bus busy periods, while single sectors are transferred. SDHC cards don't support partial sector transfers, so there just doesn't seem to be any alternative.

Bill's doing excellent work here, with an option to use software SPI that won't tie up the hardware SPI, and of course highly optimized code that will probably get that hardware SPI busy time as short as possible (probably around to 180 us would be the theoretical best case). Teensy 3.1 supports 24 MHz SPI clock speed, which is very close to the 25 MHz maximum SPI speed spec for SD cards. I'm confident we're going to end up with things being very close to the best theoretical possible performance.

I know you're doing a lot of work with HopeRF radios and you really, really want 100 us latency. Unfortunately, SD cards sharing the same bus aren't ever going to meet that requirement.
 
Of course the need is driven by real-time events - message arrival rates, not an arbitrary wish. Until we have a stop-sign for RF bits in the air. :)

That's why I suggested software SPI for the ISR of radios and not share a port. It eliminates the issue. The non-ISR radio code can use a shared port as that's not as critical - just dealing with ACK timeouts on the order of 250mSec.
 
I have a first cut of SPI transactions in SdFat. I added two configuration defines.
Code:
//------------------------------------------------------------------------------
/**
 * Set ENABLE_SPI_TRANSACTION nonzero to enable the SPI transaction feature
 * of the standard Arduino SPI library.  You must include SPI.h in your
 * sketches when ENABLE_SPI_TRANSACTION is nonzero.
 */
#define ENABLE_SPI_TRANSACTION 1
/**
 * Set ENABLE_SPI_YIELD nonzero to enable release of the SPI bus during
 * SD card busy waits when ENABLE_SPI_TRANSACTION is nonzero.  
 * 
 * Setting ENABLE_SPI_YIELD will introduce some extra overhead and will
 * slightly slow transfer rates.  A few older SD cards may fail when 
 * ENABLE_SPI_YIELD is nonzero.
 */
#define ENABLE_SPI_YIELD 1
These two defines allow control of the fineness of the sharing with interrupts. When both are enable, there is a 5% slow down in read.

I did some tests for the time to transfer a block. I put a digitalWriteFast(2,1) before each transfer and a digitalWriteFast(2,0) after each transfer. They are very close to optimal, about 180 usec. I attached the scope traces for read and write.
 

Attachments

  • read.png
    read.png
    23 KB · Views: 242
  • write.png
    write.png
    22.7 KB · Views: 207
Of course the need is driven by real-time events - message arrival rates, not an arbitrary wish. Until we have a stop-sign for RF bits in the air. :)

That's why I suggested software SPI for the ISR of radios and not share a port. It eliminates the issue. The non-ISR radio code can use a shared port as that's not as critical - just dealing with ACK timeouts on the order of 250mSec.
rethinking... to do software SPI in the ISR and same pin software SPI in the non-ISR - sure. But my suggestion of a hybrid, with software SPI in the ISR has the "minor" issue of the the SCK and data lines differing. So this isn't likely viable. All software SPI is, and maybe preferred for SPI peripherals that interrupt.
 
software SPI .... inquiring minds.

FWIW
I was curious how software SPI might work. Bill's version in the DigitalIO library only works for UNO. The version in SdFat lib works with teensy 3s. I hooked up analyzer and sent a 1000 bytes over the soft SPI. With T3.2@96mhz, SPI is clocking at about 7.9MHz (@48mhz clock is 4 Mhz, @120MHz clock is 9.9mhz) and MOSI data is good.

T3.6@180mhz, soft SPI clock is 15mhz
 
Last edited:
Status
Not open for further replies.
Back
Top