USBMSD SD card Hello World for Mbed platforms
Dependencies: mbed USBMSD_SD USBDevice
Revision 3:0ffb2eee9e06, committed 2011-11-11
- Comitter:
- samux
- Date:
- Fri Nov 11 16:12:21 2011 +0000
- Parent:
- 2:27a7e7f8d399
- Child:
- 4:980e6470dcce
- Commit message:
- good: we can use the sd card as mass storage device
Changed in this revision
--- a/SDcard.cpp Fri Nov 11 15:22:53 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,197 +0,0 @@ -/* mbed Microcontroller Library - SDFileSystem - * Copyright (c) 2008-2009, sford - * - * Introduction - * ------------ - * SD and MMC cards support a number of interfaces, but common to them all - * is one based on SPI. This is the one I'm implmenting because it means - * it is much more portable even though not so performant, and we already - * have the mbed SPI Interface! - * - * The main reference I'm using is Chapter 7, "SPI Mode" of: - * http://www.sdcard.org/developers/tech/sdcard/pls/Simplified_Physical_Layer_Spec.pdf - * - * SPI Startup - * ----------- - * The SD card powers up in SD mode. The SPI interface mode is selected by - * asserting CS low and sending the reset command (CMD0). The card will - * respond with a (R1) response. - * - * CMD8 is optionally sent to determine the voltage range supported, and - * indirectly determine whether it is a version 1.x SD/non-SD card or - * version 2.x. I'll just ignore this for now. - * - * ACMD41 is repeatedly issued to initialise the card, until "in idle" - * (bit 0) of the R1 response goes to '0', indicating it is initialised. - * - * You should also indicate whether the host supports High Capicity cards, - * and check whether the card is high capacity - i'll also ignore this - * - * SPI Protocol - * ------------ - * The SD SPI protocol is based on transactions made up of 8-bit words, with - * the host starting every bus transaction by asserting the CS signal low. The - * card always responds to commands, data blocks and errors. - * - * The protocol supports a CRC, but by default it is off (except for the - * first reset CMD0, where the CRC can just be pre-calculated, and CMD8) - * I'll leave the CRC off I think! - * - * Standard capacity cards have variable data block sizes, whereas High - * Capacity cards fix the size of data block to 512 bytes. I'll therefore - * just always use the Standard Capacity cards with a block size of 512 bytes. - * This is set with CMD16. - * - * You can read and write single blocks (CMD17, CMD25) or multiple blocks - * (CMD18, CMD25). For simplicity, I'll just use single block accesses. When - * the card gets a read command, it responds with a response token, and then - * a data token or an error. - * - * SPI Command Format - * ------------------ - * Commands are 6-bytes long, containing the command, 32-bit argument, and CRC. - * - * +---------------+------------+------------+-----------+----------+--------------+ - * | 01 | cmd[5:0] | arg[31:24] | arg[23:16] | arg[15:8] | arg[7:0] | crc[6:0] | 1 | - * +---------------+------------+------------+-----------+----------+--------------+ - * - * As I'm not using CRC, I can fix that byte to what is needed for CMD0 (0x95) - * - * All Application Specific commands shall be preceded with APP_CMD (CMD55). - * - * SPI Response Format - * ------------------- - * The main response format (R1) is a status byte (normally zero). Key flags: - * idle - 1 if the card is in an idle state/initialising - * cmd - 1 if an illegal command code was detected - * - * +-------------------------------------------------+ - * R1 | 0 | arg | addr | seq | crc | cmd | erase | idle | - * +-------------------------------------------------+ - * - * R1b is the same, except it is followed by a busy signal (zeros) until - * the first non-zero byte when it is ready again. - * - * Data Response Token - * ------------------- - * Every data block written to the card is acknowledged by a byte - * response token - * - * +----------------------+ - * | xxx | 0 | status | 1 | - * +----------------------+ - * 010 - OK! - * 101 - CRC Error - * 110 - Write Error - * - * Single Block Read and Write - * --------------------------- - * - * Block transfers have a byte header, followed by the data, followed - * by a 16-bit CRC. In our case, the data will always be 512 bytes. - * - * +------+---------+---------+- - - -+---------+-----------+----------+ - * | 0xFE | data[0] | data[1] | | data[n] | crc[15:8] | crc[7:0] | - * +------+---------+---------+- - - -+---------+-----------+----------+ - */ - -#include "SDcard.h" - -#define SD_COMMAND_TIMEOUT 5000 - -SDcard::SDcard(PinName mosi, PinName miso, PinName sclk, PinName cs) : - _spi(mosi, miso, sclk), _cs(cs) { - _cs = 1; -} - -int SDcard::disk_write(const char *buffer, int block_number) { - // set write address for single block (CMD24) - if(_cmd(24, block_number * 512) != 0) { - return 1; - } - - // send the data block - _write(buffer, 512); - return 0; -} - -int SDcard::disk_read(char *buffer, int block_number) { - // set read address for single block (CMD17) - if(_cmd(17, block_number * 512) != 0) { - return 1; - } - - // receive the data - _read(buffer, 512); - return 0; -} - -// PRIVATE FUNCTIONS - -int SDcard::_cmd(int cmd, int arg) { - _cs = 0; - - // send a command - _spi.write(0x40 | cmd); - _spi.write(arg >> 24); - _spi.write(arg >> 16); - _spi.write(arg >> 8); - _spi.write(arg >> 0); - _spi.write(0x95); - - // wait for the repsonse (response[7] == 0) - for(int i=0; i<SD_COMMAND_TIMEOUT; i++) { - int response = _spi.write(0xFF); - if(!(response & 0x80)) { - _cs = 1; - return response; - } - } - _cs = 1; - return -1; // timeout -} - -int SDcard::_read(char *buffer, int length) { - _cs = 0; - - // read until start byte (0xFF) - while(_spi.write(0xFF) != 0xFE); - - // read data - for(int i=0; i<length; i++) { - buffer[i] = _spi.write(0xFF); - } - _spi.write(0xFF); // checksum - _spi.write(0xFF); - - _cs = 1; - return 0; -} - -int SDcard::_write(const char *buffer, int length) { - _cs = 0; - - // indicate start of block - _spi.write(0xFE); - - // write the data - for(int i=0; i<length; i++) { - _spi.write(buffer[i]); - } - - // write the checksum - _spi.write(0xFF); - _spi.write(0xFF); - - // check the repsonse token - if((_spi.write(0xFF) & 0x1F) != 0x05) { - _cs = 1; - return 1; - } - - // wait for write to finish - while(_spi.write(0xFF) == 0); - - _cs = 1; - return 0; -}
--- a/SDcard.h Fri Nov 11 15:22:53 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,27 +0,0 @@ -/* mbed Microcontroller Library - SDFileSystem - * Copyright (c) 2008-2009, sford - */ - -#ifndef SDCARD_H -#define SDCARD_H - -#include "mbed.h" - -class SDcard -{ -public: - SDcard(PinName mosi, PinName miso, PinName sclk, PinName cs); - virtual int disk_write(const char *buffer, int block_number); - virtual int disk_read(char *buffer, int block_number); - -protected: - - int _cmd(int cmd, int arg); - int _read(char *buffer, int length); - int _write(const char *buffer, int length); - - SPI _spi; - DigitalOut _cs; -}; - -#endif
--- a/USBDevice/USBMSD/AT45.cpp Fri Nov 11 15:22:53 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,829 +0,0 @@ -#include "AT45.h" -#include "mbed.h" - -/* mbed Library - AT45 - * (c)2007, cstyles - */ - - - -/* - * Constructor - */ - -AT45::AT45(PinName mosi, PinName miso, PinName clk, PinName ncs) - : _spi(mosi, miso, clk), _ncs(ncs) { - - _pages = -1; // number of pages - _pagesize = -1; // size of pages 256/264, 512/528, 1024/1056 - _devicesize = -1; // In bytes - - _buffer1address = -1; // page address in buffer 1 - _buffer2address = -1; // page address in buffer 2 - _lru = -1; // leas recently used buffer (for eviction) - - _reset(); // Populate all this stuff - -} - - - - - -/* - * This is the reset function of the constructor. - * Interrogate the part and set up some variables. - */ - -void AT45::_reset() { - int _id = 0; - int _status = 0; - - _spi.format(8,0); - _spi.frequency(5000000); - - _id = id(); - _status = status(); - - if ( (_id & 0x1f) == 0x2) { // 1Mbit - _devicesize = 131072; // size in bytes - _pages = 512; // number of pages - _blocks = 256; // Number of 512 byte blocks - if (_status & 0x1) { - _pagesize = 256;} - else { - _pagesize = 264;} - } - - else if ( (_id & 0x1f) == 0x3) { // 2M - _devicesize = 262144; // Size in bytes - _pages = 1024; // Number of pages - _blocks = 512; // Number of 512 byte blocks - if (_status & 0x1) { - _pagesize = 256;} - else { - _pagesize = 264;} - } - - else if ( (_id & 0x1f) == 0x4) { // 4M - _devicesize = 524288; - _pages = 2048; - _blocks = 1024; // Number of 512 byte blocks - if (_status & 0x1) { - _pagesize = 256;} - else { - _pagesize = 264;} - } - - else if ( (_id & 0x1f) == 0x5) { // 8M - _devicesize = 1048576; - _pages = 4096; - _blocks = 2048; // Number of 512 byte blocks - if (_status & 0x1) { - _pagesize = 256;} - else { - _pagesize = 264;} - } - - else if ( (_id & 0x1f) == 0x6) { // 16M - _devicesize = 2097152; - _pages = 4096; - _blocks = 4096; // Number of 512 byte blocks - if (_status & 0x1) { - _pagesize = 512;} - else { - _pagesize = 528;} - } - - else if ( (_id & 0x1f) == 0x7) { // 32M - _devicesize = 4194304; - _pages = 8192; - _blocks = 8192; // Number of 512 byte blocks - if (_status & 0x1) { - _pagesize = 512;} - else { - _pagesize = 528;} - } - - else if ( (_id & 0x1f) == 0x8) { // 64M - _devicesize = 8388608; - _pages = 8192; - _blocks = 16384; - if (_status & 0x1) { - _pagesize = 1024;} - else { - _pagesize = 1056;} - } - - else { - _devicesize = -1; - _pages = -1; - _pagesize = -1; - } -} - - - - - -/* - * Select pulls nCS low, but also ensure SPI channel is formatted - * This is so that another device sharing the same SPI pins - * doenst leave it in an unknown state - */ - -void AT45::_select() { - // ensure that the SPI port is set up correctly still - // This allows SPI channel sharing - _spi.format(8,0); - _spi.frequency(5000000); - _ncs = 0; -} - - - - - -/* - * Deselect simply returns nCS to high - */ -void AT45::_deselect() { - _ncs = 1; -} - - - - - -/* - * Work out the page address - * If we have a 2^N page size, it is just the top N bits - * If we have non-2^N, we use the shifted address - */ - -int AT45::_getpaddr(int address) { - - int paddr; - - if (_pagesize == 256) { - paddr = address & 0xffffff00;} - else if (_pagesize == 264) { - paddr = (address << 1) & 0xfffffe00;} - else if (_pagesize == 512) { - paddr = address & 0xfffffe00;} - else if (_pagesize == 528 ) { - paddr = (address << 1) & 0xfffffc00;} - else if (_pagesize == 1024) { - paddr = address & 0xfffffc00;} - else if (_pagesize == 1056 ) { - paddr = (address << 1) & 0xfffff800;} - else { - paddr = 0xdeadbeef;} - - return (paddr); - -} - - - - - -/* - * Clean the buffer address. This is the 8/9/10 LSBs - */ - -int AT45::_getbaddr(int address) { - - int baddr; - - if ((_pagesize == 256) || (_pagesize == 264 )) { - baddr = address & 0xff;} - else if ((_pagesize == 512) || (_pagesize == 528 )) { - baddr = address & 0x1ff;} - else if ((_pagesize == 1024) || (_pagesize == 1056 )) { - baddr = address & 0x3ff;} - else { - baddr = 0xcafebabe;} - - return (baddr); -} - - - - - -/* - * Return the Id of the part - */ - -int AT45::id() { - int id = 0; - _select(); - _spi.write(0x9f); - id = (_spi.write(0x00) << 8); - id |= _spi.write(0x00); - _deselect(); - return id; -} - - - - - -/* - * return the Status - */ - -int AT45::status() { - int status = 0; - _select(); - _spi.write(0xd7); - status = (_spi.write(0x00) << 8 ); - status |= _spi.write(0x00); - _deselect(); - return status; -} - - - - - -/* - * Erase the entire chip - */ - -void AT45::erase() { - - _pollbusy(); // make sure flash isnt already in busy. - - // There are errata on this. For now, do itthe long way :-( - _select(); - // 4 byte command sequence - _spi.write(0xc7); - _spi.write(0x94); - _spi.write(0x80); - _spi.write(0x9a); - _deselect(); - - _pollbusy(); // Make erase a blocking function - -} - - - - - -/* - * return the size of the part in bytes - */ - -int AT45::size() { - return _devicesize; -} - - - - - -/* - * return the page size of the part in bytes - */ - -int AT45::pagesize() { - return _pagesize; -} - - - - - -/* - * return the numbers of pages - */ - -int AT45::pages() { - return _pages; -} - - - - - -/* - * Make sure the Flash isnt already doing something - */ -void AT45::_pollbusy() { - volatile int busy = 1; - while (busy) { - // if bit 7 is set, we can proceed - if ( status() & 0x80 ) { - busy = 0;} - } -} - - - - - -/* - * Make sure the Flash isnt already doing something - */ - -void AT45::pollbusy() { - _pollbusy(); -} - - - - - -/* - * This function returns the char - */ - -char AT45::read(int address) { - // return byte from address - return (_memread( address )); -} - - - - - -/* - * This function writes the char to the address supplied - * Note : We pass the raw address to the underlying functions - */ - -void AT45::write(int address, char data) { - - _pollbusy(); - _flashread(1,address); // read the Flash page into SRAM buffer - _pollbusy(); // wait for the read to complete - _sramwrite(1,address,data); // Write new data into SRAM - _pollbusy(); // Make sure flash isnt busy - _flashwrite(1,address); // Write back to the page address -} - - - - - -/* - * Read from an SRAM buffer - * Note : We create buffer and page addresses in _sram and _flash - */ - -int AT45::_sramread(int buffer, int address) { - - int cmd = 0; - int baddr = 0; - - baddr = _getbaddr(address); - - _select(); - - if(buffer == 1) - {cmd = 0xd4;} - else - {cmd = 0xd6;} - - _spi.write(cmd); - _sendaddr (baddr); - _spi.write (0x0); // dont care byte - cmd = _spi.write (0x0); - - _deselect(); - - return (cmd); -} - - - - - - -/* - * Write to an SRAM buffer - * Note : We create buffer and page addresses in _sram and _flash - */ - -void AT45::_sramwrite(int buffer, int address, int data) { - - int cmd = 0; - int baddr = 0; - - baddr = _getbaddr(address); - - _pollbusy(); - - _select(); - - if (buffer == 1) - {cmd = 0x84;} - else - {cmd = 0x87;} - - _spi.write(cmd); - _sendaddr (baddr); - _spi.write (data); - - _deselect(); -} - - - - - -/* - * Read from Flash memory into SRAM buffer - */ - -void AT45::_flashread (int buffer, int address) { - - int cmd = 0; - int paddr = _getpaddr(address); // calculate page address - - _pollbusy(); // Check flash is not busy - _select(); - - if (buffer == 1) - {cmd = 0x53;} - else - {cmd = 0x55;} - - _spi.write (cmd); - _sendaddr (paddr); - _deselect(); - -} - - - - - -/* - * Write and SRAM buffer back to main memory - */ - -void AT45::_flashwrite (int buffer, int address) { - - int cmd = 0; - int paddr = _getpaddr(address); - - _pollbusy(); // Check flash is not busy - - _select(); - - if (buffer == 1) - {cmd = 0x83;} - else - {cmd = 0x86;} - - _spi.write (cmd); - _sendaddr (paddr); - _deselect(); - - _pollbusy(); // Check flash is not busy - -} - - - - - -/* - * Read directly from main memory - */ - -int AT45::_memread (int address) { - - int data = 0; - int addr; - - addr = _getpaddr(address) | _getbaddr(address); - - _pollbusy(); - - _select(); - - _spi.write (0xd2); // Direct read command - _sendaddr (addr); - - // 4 dont care bytes - _spi.write (0x00); - _spi.write (0x00); - _spi.write (0x00); - _spi.write (0x00); - - // this one clocks the data - data = _spi.write (0x00); - _deselect(); - - _pollbusy(); - - return data; -} - - - - - -/* - * Sends the three lest significant bytes of the supplied address - */ - -void AT45::_sendaddr (int address) { - _spi.write(address >> 16); - _spi.write(address >> 8); - _spi.write(address); -} - - - - - -/* - * Return the number of 512 byte blocks in this device - */ - -int AT45::blocks(void) { - return _blocks; -} - - - - - -/* - * Read the numbered block into the data array supplied - */ - -int AT45::blockread (char* data, int block) { - - // For 256 byte pages, we read two pages - if((_pagesize == 256) || (_pagesize == 264)) { - - int address = block * 512; // This is the start address of the 512 byte block - - _flashread(1,address); // read the first page of the block into SRAM buffer 1 - _pollbusy(); // Wait until First page has loaded into buffer 1 - - // I'll do this directly as we are reading back an entire buffer, and this can be done more optimally - // than using _sramread - _select(); - _spi.write(0xd4); - _sendaddr (0x0); - _spi.write (0x0); // dont care byte - - for(int i=0;i<256;i++) { - data[i] = _spi.write (0x0); - } - _deselect(); - - _flashread(1,address+256); // read the second page of the block into SRAM buffer 2 - _pollbusy(); // Wait until second page has loaded into buffer 2 - - // Now the second page is loaded, pull this out into the second half of the data buffer - // I'll do this directly as we are reading back an entire buffer, and this can be done more optimally - // than using _sramread - _select(); - _spi.write(0xd4); - _sendaddr (0x0); - _spi.write (0x0); // dont care byte - - for(int i=0;i<256;i++) { - data[256+i] = _spi.write (0x0); - } - _deselect(); - return (0); - } - - - // For 512 byte pages, we read just the single page, transfer it - else if((_pagesize == 512) || (_pagesize == 528)) { - - int address = block * 512; // This is the start address of the 512 byte block - - _pollbusy(); // Wait until First page has loaded into buffer 1 - _flashread(1,address); // read the first page of the block into SRAM buffer 1 - _pollbusy(); // Wait until First page has loaded into buffer 1 - - // Now the page has loaded, simply transfer it from the sram buffer to the data array - // I'll do this directly as we are reading back an entire buffer, and this can be done more optimally - // than using _sramread - _select(); - _spi.write(0xd4); - _sendaddr (0x0); - _spi.write (0x0); // dont care byte - - for(int i=0;i<512;i++) { - data[i] = _spi.write (0x0); - } - _deselect(); - return (0); - } - - - // For 1024 byte pages, we read just a single page, transfer half of it - else if((_pagesize == 1024) || (_pagesize == 1056)) { - int address = _getpaddr(block * 512); // This is the start address of the 512 byte block - - _pollbusy(); // Wait until First page has loaded into buffer 1 - - _flashread(1,address); // read the first page of the block into SRAM buffer 1 - - _pollbusy(); // Wait until First page has loaded into buffer 1 - - // Now the page has loaded, simply transfer it from the sram buffer to the data array - // I'll do this directly as we are reading back an entire buffer, and this can be done more optimally - // than using _sramread - - _select(); - _spi.write(0xd4); - - if (block %2) { // odd numbered block, read from adress 0x200 - _sendaddr (0x200); - } - else {// even numbered block, then we are reading from sram buffer 0x0 - _sendaddr (0x0); - } - - - _spi.write (0x0); // dont care byte - - for(int i=0;i<512;i++) { - data[i] = _spi.write (0x0); - } - _deselect(); - return (0); - } - - else { - return (-1); // something isnt configured right - } -} - - - - - -/* - * Write the buffer to the numbered block - */ - -int AT45::blockwrite(char* data, int block) { - - // For 256 byte pages, we overwrite two pages - if((_pagesize == 256) || (_pagesize == 264)) { - - // fill the first buffer with the first half of the block - // do this directly, for better performance - _select(); - _spi.write(0x84); // writing to buffer #1 - _sendaddr (0); // we are writing to the entire buffer - - for(int i=0;i<256;i++) { - _spi.write (data[i]); - } - _deselect(); - - _flashwrite(1,(block*512)); - - // fill the buffer with the second half of the block - // do this directly, for better performance - _select(); - _spi.write(0x84); // writing to buffer #1 - _sendaddr (0); // we are writing to the entire buffer - - for(int i=0;i<256;i++) { - _spi.write (data[256+i]); - } - _deselect(); - - _flashwrite(1,((block*512)+256)); - } - - - // For 512 byte pages, we overwrite a single page - else if((_pagesize == 512) || (_pagesize == 528)) { - - // fill the first buffer with the block data - // do this directly, for better performance - _select(); - _spi.write(0x84); // writing to buffer #1 - _sendaddr (0); // we are writing to the entire buffer - - for(int i=0;i<512;i++) { - _spi.write (data[i]); - } - _deselect(); - - _pollbusy(); // make sure the Flahs isnt busy - - // issue command to write buffer 1 to the appropraite flash page - _select(); - _spi.write (0x83); - _sendaddr (_getpaddr(block * 512)); - _deselect(); - _pollbusy(); // make sure the Flahs isnt busy - } - - - // For 1024 byte pages, we do a read modify write - // must make sure we overwrite the right half of the page! - else if((_pagesize == 1024) || (_pagesize == 1056)) { - - _pollbusy(); // make sure the flash isnt busy - - int address = _getpaddr(block*512); - - // Read the page into sram - _flashread(1,address); - - // wait for this operation to complete - _pollbusy(); - - // Overwrite the appropriate half - // do this directly, for better performance - _select(); - _spi.write(0x84); // writing to buffer #1 - - if(block%2) { // this is an odd block number, overwrite second half of buffer - _sendaddr (0x200); // we are writing to the entire buffer - } - else { // this is an even block, overwrite the first half - _sendaddr (0x0); // we are writing to the entire buffer - } - - for(int i=0;i<512;i++) { - _spi.write (data[i]); - } - _deselect(); - - // Write the page back - _pollbusy(); - _flashwrite(1,address); - - } - - - // Something has gone wrong - else { - return (-1); - } - - return (0); -} - - -/* - * Return the number of 512 byte blocks in this device - */ -void blockerase (int block) {} - - - -/* - * returns the number of buffer to use - * If page is already open, use it - * If one of the buffers is unused, use it - * else evict the least recently used. - - */ -int _allocatebuffer (int address) { - - int buf = 0; - // is page already open in buffer 1? - // is page already open in buffer 2 - // is buffer 1 unusued? - // is buffer 2 unused - // evict LRU - - return (buf); - -} - - - -/* - * write the buffer back to flash - * set it as LRU - * make _bufferNaddress = -1 - */ -void _flushbuffer (int buffer) { - - // is page already open in buffer 1? - // is page already open in buffer 2 - // is buffer 1 unusued? - // is buffer 2 unused - // evict LRU - -} - - - - - - - \ No newline at end of file
--- a/USBDevice/USBMSD/AT45.h Fri Nov 11 15:22:53 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,103 +0,0 @@ -/* mbed Library - AT45 - * Copyright (c) 2008, cstyles - - -Class to make the AT45 SPI flash parts from ATMEL appear as SRAM or 512 byte block devices - -This class supports 011,021,041,081,161,321,641 variants of the AT45DBxxx family - -|| Code || Density || Page size || Pages || Package || -|| 011 || 1 || 256 || 512 || 8 SOIC || -|| 021 || 2 || 256 || 1024 || 8 SOIC || -|| 041 || 4 || 256 || 2048 || 8 SOIC || -|| 081 || 8 || 256 || 4096 || 8 SOIC || -|| 161 || 16 || 512 || 4096 || 8 SOIC || -|| 321 || 32 || 512 || 8192 || 8 SOIC || -|| 641 || 64 || 1024 || 8192 || 28 TSOP || - -To do : - - - Check for current status on Erase Chip command not working. - - */ - -#ifndef MBED_AT45_H -#define MBED_AT45_H - -#include "mbed.h" - -class AT45 { - - // Public functions - public: - - AT45(PinName mosi = p5, PinName miso = p6, PinName clk = p7, PinName ncs = p8); - - void erase (void); - - // Integer RAM access - char read (int address); // read int from address. Automatically word-aligns address - void write (int address, char data); // Write int to address. Automatically world-aligns address - - // Block device access - int blocks (void); // returns the number of 512 byte blocks - int blockread (char* data, int block); // read a block - int blockwrite (char* data, int block); // write a block - int blockerase (int block); // erase a block - - // Part interrogation - int size (void); // Device size in bytes - int pages (void); // Device size in bytes - int pagesize (void); // Device size in bytes - - int id (void); // ID of part - int status (void); // Status register - void pollbusy (void); // Wait until Flash is not busy - - // Private variables - private : - - SPI _spi; - DigitalOut _ncs; - - int _pages; // Integer number of pages - int _pagesize; // page size, in bytes - int _devicesize; // device size in bytes - int _blocks; // Number of 512 byte blocks - - // Some flags for buffering - int _buffer1address; - int _buffer2address; - int _lru; - - // Helper routunes - void _reset(); - void _select(); - void _deselect(); - void _pollbusy (void); - - // accessing SRAM buffers - void _sramwrite (int buffer, int address, int data); - int _sramread (int buffer, int address); - - // Transferring SRAM buffers to/from FLASH - void _flashwrite (int buffer, int paddr); - void _flashread (int buffer, int paddr); - - // Allocate buffer - int _allocatebuffer(int address); - void _flushbuffer(int buffer); - - // Reading FLASH directly - int _memread (int address); - - // Calculate page/subpage addresses - int _getpaddr (int); - int _getbaddr (int); - - // Send 3 byte address - void _sendaddr (int address); - -}; - -#endif
--- a/USBDevice/USBMSD/USBMSD.cpp Fri Nov 11 15:22:53 2011 +0000 +++ b/USBDevice/USBMSD/USBMSD.cpp Fri Nov 11 16:12:21 2011 +0000 @@ -38,7 +38,7 @@ #define MAX_PACKET MAX_PACKET_SIZE_EPBULK // memory size -#define MemorySize 0x2000000 +#define MemorySize 0x200000 //number of blocks #define BlockCount (MemorySize / BlockSize) @@ -51,7 +51,7 @@ }; -USBMSD::USBMSD(uint16_t vendor_id, uint16_t product_id, uint16_t product_release): USBDevice(vendor_id, product_id, product_release), mem(p5, p6, p7, p8) { +USBMSD::USBMSD(uint16_t vendor_id, uint16_t product_id, uint16_t product_release): USBDevice(vendor_id, product_id, product_release) { } // Called in ISR context to process a class specific request @@ -164,7 +164,7 @@ // if the array is filled, write it in memory if (!((addr + size)%BlockSize)) - mem.disk_write((char *)page, addr/BlockSize); + blockWrite(page, addr/BlockSize); addr += size; length -= size; @@ -187,7 +187,7 @@ // beginning of a new block -> load a whole block in RAM if (!(addr%BlockSize)) - mem.disk_read((char *)page, addr/BlockSize); + blockRead(page, addr/BlockSize); // info are in RAM -> no need to re-read memory for (n = 0; n < size; n++) { @@ -430,7 +430,7 @@ } if (!(addr%BlockSize)) - mem.disk_read((char *)page, addr/BlockSize); + blockRead(page, addr/BlockSize); writeNB(EPBULK_IN, &page[addr%BlockSize], n, MAX_PACKET_SIZE_EPBULK);
--- a/USBDevice/USBMSD/USBMSD.h Fri Nov 11 15:22:53 2011 +0000 +++ b/USBDevice/USBMSD/USBMSD.h Fri Nov 11 16:12:21 2011 +0000 @@ -5,13 +5,14 @@ /* * Guide to adapt this class to your storage chip: * -* - adapt the BlockSize symbol in USBMSD.h -* - adapt the MemorySize symbol in USBMSD.cpp -* - declare your own object to store data: here AT45 mem -* - Be sure to provide : -* - mem.blockread(page, block_number); -* - mem.blockwrite(page, block_number); +* - Adapt the BlockSize symbol in USBMSD.h +* - Adapt the MemorySize symbol in USBMSD.cpp +* - Declare a class which inherits from USBMSD +* - Define two virtual functions: +* - blockRead(uint8_t * page, uint16_t block_number); +* - blockWrite(uint8_t * page, uint16_t block_number); * These functions are used by USBMSD class to read or write data +* - Instanciate your object */ #ifndef USBMSD_H @@ -23,7 +24,6 @@ #include "USBDevice_Types.h" #include "USBDevice.h" -#include "SDcard.h" #define DEFAULT_CONFIGURATION (1) @@ -58,21 +58,7 @@ uint8_t Status; } CSW; -/** -* USBMSD example -* -* @code -* #include "mbed.h" -* #include "USBMSD.h" -* -* USBMSD msd; -* -* int main() { -* while(1); -* } -* -* @endcode -*/ + class USBMSD: public USBDevice { public: @@ -85,9 +71,26 @@ */ USBMSD(uint16_t vendor_id = 0x0703, uint16_t product_id = 0x0104, uint16_t product_release = 0x0001); - + /* + * read a block on a storage chip + * + * @param data pointer where will be stored read data + * @param block block number + * @returns 0 if successful + */ + virtual int blockRead(uint8_t * data, uint16_t block){return 1;}; + + /* + * write a block on a storage chip + * + * @param data data to write + * @param block block number + * @returns 0 if successful + */ + virtual int blockWrite(uint8_t * data, uint16_t block){return 1;}; + +protected: -protected: /* * Get number of logical unit - 1 (here 0) @@ -159,14 +162,6 @@ // cache in RAM before writing in memory. Useful also to read a block. uint8_t page[BlockSize]; - - // memory (Atmel AT45 family) - // - // You can change this memory to use another one (SDcard, ...) - // You need to provide : - // - mem.blockread(page, block_number); - // - mem.blockwrite(page, block_number); - SDcard mem; void CBWDecode(uint8_t * buf, uint16_t size); void sendCSW (void); @@ -186,4 +181,3 @@ }; #endif -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USB_SDcard.cpp Fri Nov 11 16:12:21 2011 +0000 @@ -0,0 +1,197 @@ +/* mbed Microcontroller Library - SDFileSystem + * Copyright (c) 2008-2009, sford + * + * Introduction + * ------------ + * SD and MMC cards support a number of interfaces, but common to them all + * is one based on SPI. This is the one I'm implmenting because it means + * it is much more portable even though not so performant, and we already + * have the mbed SPI Interface! + * + * The main reference I'm using is Chapter 7, "SPI Mode" of: + * http://www.sdcard.org/developers/tech/sdcard/pls/Simplified_Physical_Layer_Spec.pdf + * + * SPI Startup + * ----------- + * The SD card powers up in SD mode. The SPI interface mode is selected by + * asserting CS low and sending the reset command (CMD0). The card will + * respond with a (R1) response. + * + * CMD8 is optionally sent to determine the voltage range supported, and + * indirectly determine whether it is a version 1.x SD/non-SD card or + * version 2.x. I'll just ignore this for now. + * + * ACMD41 is repeatedly issued to initialise the card, until "in idle" + * (bit 0) of the R1 response goes to '0', indicating it is initialised. + * + * You should also indicate whether the host supports High Capicity cards, + * and check whether the card is high capacity - i'll also ignore this + * + * SPI Protocol + * ------------ + * The SD SPI protocol is based on transactions made up of 8-bit words, with + * the host starting every bus transaction by asserting the CS signal low. The + * card always responds to commands, data blocks and errors. + * + * The protocol supports a CRC, but by default it is off (except for the + * first reset CMD0, where the CRC can just be pre-calculated, and CMD8) + * I'll leave the CRC off I think! + * + * Standard capacity cards have variable data block sizes, whereas High + * Capacity cards fix the size of data block to 512 bytes. I'll therefore + * just always use the Standard Capacity cards with a block size of 512 bytes. + * This is set with CMD16. + * + * You can read and write single blocks (CMD17, CMD25) or multiple blocks + * (CMD18, CMD25). For simplicity, I'll just use single block accesses. When + * the card gets a read command, it responds with a response token, and then + * a data token or an error. + * + * SPI Command Format + * ------------------ + * Commands are 6-bytes long, containing the command, 32-bit argument, and CRC. + * + * +---------------+------------+------------+-----------+----------+--------------+ + * | 01 | cmd[5:0] | arg[31:24] | arg[23:16] | arg[15:8] | arg[7:0] | crc[6:0] | 1 | + * +---------------+------------+------------+-----------+----------+--------------+ + * + * As I'm not using CRC, I can fix that byte to what is needed for CMD0 (0x95) + * + * All Application Specific commands shall be preceded with APP_CMD (CMD55). + * + * SPI Response Format + * ------------------- + * The main response format (R1) is a status byte (normally zero). Key flags: + * idle - 1 if the card is in an idle state/initialising + * cmd - 1 if an illegal command code was detected + * + * +-------------------------------------------------+ + * R1 | 0 | arg | addr | seq | crc | cmd | erase | idle | + * +-------------------------------------------------+ + * + * R1b is the same, except it is followed by a busy signal (zeros) until + * the first non-zero byte when it is ready again. + * + * Data Response Token + * ------------------- + * Every data block written to the card is acknowledged by a byte + * response token + * + * +----------------------+ + * | xxx | 0 | status | 1 | + * +----------------------+ + * 010 - OK! + * 101 - CRC Error + * 110 - Write Error + * + * Single Block Read and Write + * --------------------------- + * + * Block transfers have a byte header, followed by the data, followed + * by a 16-bit CRC. In our case, the data will always be 512 bytes. + * + * +------+---------+---------+- - - -+---------+-----------+----------+ + * | 0xFE | data[0] | data[1] | | data[n] | crc[15:8] | crc[7:0] | + * +------+---------+---------+- - - -+---------+-----------+----------+ + */ + +#include "USB_SDcard.h" + +#define SD_COMMAND_TIMEOUT 5000 + +USB_SDcard::USB_SDcard(PinName mosi, PinName miso, PinName sclk, PinName cs) : + _spi(mosi, miso, sclk), _cs(cs) { + _cs = 1; +} + +int USB_SDcard::blockWrite(uint8_t * buffer, uint16_t block_number) { + // set write address for single block (CMD24) + if(_cmd(24, block_number * 512) != 0) { + return 1; + } + + // send the data block + _write(buffer, 512); + return 0; +} + +int USB_SDcard::blockRead(uint8_t * buffer, uint16_t block_number) { + // set read address for single block (CMD17) + if(_cmd(17, block_number * 512) != 0) { + return 1; + } + + // receive the data + _read(buffer, 512); + return 0; +} + +// PRIVATE FUNCTIONS + +int USB_SDcard::_cmd(int cmd, int arg) { + _cs = 0; + + // send a command + _spi.write(0x40 | cmd); + _spi.write(arg >> 24); + _spi.write(arg >> 16); + _spi.write(arg >> 8); + _spi.write(arg >> 0); + _spi.write(0x95); + + // wait for the repsonse (response[7] == 0) + for(int i=0; i<SD_COMMAND_TIMEOUT; i++) { + int response = _spi.write(0xFF); + if(!(response & 0x80)) { + _cs = 1; + return response; + } + } + _cs = 1; + return -1; // timeout +} + +int USB_SDcard::_read(uint8_t * buffer, uint16_t length) { + _cs = 0; + + // read until start byte (0xFF) + while(_spi.write(0xFF) != 0xFE); + + // read data + for(int i=0; i<length; i++) { + buffer[i] = _spi.write(0xFF); + } + _spi.write(0xFF); // checksum + _spi.write(0xFF); + + _cs = 1; + return 0; +} + +int USB_SDcard::_write(uint8_t * buffer, uint16_t length) { + _cs = 0; + + // indicate start of block + _spi.write(0xFE); + + // write the data + for(int i=0; i<length; i++) { + _spi.write(buffer[i]); + } + + // write the checksum + _spi.write(0xFF); + _spi.write(0xFF); + + // check the repsonse token + if((_spi.write(0xFF) & 0x1F) != 0x05) { + _cs = 1; + return 1; + } + + // wait for write to finish + while(_spi.write(0xFF) == 0); + + _cs = 1; + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USB_SDcard.h Fri Nov 11 16:12:21 2011 +0000 @@ -0,0 +1,43 @@ +/* mbed Microcontroller Library - SDFileSystem + * Copyright (c) 2008-2009, sford + */ + +#ifndef USB_SDCARD_H +#define USB_SDCARD_H + +#include "mbed.h" +#include "USBMSD.h" + +class USB_SDcard: public USBMSD +{ +public: + USB_SDcard(PinName mosi = p5, PinName miso = p6, PinName sclk = p7, PinName cs = p8); + + /* + * read a block on a storage chip + * + * @param data pointer where will be stored read data + * @param block block number + * @returns 0 if successful + */ + virtual int blockRead(uint8_t * data, uint16_t block); + + /* + * write a block on a storage chip + * + * @param data data to write + * @param block block number + * @returns 0 if successful + */ + virtual int blockWrite(uint8_t * data, uint16_t block); + +protected: + int _cmd(int cmd, int arg); + int _read(uint8_t * buffer, uint16_t length); + int _write(uint8_t * buffer, uint16_t length); + + SPI _spi; + DigitalOut _cs; +}; + +#endif
--- a/main.cpp Fri Nov 11 15:22:53 2011 +0000 +++ b/main.cpp Fri Nov 11 16:12:21 2011 +0000 @@ -1,7 +1,7 @@ #include "mbed.h" -#include "USBMSD.h" +#include "USB_SDcard.h" -USBMSD msd; +USB_SDcard sd; int main() { while(1);