SPI Flash memory

Dependents:   AT45DB161D SPIFLASH_AT45DB n-bed_AT45DB161E AT45DB161D

Revision:
0:2e953bbaf3a5
Child:
1:b379e16fdf6f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/at45db161d.cpp	Sun Sep 11 06:12:15 2011 +0000
@@ -0,0 +1,539 @@
+/**
+ * AT45DB161D module for arduino (C) Vincent
+ *   SPI flash memory
+ *   http://blog.blockos.org/?p=27
+ *
+ * bug fix by todotani
+ *   http://todotani.cocolog-nifty.com/blog/2009/07/arduino-4cf4.html
+ *
+ * Modified for mbed, 2011 Suga.
+ */
+#include "at45db161d.h"
+
+/** CTOR **/
+ATD45DB161D::ATD45DB161D(PinName mosi, PinName miso, PinName sclk, PinName cs)
+  : _spi(mosi, miso, sclk), _cs(cs)
+{}
+
+ATD45DB161D::ATD45DB161D(SPI &spi, PinName cs)
+  : _spi(spi), _cs(cs)
+{}
+
+/** DTOR **/
+ATD45DB161D::~ATD45DB161D()
+{}
+
+/** Setup SPI and pinout **/
+void ATD45DB161D::Init()
+{
+//    uint8_t clr;
+    
+    /* Initialize pinout */
+/*
+    pinMode(DATAOUT, OUTPUT);
+    pinMode(DATAIN, INPUT);
+    pinMode(SPICLOCK, OUTPUT);
+    pinMode(SLAVESELECT, OUTPUT);
+    pinMode(DATAIN, INPUT);
+*/  
+      /* Disable device */
+      DF_CS_inactive;
+  
+    /* Setup SPI */
+//    SPCR = (1 << SPE) | (1 << MSTR) | (1 << CPOL) | (1 << CPHA);
+    _spi.format(8, 0);
+    _spi.frequency(1000000);
+
+    /* Cleanup registers */
+//    clr = SPSR;
+//    clr = SPDR;
+}
+
+/** 
+ * Read status register
+ * @return The content of the status register
+ **/
+uint8_t ATD45DB161D::ReadStatusRegister()
+{
+    uint8_t status;
+
+    DF_CS_inactive;    /* Make sure to toggle CS signal in order */
+    DF_CS_active;      /* to reset Dataflash command decoder     */
+  
+    /* Send status read command */
+    spi_transfer(AT45DB161D_STATUS_REGISTER_READ);
+    /* Get result with a dummy write */
+    status = spi_transfer(0x00);
+
+    return status;
+}
+
+/** 
+ * Read Manufacturer and Device ID 
+ * @note if id.extendedInfoLength is not equal to zero,
+ *       successive calls to spi_transfer(0xff) will return
+ *       the extended device information string bytes.
+ * @param id Pointer to the ID structure to initialize
+ **/
+void ATD45DB161D::ReadManufacturerAndDeviceID(struct ATD45DB161D::ID *id)
+{
+    
+    DF_CS_inactive;    /* Make sure to toggle CS signal in order */
+    DF_CS_active;      /* to reset Dataflash command decoder     */
+  
+    /* Send status read command */
+    spi_transfer(AT45DB161D_READ_MANUFACTURER_AND_DEVICE_ID);
+
+    /* Manufacturer ID */
+    id->manufacturer = spi_transfer(0xff);
+    /* Device ID (part 1) */
+    id->device[0] = spi_transfer(0xff);
+    /* Device ID (part 2) */
+    id->device[1] = spi_transfer(0xff);
+    /* Extended Device Information String Length */
+    id->extendedInfoLength = spi_transfer(0xff);
+    
+}
+
+/** 
+ * Main Memory Page Read. 
+ * A main memory page read allows the user to read data directly from
+ * any one of the 4096 pages in the main memory, bypassing both of the
+ * data buffers and leaving the contents of the buffers unchanged.
+ *
+ * @param page Page of the main memory to read
+ * @param offset Starting byte address within the page
+ **/
+void ATD45DB161D::ReadMainMemoryPage(uint16_t page, uint16_t offset)
+{
+    DF_CS_inactive;    /* Make sure to toggle CS signal in order */
+    DF_CS_active;      /* to reset Dataflash command decoder     */
+
+    /* Send opcode */
+    spi_transfer(AT45DB161D_PAGE_READ);
+    
+    /* Address (page | offset)  */
+    spi_transfer((uint8_t)(page >> 6));
+    spi_transfer((uint8_t)((page << 2) | (offset >> 8)));
+    spi_transfer((uint8_t)(offset & 0xff));
+    
+    /* 4 "don't care" bytes */
+    spi_transfer(0x00);
+    spi_transfer(0x00);
+    spi_transfer(0x00);
+    spi_transfer(0x00);
+}
+
+/** 
+ * Continuous Array Read.
+ * Sequentially read a continuous stream of data.
+ * @param page Page of the main memory where the sequential read will start
+ * @param offset Starting byte address within the page
+ * @param low If true the read operation will be performed in low speed mode (and in high speed mode if it's false).
+ * @note The legacy mode is not currently supported
+ **/
+void ATD45DB161D::ContinuousArrayRead(uint16_t page, uint16_t offset, uint8_t low)
+{
+    DF_CS_inactive;    /* Make sure to toggle CS signal in order */
+    DF_CS_active;      /* to reset Dataflash command decoder     */
+
+    /* Send opcode */
+    spi_transfer( low ? AT45DB161D_CONTINUOUS_READ_LOW_FREQ :
+                        AT45DB161D_CONTINUOUS_READ_HIGH_FREQ );
+
+    /* Address (page | offset)  */
+    spi_transfer((uint8_t)(page >> 6));
+    spi_transfer((uint8_t)((page << 2) | (offset >> 8)));
+    spi_transfer((uint8_t)(offset & 0xff));
+    
+    if (!low) { spi_transfer(0x00); }
+}
+
+
+/** 
+ * Read the content of one of the SRAM data buffers (in low or high speed mode).
+ * @param bufferNum Buffer to read (1 or 2)
+ * @param offset Starting byte within the buffer
+ * @param low If true the read operation will be performed in low speed mode (and in high speed mode if it's false).
+ **/
+void ATD45DB161D::BufferRead(uint8_t bufferNum, uint16_t offset, uint8_t low)
+{
+    DF_CS_inactive;    /* Make sure to toggle CS signal in order */
+    DF_CS_active;      /* to reset Dataflash command decoder     */
+
+    /* Send opcode */
+    if(bufferNum == 1)
+    {
+        spi_transfer(low ? AT45DB161D_BUFFER_1_READ_LOW_FREQ :
+                           AT45DB161D_BUFFER_1_READ);
+    }
+    else
+    {
+        spi_transfer(low ? AT45DB161D_BUFFER_2_READ_LOW_FREQ :
+                           AT45DB161D_BUFFER_2_READ);
+
+    }
+    
+    /* 14 "Don't care" bits */
+    spi_transfer(0x00);
+    /* Rest of the "don't care" bits + bits 8,9 of the offset */
+    spi_transfer((uint8_t)(offset >> 8));
+    /* bits 7-0 of the offset */
+    spi_transfer((uint8_t)(offset & 0xff));
+}
+
+/** 
+ * Write data to one of the SRAM data buffers. Any further call to
+ * spi_tranfer will return bytes contained in the data buffer until
+ * a low-to-high transition is detected on the CS pin. If the end of
+ * the data buffer is reached, the device will wrap around back to the
+ * beginning of the buffer. 
+ * @param bufferNum Buffer to read (1 or 2)
+ * @param offset Starting byte within the buffer
+ **/
+void ATD45DB161D::BufferWrite(uint8_t bufferNum, uint16_t offset)
+{
+    DF_CS_inactive;    /* Make sure to toggle CS signal in order */
+    DF_CS_active;      /* to reset Dataflash command decoder     */
+
+    spi_transfer( (bufferNum == 1) ? AT45DB161D_BUFFER_1_WRITE :
+                                     AT45DB161D_BUFFER_2_WRITE);
+    
+    /* 14 "Don't care" bits */
+    spi_transfer(0x00);
+    /* Rest of the "don't care" bits + bits 8,9 of the offset */
+    spi_transfer((uint8_t)(offset >> 8));
+    /* bits 7-0 of the offset */
+    spi_transfer((uint8_t)(offset & 0xff));
+}
+
+/**
+ * Transfer data from buffer 1 or 2 to main memory page.
+ * @param bufferNum Buffer to use (1 or 2)
+ * @param page Page where the content of the buffer will transfered
+ * @param erase If set the page will be first erased before the buffer transfer.
+ * @note If erase is equal to zero, the page must have been previously erased using one of the erase command (Page or Block Erase).
+ **/
+void ATD45DB161D::BufferToPage(uint8_t bufferNum, uint16_t page, uint8_t erase)
+{
+    DF_CS_inactive;    /* Make sure to toggle CS signal in order */
+    DF_CS_active;      /* to reset Dataflash command decoder     */
+
+    /* Opcode */
+    if(erase)
+    {
+        spi_transfer( (bufferNum == 1) ? AT45DB161D_BUFFER_1_TO_PAGE_WITH_ERASE :
+                                         AT45DB161D_BUFFER_2_TO_PAGE_WITH_ERASE);
+    }
+    else
+    {
+        spi_transfer( (bufferNum == 1) ? AT45DB161D_BUFFER_1_TO_PAGE_WITHOUT_ERASE :
+                                         AT45DB161D_BUFFER_2_TO_PAGE_WITHOUT_ERASE);
+    }
+    
+    /*
+     * 3 address bytes consist of :
+     *     - 2 don&#65533;ft care bits
+     *     - 12 page address bits (PA11 - PA0) that specify the page in 
+     *       the main memory to be written
+     *     - 10 don&#65533;ft care bits
+     */
+    spi_transfer((uint8_t)(page >> 6));
+    spi_transfer((uint8_t)(page << 2));
+    spi_transfer(0x00);
+    
+    DF_CS_inactive;  /* Start transfer */
+    DF_CS_active;    /* If erase was set, the page will first be erased */
+
+    /* Wait for the end of the transfer */
+      while(!(ReadStatusRegister() & READY_BUSY))
+      {}
+}
+
+/**
+ * Transfer a page of data from main memory to buffer 1 or 2.
+ * @param page Main memory page to transfer
+ * @param buffer Buffer (1 or 2) where the data will be written
+ **/
+void ATD45DB161D::PageToBuffer(uint16_t page, uint8_t bufferNum)
+{
+    DF_CS_inactive;    /* Make sure to toggle CS signal in order */
+    DF_CS_active;      /* to reset Dataflash command decoder     */
+ 
+    /* Send opcode */
+    spi_transfer((bufferNum == 1) ? AT45DB161D_TRANSFER_PAGE_TO_BUFFER_1 :
+                                    AT45DB161D_TRANSFER_PAGE_TO_BUFFER_2);
+
+    /*
+     * 3 address bytes consist of :
+     *     - 2 don&#65533;ft care bits
+     *     - 12 page address bits (PA11 - PA0) that specify the page in 
+     *       the main memory to be written
+     *     - 10 don&#65533;ft care bits
+     */
+    spi_transfer((uint8_t)(page >> 6));
+    spi_transfer((uint8_t)(page << 2));
+    spi_transfer(0x00);
+        
+    DF_CS_inactive;  /* Start page transfer */
+    DF_CS_active;
+
+    /* Wait for the end of the transfer */
+      while(!(ReadStatusRegister() & READY_BUSY))
+      {}
+}
+
+/** 
+ * Erase a page in the main memory array.
+ * @param page Page to erase
+ **/
+void ATD45DB161D::PageErase(uint16_t page)
+{
+    DF_CS_inactive;    /* Make sure to toggle CS signal in order */
+    DF_CS_active;      /* to reset Dataflash command decoder     */
+
+    /* Send opcode */
+    spi_transfer(AT45DB161D_PAGE_ERASE);
+    
+    /*
+     * 3 address bytes consist of :
+     *     - 2 don&#65533;ft care bits
+     *     - 12 page address bits (PA11 - PA0) that specify the page in 
+     *       the main memory to be written
+     *     - 10 don&#65533;ft care bits
+     */
+    spi_transfer((uint8_t)(page >> 6));
+    spi_transfer((uint8_t)(page << 2));
+    spi_transfer(0x00);
+        
+    DF_CS_inactive;  /* Start block erase */
+    DF_CS_active;
+
+    /* Wait for the end of the block erase operation */
+      while(!(ReadStatusRegister() & READY_BUSY))
+      {}
+}
+
+/**
+ * Erase a block of eight pages at one time.
+ * @param block Index of the block to erase
+ **/
+void ATD45DB161D::BlockErase(uint16_t block)
+{
+    DF_CS_inactive;    /* Make sure to toggle CS signal in order */
+    DF_CS_active;      /* to reset Dataflash command decoder     */
+
+    /* Send opcode */
+    spi_transfer(AT45DB161D_BLOCK_ERASE);
+    
+    /*
+     * 3 address bytes consist of :
+     *     - 2 don&#65533;ft care bits
+     *     - 9 block address bits (PA11 - PA3)
+     *     - 13 don&#65533;ft care bits
+     */
+    spi_transfer((uint8_t)(block >> 3));
+    spi_transfer((uint8_t)(block << 5));
+    spi_transfer(0x00);
+        
+    DF_CS_inactive;  /* Start block erase */
+    DF_CS_active;
+
+    /* Wait for the end of the block erase operation */
+      while(!(ReadStatusRegister() & READY_BUSY))
+      {}
+}
+
+/** 
+ * Erase a sector in main memory. There are 16 sector on the
+ * at45db161d and only one can be erased at one time.
+ * @param sector Sector to erase (1-15)
+ **/
+void ATD45DB161D::SectoreErase(uint8_t sector)
+{
+    DF_CS_inactive;    /* Make sure to toggle CS signal in order */
+    DF_CS_active;      /* to reset Dataflash command decoder     */
+
+    /* Send opcode */
+    spi_transfer(AT45DB161D_SECTOR_ERASE);
+    
+    /*
+     * 3 address bytes consist of :
+     */
+    if((sector == 0x0a) || (sector == 0x0b))
+    {
+        /*
+         *  - 11 don&#65533;ft care bits
+         *  - 
+         *  - 12 don&#65533;ft care bits
+         */
+        spi_transfer(0x00);
+        spi_transfer(((sector & 0x01) << 4));
+        spi_transfer(0x00);
+    }
+    else
+    {
+        /*
+         *  - 2 don't care bits 
+         *  - 4 sector number bits
+         *  - 18 don't care bits 
+         */
+        spi_transfer(sector << 1);
+        spi_transfer(0x00);
+        spi_transfer(0x00);
+    }
+                
+    DF_CS_inactive;  /* Start block erase */
+    DF_CS_active;
+
+    /* Wait for the end of the block erase operation */
+      while(!(ReadStatusRegister() & READY_BUSY))
+      {}
+}
+
+/** 
+ * Erase the entire chip memory. Sectors proteced or locked down will
+ * not be erased.
+ **/
+void ATD45DB161D::ChipErase()
+{
+    DF_CS_inactive;    /* Make sure to toggle CS signal in order */
+    DF_CS_active;      /* to reset Dataflash command decoder     */
+
+    /* Send chip erase sequence */
+    spi_transfer(AT45DB161D_CHIP_ERASE_0);
+    spi_transfer(AT45DB161D_CHIP_ERASE_1);
+    spi_transfer(AT45DB161D_CHIP_ERASE_2);
+    spi_transfer(AT45DB161D_CHIP_ERASE_3);
+                
+    DF_CS_inactive;  /* Start chip erase */
+    DF_CS_active;
+
+    /* Wait for the end of the chip erase operation */
+      while(!(ReadStatusRegister() & READY_BUSY))
+      {}
+}
+
+/**
+ * This a combination of Buffer Write and Buffer to Page with
+ * Built-in Erase.
+ * @note You must call EndAndWait in order to start transfering data from buffer to page
+ * @param page Page where the content of the buffer will transfered
+ * @param offset Starting byte address within the buffer
+ * @param bufferNum Buffer to use (1 or 2)
+ * @warning UNTESTED
+ **/
+void ATD45DB161D::BeginPageWriteThroughBuffer(uint16_t page, uint16_t offset, uint8_t bufferNum)
+{
+    DF_CS_inactive;    /* Make sure to toggle CS signal in order */
+    DF_CS_active;      /* to reset Dataflash command decoder     */
+
+    /* Send opcode */
+    spi_transfer((bufferNum == 1) ? AT45DB161D_PAGE_THROUGH_BUFFER_1 :
+                                    AT45DB161D_PAGE_THROUGH_BUFFER_2);
+
+    /* Address */
+    spi_transfer((uint8_t)(page >> 6));
+    spi_transfer((uint8_t)((page << 2) | (offset >> 8)));
+    spi_transfer((uint8_t)offset);
+}
+
+/**
+ * Perform a low-to-high transition on the CS pin and then poll
+ * the status register to check if the dataflash is busy.
+ **/
+void ATD45DB161D::EndAndWait()
+{
+    DF_CS_inactive;  /* End current operation */
+    DF_CS_active;    /* Some internal operation may occur
+                      * (buffer to page transfer, page erase, etc... ) */
+
+    /* Wait for the chip to be ready */
+      while(!(ReadStatusRegister() & READY_BUSY))
+      {}
+      
+    DF_CS_inactive;  /* Release SPI Bus */ 
+}
+
+/**
+ * Compare a page of data in main memory to the data in buffer 1 or 2.
+ * @param page Page to test
+ * @param bufferNum Buffer number
+ * @return
+ *        - 1 if the page and the buffer contains the same data
+ *         - 0 else
+ * @warning UNTESTED
+ **/
+int8_t ATD45DB161D::ComparePageToBuffer(uint16_t page, uint8_t bufferNum)
+{
+    uint8_t status;
+    
+    DF_CS_inactive;    /* Make sure to toggle CS signal in order */
+    DF_CS_active;      /* to reset Dataflash command decoder     */
+
+    /* Send opcode */
+    spi_transfer((bufferNum == 1) ? AT45DB161D_COMPARE_PAGE_TO_BUFFER_1 :
+                                    AT45DB161D_COMPARE_PAGE_TO_BUFFER_2);
+    
+    /* Page address */
+    spi_transfer((uint8_t)(page >> 6));
+    spi_transfer((uint8_t)(page << 2));
+    spi_transfer(0x00);
+    
+    DF_CS_inactive;  /* Start comparaison */
+    DF_CS_active;
+
+    /* Wait for the end of the comparaison and get the result */
+      while(!((status = ReadStatusRegister()) & READY_BUSY))
+      {}
+          
+//      return ((status & COMPARE) == COMPARE);
+      return ((status & COMPARE) ? 0 : 1);
+}
+
+/**
+ * Put the device into the lowest power consumption mode.
+ * Once the device has entered the Deep Power-down mode, all
+ * instructions are ignored except the Resume from Deep
+ * Power-down command.
+ * @warning UNTESTED
+ **/
+void ATD45DB161D::DeepPowerDown()
+{
+    DF_CS_inactive;    /* Make sure to toggle CS signal in order */
+    DF_CS_active;      /* to reset Dataflash command decoder     */
+    
+    /* Send opcode */
+    spi_transfer(AT45DB161D_DEEP_POWER_DOWN);
+    
+    /* Enter Deep Power-Down mode */
+    DF_CS_inactive;
+    
+    /* Safety delay */
+//    delay(100);
+    wait_ms(100);
+}
+
+/**
+ * Takes the device out of Deep Power-down mode.
+ **/
+void ATD45DB161D::ResumeFromDeepPowerDown()
+{
+    DF_CS_inactive;    /* Make sure to toggle CS signal in order */
+    DF_CS_active;      /* to reset Dataflash command decoder     */
+    
+    /* Send opcode */
+    spi_transfer(AT45DB161D_RESUME_FROM_DEEP_POWER_DOWN);
+    
+    /* Resume device */
+    DF_CS_inactive;
+    
+    /* The CS pin must stay high during t_RDPD microseconds before the device
+     * can receive any commands.
+     * On the at45db161D t_RDPD = 35 microseconds. But we will wait 100
+     * (just to be sure). */
+//    delay(100);
+    wait_ms(100);
+}
+ 
+/** **/