USBMSD SD card Hello World for Mbed platforms

Dependencies:   mbed USBMSD_SD USBDevice

USBDevice/USBMSD/AT45.cpp

Committer:
samux
Date:
2011-11-11
Revision:
2:27a7e7f8d399

File content as of revision 2:27a7e7f8d399:

#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
  
}