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);
Samuel Mokrani