Introduction
This guide covers how to connect the Makerverse Micro SD Card Adapter to a Raspberry Pi Pico and program a basic data logging script in MicroPython.
To follow along, it's best to have:
- A Makerverse Micro SD Card Adapter
- A Raspberry Pi Pico
- A small breadboard
- Six jumper wires (Female-Female, Male-Male, or Female-Male, as appropriate)
- A potentiometer or other source of physical test data
Contents
- Connections
- Downloading MicroPython Modules
- SD Card Format
- Initialising an SD Card
- Data Logging
- Clock Speeds and Wiring
Connections
The SD card module requires six connections:
- Power (either 5.0V or 3.3V - independent of logic voltage)
- GND
- CS
- MOSI
- CLK
- MISO
There is also a CD (card detect) pin which is connected to ground when a card is fully inserted. This pin is not required to follow this guide but can be used for your own projects. The Makerverse SD Card Module does not contain a pull-up resistor for this pin.
The on-board 3.3V regulator allows the power supply to be quite flexible. The 3.3V pin requires a regulated 3.3V input (the Raspberry Pi Pico's 3V3 pin is fine). Connection of more than 3.6V on the 3.3V pin will destroy the SD card. The 5.0V input can accept from 4.5V to 12V.
The MOSI, CLK, and MISO signals form an SPI bus. You can read more about the details of SPI in our Arduino SPI tutorial. On the Raspberry Pi Pico you have the option of using the software SPI bus but the hardware SPI bus is recommended for increased speed. Note, however, that while the software SPI bus is slower it can be configured on any GPIO pins while the hardware SPI bus is limited to dedicated pins.
The Raspberry Pi Pico has two hardware SPI busses with the following pin options:
Signal | SPI0 | SPI1 |
MOSI | 3, 7, 19 | 11, 15 |
MISO | 0, 4, 16 | 8, 12 |
CLK | 2, 6, 18 | 10, 14 |
In the MicroPython SDCard library the CS (chip select) line is software driven and can therefore connect to any otherwise unused GPIO pin.
For the examples below, GPIO pins 12 (MISO), 13 (CS), 14 (CLK), and 15 (MOSI) will be used.
Downloading MicroPython Modules
At time of writing MicroPython includes support for SD cards but the module is not built into the v1.17 distribution for the Raspberry Pi Pico.
Thankfully, you can easily download the required file from GitHub: sdcard.py (Right Click -> Save As...)
Download sdcard.py and copy it to your Raspberry Pi Pico.
SD Card Format
For compatibility with MicroPython's uos module the SD card must be formatted with a FAT32 filesystem. Generally SD cards are formatted FAT32 "from the factory" but in case you need to manually reformat a card (say, after using it in a mobile phone or Raspberry Pi) the steps in Windows are as follows:
- Connect the card to the Windows machine
- Open Explorer
- Right click on the card's drive letter
- Select "Format..."
- Ensure that FAT32 is selected under the "File system" drop-down menu
- Click "Start"
Under Linux they can be formatted by running sudo mkfs.vfat /dev/sdXXX in a terminal, replacing sdXXX with the cards assigned device identifier as seen in the dmesg output.
Initialising an SD Card
Ensure that all six connections have been made correctly and that the sdcard.py module has been downloaded. For this guide the Makerverse Micro SD Module and Raspberry Pi Pico have been assembled onto a small breadboard.
The example below is a bare bones test to ensure that the library is installed and SD card is working. It assumes pins 12, 13, 14, and 15 are used for the SPI bus, as shown in the photo above.
from machine import SPI, Pin import sdcard, uos spi = SPI(1,sck=Pin(14), mosi=Pin(15), miso=Pin(12)) cs = Pin(13) sd = sdcard.SDCard(spi, cs) uos.mount(sd, '/sd') print(uos.listdir('/sd'))
After execution you should see the card's file listing printed to the console in addition to the "sd" folder in the Raspberry Pi Pico's file list. If it doesn't appear automatically you can force a refresh by clicking the "three horizontal lines" icon at the top of the Pico's file listing and clicking "Refresh".
Data Logging
This section demonstrates the creation of a simple data logger which reads samples from the ADC0 input (pin 26) and writes them to a timestamped log file. A call to time.sleep_ms(500) sets the sample rate to slightly less than twice per second. The logging is performed inside a while True: loop so it will continue collecting data until it is stopped by a ctrl c in Thonny or the card is physically removed.
Note that when f.write() is called the data is written to an internal buffer, not the physical SD card. As such a power failure (or forced removal of the card) is highly likely to result in data loss. To avoid data loss a call to f.flush() is performed inside the data sampling loop. This function forces data in the internal buffer to be physically sent to the SD card for writing. There will be a short delay while the SD card performs a write operation so it is still possible to lose one sample but unlikely. If you are writing a lot of data the call to f.flush() may need to be removed for increased performance and to reduce the number of write cycles on each SD card sector.
Before runing this example you can connect a potentiometer between 3.3V, pin 26, and GND, to act as a source of test data. Alternatively, line 17 can be modified to read from a sensor of your own choosing. Note that with this algorithm the ADC samples will be taken when the SPI bus is idle, reducing ADC errors due to electrical noise.
from machine import SPI, Pin, ADC import sdcard, uos import time spi = SPI(1,sck=Pin(14), mosi=Pin(15), miso=Pin(12)) cs = Pin(13) sd = sdcard.SDCard(spi, cs) uos.mount(sd, '/sd') print(uos.listdir('/sd')) print("Starting ADC samples") adc = ADC(0) with open('/sd/adcData.txt', "w") as f: while True: x = adc.read_u16() # Replace this line with a sensor reading of your choosing t = time.ticks_ms()/1000 f.write(str(t)) # Write time sample was taken in seconds f.write(' ') # A space f.write(str(x)) # Write sample data f.write('\n') # A new line f.flush() # Force writing of buffered data to the SD card print(t, x) time.sleep_ms(500)
The screenshot below shows an example execution of the above script with the potentiometer connected and set to about mid-scale. The script was left to run for a few seconds, stopped with a ctrl c, the Pico's files refreshed, and the adcData.txt file opened.
Clock Speeds and Wiring
The SD card standard allows the SPI bus to be clocked up to 25MHz. Compared to most "maker" projects this is reasonably fast (I2C, for example, is typically 0.4MHz) and may need careful consideration of physical construction for reliable long term operation.
To keep things simple, the MicroPython library, discussed above, initialises the SD card at a clock rate of 100kHz before switching to 1.32MHz for bulk data transfer (at time of writing this is set on line 115 of sdcard.py). Unless your project has a need for higher bandwidth It is recommended that the 1.32MHz clock be kept to minimise the risk of unreliable operation or data corruption.
The Makerverse Micro SD Module is designed for high speed operation up to 25MHz however at this speed the use of 10-20cm long jumper cables is not recommended. If high speed clocking is required, we recommend PCB mounting on a high frequency optimised protoboard.
If long term stability is found to be poor you can consider:
- Shortening jumper wires
- Upgrading jumper wire connectors to solder joints
- Building the whole project a prototype PCB
- Using a lower clock rate
- Placing a 100 Ohm resistor just before the MISO pin on the SD card module
The MISO (master-in-slave-out) pin is driven directly by the SD card and, with modern cards, may have very fast edges. A 100 Ohm resistor will help to slow down these eges, resulting in more reliable operation due to decreased crosstalk and ground bounce.