Iterating on my precedent post, the next logical step is to use the USB interface to access the SD card connected to the Teensy2/ATMEGA32U4 with a Mass Storage profile.
LUFA Mass Storage demo
The LUFA library offers a mass storage demo. It is based on the AT90USBKEY demonstration board that provides two onchip serial flash memories accessed via the SPI bus. With the LUFA demo, the flash memories can ve viewed as an USB flash drive from a host PC.
To read/write data in the flash storage, the host uses the SCSI Primary Command (SPC) Set and SCSI Block Command (SBC) Set. Those SCSI commands are wrapped in Command Block Wrapper (CBW) and Command Status Wrapper (CSW) structures.
Here is a bloc diagram of the demo code:
The CBW are received from the host and decoded in MassStorage.c. The extracted SCSI commands are handled by SCSI.c. Then DataFlashManager.c provides an interface to read/write data blocks using the DataFlash driver.
In the SCSI_Command_Read_Capacity_10 handler, the block size VIRTUAL_MEMORY_BLOCK_SIZE is set to 512 bytes. The flash capacity is returned as a number of 512-bytes blocks. This is the same block size used in SD card with capacity smaller than 2GB.
The DataFlashManager directly reads small data blocks from the OUT endpoint and write them to the data flash. Doing like that you don’t need to store full data blocks in the MCU RAM. Incoming data are only stored in the endpoint buffer before being written to the data flash. The same principle is used to read data blocks from the data flash to the IN endpoint.
Integration of a SD driver
To access a SD card instead of a dataflash, I simply need a driver able to read/write raw blocks through the SPI bus. I will re-use the sd_raw driver from the sd-reader library introduced in the previous post.
Between the SCSI decoder and the sd_raw driver, I developped a SdCardManager adapted from the DataFlashManager of the original LUFA demo.
Here is the bloc diagram with my modifications:
The SDCardManager layer implements read/write operations directly from/to the USB endpoints.
Because data blocks are not stored in RAM, I can not directly use the functions sd_raw_read and sd_raw_write. Instead I will use the functions sd_raw_read_interval and sd_raw_write_interval that allows to do data transfers using callbacks.
The hardware used to test my application is a Teensy2/ATMEGA32U4 with a SD card adapter connected on the SPI bus. The serial port is used to output debug traces.
Compiling the project
See at the end of the post how to download my source code.
The application code is located in the LowLevelMassStorage+SD directory. Just type make to compile it.
The resulting MassStorage.hex binary can be downloaded to a Teensy2 with the Teensy loader.
Connecting to the host
I insert a 2GB SD in my adapter and connect the Teensy2 to OS X. Using the serial port and printf, I can see read and write commands being issued to the application:
$ cu -s 9600 -l /dev/tty.usbserial MMC/SD initialization failed USB Connect USB_Device_SetConfiguration USB Ready R 0 1 W 0 1 R 0 48 R 0 1 R 0 1 R 0 1 R 1 1 R 63 8 R 550 1 R 551 1 R 552 1 R 553 1 R 63 8 R 64 1 R 63 8 R 543 8 R 551 8 R 559 8 R 567 8 R 575 8 R 583 8 R 63 8 R 64 243 W 64 8 W 307 8 R 550 32 R 582 64 W 2694 8 W 64 8 W 307 8 W 550 32 W 2630 64 R 646 8 R 902 64 W 64 8 W 307 8 W 582 64
When the activity stops, I can see my SD card successfully mounted in the Finder:
I can copy files on the SD card and then read them again without problem.
It is just that the transfer speed is suddenly very slow compared to a real SD card reader .
As an experiment, the source code for this project can be downloaded from my BitBucket Mercurial repository: http://bitbucket.org/elasticsheep/lufa-sdcard-mass-storage-demo/get/V1.zip. A binary is also available.
The repository root address is http://bitbucket.org/elasticsheep/lufa-sdcard-mass-storage-demo/