RPi Pico MCU and SD cards deep dive

For our Tarot Badge this year, we chose a display with an onboard SD Card slot. The flash amount on the Pico was nowhere near sufficient for storing even one tarot deck at the display resolution, much less the three decks we included in the badge.

Early on, superdev Kevin had major success in coding our routines. Everything pretty much worked, loading cards wasn’t super fast due to limitations of the hardware, but splitting off the animations to their own core while card loading happened made things appear more seamless for the user.

Of course in badgelife we count our pennies and look for deals, so I bought bulk microSD cards and adapters — in the quantities we were buying, even bulk retail was at least triple the cost of what we ended up with, which was a whole bunch of 512MB Nokia cards that appeared to be pulled from phones and surplussed.

Then we started noticing anomalies. Sometimes they would work, and sometimes they would simply fail to mount. We spent a lot of time troubleshooting this, and eventually bought a bunch of Gigastone MicroSD cards so that we’d have something to hand out at Defcon with the badges released there.

When we got back, however, we went into persistent mode. There was no way we were going to let this go without at least an explanation. SD cards are pretty low tech, and there aren’t that many variables in the mounting process — so why are they failing to mount when they read perfectly well on a PC.

The answer, it turned out, was baud rate. Kevin and I patiently sorted through numerous iterations of code with various cards, working with different variables until we found it. Turns out when you drop the baud rate down low enough, it mounts. And it doesn’t seem to really drastically affect the image load time. So it’s usable.

But we wanted to drill down even further. Because some Nokia cards worked and others didn’t. Eventually we came up with an iterative mount sequence, which is implemented in the current version of the release firmware. Basically, we try mounting at 4M. If that fails, we try at 1M. Then 500K. Then 100K. Then 50K. Then 4K. If it fails to mount at 4K, we call it a failure and toss that card.

try:
    sd_card_spi = SPI(1, baudrate=4000000, polarity=0, phase=0, bits=8, firstbit=SPI.MSB, sck=Pin(10, Pin.OUT),
                      mosi=Pin(11, Pin.OUT), miso=Pin(8, Pin.OUT))
    sd_card = SDCard(sd_card_spi, cs=Pin(9, Pin.OUT))
    vfs = uos.VfsFat(sd_card)
    uos.mount(vfs, '/sd')
    sd_card_status = 'sd_card: PASS (4M)'

So when you boot your badge, it tries that entire sequence described above behind the scenes, and reports the results once it settles on an acceptable baud rate.

I’d like to explore further and understand what determines the usable baud rate on a card, especially given that they are all, by all appearances, very similar models with the exact same capacity. If time permits, I may do a correlation of actual model numbers with detected baud rates.

If anyone knows, feel free to get in touch for further discussion. Meanwhile, I hope this helps anyone working with SD cards in the RPi Pico or RP2040 platform.