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