SdFat beta with SPI transactions and Software SPI for Teensy 3.x

Status
Not open for further replies.

Bill Greiman

Well-known member
I have posted two beta libraries on GitHub to help with problems sharing SPI between SdFat and interrupt routines that access the SPI bus.

The first, DigitalIO https://github.com/greiman/DigitalIO-beta, has general software SPI support for Teensy 3.x. The SoftSPI class can be used to access SPI devices at 4-5 MHz using any digital pins. See the testSoftSPI example.

The second, SdFat https://github.com/greiman/SdFat-beta, has support for SPI transactions and software SPI access to SD cards.

To use SPI transactions in SdFat, edit this section of SdFatConfig.h.
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 0
//------------------------------------------------------------------------------
/**
 * Set ENABLE_SPI_YIELD nonzero to enable release of the SPI bus during
 * SD card busy waits.  
 *
 * This will allow interrupt routines to access the SPI bus if 
 * 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 0

To use software SPI in SdFat, edit this section of SdFatConfig.h
Code:
//------------------------------------------------------------------------------
/**
 * Define AVR_SOF_SPI nonzero to use software SPI on all AVR Arduinos.
 */
#define AVR_SOFT_SPI 0
//------------------------------------------------------------------------------
/**
 * Define DUE_SOFT_SPI nonzero to use software SPI on Due Arduinos.
 */
#define DUE_SOFT_SPI 0
//------------------------------------------------------------------------------

/**
 * Define LEONARDO_SOFT_SPI nonzero to use software SPI on Leonardo Arduinos.
 * LEONARDO_SOFT_SPI allows an unmodified 328 Shield to be used
 * on Leonardo Arduinos.
 */
#define LEONARDO_SOFT_SPI 0
//------------------------------------------------------------------------------
/**
 * Define MEGA_SOFT_SPI nonzero to use software SPI on Mega Arduinos.
 * MEGA_SOFT_SPI allows an unmodified 328 Shield to be used
 * on Mega Arduinos.
 */
#define MEGA_SOFT_SPI 0
//------------------------------------------------------------------------------
/**
 * Set TEENSY3_SOFT_SPI nonzero to use software SPI on Teensy 3.x boards.
 */
#define TEENSY3_SOFT_SPI 0
//------------------------------------------------------------------------------
/** 
 * Define software SPI pins.  Default allows Uno shields to be used on other 
 * boards.
 */
// define software SPI pins
/** Default Software SPI chip select pin */
uint8_t const SOFT_SPI_CS_PIN = 10;
/** Software SPI Master Out Slave In pin */
uint8_t const SOFT_SPI_MOSI_PIN = 11;
/** Software SPI Master In Slave Out pin */
uint8_t const SOFT_SPI_MISO_PIN = 12;
/** Software SPI Clock pin */
uint8_t const SOFT_SPI_SCK_PIN = 13;
 
Oh cool! I'll try that when DmaSpi is ready and see how they behave together (I'm not trying to speed up SdFat, don't get that wrong)
 
Oh cool! I'll try that when DmaSpi is ready and see how they behave together (I'm not trying to speed up SdFat, don't get that wrong)

DMA won't help in most cases. At 24 MHz the FIFO transfer of a data block, 514 bytes (512 data and 2 CRC), take less than 180 us. This is within a few us of optimal.

For normal small transfers most of the time is SD busy time.
 
Last edited:
No, DMA probably won't help with SD cards. But when I started a DMA SPI transfer to some other device (display or whatever) it should finish before SdFat uses the SPI bus. Now that we have SPI transactions, that should work out of the box even if I try hard to make it fail.
 
DMA won't help in most cases. At 24 MHz the FIFO transfer of a data block, 514 bytes (512 data and 2 CRC), take less than 180 us. This is within a few us of optimal.

For normal small transfers most of the time is SD busy time.

Is SD block transfers that low always? I didn't remember it being that quick even with pre-allocation & circular buffers. I seem to remember that it would occasionally go considerably longer? Has it been improved?
And wouldn't dma still help if you are using fast sample rates? Cause 512 bytes is only 256 samples with 2 byte samples.
 
DMA transfer can only make a difference if you don't wait for the transfer to finish. I don't know enough about SD card handling, but if the block of data needs some handling after it has been sent, you can't save any time.
 
Is SD block transfers that low always? I didn't remember it being that quick even with pre-allocation & circular buffers.

The actual data transfer time for a block on Teensy 3.x has always been fast. In SPI mode, most of time is busy wait. With SPI transactions I only do a few bytes to determine if the card is busy and release it and then reserve it to allow interrupt access. It can still take 100 ms to write a block where only 175 us is the actual data transfer over the SPI bus.

There is a lot of handling after the actual data transfer so DMA just does not help. For file access, the transfer is almost always to a cache block buffer with data copied to or from the user buffer. You can't just set chip select high to end a transaction.

The real win is to have a MPU with a SDIO controller and DMA. You can just set up a multi-block transfer and wait for a complete interrupt. The SDIO controller handles any busy wait and there is less busy time in SDIO mode. I have done up to 20 MB/sec write and read with a STM32's SDIO controller. This is great with an RTOS since the calling thread can sleep.

SPI DMA doesn't have much advantage because most of the time is spent with single byte transfers to check for busy.
 
I am not sure if I do it correctly, but it seems that there is not difference in changing variables in SdFatConfig.h.

Some really basic questions:
1. I do have to include the SdFatConfig.h in my *.ino file right?
i.e.
Code:
#include <SPI.h> // Included for SFE_LSM9DS0 library
#include <Wire.h>
#include <SFE_LSM9DS0.h>

#include <SdFatConfig.h>
#include <SdFat.h>
#include <SdFatUtil.h>

2. I change the variable in the SdFatConfig.h?
i.e.
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

But somehow it does not matter if there is an 1 or 0 for ENABLE_SPI_TRANSACTION. The SPI settings change anyway...
do I have to include the SdFatConfig.h in the folder of my *.ino file?
 
Okay, now it works... I didn't know that the librarys get copied in the folder where my projects are saved. I always changed the SdFatConfig.h in my ...\arduino-1.0.6\libraries\ directory, but I had to change the SdFatConfig.h in my ...\Documents\Arduino\libraries. So much time for a noob error :-/
 
Status
Not open for further replies.
Back
Top