Elastic Sheep

Because elasticdog was already taken

Elastic Sheep header image 2

Teensy2 USB Mass Storage with an SD card

April 12th, 2010 · 36 Comments · Uncategorized

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

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 ;) .

Source code

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/V2.zip. A binary is also available.

The repository root address is http://bitbucket.org/elasticsheep/lufa-sdcard-mass-storage-demo/

Tags: ·····

36 Comments so far ↓

  • Memos From the Cube » Excitement and exhaustion

    [...] it a warmth I haven’t felt from a textbook before. Good stuff.A few days ago I came across a really neat post about the LUFA Mass Storage Device demo code, and how to modify it to support SD cards instead of [...]

  • Daniel

    works a treat on my at90usb1287 first go… although your make file is corrupt…

    whats the licensing on your code?

  • The Chief Sheep

    Hi Daniel,

    Thanks for your comment. Can you elaborate on the problem you run into with my Makefile ?

    To answer your question about code license:
    * the LUFA library is covered by the MIT license.
    * the sd-reader files (sd_raw.*) are covered by the LGPL 2.1 license.
    * my own code (SDCardManager.*) is covered by the MIT license.

  • Steven Meyer

    I’m assuming the file is at /Lufa/Demos/Device/LowLevel/ and when I try compiling it it says:

    ——– begin ——–
    avr-gcc (GCC) 4.3.2
    Copyright (C) 2008 Free Software Foundation, Inc.
    This is free software; see the source for copying conditions. There is NO
    warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

    make: *** No rule to make target `MassStorage.elf’, needed by `elf’. Stop.

    Any ideas on what the problem is?

  • Steven Meyer

    WOW, nvm I’m dumb. Now when I try compiling it I get this:

    steven@steven-laptop:/media/USB/lufa-sdcard-mass-storage-demo/LowLevelMassStorage+SD$ make

    ——– begin ——–
    avr-gcc (GCC) 4.3.2
    Copyright (C) 2008 Free Software Foundation, Inc.
    This is free software; see the source for copying conditions. There is NO
    warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

    Compiling C: MassStorage.c
    avr-gcc -c -mmcu=atmega32u4 -I. -gdwarf-2 -DF_CPU=16000000UL -DF_CLOCK=16000000UL -DBOARD=BOARD_USER -D USB_DEVICE_ONLY -D FIXED_CONTROL_ENDPOINT_SIZE=8 -D FIXED_NUM_CONFIGURATIONS=1 -D USE_FLASH_DESCRIPTORS -D USE_STATIC_OPTIONS=”(USB_DEVICE_OPT_FULLSPEED | USB_OPT_REG_ENABLED | USB_OPT_AUTO_PLL)” -D INTERRUPT_CONTROL_ENDPOINT -Os -funsigned-char -funsigned-bitfields -ffunction-sections -fno-inline-small-functions -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wundef -Wa,-adhlns=./MassStorage.lst -I../LUFA_091223/ -std=gnu99 -Wundef -MMD -MP -MF .dep/MassStorage.o.d MassStorage.c -o MassStorage.o
    MassStorage.c: In function ‘SetupHardware’:
    MassStorage.c:74: warning: implicit declaration of function ‘clock_prescale_set’
    MassStorage.c:74: error: ‘clock_div_1’ undeclared (first use in this function)
    MassStorage.c:74: error: (Each undeclared identifier is reported only once
    MassStorage.c:74: error: for each function it appears in.)
    make: *** [MassStorage.o] Error 1

  • The Chief Sheep

    Hi Steven,

    It looks like your version of avr-libc does not support the ATMEGA32U4 microcontroller.

    I checked in the avr-libc history and it appears that the support of the clock_prescale_set macro for the ATMEGA32U4 has been added in the 1.6.3 version. I am currently using the avr-libc-1.6.7 from CrossPack-AVR-20100115.

  • JRSmile

    Hi there, you are the one with the most progress done so far i found on the internet, im looking for someone who could help me converting the teensy to a bootable USB CD-ROM Emulator.

    i want to develop a usb-stick like device which can be dip-switched to a cd-rom drive loading an iso file from the sd card.

    but at first i have the problem that if i change the SCSI_INQUIERY_RESPONSE to a cd-rom drive instead of a mass-storage device the attached pc freezes, any idea what that could be?

  • The Chief Sheep

    Hi Jan,

    This is an interesting project.

    From what I could gather about Mass Storage profile for CD drive, you can specify a CD/DVD Peripheral Device Type in the InquiryData structure returned in response to the SCSI_CMD_INQUIRY command in SCSI.c. But then in this case you have to implement the MMC-4 command set instead of the SBC-2 command set used for flash drive.

    I suppose your PC is freezing because it receives bad response to its requests.
    I would suggest enabling the printf in the SCSI_DecodeSCSICommand function to check the requests sent by the PC.

    The MMC-4 spec can be found at this address: http://www.13thmonkey.org/documentation/SCSI/mmc4r05a.pdf

  • Manolo

    Congratulations on your work.

    I’m working on your code, when I try load it with the hex file, works, but if I make a new project with the avrstudio, the sd card isn´t recognized automatically as with the hex file. The sd card is detected by the pc, but isn´t detected as a storage unit.

    Any idea where it might be the problem?

    thanks

  • The Chief Sheep

    Hi Manolo,

    What version of AvrStudio are you using ? How do you compile the project ?

    I tried to build the project with the latest AVRStudio 4.18SP2 and got an HEX file that is almost identical to the one provided. With this HEX file, when I connect the Teensy2 to Windows XP or 7, I am able to see the content of the SD card.

    I compared the LSS files from CrossPack vs AVRStudio and there is no significant difference in the generated assembly code.

  • Manolo

    thanks so much, I´ve got other problem, I need write, text and values of adc (the adc, run sucesfully ) in a file .txt in the sd card, . I´m testing with the functions of avr libs (fdevopen) and the functions in your file, sd_raw. I know that I need work in blocks of 512 bits (fat32), but it doesn´t run me.
    I saw the rest of your project with the atmega32u4, and they´re very good.

  • Vikas

    Can you elaborate on the speed of read/write you are getting for your implmentation.

    I did something similar on pic18f with USB and SD card.Im finding it very slow.

  • The Chief Sheep

    Hello Vikas,

    Same experience as you, reading the SD is slow and writing on it is very slow…

    I am done some tests with my WAV player application and the best case I observe is 300kBytes/s when reading raw blocks from the SD. It should be about the same with the Mass Storage application.

    The main bottleneck is the SPI clock limited to 8MHz with a 16MHz crystal. The SD card specifications state a maximum clock frequency of 25MHz, and the MMC specifications state a maximum clock frequency of 52MHz depending on the device.

  • JRSmile

    Hi there,
    after a lot of trial and error i stopped developing with the teensy for a while, and buyed myself an http://www.i-odd.com to get what i want.

    But i have plenty of spare time now and want to try the teensy (usb-stick) version of the bootable cd-rom emulator again.

    so id like to request if you could try to write an “howto” how to get an iso image of a fat32 partition mounted as cd-rom.

    best regards,
    JR

  • Paul

    Can you also use a teensy 2.0++ without a SD card?
    The teensy 2.0++ has proper storage.
    Thanks for any response
    Paul

  • The Chief Sheep

    Hi Paul,

    It depends on your application requirements.

    The Teensy++ 2.0 uses an AT90USB1286 microcontroller with 128KB of Flash memory.

    If your storage needs are within this capacity, it is indeed easier to store your data as const arrays in your flash memory (small pictures, LCD fonts…). But as soon as you want to use audio media in your application or big data files (1MB, 10MB…), an external SD card is much more practical. For a WAV audio playback application, you could store more than 3 hours of CD-quality audio (44.1kHz stereo 16 bits) on a 2GB SD card…

    Another solution is to use an external serial Flash memory device integrated in your design, but it will lack the removable media practicality for your user.

    Mathieu

  • gabriel

    So this gave me the great idea of activating the “flashdrive” (SD Card adaptor) – copy the files to the PC via CMD and then disable the SD card adaptor.

  • The Chief Sheep

    Hi Gabriel,

    Do you mean driving cmd.exe from a USB keyboard profile in the Teensy2 ? Yes it could be done with a composite USB profile.

    But I think the malicious potential (like in Teensy AVRs used in penetration testing) could limit the usefulness of the idea.

    BR
    Mathieu

  • gabriel

    Thanks for the reply,

    The idea bascially is:

    - Make the teensy a composite USB = Flashdrive + Keyboard
    - Connect this to the host
    - Make the “Keyboard” copy the files in the “Flashdrive” to the computer
    - Dissmount the “Flashdrive”
    - Execute the files

    Keep up the good work !

  • Prashanth. G

    Hi,
    I tried running this application using the at90usb162 but it doesn’t work as the memory card, I reduced the buffer size that’s used to access the memory card, then it shows the device connected as a USB MassStorage device but the device is not mounted.
    My question is will the buffer of 256 bytes is ok to access the data from MMC, is there any way that I could use at90usb162(with 512 bytes SRAM) as a card reader.

    Thanks in advance.

  • The Chief Sheep

    Hi Prashanth,

    By default, the sd-reader library caches the last read or written SD card sector in memory and thus can not work with the AT90USB162 as a sector is 512 bytes.
    You can use less memory by setting SD_RAW_SAVE_RAM to 1 in sd_raw_config.h. You must also set SD_RAW_WRITE_SUPPORT to 0, because buffering is mandatory for write operations.
    With this configuration, you should be able to use the AT90USB162 as a read-only card reader.

    Because the Mass Storage profile is block oriented, you could implement block writing without buffering. It would require modifying the sd_raw_write_interval function to send a CMD_WRITE_SINGLE_BLOCK command to the SD card and then get the data from a callback that would read directly 16-byte packets from the USB endpoint, like it is done in SDCardManager_WriteBlockHandler.

    I had not the courage to implement this for the Teensy1 and switched to the Teensy2 instead when I built my demo code ;)
    BR,
    Mathieu

  • Prashanth. G

    Hi Chief,
    Thanks for the previous reply for at90usb162 I tried SD_RAW_SAVE_RAM to 1 and SD_RAW_WRITE_SUPPORT to 0, I had the write functions undefined removed the SDCardManager_WriteBlocks function call and compiled the same code, the card is detected by the controller but the MMC device doesnot mount on ubuntu never tried with a different OS, so should I change something on the OS side, and my at90usb162 runs at 8Mhz not 16 MHz
    the messages that I get on minicom (serial port is as follows) when the MMC card is connected, for single restart.

    Connect
    Connect
    Connect
    Connect
    Connect
    Ready
    Connect
    Connect
    Ready
    Connect
    Connect
    Ready
    Connect
    Connect
    Ready
    Connect
    Connect
    Ready

    and when the MMC card is not connected I get this message continuously on my serial port

    MMC/SD initialization failed

    Any further help greatly appreciated thanks in advance,

    Prashanth. G

  • Prashanth. G

    Hi Chief,
    My at90usb162 works as a read only MMC card reader with the changes you suggested and in addition to that, I changed the configuration of endpoints which were double bank to single bank, it doesn’t work with ejecting the card,

    Prashanth. G

  • The Chief Sheep

    Hi Prashanth,

    The USB communication does not seem to work correctly. You should only see one Connect followed by one Ready.
    You are not reaching the moment where the PC tries to read or write from the SD card.

    Have you changed the values for your target in the project Makefile ?
    MCU = at90usb162
    F_CPU = 8000000
    F_CLOCK = 8000000

    BR,
    Mathieu

  • Prashanth. G

    Hi Cheif I changed the MCU, F_CPU, F_CLOCK parameters in the make file now I can read the files from the MMC but I get more number of connect and ready the sequence is as follows.
    USB Disconnect
    Connect
    Connect
    USB Disconnect
    Connect
    Connect
    Connect
    Ready
    SD blocks: 0
    Invalid Command
    Invalid Command
    more than 12 Invalid commands
    then
    R 0 8
    6 Invalid Commands then it displays the usual text, it works as a read only card reader but eject section is an error
    I still have to edit the sd_raw_write_interval function

    Regards,
    Prashanth. G

  • Leif Helge

    Hi Chief!

    I have compiled and tested the SW on a Mattair Tech ATmega32U4 USB Development Board, and it works as a charm. But I would love to have the project in AVR Studio, not to edit and compile it in an alfashioned DOS window.

    How should I arrange all the files to enable use of the AVR Studio environment?

    Any help highly apreciated!

  • zach

    http://ww1.microchip.com/downloads/en/DeviceDoc/22288A.pdf

    I’m trying to do the same thing, will this work?

  • The Chief Sheep

    Hi Zach,
    As long as you have a SPI interface you should be able to access a SD card.
    Now with this USB/SPI converter, you would have to implement your driver on a PC host instead of inside of inside a microcontroller.
    Though it may be really slow because of the USB Full Speed (12Mb/s) bottleneck.
    BR
    Mathieu

  • Naveen

    Hi,

    I want to add USB serial + Mass storage in same controller can this be possible?

    my application to have mass storage to save data from PC application + some on board sensor sends data to PC serial port. both has to be from same USB port.

    Suggest me

    Thanks
    Naveen

  • Luke

    Nice work, Chief Sheep

    I tried compiling your code, unmodified, using your makefile and avrgcc 4.7.2. But I have several errors.

    Descriptors.c:57:33: error: variable ‘DeviceDescriptor’ must be const in order to be put into read-only section by means of ‘__attribute__((progmem))’

    Errors of the same type recur on about 4 or 5 other lines, but I’ll only post it once, the type of error is identical on the other lines.

    What do you think? How can I fix this? Inappropriate choice of avr-gcc on my computer, or something like that?

  • Luke

    Of course, I could just make the appropriate variables consts and that will fix it, but are there any possible ramifications of doing so that aren’t immediately obvious?

  • The Chief Sheep

    Hi Luke,
    Thanks for the info. I have patched my project with const declarations like you did.
    This should not cause any issue as PROGMEM structures are Flash constants anyway.
    BR
    Mathieu

  • The Chief Sheep

    Hi Naveen,
    This should be possible with a composite USB device.
    Have a look on “Virtual Serial/Mass Storage Device” in the latest LUFA version.
    BR
    Mathieu

  • Ivoah

    Would this work on the Arduino Micro? It uses the same ATMEGA32U4 processor. What pins would I connect the sd card to?

  • The Chief Sheep

    Hi Ivoah,
    I think it should also work on the Arduino Micro using the SCK, MOSI, MISO and SS pins.
    BR
    Mathieu

  • Nahid

    Hi,

    I want to store the data of a sensor with 1MHz clock rate. Is this project going to work for me? What is the rate of saving data on the SD card? I am working with ATEMGA32u4.
    Thanks.

Leave a Comment