USBMSD SD card Hello World for Mbed platforms
Dependencies: mbed USBMSD_SD USBDevice
Diff: USBDevice/USBMSD/AT45.cpp
- Revision:
- 2:27a7e7f8d399
diff -r 000000000000 -r 27a7e7f8d399 USBDevice/USBMSD/AT45.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBDevice/USBMSD/AT45.cpp Fri Nov 11 15:22:53 2011 +0000 @@ -0,0 +1,829 @@ +#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