SD (Secure Digital) Card Reader/Writers...

From time to time it becomes necessary to record data more permanently than we can in the memory of an AVR device. There are a number of ways we can do this such as streaming the data to another computer via the Serial port or using Ethernet but, probably the most popular and easiest to implement method is the SD (Secure Digital) Card.

The SD Card reader/writer (SDRW) shown on the left is a generic version which has no special features and simply acts as an interface between the SD Card and the project Arduino or AVR device. I've chosen this SDRW as it allows me to explain some of the things that it is worth knowing and present some solutions to problems users may encounter.

There are many types of SDRW and there are even SDRWs incorporated into other products and shields but I am going to look at the SDRW on its own.

The first thing to remember about SD Cards is that they are designed to work on a 3.3 Volt supply and their inputs and outputs are also based on 3.3 Volt logic so, connecting a SD Card to an Arduino working on a 5 Volt supply is going to do some serious damage to the SD Card.

Just remember the voltage restrictions before connecting any SD Card device to an Arduino or project! It is fairly simple to interface SD Card devices to 5 Volt systems and some SD Card devices already come with the necessary circuitry built onto the module but just make sure that you know what your chosen device is designed for.

The schematic of the featured SDRW is shown on the left and as you can see, it is very simple. The data interface uses the SPI interface of the Arduino/AVR and, in this instance, either 5V or 3.3V can be supplied to the module because a 3.3V regulator is included. Not all SDRW modules have a regulator so be careful. Some SDRW modules have additional signals such as "Card Present" or "Card Removed/Inserted" but I will not cover these in too much detail.

The schematic shows that a 10K pull-up resistor is provided on each input although the more observant amongst you will notice that CS does not have a pull-up resistor but this does not seem to cause a problem in actual use. The table below shows the signals for the 3 sizes of SD Card with their function and Arduino/AVR assignments.

SD Pin# SD MiniSD MicroSD Function Arduino or AVR
1 CS CS CS Chip Select User Selected CS pin
2 DI DI DI Data In MOSI
3 VSS VSS VSS GROUND GND
4 VDD VDD VDD 3.3V Power 3.3V
5 CLK CLK CLK Serial Clock SCK
6 VSS VSS VSS GROUND GND
7 DO DO DO Data Out MISO
8 IRQ IRQ IRQ Interrupt Request Not normally used
9 NC NC NC No Connection Not used

If your Arduino or project AVR is already working on a 3.3V supply, you need not do anything except to connect up the SDRW and start working on the software but, if your Arduino or project AVR is working on a 5V supply it is necessary to "level shift" the data and control signals.

Using buffers to level shift

A simple 5V to 3.3V logic level shifter using diodes

There are various methods that can be employed to level shift the signals to protect the SD Card from overload including transistors, FETs, CMOS logic gates and High Speed CMOS logic Gates. Small modules are available on the Internet to do the job but there is no need to spend unnecessary money on what is a simple item. Be careful though, some SD Card modules are supplied with a level shifting IC already built onto the PCB. Make sure that you check the specification and schematic of your module before considering external level shifting circuitry.

The example, above left, uses 3 buffer gates which are part of a larger logic integrated circuit. here are many ICs that can be used for this purpose but, the IC type must be ether CMOS or HC (High-speed CMOS). This example uses the 74HC08 Quad AND gate.

The circuit on the left shows one of each AND gate's inputs tied to Vcc so that we use the other, free, input to switch the gate. A 5V "1" or "0" on the input will be reflected on the output but, because the 74HC08 is using a Vcc of 3.3V the logic ouput level will be 3.3V for a "1" and 0V for a "0". The 74HC08 is quite capable of handling 5V logic inputs which are above its Vcc because of the CMOS technology. You could use other ICs such as the 74HC4050, CD4050 as long as they are non-inverting buffers. The important thing is that they are either CMOS or HC ICs. DO NOT use HCT ICs such as the 74HCT08 because their outputs are designed for TTL (Transistor Transistor Logic) which is based on 0V - 5V levels and don't give suitable output levels for our purpose.

Inputs 1A and 1B on the 74HC08 are both tied to Vcc because we are not using this gate. DO NOT connect any logic gate output to either Vcc or Ground! This will permanently damage the IC.

Another simple, tried and tested method which costs pennies and can be built easily onto a small piece of strip board is shown above right.

At first glance the circuit may seem to be back to front but this is the way it has to be. What we want to do is prevent the Arduino/AVR 0-5V logic signals reaching the SD Card and damaging it.

To do this we rely on the pull-up resistors, shown in grey, which should already be on the SDRW module and simply use the Arduino/AVR outputs to pull the diode/resistor and relevant SD Card input to Ground. As mentioned before, even without a pull-up resistor on the CS input, this still works.

The MISO signal does not have a diode as it is from SD Card to Arduino/AVR at 0-3.3V which is well within the input switching levels of the Arduino/AVR.

Trust me, this works and I have even used this method of level shifting on ICSP signals when programming 3.3V AVR circuits with a 5V programmer. It has been reported by some users that certian SD Cards don't work using the diode method but I have yet find one that doesn't!

A number of writers on forums and blogs have suggested using resistor dividers to drop the voltage to the SD Card inputs which is not a very good method and I do not advise that anyone use it. The diodes I have specified are 1N4148s but, a wide range of Silicon diodes can be used and I have even used 1N4001 diodes without any problems.

All you need to remember is that it is only the inputs to the SD Card that need level shifting, the outputs from the SD Card are fine as they are!

The signal names on your chosen SDRW module may be the same as the SPI signals i.e. SCK, MISO, MOSI which makes life easier but I'm sure you will work it out.

Lastly, before you dive head first into hooking up a SDRW module to your chosen AVR, just make sure that it is SPI capable. Most AVRs have SPI input/outputs but some are only for programming, not communication with other SPI devices. A good clue as to whether or not an AVR is fully SPI compliant is the "SS" pin (Arduino pin 10 on the UNO). If there isn't an "SS" pin, the SPI interface is only for programming. Atmega328s are fine but Attiny85s are not.

Never use the "SS" pin on an AVR as a Chip Select unless you have no choice. The SPI "SS" (Slave Select) pin is for SPI Master/Slave signalling and IS NOT intended for Chip Select functions although it can be used for a single SPI device if necessary. "SS" is pin D10 on the Arduino UNO.

Most SPI related libraries give the user the opportunity to choose a CS pin and I recommend that you do this. In the example below I have chosen to use Arduino pin D2. You can use "SS" if you want, for a single device such as a SDRW but consider if you will need the "SS" in the future for something else such as SPI communication or another SPI peripheral device.. 

The Software:

 Arduino provide an excellent library for SD Cards called SD.h and you should have no trouble in getting a SDRW module working with it but, it lacks one vital function. The SD library does not allow you to restart an SD Card if you remove and reinsert it so you have to restart the entire program if you do anything with the card.

I have modified the SD Card library and renamed it SD_MOD to facilitate restarting after a card removal/reinsertion event so that you can issue the SD card startup "SD_MOD.begin()" from within the program. If your SDRW module is able to signal a card event you can detect this and simply issue a new "SD_MOD.begin()" and hopefully restart the process. I have also added support for the Atmega1284 AVR should anyone want to make use of one of these larger AVRs. The SD_MOD library is available on the downloads page.

The SD & SD_MOD libraries use a lot of Flash and SRAM so they are not suited to smaller AVRs. The sketch below compiles to 13,622 Bytes for example! I ran out of both Flash and SRAM on a recent project using an Atmega328 and had to move to an Atmega1284 due to the amount of overhead required by the following libraries:

#include <Wire.h>
#include <RF24Network.h>
#include <RF24.h>
#include <SPI.h>
#include <Time.h>.
#include <Streaming.h>
#include <Timer.h>
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
//#include <Keypad_I2C.h>
#include <SD.h>
#include <EEPROM.h>
#include <EEPROMAnything.h>

Here's a simple sketch that will open the SD Card and save "Hello World!" to the SD Card every 10 seconds until you stop it. I don't recommend running this 24 hours a day as it has to be remembered that like any other Flash memory device, there is a finite number of write cycles that can reliably be done. The figure generally stated is circa 100,000 write cycles before data corruption can occur. This does not mean that the SD Card will fail just like that but it does mean that data integrity could be compromised.

#include <SD.h> // SD Card library

/* SD CARD FILE OBJECT */
    File myFile;

#define SD_CS_PIN 2                                                   // your choice of CS pin. 2 is an example

void setup(){
    Serial.begin(9600);                                                   // start the Serial port
    bool sdStatus = SD.begin(SD_CS_PIN);                 // startup the SD card using Arduino pin D2 as CS
    Serial.print("SD Card startup ");                               // first part of status message  
    Serial.println(sdStatus?"Successful!":"Failed!");      // second part of status message
}

void loop(){
    myFile = SD.open("test.txt", FILE_WRITE);              // open the file on the SD Card
    if(myFile){                                                                  // check that the file opened correctly
        bool ok = myFile.print("Hello World!\n");               // write the message to the SD Card and get status
        Serial.print("File writing ");                                    // first part of status message
        Serial.println(ok?"Successful":"Failed!");              // second part of status message
        myFile.close();                                                      // close the file
    }
    delay(10000);                                                           // wait for 10 seconds
}

It's over to you now to play and try the different functions of the SD or SD_MOD library.