Adafruit Microsd Spi Sdio
Adafruit Microsd Spi Sdio
Breakout Board
Created by Jeff Epler
https://learn.adafruit.com/adafruit-microsd-spi-sdio
Overview 5
Pinouts 8
CircuitPython 9
• Preparing your SD card
• General Steps for Accessing SD Cards
• Tip: Create a mount_sd.py file
• sdcardio: For SD cards on SPI interfaces
• sdioio: For SD cards on SDIO interfaces
• adafruit_sdcard.py: For legacy uses
Using sdcardio 12
• Initialize & Mount SD Card Filesystem
• Reading & Writing Data
• Example complete mount_sd.py
PyGamer 19
Python Docs 24
Using sdioio 24
• Initialize & Mount SD Card Filesystem
• Reading & Writing Data
• Example complete mount_sd.py
Python Docs 30
Using adafruit_sdcard 31
• Adafruit CircuitPython Module Install
• Initialize & Mount SD Card Filesystem
• Reading & Writing Data
• Example complete mount_sd.py
Adalogger M0 Feather 35
Python Docs 36
Examples 36
Downloads 44
• Schematic
• Files
• Fab Print
• 3D Model
Wiring up SD cards when you're starting out is super easy - you just pick up one of
Adafruit's SPI-to-SD breakout boards (http://adafru.it/254), wire it to an SPI port and a
chip select pin and with a little library work you're reading and writing files (https://
adafru.it/Myb). That's all good and great but you may eventually find that your SD card
project is a little...slow. Even with fast SPI ports, there's only one data pin, which can
make it hard to stream large files fast.
If you've hit that limit, this new breakout (http://adafru.it/4682) is designed to help.
Unlike previous adapters, it is not fixed for SPI usage, and can be used with SDIO
hardware support. SDIO is a multi-pin data protocol (up to 4 data pins at once!) SDIO
also tends to be able to be clocked faster than SPI. Of course, your speeds will vary
depending on what microcontroller you hook it up to. When we used SDIO instead of
SPI on CircuitPython with the SAMD51 Grand Central (http://adafru.it/4064), we got a
speed increase of about 200% when reading data off the card
For use with 3V power and logic microcontrollers only! The SDIO pins are bi-
directional, and we've never seen a 5V microcontroller with SDIO, so there's no level
shifting or power regulator.
The breakout comes with a bit of header so you can use in a breadboard. It doesn't
come with the micro SD card itself!
Wouldn't it be nice if you could connect a microSD card to a Python board and expand
its storage? It turns you can use microSD cards with CircuitPython! In fact some
boards like the Metro M4 Grand Central come with microSD card support built-in, and
for other boards like the Feather M4 Express family they can easily be connected to a
microSD card that expands their storage.
WIth the Adafruit MicroSD SPI or SDIO Card Breakout Board, you can add an SD card
to your project using the common SPI interface or the higher performance SDIO
interface.
This guide also shows how to use a microSD card to store files for a CircuitPython
board. Specifically using a microSD card with CircuitPython boards like Feather M0
Adalogger, Grand Central M4, ItsyBitsy M4, and others will be covered in this guide.
You'll learn how to connect a microSD card to the board and mount it as a new
filesystem that store code & data.
Starting with CircuitPython 6, there are two new modules to improve the performance
of SD cards using SDIO.
Power Pins
• 3V - This is the power pin. MicroSD cards must use 3.3V, so take care to only
hook 3.3V to this pin. Hooking 5V or VBAT to this pin will damage your microSD
card.
• GND - common ground for power and logic.
• DET - Detect whether a microSD card is inserted. This pin is connected to GND
internally when there's no card, but when one is inserted it is pulled up to 3V
with a 4.7kΩ resistor. That means that when the pin's logic level is False there's
no card and when it's True there is.
If you're using SPI to connect to your SD cards, the pins have the following functions:
• CLK - This is the SPI Clock pin / SCK Serial Clock, its an input to the chip.
• SO - this is the Serial Out / Microcontroller In Serial Out pin, for data sent from
the SD card to your processor.
If you're using SDIO to connect to your SD cards, the pins have the following
functions:
• CLK - The SDIO clock pin. A clock signal is sent by the microcontroller to the SD
card on this pin.
• CMD - A bidirectional pin for communication between the microcontroller and
the SD card, used for commands and information.
• 4 Data pins - Four bidirectional pins for communication between the
microcontroller and the SD card, used for transferring bulk data. Take care to
wire these in the correct order! The silk screen legends read: D0, D1, DAT2,
D3.
Note that when inserted, the SD card extends slightly beyond the edge of the PCB.
Check out the mechanical drawing on the Downloads page (https://adafru.it/Qce).
CircuitPython
As of CircuitPython 9, a mount point (folder) named /sd is required on the
CIRCUITPY drive. Make sure to create that directory after upgrading
CircuitPython.
Are you new to using CircuitPython? No worries, there is a full getting started guide
here (https://adafru.it/cpy-welcome).
It's always good idea to format the card before using, even if it's new! Note that
formatting will erase the card, so save anything you want first.
We strongly recommend you use the official SD card formatter utility - written
by the SD association. It solves many problems that come with bad formatting!
Download it and run it on your computer, there's also a manual linked from that page
for additional instructions.
1. If necessary, create an SPI bus object connecting to the card (cards attached
by SDIO do not have a separate bus object)
2. Construct an SDCard object
3. Create a vfs (virtual filesystem) object
4. "Mount" the vfs object to make the SD card's files appear so they can be
accessed by functions like open()
import mount_sd
In the examples, we'll assume you have already created a mount_sd.py file.
CircuitPython has trouble recognizing cards bigger than 32GB. 32GB of data
with CIrcuitPython? That's a lot.
The built in micro SD slot on the STM32F405 Feather is connected using SDIO. The
Grand Central M4 can connect the SD card using SDIO or SPI, depending which pins
are used.
Using sdcardio
As of CircuitPython 9, a mount point (folder) named /sd is required on the
CIRCUITPY drive. Make sure to create that directory after upgrading
CircuitPython.
Use the code and wiring examples in this section if these conditions are met:
• Your SD card slot uses the SPI bus to connect to the microcontroller (If it uses
SDIO, go to the pages for sdioio (https://adafru.it/MA3))
• Your CircuitPython board supports the sdcardio module. (If not, go to the
pages for adafruit_sdcard (https://adafru.it/MA4))
• Your CicuitPython version supports the sdcardio module. Only CircuitPython
6 and newer have sdcardio. (If not, go to the pages for
adafruit_sdcard (https://adafru.it/MA4))
The following section will show how to initialize the SD card and read & write data to
it. You'll want to put the correct lines to initialize it in mount_sd.py on your CIRCUITPY
drive. For specific wiring information and exact mount_sd.py files for several different
boards, check out the following pages.
import board
import busio
import sdcardio
import storage
Next create the SPI bus and a digital output for the microSD card's chip select line (be
sure to select the right pin names for your wiring):
# For breakout boards, you can choose any GPIO pin that's convenient:
cs = board.D10
# Boards with built in SPI SD card slots will generally have a
# pin called SD_CS:
#cs = board.SD_CS
Note that when you use sdcardio , cs is a Pin object, not a DigitalInOut
object. If you change your code to use adafruit_sdcard , you need to use a
DigitalInOut object instead.
At this point you're ready to create the microSD card object and the filesystem object:
Notice the sdcardio module has a SDCard class which contains all the logic for
talking to the microSD card at a low level. This class needs to be told the SPI bus and
chip select pin in its constructor.
Finally you can mount the microSD card's filesystem into the CircuitPython filesystem.
For example to make the path /sd on the CircuitPython filesystem read and write from
the card run this command:
storage.mount(vfs, "/sd")
At this point, you can read and write to the SD card using common Python functions
like open , read , and write . The filenames will all begin with "/sd/" to
differentiate them from the files on the CIRCUITPY drive. If you're not familiar, here's
an overview.
For example to create a file and write a line of text to it you can run:
Notice the with statement is used to create a context manager that opens and
automatically closes the file. This is handy because with file access you Python you
must close the file when you're done or else all the data you thought was written
might be lost!
The open function is used to open the file by telling it the path to it, and the mode (w
for writing). Notice the path is under /sd, /sd/test.txt. This means the file will be
created on the microSD card that was mounted as that path.
Inside the context manager you can access the f variable to operate on the file while
it's open. The write function is called to write a line of text to the file. Notice that
unlike a print statement you need to end the string passed to write with explicit
carriage returns and new lines.
You can also open a file and read a line from it with similar code:
When reading a line from a file with readline , the newline character '\n' at the
end of the line is included. By printing with end='' , the newline normally added by
print is skipped. Otherwise, when reading back the file, it would appear that there
were extra blank lines after every line of the file.
If you wanted to read and print all of the lines from a file you could call readline in
a loop. Once readline reaches the end of the file it will return an empty string so
you know when to stop:
You can also just use the open file object in a for loop. Each time the loop runs, the
loop variable will be assigned the content of the next line of the file:
Finally one other very common file scenario is opening a file to add new data at the
end, or append data. This works exactly the same as in Python and the open function
can be told you'd like to append instead of erase and write new data (what normally
happens with the w option for open ). For example to add a line to the file:
Notice the a option in the open function--this tells Python to add data at the end of
the file instead of erasing it and starting over at the top. Try reading the file with the
code above to see the new line that was added!
Those are the basics to manipulating files on microSD cards with CircuitPython! Once
you have it working, put the correct lines in mount_sd.py in CIRCUITPY, then
continue learning by checking out the example pages to print directory listings, log
sensor data, play MP3 files and display bitmap graphics.
import board
import busio
import sdcardio
import storage
spi = board.SPI()
cs = board.D10
Your board may be different. Refer to the following pages for more details.
Ready to do a little wiring? Almost any Adafruit CircuitPython board will connect to
the MicroSD Breakout Board using SPI. The following is an example using the Itsy
BItsy M4 express.
import board
import busio
import sdcardio
import storage
spi = board.SPI()
cs = board.D10 # Use the pin you wired to the breakout CS
The default communication rate is 8MHz, which works with most SD cards. A higher or
lower rate can be supplied with sdcardio.SDCard(spi, cs, baudrate=...) but
the limitation depends on
The Adafruit Grand Central M4 Express has a built-in SD card connected to the SPI
bus, so no wiring or soldering is needed!
To mount the SD card, use the following code. Remember to put it in the file
mount_sd.py on CIRCUITPY so you can easily call it from code.py or the REPL with
import mount_sd .
import board
import busio
import sdcardio
import storage
PyGamer
sdcardio is an SPI interface SD card library in CircuitPython 6.0 that is
optimized in C to be much faster than the original adafruit_sdcard library.
Some much smaller/older boards don't have enough memory for this module,
in which case you will have to use adafruit_sdcard
The PyGamer has a built-in SD card connected to a SPI bus, so no wiring or soldering
is needed!
To mount the SD card, use the following code. Remember, to put it in the file
mount_sd.py on CIRCUITPY so you can easily call it from code.py or the REPL with
import mount_sd .
import board
import sdcardio
import storage
With all its built-in sensors and battery power, the Feather M4 Sense is great for data-
logging projects. Stacking headers or FeatherWing Doublers need a little soldering
but no custom wiring.
Don't forget that the Adalogger FeatherWing has a Real Time Clock (RTC) (https://
adafru.it/MA5) so you can even timestamp your logs!
import board
import busio
import sdcardio
import storage
spi = board.SPI()
cs = board.D10
Using sdioio
As of CircuitPython 9, a mount point (folder) named /sd is required on the
CIRCUITPY drive. Make sure to create that directory after upgrading
CircuitPython.
Use the code and wiring examples in this section if these conditions are met:
• Your SD card slot uses the SDIO bus to connect to a 3V microcontroller (If it uses
SPI, go to the pages for sdcardio (https://adafru.it/MA6))
• Your CircuitPython board supports the sdioio module. (If not, you may be able
to use bitbangio and adafruit_sdcard )
The following section will show how to initialize the SD card and read & write data to
it. You'll want to put the correct lines to initialize it in mount_sd.py on your
CIRCUITPY drive. For specific wiring information and exact mount_sd.py files for
several different boards, check out the following pages.
At this point you're ready to create the microSD card object and the filesystem object:
sdcard = sdioio.SDCard(
clock=board.SDIO_CLOCK,
command=board.SDIO_COMMAND,
data=board.SDIO_DATA,
frequency=25000000)
vfs = storage.VfsFat(sdcard)
Notice the sdioio module has a SDCard class which contains all the logic for
talking to the microSD card at a low level. This class needs to be told the pins to use
in its constructor. SDIO_DATA is actually a tuple of up to 4 data pins.
Finally you can mount the microSD card filesystem into the CircuitPython filesystem.
For example to make the path /sd on the CircuitPython filesystem read and write from
the card run this command:
storage.mount(vfs, "/sd")
At this point, you can read and write to the SD card using common Python functions
like open , read , and write . The filenames will all begin with "/sd/" to
differentiate them from the files on the CIRCUITPY drive. If you're not familiar, here's
an overview.
For example to create a file and write a line of text to it you can run:
The open function is used to open the file by telling it the path to it, and the mode (w
for writing). Notice the path is under /sd, /sd/test.txt. This means the file will be
created on the microSD card that was mounted as that path.
Inside the context manager you can access the f variable to operate on the file while
it's open. The write function is called to write a line of text to the file. Notice that
unlike a print statement you need to end the string passed to write with explicit
carriage returns and new lines.
You can also open a file and read a line from it with similar code:
When reading a line from a file with readline , the newline character '\n' at the
end of the line is included. By printing with end='' , the newline normally added by
print is skipped. Otherwise, when reading back the file, it would appear that there
were extra blank lines after every line of the file.
If you wanted to read and print all of the lines from a file you could call readline in
a loop. Once readline reaches the end of the file it will return an empty string so
you know when to stop:
You can also just use the open file object in a for loop. Each time the loop runs, the
loop variable will be assigned the content of the next line of the file:
Finally one other very common file scenario is opening a file to add new data at the
end, or append data. This works exactly the same as in Python and the open function
Notice the a option in the open function--this tells Python to add data at the end of
the file instead of erasing it and starting over at the top. Try reading the file with the
code above to see the new line that was added!
Those are the basics to manipulating files on microSD cards with CircuitPython! Once
you have it working, put the correct lines in mount_sd.py in CIRCUITPY, then
continue learning by checking out the example pages to print directory listings, log
sensor data, play MP3 files and display bitmap graphics.
import board
import sdioio
import storage
sdcard = sdioio.SDCard(
clock=board.SDIO_CLOCK,
command=board.SDIO_COMMAND,
data=board.SDIO_DATA,
frequency=25000000)
vfs = storage.VfsFat(sdcard)
storage.mount(vfs, "/sd")
Your board may be different. Refer to the following pages for more details.
The Adafruit Grand Central M4 Express supports SDIO mode with the SDIO Breakout
Board. First, wire the two together as shown:
To mount the SD card, use the following code. Remember to put it in the file
mount_sd.py on CIRCUITPY so you can easily call it from code.py or the REPL with
import mount_sd .
import board
import sdioio
import storage
sdcard = sdioio.SDCard(
clock=board.SDIO_CLOCK,
command=board.SDIO_COMMAND,
# Note that board.SDIO_DATA is a tuple of four pins, not a single pin.
data=board.SDIO_DATA,
frequency=25000000)
vfs = storage.VfsFat(sdcard)
storage.mount(vfs, "/sd")
The Feather STM32F405 Express uses SDIO to connect its built in micro SD card
slot. Didn't notice it had an SD card slot? We hid it on the underside.
import board
import sdioio
import storage
sdcard = sdioio.SDCard(
clock=board.SDIO_CLOCK,
command=board.SDIO_COMMAND,
# Note that board.SDIO_DATA is a tuple of four pins, not a single pin.
data=board.SDIO_DATA,
frequency=25000000)
vfs = storage.VfsFat(sdcard)
storage.mount(vfs, "/sd")
Python Docs
Python Docs (https://adafru.it/Mmd)
This is the case particularly for older and lower-capacity microcontrollers like SAMD21
(M0).
The following section will show how to initialize the SD card and read & write data to
it. You'll want to put the correct lines to initialize it in mount_sd.py on your
CIRCUITPY drive. For typical wiring information and exact mount_sd.py files for
several different boards, check out next page.
First make sure you are running the latest version of Adafruit CircuitPython (https://
adafru.it/Amd) for your board.
Next you'll need to install the necessary libraries to use the hardware--carefully follow
the steps to find and install these libraries from Adafruit's CircuitPython library bundle
(https://adafru.it/ENC).
adafruit_sdcard.mpy
adafruit_bus_device
import adafruit_sdcard
import board
import busio
import digitalio
import storage
Next create the SPI bus and a digital output for the microSD card's chip select line (be
sure to select the right pin names for your wiring):
At this point you're ready to create the microSD card object and the filesystem object:
Finally you can mount the microSD card's filesystem into the CircuitPython filesystem.
For example to make the path /sd on the CircuitPython filesystem read and write from
the card run this command:
storage.mount(vfs, "/sd")
At this point, you can read and write to the SD card using common Python functions
like open , read , and write . The filenames will all begin with "/sd/" to
differentiate them from the files on the CIRCUITPY drive. If you're not familiar, here's
an overview.
For example to create a file and write a line of text to it you can run:
Notice the with statement is used to create a context manager that opens and
automatically closes the file. This is handy because with file access you Python you
must close the file when you're done or else all the data you thought was written
might be lost!
The open function is used to open the file by telling it the path to it, and the mode (w
for writing). Notice the path is under /sd, /sd/test.txt. This means the file will be
created on the microSD card that was mounted as that path.
Inside the context manager you can access the f variable to operate on the file while
it's open. The write function is called to write a line of text to the file. Notice that
You can also open a file and read a line from it with similar code:
When reading a line from a file with readline , the newline character '\n' at the
end of the line is included. By printing with end='' , the newline normally added by
print is skipped. Otherwise, when reading back the file, it would appear that there
were extra blank lines after every line of the file.
If you wanted to read and print all of the lines from a file you could call readline in
a loop. Once readline reaches the end of the file it will return an empty string so
you know when to stop:
You can also just use the open file object in a for loop. Each time the loop runs, the
loop variable will be assigned the content of the next line of the file:
Finally one other very common file scenario is opening a file to add new data at the
end, or append data. This works exactly the same as in Python and the open function
can be told you'd like to append instead of erase and write new data (what normally
happens with the w option for open ). For example to add a line to the file:
Notice the a option in the open function--this tells Python to add data at the end of
the file instead of erasing it and starting over at the top. Try reading the file with the
code above to see the new line that was added!
Those are the basics to manipulating files on microSD cards with CircuitPython! Once
you have it working, put the correct lines in mount_sd.py in CIRCUITPY, then
import adafruit_sdcard
import board
import busio
import digitalio
import storage
spi = board.SPI()
cs = digitalio.DigitalInOut(board.SD_CS)
Your board may be different. Refer to the following pages for more details.
Adalogger M0 Feather
The Feather M0 Adalogger is a classic board that doesn't have the same power as its
newer siblings, but it's still good for logging. Best of all, no soldering is necessary to
access the SD card.
To mount the SD card, use the following code. Remember, to put it in the file
mount_sd.py on CIRCUITPY so you can easily call it from code.py or the REPL with
import mount_sd .
import adafruit_sdcard
import board
import busio
import digitalio
import storage
spi = board.SPI()
cs = digitalio.DigitalInOut(board.SD_CS)
Python Docs
Python Docs (https://adafru.it/C5z)
Examples
As of CircuitPython 9, a mount point (folder) named /sd is required on the
CIRCUITPY drive. Make sure to create that directory after upgrading
CircuitPython.
This example will work on any board where you've configured mount_sd.py (https://
adafru.it/MA7)!.
The function print_directory works using os.listdir (to list files in a directory)
and os.stat (to get information about an item, such as size or whether it's a
directory).
By keeping track of the nesting of directories within directories, names are indented
according to the value of tabs to show the directory structure.
import os
# pylint: disable=unused-import
import mount_sd # You must create a module mount_sd.py that mounts your sd card!
prettyprintname = ""
for _ in range(tabs):
prettyprintname += " "
prettyprintname += file
if isdir:
prettyprintname += "/"
print('{0:<40} Size: {1:>10}'.format(prettyprintname, sizestr))
print("Files on filesystem:")
This example will work on any board where you've configured mount_sd.py (https://
adafru.it/MA7)!.
We saw some great speed increases going from good ( adafruit_sdcard ) to better
( sdcardio ) to best ( sdio ). However, every SD card is different and (especially
when writing data) slow down as they "age", so your mileage may vary:
◦ Reads up to 120kB/s
◦ Data logging at up to 160 of lines per second
◦ Reads up to 650kB/s
◦ Data logging at up to 700 of lines per second
◦ Reads up to 1100kB/s
◦ Data logging at over 1000 lines per second
import time
import os
# pylint: disable=unused-import
import mount_sd # You must create a module mount_sd.py that mounts your sd card!
print()
print("SD card I/O benchmarks")
dt = (t1-t0) / 1e9
print(f"write {len(b)} x {n} in {dt}s {n * len(b) / dt / 1000:.1f}Kb/s")
dt = (t1-t0) / 1e9
# Test "logging" speed and report the time to write each line.
# Note that in this test the file is not closed or flushed after each
# line, so in the event of power loss some lines would be lost. However,
# it allows much more frequent logging overall.
#
# If keeping data integrity is your highest concern, follow the logging
# example, not this logging benchmark!
print("logging test")
with open("/sd/log.txt", "wt") as logfile:
t0 = time.monotonic_ns()
for i in range(10000):
t1 = time.monotonic_ns()
dt = (t1-t0) / 1e9
print(f"Line {i}, {dt:2f}s elapsed", file=logfile)
t1 = time.monotonic_ns()
dt = (t1-t0) / 1e9
This is a great example to extend with the sensor(s) of your choice, as well as with a
Real Time Clock (RTC) so that you know the time that each reading was recorded. We
chose the internal temperature to make this work with a wide range of boards, as long
you've configured mount_sd.py (https://adafru.it/MA7)!
import time
import board
import digitalio
import microcontroller
# pylint: disable=unused-import
import mount_sd # You must create a module mount_sd.py that mounts your sd card!
led = digitalio.DigitalInOut(board.D13)
led.direction = digitalio.Direction.OUTPUT
This code plays MP3 files without volume control. If you use headphones,
choose a set with an in-line volume control and always listen at a safe volume.
CircuitPython supports any MP3 file you like. We've found that mono and stereo files
from 32kbit/s to 128kbit/s work, with sample rates from 16kHz to 44.1kHz. The DAC
output on the SAMD51 M4 is just 12-bit so there's not much point in using higher
bitrates.
With this example, tailored to the PyGamer, you can loop MP3s to your heart's content
(or as long as power lasts). Put the MP3 files in the main folder of your SD card, put
the code below in code.py, and make sure you've configured mount_sd.py (https://
adafru.it/MA7)!
This example can be adapted to a lot of SAMD51 and nRF52840 boards. Where
there's no headphone jack or speaker connection, consider the Prop Maker
Featherwing for your sound-making needs. For more about playing MP3s, visit the
page in the essentials guide (https://adafru.it/MAs). For a full MP3 player for your
PyGamer, see the dedicated guide (https://adafru.it/MAt).
import os
import time
import board
import digitalio
# pylint: disable=unused-import
import mount_sd # You must create a module mount_sd.py that mounts your sd card!
voodoo = [1,2,3]
# You have to specify some mp3 file when creating the decoder
mp3 = open(mp3files[0], "rb")
decoder = MP3Decoder(mp3)
audio = AudioOut(board.A0, right_channel=board.A1)
speaker_enable = digitalio.DigitalInOut(board.SPEAKER_ENABLE)
speaker_enable.switch_to_output(True)
while True:
for filename in mp3files:
print("Playing", filename)
This example can be adapted to a lot of CircuitPython boards with support for
displayio (https://adafru.it/EGh).
For more information on making bitmap files, check out this guide (https://adafru.it/
MbZ).
import os
import time
import board
import displayio
display = board.DISPLAY
while True:
if len(bmpfiles) == 0:
print("No BMP files found")
break
bitmap = displayio.OnDiskBitmap(filename)
tile_grid = displayio.TileGrid(bitmap, pixel_shader=bitmap.pixel_shader)
group = displayio.Group()
group.append(tile_grid)
display.root_group = group
Downloads
Files
• SD card simplified specification (https://adafru.it/MAu)
• EagleCAD files on GitHub (https://adafru.it/MAv)
• 3D models on GitHub (https://adafru.it/19eo)
Schematic
Fab Print