16Mb Flash

The AT45DB161D is a serial interface flash memory chip, that can hold a lot of those 1 and 0's we are so fond of.  Specifically about 16 mega bits worth.  Because the chip is a SOIC wide format, the Sparkfun adapter is a bit tight.  Although all the I/O lines are 5 volt tolerant, the chip still needs a 3.3 volt supply.

Datasheet
More stuff
Library

Now the cool part is where my LD33V voltage regulator can fit right across three of the pins:

Blue pin 1      -->  Gnd pin 7
3.3 v pin 2     -->  VCC pin 6
Yellow pin 3   -->  WP pin 5

So your saying that I should put some sort of filtering capacitor on the output of my voltage regulator?  Well yes, I should but I don't want my little silicone friends to get all soft and start crying every time a little ripple comes their way.  I want them to be tough as nails and smoking from time to time.  Just trying to weed out the weak chips is all.
 

 

Now the code below is my attempt at making sense of how someone might really use one of these chips.  My brain could only imagine a logging application where I had fixed length records with a few mixed variable types.  To save space you don't want to convert everything into a string.  You want to leave it in it's raw binary form, so I created the code below.

By the way I left the really hard part of the code to somebody else.  I think I found the library here.

Two application defined subroutines (pack_and_write_record and read_and_unpack_record) combine all the custom fields into a record (dat) that will ultimately be written to the flash.  Then the generic subroutines (write record and read_record) get the record to and from the device.

The other sweet thing about this code is it will flush the 528 byte buffer to the main memory page only when the next record should be written to a different main memory page.  This allows you to continuously or randomly write records while keeping main memory writes to a minimum (maximizing longevity).  One down side to this design is the waste of space because a record can not span two pages.  The bits wasted is calculated by (528 mod record size) * 4095.   From then on your code can deal with simple record numbers without fiddling with pages, buffers and what not.

Care should be taken not to "burn up" your chip by needlessly writing to it.  Also because of the way I flush the buffer only when I have too, you need to force the last buffer flush by reading a different page before powering off.

 

extern "C" {
  #include "at45db161d.h"
}

// defines for splitting the unsigned long int into bytes for storage:
#define HIGHBYTELONG(x) ((byte)((x) >> 24))
#define LOWBYTELONG(x)  ((byte)((x) >> 16))
#define HIGHBYTE(x)     ((byte)((x) >>  8))
#define LOWBYTE(x)      ((byte)((x) & 0x00ff))
#define WORD(x,y)       (( ((byte)(x)) << 8) | ((byte)(y)))

#define MAX_PAGE_SIZE 528
#define MAX_PAGES 4096

byte dat[11];
unsigned int  record_len = sizeof(dat);
unsigned int  records_per_page = MAX_PAGE_SIZE / record_len;
unsigned long max_records = (long)MAX_PAGES * (long)records_per_page;
unsigned int  current_page = 0;
unsigned int  buffer_flushed = 1;

ATD45DB161D dataflash;

///////////////////////////////////////////////////////////////////////////////////////////////////////
void setup() {   
  uint8_t        status;
  ATD45DB161D::ID id;
  
  delay(3000);                                          // Allow time to press the serial monitor button.
  dataflash.Init();                                     // Initialize dataflash
  //dataflash.ChipErase();                              // Clean the chip off every 10,000 writes
  delay(10);
  status = dataflash.ReadStatusRegister();              // Read status register
  dataflash.ReadManufacturerAndDeviceID(&id);           // Read manufacturer and device ID

  pinMode(2, INPUT);
  Serial.begin(115200);                                 // Set baud rate for serial communication

  // Display status register
  Serial.print("Status register: ");
  Serial.println(status, BIN);

  // Display manufacturer and device ID
  Serial.print("Manufacturer ID: ");  // Should be 00011111
  Serial.println(id.manufacturer, HEX);

  Serial.print("Device ID (part 1): "); // Should be 00011111
  Serial.println(id.device[0], HEX);

  Serial.print("Device ID (part 2): "); // Should be 00000000
  Serial.println(id.device[1], HEX);

  Serial.print("Extended Device Information String Length: "); // 00000000
  Serial.println(id.extendedInfoLength, HEX);

  Serial.print("records_per_page: ");
  Serial.println(records_per_page);

  Serial.print("max_records: ");
  Serial.println(max_records);

  Serial.println("--------------------------------------");
}

///////////////////////////////////////////////////////////////////////////////////////////////////////
void loop() {
 
  if (digitalRead(2) == HIGH) {
    pack_and_write_record(47,   millis(), 53, 11,  "ABCD");
    pack_and_write_record(48,   millis(), 64, 22,  "EFGH");
    pack_and_write_record(49,   millis(), 75, -33, "IJKL");
    pack_and_write_record(4300, millis(), 86, 44,  "MNOP");
  } 

  Serial.println("--------------------------------------");

  read_and_unpack_record(47);
  read_and_unpack_record(48);
  read_and_unpack_record(49);
  read_and_unpack_record(4300);

  delay(60000); 
}

///////////////////////////////////////////////////////////////////////////////////////////////////////
void read_record(unsigned long record) {
  unsigned int i;
  unsigned int requested_page = record / records_per_page;
  unsigned int record_offset = (record % records_per_page) * record_len;

  Serial.print("Read_record ");
  Serial.print(record);

  if (! buffer_flushed) {
    // Transfer buffer 1 to current_page (with builtin erase)
    dataflash.BufferToPage(1, current_page, 1);
    buffer_flushed = 1;    
    Serial.print(" after flushing page ");
    Serial.print(current_page);
  }

  dataflash.ReadMainMemoryPage(requested_page, record_offset); 
      
  for (i=0; i
    dat[i] = spi_transfer(0xff);
  }

  Serial.println();
}

///////////////////////////////////////////////////////////////////////////////////////////////////////
void write_record(unsigned long record) {
  unsigned int i;
  unsigned int requested_page = record / records_per_page;
  unsigned int record_offset = (record % records_per_page) * record_len;

  Serial.print("Write_record ");
  Serial.print(record);
  Serial.print(" to page ");
  Serial.print(requested_page);

  if (current_page != requested_page) {
    if (! buffer_flushed) {
      // Transfer buffer 1 to current_page (with builtin erase)
      dataflash.BufferToPage(1, current_page, 1);
      buffer_flushed = 1;    
      Serial.print(" after flushing page ");
      Serial.print(current_page);
    }
    
    dataflash.PageToBuffer(requested_page, 1);    
    dataflash.BufferRead(1, 0, 1);
    current_page = requested_page;
  }

  // Set dataflash so that any call to spi_tranfer will write the byte
  // given as argument to the Buffer 1, offset by record_offset
  dataflash.BufferWrite(1, record_offset);
  buffer_flushed = 0;    

  // Transfer the message
  for(i=0; i
    spi_transfer(dat[i]);
  }

  Serial.println();
}

///////////////////////////////////////////////////////////////////////////////////////////////////////
void pack_and_write_record(unsigned long record, unsigned long time, unsigned int val1, int val2, char* str1) {
  unsigned int i;
  
  dat[0]=(HIGHBYTELONG(time));
  dat[1]=(LOWBYTELONG(time));
  dat[2]=(HIGHBYTE(time));
  dat[3]=(LOWBYTE(time));

  dat[4]=(HIGHBYTE(val1));
  dat[5]=(LOWBYTE(val1));

  dat[6]=(HIGHBYTE(val2));
  dat[7]=(LOWBYTE(val2));

  for (i=0; i<3; i++) {
    dat[i+8] = str1[i];
  }

  write_record(record);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////
void read_and_unpack_record(unsigned long record) {
  //unsigned int i;
  unsigned long mytime;
  unsigned long myulong;
  unsigned int myuint;
  int myint;
  char mystr[4];
  
  read_record(record);

  mytime   = (dat[0] << 8) | (dat[1] << 8) | (dat[2] << 8) | (dat[3] );
  myuint   = (dat[4] << 8) | dat[5];
  myint    = (dat[6] << 8) | dat[7];
  mystr[0] = dat[8];
  mystr[1] = dat[9];
  mystr[2] = dat[10];
  mystr[3] = '\0';

  Serial.print("millis: ");
  Serial.println(mytime);

  Serial.print("myuint: ");
  Serial.println(myuint);

  Serial.print("myint: ");
  Serial.println(myint);

  Serial.print("mystr: ");
  Serial.println(mystr);

  Serial.println("--------------------------------------");
  delay(5);           // Add a little delay otherwise the display will be too fast   
}

///////////////////////////////////////////////////////////////////////////////////////////////////////

 

Example Output:

Status register: 10101100
Manufacturer ID: 1F
Device ID (part 1): 26
Device ID (part 2): 0
Extended Device Information String Length: 0
records_per_page: 48
max_records: 196608
--------------------------------------
Write_record 47 to page 0
Write_record 48 to page 1 after flushing page 0
Write_record 49 to page 1
Write_record 4300 to page 89 after flushing page 1
--------------------------------------
Read_record 47 after flushing page 89
millis: 3032
myuint: 53
myint: 11
mystr: ABC
--------------------------------------
Read_record 48
millis: 3034
myuint: 64
myint: 22
mystr: EFG
--------------------------------------
Read_record 49
millis: 3048
myuint: 75
myint: -33
mystr: IJK
--------------------------------------
Read_record 4300
millis: 3050
myuint: 86
myint: 44
mystr: MNO
--------------------------------------

Dullbits.com

©DullBits.com 2016, All rights reserved and stuff like that...
Now featuring newly broken pages.