USBMSD device (USB Flashdisk) library using AT45DBxx serial flash storage chip. Works with 2, 4, 8, 16, 32 and 64 Mbit chips within AT45DBxx series.
Dependents: USBMSD_AT45_HelloWorld
USBMSD_AT45.cpp
- Committer:
- llumpu
- Date:
- 2012-10-27
- Revision:
- 0:c0dc2df7c9fe
File content as of revision 0:c0dc2df7c9fe:
/* Copyright (c) <2012> <llumpu>, MIT License * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software * and associated documentation files (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, publish, distribute, * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all copies or * substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* * Inspired by Steen Joergensen (stjo2809) and Chris Styles AT45 libraries */ #include "mbed.h" #include "USBMSD_AT45.h" #define IS_PAGE_BINARY 0x01 #define IS_FLASH_READY 0x80 #define TWO_MBIT 0x03 #define FOUR_MBIT 0x04 #define EIGHT_MBIT 0x05 #define SIXTEEN_MBIT 0x06 #define THIRTYTWO_MBIT 0x07 #define SIXTYFOUR_MBIT 0x08 DigitalOut read_act_led(LED1); // LED indicating data are being read from storage chip DigitalOut write_act_led(LED2); // LED indicating data are being written to storage chip //Serial _pc(USBTX, USBRX); /************************************** Constructor **************************************/ USBMSD_AT45::USBMSD_AT45(PinName mosi, PinName miso, PinName sclk, PinName ncs, int transport_block_size) : _spi(mosi, miso, sclk), _ncs(ncs), _transport_block_size (transport_block_size) { //_pc.baud(921600); _spi.format(8,3); _spi.frequency(24000000); write_act_led = 0; read_act_led = 0; _init_status = 1; // memory chip is not ready, disk_initialize() will be called in connect() connect(); } /************************************ Public methods ************************************/ // This method is called when disk_status returns 0x01 (not initialized) int USBMSD_AT45::disk_initialize() { _initialize(); // Determine storage chip parameters write_act_led = 1; read_act_led = 1; _init_status = 0; // Set status to 0x00 (initialized) return _init_status; } // Returns size of storage chip in bytes int USBMSD_AT45::disk_size() { return _flash_size; } // Returns count of sectors of storage chip int USBMSD_AT45::disk_sectors() { return (_flash_size / _transport_block_size); } // Returns status of storage chip - 0x00 ready, 0x01 not initialized (disk_initialize is then called) int USBMSD_AT45::disk_status() { //_pc.printf("d_status \n\r "); return _init_status; } // Reads block of data from storage chip. Size of block is set in constructor. int USBMSD_AT45::disk_read(char* data, int block) { read_act_led = 0; //_pc.printf("r 0x%2d ", block); //_pc.printf(" \n\r"); int address = block * _transport_block_size; // Start address of block int count = (_transport_block_size / _flash_buffer_size); int transport_address = 0; // If block transported over USB is bigger than size of AT45 SRAM buffer. // We read all parts of block one by one to SRAM 1 and SRAM 2 and then transfer them to host at once. if(_transport_block_size > _flash_buffer_size) { // We load data to first SRAM buffer and then to second SRAM buffer // We do this again if block transported over USB is more than 2 x bigger than SRAM buffer is for(int i=0; i<(count / 2); i++) { _busy(); // Check if we can proceed _flashread(1, address); // Read first part of block into SRAM 1 buffer _busy(); // Check if we can proceed _ncs = 0; // Chip select _spi.write(0xd4); // Read data from SRAM 1 buffer _sendaddr (0x0); // Start address of block in SRAM buffer : 0 - We read entire buffer _spi.write (0x0); // Don't care byte for(int i = 0; i < _flash_buffer_size; i++) { data[transport_address + i] = _spi.write (0x0); } _ncs = 1; // Chip deselect transport_address = (transport_address + _flash_buffer_size); address = (address + _flash_buffer_size); _busy(); // Check if we can proceed _flashread(2,address); // Read first part of block into SRAM 2 buffer _busy(); // Check if we can proceed _ncs = 0; // Chip select _spi.write(0xd6); // Read data from SRAM 2 buffer _sendaddr (0x0); // Start address of block in SRAM buffer : 0 - We read entire buffer _spi.write (0x0); // Don't care byte for(int i = 0; i < _flash_buffer_size; i++) { data[transport_address + i] = _spi.write (0x0); } _ncs = 1; // Chip deselect transport_address = (transport_address + _flash_buffer_size); address = (address + _flash_buffer_size); } } // If block transported over USB equals size of AT45 SRAM buffer. // We read whole block into SRAM 1 buffer and then transport it to host. else if(_transport_block_size == _flash_buffer_size) { _busy(); // Check if we can proceed _flashread(1, address); // Read whole block into SRAM 1 buffer _busy(); // Check if we can proceed _ncs = 0; // Chip select _spi.write(0xd4); // Read data from SRAM 1 buffer _sendaddr (0x0); // Start address of block in SRAM buffer : 0 - We read entire buffer _spi.write (0x0); // Don't care byte for(int i = 0; i < _flash_buffer_size; i++) { data[transport_address + i] = _spi.write (0x0); } _ncs = 1; // Chip deselect } // If block transported over USB is smaller than size of AT45 SRAM buffer. // We read whole page into SRAM 1 and then transfer only desired part of SRAM buffer. else if(_transport_block_size < _flash_buffer_size) { _busy(); // Check if we can proceed _flashread(1, address); // Read whole memory page into SRAM 1 buffer _busy(); // Check if we can proceed _ncs = 0; // Chip select _spi.write(0xd4); // Read data from SRAM 1 buffer _sendaddr (0x0); // Start address of block in SRAM buffer : 0 - We read entire buffer _spi.write (0x0); // dont care byte for(int i = 0; i < _transport_block_size; i++) { data[transport_address + i] = _spi.write (0x0); } _ncs = 1; // Chip deselect } read_act_led = 1; return (0); } // Writes block of data to storage chip. Size of block is set in constructor int USBMSD_AT45::disk_write(const char* data, int block) { write_act_led = 0; //_pc.printf("w 0x%2d ", block); //_pc.printf(" \n\r"); int address = block * _transport_block_size; // This is the start address of the block int count = (_transport_block_size / _flash_buffer_size); int transport_address = 0; // If block transported over USB is bigger than size of AT45 SRAM buffer. // We write all parts of block one by one to SRAM 1 and SRAM 2 and then we write them to flash. if(_transport_block_size >_flash_buffer_size) { // But if memory page size (and SRAM buffer size) is not binary // Before each write, we must read desired block from flash to SRAM buffer first // then write data from host to same SRAM buffer. After this we store whole // SRAM buffer back to flash. This slows down writing speed but must be done because // SRAM buffer size is bigger than (part of) data we want to write and it would corrupt // previously stored data if not done. if(_page_size > _flash_buffer_size) { for(int i=0; i<(count / 2); i++) { _busy(); // Check if we can proceed _flashread(1, address); // Read first part of block into SRAM 1 buffer _busy(); // Check if we can proceed _ncs = 0; // Chip select _spi.write(0x84); // Write data to SRAM 1 buffer _sendaddr (0); // Start address of block written to SRAM buffer : 0 - We write buffer from start for(int i = 0; i < _flash_buffer_size; i++) { _spi.write (data[transport_address + i]); } _ncs = 1; // Chip deselect _flashwrite(1, address); // Write first part of block from SRAM 1 buffer to flash transport_address = (transport_address + _flash_buffer_size); address = (address + _flash_buffer_size); _busy(); // Check if we can proceed _flashread(2, address); // Read next part of block into SRAM 2 buffer _busy(); // Check if we can proceed _ncs = 0; // Chip select _spi.write(0x87); // Write data to SRAM 2 buffer _sendaddr (0); // Start address of block written to SRAM buffer : 0 - We write buffer from start for(int i = 0; i < _flash_buffer_size; i++) { _spi.write (data[transport_address + i]); } _ncs = 1; // Chip deselect _flashwrite(2, address); // Write next part of block from SRAM 2 buffer to flash transport_address = (transport_address + _flash_buffer_size); address = (address + _flash_buffer_size); } } // Else if memory page size (and SRAM buffer size) is binary // We write all parts of block one by one to SRAM 1 and SRAM 2 and then store them to flash else { for(int i=0; i<(count / 2); i++) { _ncs = 0; // Chip select _spi.write(0x84); // Write data to SRAM 1 buffer _sendaddr (0); // Start address of block written to SRAM buffer : 0 - We write buffer from start for(int i = 0; i < _flash_buffer_size; i++) { _spi.write (data[transport_address + i]); } _ncs = 1; // Chip deselect _flashwrite(1, address); // Write first part of block from SRAM 1 buffer to flash transport_address = (transport_address + _flash_buffer_size); address = (address + _flash_buffer_size); _ncs = 0; // Chip select _spi.write(0x87); // Write data to SRAM 2 buffer _sendaddr (0); // Start address of block written to SRAM buffer : 0 - We write buffer from start for(int i = 0; i < _flash_buffer_size; i++) { _spi.write (data[transport_address + i]); } _ncs = 1; // Chip deselect _flashwrite(2, address); // Write next part of block from SRAM 2 buffer to flash transport_address = (transport_address + _flash_buffer_size); address = (address + _flash_buffer_size); } } } // If block transported over USB equals size of AT45 SRAM buffer. // We write whole block into SRAM 1 buffer and then we write SRAM 1 buffer to flash. else if(_transport_block_size == _flash_buffer_size) { // But if memory page size (and SRAM buffer size) is not binary // Before each write, we must read desired block from flash to SRAM buffer first // then write data from host to same SRAM buffer. After this we store whole // SRAM buffer back to flash. This slows down writing speed but must be done because // SRAM buffer size is bigger than (part of) data we want to write and it would corrupt // previously stored data if not done. if(_page_size > _flash_buffer_size) { _busy(); // Check if we can proceed _flashread(1, address); // Read block into SRAM 1 buffer _busy(); // Check if we can proceed _ncs = 0; // Chip select _spi.write(0x84); // Write data to SRAM 1 buffer _sendaddr (0); // Start address of block written to SRAM buffer : 0 - We write buffer from start for(int i = 0; i < _flash_buffer_size; i++) { _spi.write (data[transport_address + i]); } _ncs = 1; // Chip deselect _flashwrite(1, address); // Write block from SRAM 1 buffer to flash } // Else if memory page size (and SRAM buffer size) is binary // We write whole block of data to SRAM 1 buffer and then store it to flash else { _ncs = 0; // Chip select _spi.write(0x84); // Write data to SRAM 1 buffer _sendaddr (0); // Start address of block written to SRAM buffer : 0 - We write buffer from start for(int i = 0; i < _flash_buffer_size; i++) { _spi.write (data[transport_address + i]); } _ncs = 1; // Chip deselect _flashwrite(1, address); // Write block from SRAM 1 buffer to flash } } // If block transported over USB is smaller than size of AT45 SRAM buffer // We always have to read block being written because we store whole SRAM buffer which is bigger // than block we want write and if not done, data in previously stored blocks will be corrupted. // Before each write, we must read desired block from flash to SRAM buffer first // then write data from host to same SRAM buffer. After this we store whole // SRAM buffer back to flash. This slows down writing speed but must be done because // SRAM buffer size is bigger than (part of) data we want to write and it would corrupt // previously stored data if not done. else if(_transport_block_size < _flash_buffer_size) { _busy(); // Check if we can proceed _flashread(1, address); // Read block into SRAM 1 buffer _busy(); // Check if we can proceed _ncs = 0; // Chip select _spi.write(0x84); // Write data to SRAM 1 buffer _sendaddr (0); // Start address of block written to SRAM buffer : 0 - We write buffer from start for(int i = 0; i < _transport_block_size; i++) { _spi.write (data[transport_address + i]); } _ncs = 1; // Chip deselect _flashwrite(1, address); // Write block from SRAM 1 buffer to flash }// if block smaller ends write_act_led = 1; return (0); } /************************************ Protected methods ************************************/ // Determine storage chip parameters void USBMSD_AT45::_initialize() { _busy(); // Must be here? - Check status of storage chip _ncs = 0; // Chip select _spi.write(0x9f); // Read Manufacturer ID & Device ID int ManufacturerID = (_spi.write(0x00)); int DeviceID_0 = (_spi.write(0x00)); int DeviceID_1 = (_spi.write(0x00)); int ExtendedID_length = (_spi.write(0x00)); _ncs = 1; // Chip deselect int DensityCode = (DeviceID_0 & 0x1f); // Determine density of storage chip in Mbits _ncs = 0; // Chip select _spi.write(0xd7); // Read status of storage chip and determine if memory page size is binary int PageIsBinary = ((_spi.write(0x00)) & IS_PAGE_BINARY); // Check if bit 0 is set to 1 _ncs = 1; // Chip deselect //_pc.printf("M %x \n\r", ManufacturerID); //_pc.printf("D0 %x \n\r", DeviceID_0); //_pc.printf("D1 %x \n\r", DeviceID_1); //_pc.printf("E %x \n\r", ExtendedID_length); if (DensityCode == TWO_MBIT) { // 2Mbits if (PageIsBinary) { _flash_size = 262144; _flash_buffer_size = 256; _page_size = 256; } else { _flash_size = 270336; _flash_buffer_size = 256; _page_size = 264; } } else if (DensityCode == FOUR_MBIT) { // 4Mbits if (PageIsBinary) { _flash_size = 524288; _flash_buffer_size = 256; _page_size = 256; } else { _flash_size = 540672; _flash_buffer_size = 256; _page_size = 264; } } else if (DensityCode == EIGHT_MBIT) { // 8Mbits if (PageIsBinary) { _flash_size = 1048576; _flash_buffer_size = 256; _page_size = 256; } else { _flash_size = 1081344; _flash_buffer_size = 256; _page_size = 264; } } else if (DensityCode == SIXTEEN_MBIT) { // 16Mbits if (PageIsBinary) { _flash_size = 2097152; _flash_buffer_size = 512; _page_size = 512; } else { _flash_size = 2162688; _flash_buffer_size = 512; _page_size = 528; } } else if (DensityCode == THIRTYTWO_MBIT) { // 32Mbits if (PageIsBinary) { _flash_size = 4194304; _flash_buffer_size = 512; _page_size = 512; } else { _flash_size = 4325376; _flash_buffer_size = 512; _page_size = 528; } } else if (DensityCode == SIXTYFOUR_MBIT) { // 64Mbits if (PageIsBinary) { _flash_size = 8388608; _flash_buffer_size = 1024; _page_size = 1024; } else { _flash_size = 8650752; _flash_buffer_size = 1024; _page_size = 1056; } } else { _flash_size = -1; _flash_buffer_size = -1; _page_size = -1; } } void USBMSD_AT45::_busy() { //_pc.printf("BUSY? \n\r"); volatile int IsBusy = 1; while (IsBusy) { _ncs = 0; // Chip select _spi.write(0xd7); // Read status register of storage chip int IsReady = ((_spi.write(0x00)) & IS_FLASH_READY); // If bit 7 is set we can proceed _ncs = 1; // Chip deselect if (IsReady) { IsBusy = 0; } } } // Write and SRAM buffer to main memory void USBMSD_AT45::_flashwrite (int buffer, int address) { int cmd = 0; int paddr = _getpaddr(address); // Calculate address _ncs = 0; // Chip select if (buffer == 1) { cmd = 0x83; // Write SRAM 1 buffer to flash } else { cmd = 0x86; // Write SRAM 2 buffer to flash } _spi.write (cmd); _sendaddr (paddr); _ncs = 1; // Chip deselect } // Read from Flash memory into SRAM buffer void USBMSD_AT45::_flashread (int buffer, int address) { int cmd = 0; int paddr = _getpaddr(address); // Calculate address _ncs = 0; // Chip select if (buffer == 1) { cmd = 0x53; // Read from flash to SRAM 1 buffer } else { cmd = 0x55; // Read from flash to SRAM 2 buffer } _spi.write (cmd); _sendaddr (paddr); _ncs = 1; // Chip deselect } // 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 USBMSD_AT45::_getpaddr(int address) { int paddr; if (_page_size == 256) { paddr = address & 0xffffff00; } else if (_page_size == 264) { paddr = (address << 1) & 0xfffffe00; } else if (_page_size == 512) { paddr = address & 0xfffffe00; } else if (_page_size == 528 ) { paddr = (address << 1) & 0xfffffc00; } else if (_page_size == 1024) { paddr = address & 0xfffffc00; } else if (_page_size == 1056 ) { paddr = (address << 1) & 0xfffff800; } else { paddr = -1; } return (paddr); } // Sends the three least significant bytes of the supplied address void USBMSD_AT45::_sendaddr (int address) { _spi.write(address >> 16); _spi.write(address >> 8); _spi.write(address); }