Friday, January 28, 2011

Logging Data to SD Cards

Logging telemetry data from robots is invaluable for troubleshooting and prototyping.

The microSD card provides an easy and cheap basis for microcontroller data log storage. Here's how to interface it to your microcontroller.


Serial Peripheral Interface (SPI)

SD/MMC cards (including microSD) use a fast, 4-wire, Serial Peripheral Interface or SPI bus supported by many microcontrollers.

The Arduino's AVR chips all have SPI capability in hardware and so does the mbed (ARM Cortex M3) I've been working with. PIC microcontrollers (I'm no expert) apparently have a Synchronous Serial Port (SSP) that supports SPI.

Four wires are used. !CS is an active low chip select so that you can select specific SPI devices that share the same bus. I didn't need to do that with the SD card this time around. MOSI is Master Out, Slave In. Master is the MCU, slave is the SD card. MISO is Master In, Slave Out.

I find the SPI nomenclature far more intuitive than the mind-bending RX/TX and DTE/DCE craziness of RS-232 (etc). It always takes a second to remember: do I attach RX to RX or RX to TX? And is it RX from the perspective of the pc or the micro? Gah!

Not so with SPI. Just connect MOSI to MOSI, MISO to MISO and SCK to SCK. Simple.

SD/MMC Interface

SD/MMC cards safely operate at 3.3V so interfacing to a 5V MCU is a little more complex than just wiring. More on that in a moment.

Wiring
One can buy a breakout board such as the one from Sparkfun to use microSD cards or buy a connector from DigiKey, Mouser, etc., and wire it up to a custom PCB.

The ultimate in jumper-free convenience
I designed a board that simply plugs in next to your breadboard Arduino clone, ready to go. No jumpers required. All pins are labeled both SPI function and Arduino pin. It's 5V-safe with a level shifter, and has an onboard regulator. For sale on Tindie.



Another cheap, simple approach that I discovered online is to solder 0.100" pin headers onto an SD-to-MicroSD adapter (like this one), wire it up on a breadboard or whatever, and then plug microSD cards into the adapter. See the pic below for the SD card with the pin headers soldered on.

The full size SD/MMC card or adapter has several contacts on the back. Here is the pinout:


3.3V MCU Devices

If you have a 3.3V MCU, then connect MOSI to your MCU's MOSI pin, MISO to MISO, SCK to SCK. You can either attach !CS to a pin on the MCU, to a mux (like a 74*151), or hardwire it to ground if it's the only thing on the SPI bus.

5V MCU Devices

Voltage level shifting from 5V down to the SD card's 3.3V can be done in several ways. A simple resistor voltage divider seems to be adequate but could limit data rate.  You really only need to level shift the lines going from the MCU to the SD card: MOSI and SCK. Most MCUs should read the correct levels on the 3.3V MISO line.

Here's a good tutorial with some other interfacing options for level shifting. My eeZee MicroSD above has a 3.3V regulator and level shifter IC built-in so it's 5V safe.

A couple of other possibilities are the use of level shifting chips. The Propeller Robot Control Board uses bi-directional Texas Instruments TXB0108 chips, for example. You can also use a 74*244 or 74*245.

Software Interface

Once the card is wired up, you can't just throw data at it. The card has to be formatted with a FAT16 or FAT32 filesystem and the Arduino has to run FAT filesystem code to create/append files. That is, if you want to pull data off the card using your PC or Mac.

FAT16 can only handle volumes up to 2G in size. Arduino users can incorporate the small-footprint fat16lib or SDuFAT library for use with these smaller cards. When formatting the card on XP, select "FAT" as the filesystem type.

I discovered after some testing that I had to use FAT32 software, and a larger Arduino FAT library, sdfatlib, with my 4G card.

Case Study

So why am I so interested in data logging all of a sudden?  I've entered the Sparkfun Autonomous Vehicle Competition (AVC) for 2011. The design for my robot's navigation system combines a rate gyro sensor and GPS, and likely other sensors, to be fused algorithmically.

I want to collect gyro and GPS data from real test runs and then in the comfort of my home, write prototype code to process the data to estimate heading and position. I'd previously interfaced the rate gyro and the GPS to the Arduino but I needed a cheap and easy way to store the data for later download and analysis. Using an SD card was ideal.

For my project, the Sparkfun AVC robot, I found an SD card and adapter on sale at Radio Shack and I breadboarded everything, drawing 3.3V from the SMT regulator I was already using to power the LISY300AL gyro.

I used a simple voltage divider on the !CS, MOSI, and SCK lines, consisting of 330 ohm and 180 ohm resistors (sacrificing power consumption for speed, I suppose; 5V/500 ohms = ~10mA)

I added a couple of indicator LEDs for power and data transmission. The data LED indicators are powered with small signal BJTs, the bases of which are connected to MISO and MOSI, respectively, through 1K current limiting resistors.


After some testing on the bench, it was time for the maiden voyage.  I had to mount all the electronics onto the RC truck chassis: SD card breadboard, Arduino, and GPS. Then I had to merge prototype code for the gyro, gps, and FAT32 filesystem. Then, finally, drive the RC Truck around and gather some data to analyze.

More on the data capture and analysis next time.

2 comments:

  1. SPI has a master and a slave, so that's why it can use names like MISO which stands for "Master In, Slave Out".

    I don't see any way to avoid the mind-bending craziness of RX and TX. If you're just using those two lines, then RS-232 serial is a symmetric protocol: there is no master and no slave. Just remember that any line labeled "TX" should be an output (as far as I know), and don't connect two outputs together.

    ReplyDelete
  2. Thanks for the post!

    I know you know this but for others who might read the comments...

    The rule is that if it's DCE (modem) to DTE (PC) then RX goes to RX and TX goes to TX. A DCE (e.g., modem) will transmit data on RX and receive data on TX.

    So in a sense the DTE is the master and the DCE is the slave and the connection is simple.

    But with RS-232 it's possible for two DTEs to talk. In that case, you have to swap the RX and TX lines because, of course, each DTE sends data on TX and receives on RX.

    It always takes me awhile to figure it out when I'm in the middle of hacking together hardware on a protoboard. This was particularly maddening when I was trying to do serial comm with an AVR using, alternately, a Sparkfun FTDI breakout (DTE) *and* a BlueSMiRF Bluetooth modem (DCE). I built a board that accepted either but it took awhile ... and I still goofed it up a couple times and had to hack the PCB :)

    I found this link that has some good drawings in case it helps anyone out there.

    http://www.bb-elec.com/tech_articles/db9_db9_signal_directions.asp

    Take it easy,
    Michael

    ReplyDelete