speed up SD card transfer

Status
Not open for further replies.

NikoTeen

Well-known member
Hi,
I started a project with Arduino Mega2560. This project contains a library for audio output, SimpleSDAudio, see attachment. This library allows to output audio (speech in my application) by reading blocks of data from an SD card and generating the audio by PWM. Parallel to audio output the controller shall service lots of interrupts coming from a 433 MHz receiver. Both together prooved to be too much load for a Mega2560.

So I decided to change to Teensy 3.2 which is much faster (16 MHz --> 96 MHz), installed Teensyduino IDE, and modified the library to SimpleSDAudio_Teensy, see attachment. This library contains many changes concerning my application, but the part to configure the SD card is the same, see the function SD_L0_init() in sd_l0.cpp respectively sd_l0_Te.cpp.

In a first step I tested reading from the SD card and was disappointed because reading with Teensy 3.2 gets nearly the same speed as reading with Mega2560 inspite of 5 times faster clock. The transfer clock for SD card is defined by F_CPU/n (or f_OSC/n). n is defined by setting some bits in the SPCR register. This setting is the same for Mega2560 and for Teensy 3.2.

Something in the background, I guess deep in some basic libraries, is changing the setting in SPCR for Teensy with the consequence that the 96 MHz are divided down to the same clock as the 16 MHz with Mega2560.

For testing the speed of data transfer the code in SDcardTest.zip is used, see attachment.
The code in the function SD_L0_SpiSetHighSpeed(), see sd_l0_Te.cpp, is based on a posting from Paul Stoffregen:

----------------------------
...
Agreed, the proper way is to use the SPI library. But there's still quite a lot of very old AVR code out there....

I've added these missing AVR SPCR emulation features. Hopefully this makes everything work for you?

https://github.com/PaulStoffregen/cores/commit/8f137ff3fff2f230de02e466d45cc86a3f52f507

Here's the complete sketch I used for testing.

Code:

void setup() {
pinMode(20, OUTPUT); // for comparison to Teensy++ 2.0
pinMode(21, OUTPUT);
pinMode(22, OUTPUT);
digitalWrite(20, HIGH);
SPCR = (0 << SPIE) | /* SPI Interrupt Enable */
(1 << SPE) | /* SPI Enable */
(0 << DORD) | /* Data Order: MSB first */
(1 << MSTR) | /* Master mode */
(0 << CPOL) | /* Clock Polarity: SCK low when idle */
(0 << CPHA) | /* Clock Phase: sample on rising SCK edge */
(1 << SPR1) | /* Clock Frequency: f_OSC / 128 */
(1 << SPR0);
SPSR &= ~(1 << SPI2X); /* No doubled clock frequency */

SPCR &= ~((1 << SPR1) | (1 << SPR0)); /* Clock Frequency: f_OSC / 4 */;
SPSR |= (1 << SPI2X); /* Doubled Clock Frequency: f_OSC / 2 */
}

void loop() {
SPDR = 0x5A;
while (!(SPSR & _BV(SPIF))) ; // wait
SPDR;
delay(10);
}

----------------------------

The SD card shall contain a file with at least 512 consecutive blocks. In my code it is named ANSAGEK5.AHM. Any other name with 8.3 convention can be used. The file shall be located in the root directory.
The SD card is conneted to Teensy 3.2:
MISO 12
MOSI 11
SCK 13
CS 4


Now my question is, how can I increase the SD clock frequency?
 

Attachments

  • SimpleSDAudio.zip
    16.4 KB · Views: 46
  • SimpleSDAudio_Teensy.zip
    13.8 KB · Views: 44
  • SDcardTest.zip
    4.4 KB · Views: 49
hi,
my initial post was rather complex. Now I have a more simple question.
For setting the clock rate the SimpleSDAudio library contains two functions, SD_L0_init() and SD_L0_SpiSetHighSpeed().

Code:
void SD_L0_Init(void)
{
	/* Power up card */
	// not for arduino
	
	/* Setup ports */
   pinMode(SD_L0_CSPin, OUTPUT);
   SD_L0_SetCSHigh();
 
   pinMode(MISO, INPUT);
   pinMode(SCK, OUTPUT);
   pinMode(MOSI, OUTPUT);
   pinMode(SS, OUTPUT);
  
   digitalWrite(SCK, LOW);
   digitalWrite(MOSI, LOW);
   digitalWrite(SS, HIGH);
   

    /* Powering up takes at least 500us for capacitors to charge */
    // not for arduino

//Serial.print(F("SIM_SCGC6=")); Serial.println(SIM_SCGC6,HEX);
//Serial.print(F("SPI0_MCR=")); Serial.println(SPI0_MCR,HEX);
//Serial.print(F("SPI0_CTAR0=")); Serial.println(SPI0_CTAR0,HEX);

    /* initialize SPI with lowest frequency; max. 400kHz during identification mode of card */
    REGspcr = (0 << SPIE) | /* SPI Interrupt Enable */
           (1 << SPE)  | /* SPI Enable */
           (0 << DORD) | /* Data Order: MSB first */
           (1 << MSTR) | /* Master mode */
           (0 << CPOL) | /* Clock Polarity: SCK low when idle */
           (0 << CPHA) | /* Clock Phase: sample on rising SCK edge */
           (1 << SPR1) | /* Clock Frequency: f_OSC / 128 */ 
           (1 << SPR0);
	SPCR = REGspcr;
    SPSR &= ~(1 << SPI2X); /* No doubled clock frequency */

}

/**
 * Set SPI for full operation speed (up to 25 MHz).
 *
 * Will be called after first part of card 
 * initialization was successful.
 */
void SD_L0_SpiSetHighSpeed(void)
{	// fuer Teensy3.2 geht es nur ueber den backup-Wert (SPCR nur schreibbar)
    REGspcr &= ~((1 << SPR1) | (1 << SPR0)); /* Clock Frequency: f_OSC / 4 */
	SPCR = REGspcr;
					//SPI0_CTAR0 &= 0xFFFCFFF0;
    SPSR |= (1 << SPI2X);         /* Doubled Clock Frequency: f_OSC / 2 */
}
According to the comments in the code, SD_L0_SpiSetHighSpeed() should set the SPI clock to f_OSC/2. This is true for a Mega2560 with f_OSC=16 MHz but not for a Teensy3.2 with 96 MHz. Measurements with an oscilloscope show 8 MHz for both.

The manual for Teensy3.2 describes registers for SPI configuration. With register SPI0_CTAR0 the SPI clock can be set. In a first step I just wanted to print the contents of some registers to verify that I have understood the manual.

Uncommenting the three Serial.print lines within SD_L0_Init() results in a print out of SIM_SCGC6, but after the text "SPI0_MCR=" the program hangs. Serial Monitor output is
SDcardTest
SIM_SCGC6=6B000001
SPI0_MCR=

Why is'nt it possible to print register contents of SPI0_MCR and why is the program hanging?
 
Status
Not open for further replies.
Back
Top