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