maj 31, 2013 - Moduły    Komentowanie nie jest możliwe

Moduł pamięci EEPROM 256kbit AT24C256 I2C

Moduł wyposażony jest w programowalną pamięć EEPROM firmy Atmel. Na jednej magistrali podłączyć można do 4 takich samych modułów. Wybór adresu ustalany jest poprzez zworki. Moduł posiada także blokadę przed modyfikacją pamięci. Dzięki zastosowanej podstawce istnieje możliwość wymiany pamięci.

AT24C256-228x2281[1]

Parametry techniczne:
Napięcie zasilania           1,8 – 5,5 V
Pojemność                         256 kbitów
Interfejs                              I2C
Wymiary modułu          36,5 mmx 13 mm
Dokumentacja układu AT24C256 dostępna jest na:   AT24C256.pdf  or  www.smartrobots.pl/download/AT24C256.pdf
Instrukcja podłączenia do Arduino:   http://playground.arduino.cc/code/I2CEEPROM

at24c256_schemat[1]

LINKI:   01


Add I2C EEPROM to Arduino

Attaching an EEPROM to the Arduino is quite simple and the easiest way to do that is via the I2C bus. EEPROMs come in many forms but the 24 LS256 or 24LC256 is a good choice as it is easy to use and pretty cheap (85 euro cents at my supplier). The 24LC256 holds 256 kilobits of data (that is 32 kilobytes). The 24LS256 can also run on 3.3V which is handy if one is using a Lilypad or Pro Mini 3.3V. The 24LS256 uses 3 pins for selection of its address, so you can use up to eight at once on the same bus.

F0QXSX9HA4LFY2V.LARGE[1]FDTK7VBHA4MBEG8.LARGE[1]

The 24LS256 is addressed as follows: 1010A2A1A0. If you are only working with 1 EEPROM, the easiest is to connect A2-A0 with Ground. This gives the address of 1010000 which is 0×50 Hex. In reading and writing to the EEPROM one needs to realize that it has 32 kB (actuall32767) and one byte is not enough to address all the memory.

So when one wants to send read and/or write requests, one needs to send two bytes – one for the MSB or higher end of the address (the 8 bits from left to right), and the next one for the LSB or lower end of the address (the final 8 bits from left to right).

If for example one wants to use address 21000, that goes as follows: In binary, 21000 is 0101001000001000. Split that up into 01010010 and 00001000, then convert the binary values back to numerical bytes to send with Wire.send().

That sounds more complicated than it is, as there are in fact two operands to do that. This first one is >>, also known as ‚bitshift right’. This will take the highest (left) part of the byte and drop off the lower end, leaving only the first 8 bits. To get the lower (right) end of the address, one can use operator &, also known as ‚ bitwise AND’. This operand, when used with 0xFF will give the lower bits.

Writing data to the 24LS256

Writing data is quite easy. First initialize the I2C bus with:

Wire.beginTransmission(0x50); // for pins A0~A2 set to GND

then send some data. The first data to send are the two bytes for the address (25000) were one wants to write to the memory.

Wire.send(21000 >> 8);  // send the MSB of the address
Wire.send(21000 & 0xFF); // send the LSB of the address

Subsequently send the byte to store at address 21000 and  then close the connection:

Wire.send(15); //just sending ‘15’  as example
Wire.endTransmission();

That concludes the writing. now for reading:

Reading data from the 24LS256

Reading is more or less similar. First initialize the connection and provide the address of the data to read:

Wire.beginTransmission(0x50); // Chosen base address
Wire.send(21000 >> 8);  // send MSB of the address
Wire.send(21000 & 0xFF); // send LSB of the address
Wire.endTransmission();

Then, supply the number of data bytes to read starting at the current address:

Wire.beginTransmission(0x50); // base address
Wire.requestFrom(0x50,1); // We want one byte
Wire.receive(inbyte);

Here, ‚inbyte’ is a byte variable chosen to store the data retrieved from the EEPROM.

The Power of the I2C bus is of course that various devices can be connected to the same lines. The top figure shows such a set up with two EEPROMs. It is key of course that they each have their own address. In the figure  I have chosen to use addresses 0×50 and 0×51. One gets that by connecting A0-A2 to ground for  one chip, but connecting A0 to Vcc (‘ High’)  for the second chip. The resulting address is then 1010001 = 0x51

LINKI:   01


Using Arduino with an I2C EEPROM

I got my hands on an AT24C256 (256 kbit = 32 kbyte serial EEPROM). I found no library for it, so I created a small sketch with few functions to show how the i2c_eeprom_write_page and i2c_eeprom_read_byte functions work.

Because this chip is I2C, it only uses the analog pins 4 & 5 (SDA and SCL), and of course the power (5V) and GND.

Connect as follows:

Arduino analog pin 4     to EEPROM pin 5
Arduino analog pin 5     to EEPROM pin 6
Arduino 5V                        to EEPROM pin 8
Arduino GND                     to EEPROM pin 1,2,3,4

Be sure to leave pin 7 of the EEPROM open or tie it to GND otherwise the EEPROM will be write protected.

Just a few quick functions for reading/writing the EEPROM (not a library, yet). ‚deviceaddress’ refers to the EEPROM I2C address, eg. 0×50.

/*

  *  Use the I2C bus with EEPROM 24LC64

  *  Sketch:    eeprom.pde

  *  Author: hkhijhe

  *  Date: 01/10/2010

  */

  #include <Wire.h> //I2C library

  void i2c_eeprom_write_byte( int deviceaddress, unsigned int eeaddress, byte data ) {

    int rdata = data;

    Wire.beginTransmission(deviceaddress);

    Wire.send((int)(eeaddress >> 8)); // MSB

    Wire.send((int)(eeaddress & 0xFF)); // LSB

    Wire.send(rdata);

    Wire.endTransmission();

  }

  // WARNING: address is a page address, 6-bit end will wrap around

  // also, data can be maximum of about 30 bytes, because the Wire library has a buffer of 32 bytes

  void i2c_eeprom_write_page( int deviceaddress, unsigned int eeaddresspage, byte* data, byte length ) {

    Wire.beginTransmission(deviceaddress);

    Wire.send((int)(eeaddresspage >> 8)); // MSB

    Wire.send((int)(eeaddresspage & 0xFF)); // LSB

    byte c;

    for ( c = 0; c < length; c++)

      Wire.send(data[c]);

    Wire.endTransmission();

  }

  byte i2c_eeprom_read_byte( int deviceaddress, unsigned int eeaddress ) {

    byte rdata = 0xFF;

    Wire.beginTransmission(deviceaddress);

    Wire.send((int)(eeaddress >> 8)); // MSB

    Wire.send((int)(eeaddress & 0xFF)); // LSB

    Wire.endTransmission();

    Wire.requestFrom(deviceaddress,1);

    if (Wire.available()) rdata = Wire.receive();

    return rdata;

  }

  // maybe let’s not read more than 30 or 32 bytes at a time!

  void i2c_eeprom_read_buffer( int deviceaddress, unsigned int eeaddress, byte *buffer, int length ) {

    Wire.beginTransmission(deviceaddress);

    Wire.send((int)(eeaddress >> 8)); // MSB

    Wire.send((int)(eeaddress & 0xFF)); // LSB

    Wire.endTransmission();

    Wire.requestFrom(deviceaddress,length);

    int c = 0;

    for ( c = 0; c < length; c++ )

      if (Wire.available()) buffer[c] = Wire.receive();

  }

  void setup()

  {

    char somedata[] = „this is data from the eeprom”; // data to write

    Wire.begin(); // initialise the connection

    Serial.begin(9600);

    i2c_eeprom_write_page(0x50, 0, (byte *)somedata, sizeof(somedata)); // write to EEPROM

    delay(10); //add a small delay

    Serial.println(„Memory written”);

  }

  void loop()

  {

    int addr=0; //first address

    byte b = i2c_eeprom_read_byte(0x50, 0); // access the first address from the memory

    while (b!=0)

    {

      Serial.print((char)b); //print content to serial port

      addr++; //increase address

      b = i2c_eeprom_read_byte(0x50, addr); //access an address from the memory

    }

    Serial.println(” „);

    delay(2000);

  }

LINKI:   01


Adding External I2C EEPROM to Arduino (24LC256)

This tutorial was originally posted on the 10kohms.com website, which now seems to be no longer with us, so we have reproduced it here.

In my last post I discussed using the built in EEPROM to store permanent data on the Arduino. All though this is a very easy and effective way of storing data on the Arduino the built in EEPROM only offers 512 bytes of storage. When working with larger or more advanced Arduino projects we may need to store additional data so an external memory solution like the 24LC256 I²C EEPROM IC becomes necessary.

We’re using a 256kbit eeprom which is actually 32kbytes of space. 262,144 bits / 8 bits in a byte = 32,768 bytes. That’s 62 times the Arduino’s built-in storage!

Hardware Setup

In this example we’ll be using the Microchip 24LC256 IC. If you’re using a different IC please confirm that the pin-out and power requirements are the same so you don’t damage your chip.

17230_PV[1]pinout-24LC256[1]

The Microchip 24LC256 chip can be purchased in a 8 pin DIP package. The pins on the 24LC256 are pretty straightforward and consist of power(8), gnd(4), write protection(7), SCL/SDA(6,5), and three address pins(1,2,3). Before we get into the software part lets hook up the 24LC256 chip up to our Arduino.

24LC256-1[1]

Using the image above as a guide lets begin to wire the chip. First connect GND and VCC, pins 4 and 8 respectivly. Next lets go ahead and connect the data pins to the Arduino board. Since we’re using the Arduino I²C bus we’re going to be using Analog pins 4 and 5. Connect the SDA pin on the 24LC256(pin 5) to the pin 4 of the Arduino. Then connect the SCL (pin 6) to pin 5 on the Arduino. Double check that you’ve connected the correct pins on the 24LC256 to the correct pins on the Arduino; strange things will happen if you have them reversed. After our data and power pins are connected we have four left on 24LC256 chip, the WP pin and the three address pins. The WP pin stands for write-protected and this allows you to control if data can be written to the eeprom or not. If this pin is low then writing is enabled but if it’s high then writing is disable; reading is always enabled. For the purpose of this tutorial we’re going to be writing to the eeprom so we can connect the WP pin to GND.

The last three pins set the address of the 24LC256 chip which allows us to target a particular chip on the I²C bus. This particular I²C chip comes pre-wired with four bits of it’s address already set(1010) and these can not be changed. The last three bits of the address however can be changed which allows us to run up to eight 24LC256 chips on the same I²C bus. This is a little confusing at first so lets look at the figure below to explain the address in a little more detail.

24LC256-address[1]

For the purpose of explaining how the address works we can ignore the Start and Acknowledge bits. The way the I²C bus works is a 7-bit address is passed along with a read/write bit that tells the chip if it should write the incoming data or read it and send it back. The Arduino takes care of the last R/W bit for us depending on what function we’re using so as long as you’re using the standard Arduino Wire library we don’t have to worry about this bit. This leaves us with the seven middle bits and as I mentioned above the first four bits(Control Code) are hard-wired and we can’t change these. The next three bits(A2,A1,A0) are the important bits that we can change so lets look at the simple table below to see what address the chip will have depending on what we set these pins to.

So, if we were to tie pins 1,2 and 3 on the 24LC256 to GND then the chip would have address 0×50 and if were to assign them all Vcc then the chip would have address 0×57 and every combination in between. To keep things simple lets just tie all pins to GND to make the address 0×50. In a future tutorial I will show you how to use multiple eeprom chips off the same I²C at which point we will be assigning each chip a different address but for now lets stick with 0×50. With the address pins connected the hardware part of this tutorial is complete and every pin of the 24LC256 should be connected to either Vcc, GND or the Arduino. Time to move on to software!

It’s been brought to my attention that some people use pull-up resistors on the data and clock pins to the Arduino. All though this would not hurt the circuit it’s not needed because when the Wire.h library is initialized it knows pins 4 and 5 are going to be used for I²C so it also activates the built-in pull-up resistors. For more information please read http://www.arduino.cc/en/Reference/Wire.

 

Arduino Sketch

Below is the entire tutorial code, scan over it and see if you understand it before I dive into what each section does.

Note: This is written for Arduino versions before 1.0. If you are using Arduino 1.0 and above then you need to change Wire.send to Wire.write and Wire.receive to Wire.read

 

#include <Wire.h>    
 
#define disk1 0x50    //Address of 24LC256 eeprom chip
 
void setup(void)
{
  Serial.begin(9600);
  Wire.begin();  
 
  unsigned int address = 0;
 
  writeEEPROM(disk1, address, 123);
  Serial.print(readEEPROM(disk1, address), DEC);
}
 
void loop(){}
 
void writeEEPROM(int deviceaddress, unsigned int eeaddress, byte data ) 
{
  Wire.beginTransmission(deviceaddress);
  Wire.send((int)(eeaddress >> 8));   // MSB
  Wire.send((int)(eeaddress & 0xFF)); // LSB
  Wire.send(data);
  Wire.endTransmission();
 
  delay(5);
}
 
byte readEEPROM(int deviceaddress, unsigned int eeaddress ) 
{
  byte rdata = 0xFF;
 
  Wire.beginTransmission(deviceaddress);
  Wire.send((int)(eeaddress >> 8));   // MSB
  Wire.send((int)(eeaddress & 0xFF)); // LSB
  Wire.endTransmission();
 
  Wire.requestFrom(deviceaddress,1);
 
  if (Wire.available()) rdata = Wire.receive();
 
  return rdata;
}

In order to use the I²C interface we need to include the Arduino standard Wire library so first things first, include Wire.h at the top of the sketch. You’ll notice directly after the include we define a variable called disk1 and assign it a hex value of 0×50. When we setup the chip above we set the address of the chip to 0×50 by tying all the address pins to GNS so we simple set this variable to that address which allows us to access the chip from within our sketch. This variable is not required but it allows us to easily change the address we want to access without going through all of the code and replacing the value. Also, if you plan on adding more than one chip it’s easier to refer to them as disk1, disk2, etc rather than 0×50, 0×51 which might get confusing.

Moving on we have our standard setup and a loop functions, for this tutorial the loop function is left empty so we’ll just focus on the setup function. We first initialize our Serial connection for printing back to the computer and then we initiate the I²C connection by calling Wire.begin(). This enables pins 4 and 5 for I²C and also enabled the internal pull-up resistor(See note above). Next we create a new variable to store the address of the eeprom we want to write to(not the address of the eeprom IC itself but the address of the byte we want to read/write to). Since this eeprom has 32Kbytes of storage this address can be any number between 0 and 32,767; we’ll start with address 0. After we’ve initialized everything we call our two primary functions, writeEEPROM and readEEPROM which actually do the dirty work of writing and reading the bytes of data.

Lets first start off with the writeEEPROM function. This function takes three arguments, the device address(the disk1 variable), the memory address on the eeprom and the byte of data you want to write. The first argument is the address of the device you want to write to, in our case we only have one device(disk1) so we pass this on. The next argument is the address on the eeprom you want to write to and as stated above can be between 0 and 32,767. Finally we have to pass along the byte we want to store. So, writeEEPROM(disk1, address, 123) is going to write the decimal 123 to “address”(which is 0) on disk1(0×50). Lets jump into the actual writeEEPROM function to learn what it does.

void writeEEPROM(int deviceaddress, unsigned int eeaddress, byte data ) 
{
  Wire.beginTransmission(deviceaddress);
  Wire.send((int)(eeaddress >> 8));   // MSB
  Wire.send((int)(eeaddress & 0xFF)); // LSB
  Wire.send(data);
  Wire.endTransmission();
 
  delay(5);
}

We first call the Wire.beginTransmission function which sends the deviceaddress to let the chip know we want to communicate with it. Next we have to send the address on the eeprom we want to write to. Since our eeprom chip has 32,000 address locations we are using two bytes(16 bits) to store the address but we can only send one byte at a time so we have to split it up. The first send function takes the eeaddress and shifts the bits to the right by eight which moves the higher end of the 16 bit address down to the lower eight bits. Next we do a bitwise AND to get just the last eight bits. To illustrate this lets follow the steps below.

Lets say we want to write to address location 20,000 which is 0100 1110 0010 0000 in binary. We need to send the MSB(Most significant bits) first so we have to shift our address to the right eight bits.

0100 1110 0010 0000 (eeaddress)
After shifting 8 bits to the right we have
0100 1110

We now have the first half of the address, time to get the second half:

0100 1110 0010 0000 (eeaddress)
After we bitwise AND 0xFF with eeadddress we get
0010 0000

This means our 24LC256 chip gets the address 1001 1100 and then 0010 0000 which tells it to store the next byte in address location 20,000. Now that we’ve sent the address we send the data and then we end the process by calling the endTransmission function. The 24LC256 gets the data and writes the data to that address location. To finish up this function you’ll notice I’ve included a delay of 5 milliseconds. This allows the chip time to complete the write operation, without this if you try to do sequential writes weird things might happen.

Now that you’re data has been stored it’s time to get it back, lets examine the read EEPROM function.

 

byte readEEPROM(int deviceaddress, unsigned int eeaddress ) 
{
  byte rdata = 0xFF;
 
  Wire.beginTransmission(deviceaddress);
  Wire.send((int)(eeaddress >> 8));   // MSB
  Wire.send((int)(eeaddress & 0xFF)); // LSB
  Wire.endTransmission();
 
  Wire.requestFrom(deviceaddress,1);
 
  if (Wire.available()) rdata = Wire.receive();
 
  return rdata;
}

The readEEPROM accepts two arguments and returns on byte(the data). The arguments it accepts are the same first two arguments the write function, the device address and the address on the eeprom to read from. First we declare a variable to store the byte we’re going to retrieve. Next we start off just like we did with the write function by starting the process with beginTransmission and then we send the address we want to access; this works exactly the same way as the write function. Continuing on we end the the transmission and we’ve now set the 24LC256 with the address we’re interested in so now we just have to request and read the data. The next function requestFrom() sends the command to that chip to start sending the data at the address we set above. The second argument is how many bytes(starting at this address) to send back; we’re only requesting one. Finally we check to see if there is data available on the I²C bus and if there is we read it into the rdata variable. We return the byte of data and we’re done!

That’s all you really need to know in order to use and external I²C EEPROM chip with the Arduino. Take this setup and play around with it, see if you can figure out how to store more than one byte at a time or if you want a challenge try using more than one 24LC256 on the same I²C bus.

LINKI:   01

 


 

 

Eeprom Page Write (Writing long strings to 24LC256)

The Microchip 24LC256 eeprom and variants thereof are used in many projects for the storage of data when the microcontroller in use either doesn’t have any eeprom memory or not enough. These useful eeproms use a simple I2C connection and are easy to setup and use. Writing and reading single bytes of data to any of the 32k memory locations is straightforward, but what if you want to store a string of characters? You might think this is actually easy to do as well. And indeed you can send it a string of characters, up to 64 in fact before the eeprom needs to store them using a feature called Page Write. But there is a big problem with this as we shall see.

So, what is page write?

First, lets look back at how we store a single byte of data. The examples shown are Arduino (version 1.0 and above), but the same basics apply to any other programming language.

Here is the routine for writing a byte of data

void writeEEPROM(int deviceaddress, unsigned int eeaddress, byte data ) 
{
  Wire.beginTransmission(deviceaddress);
  Wire.write((int)(eeaddress >> 8));   // MSB
  Wire.write((int)(eeaddress & 0xFF)); // LSB
  Wire.write(data);
  Wire.endTransmission();
 
  delay(5);
}

We start transmission to our device (I2C) address. Next we send 2 bytes which define the memory location we are writing to. Then finally the one byte of data we are storing.

A small delay is required to give time for the eeprom to save the data.

So far so good. Now, the 24LC256 eeprom has a feature called page write. This allows you to send up to 64 bytes of data before it needs to be saved. We can extend our writeEEPROM routine to perform this function. The routine can be re-written to that shown below.

void writeEEPROM(int deviceaddress, unsigned int eeaddress, char* data) 
{
  // Write a string of chars to eeprom
  // DO NOT USE THIS CODE - IT DOES NOT WORK 
  unsigned char i=0;
  
  Wire.beginTransmission(deviceaddress);
  Wire.write((int)((address) >> 8));   // MSB
  Wire.write((int)((address) & 0xFF)); // LSB

  do{ 
     Wire.write((byte) data[i]);
     i++;
  } while(data[i]);  
  Wire.endTransmission();
     
  delay(6);  // needs 5ms for page write
}

NOTE: The Arduino Wire library only has a 32 character buffer, so that is the maximun we can send using Arduino. This buffer includes the two address bytes which limits our data payload to 30 bytes.

The above code works exactly as it should. It sends the two starting address bytes, followed by each individual byte in the string. The problem with this method is that the 24LC256 eeprom doesn’t work in the way we assume it does.

Let me explain. The eeprom’s memory is split up into a number of pages that are 64 bytes wide.

So, starting at memory location 0 the pages are

  1. 0 – 63
  2. 64 – 127
  3. 128 – 191
  4. 192 – 255
  5. etc …

The problem is „A page wite can only write to one page.”

As per the 24LC256 datasheet..

„Page write operations are limited to writing bytes within a single physical page, regardless of the number of bytes actually being written. Physical page boundaries start at addresses that are integer multiples of the page buffer size (or ‘page size’) and end at addresses that are integer multiples of [page size – 1]. If a Page Write command attempts to write across a physical page boundary, the result is that the data wraps around to the beginning of the current page (overwriting data previously stored there), instead of being written to the next page, as might be expected. It is, therefore, necessary for the application software to prevent page write operations that would attempt to cross a page boundary.”

What this means can be shown in the following diagram

page_write[1]

The blue dashed line indicates the data we are intending to write (ignoring Arduino’s 32 byte limit)

The green dashed line shows what the 24LC256 eeprom does with the data.

When storing the data, if the eeprom address reaches a page boundary, the address is wrapped back to the beginning of the page and the data is written there. This causes over writing of data.

The eeprom read functionality has no such page boundary issues and will happily read past a page boundary. So when we read the data back it looks like it is corrupted. It isn’t, it was just not saved in the way we had thought it would be.

The Solution

The solution to this is not straightforward. If storing strings that are shorter than 64 bytes and we have lots of eeprom memory available, you could opt to always store the data starting at a page boundary (i.e. 0, 64, 128 etc) and you won’t have a problem. If we are logging data and want it stored consecutively and dont want to waste memory, we need to come up with a solution.

We can store the data one byte at a time. This would indeed work and would not be effected by the page boundary problem. The problem with this solution is that each byte written needs a small delay to allow the eeprom to store the data. This is in the region of 3.5ms ( we have allowed 5ms in the code). If you write a 100 character string, this would add up to 350ms, over a third of a second. We would be restricted to only being able to write 3 such lines per second.

A better solution provided here calculates how much remaining space there is in the initial page that is being written to, and then breaks up the write into separate page write chunks to make the writing fit into the page boundaries.

E.g. We want to write 78 bytes starting at memory location 55 (ending at memory location 132). This overlaps into 3 pages.

  • 0                               64
  • |————page—————-|
  • 64                              128
  • |————–page————–|—–

  • |——————data——————-|
  • 55                                       132

The program below will split the write up as follows

  • write 9 bytes  ( 55 to  63) – up to page boundary
  • write 16 bytes ( 64 to  79)
  • write 16 bytes ( 80 to  95)
  • write 16 bytes ( 96 to 111)
  • write 16 bytes (112 to 127) – up to page boundary
  • write 5 bytes  (128 to 132)

Why 16 bytes and not 32 or 64? Remember the Arduino uses a 32 byte buffer for sending and receiving data so this is the maximum you can send. But this buffer needs to include the memory location bytes which reduces the available space to 30 bytes. So 16 bytes is used as an easy divisor of 64. Other programming languages can adjust this code to accomodate 32 or 64 byte writes.

The overhead for these 6 writes  = 5ms x 6  = 30ms, whilst the overhead for writing 78 individual bytes = 3.5ms x 78 = 273ms. A considerable improvement.

So here is the complete code. Any suggestions for improvement are always welcome

 

/*
 * EEPROM_PAGE_WRITE
 *
 * Example program showing a method for writing large amounts of
 * data to an eeprom (24LC256/512 etc), whilst avoiding page write
 * boundaries
 *
 * copyright www.hobbytronics.co.uk 2012
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version. see <http://www.gnu.org/licenses/>
 *
 * PROBLEM: There are two methods for writing data to an eeprom
 * 1. Write a single byte. Apart from the overhead of sending the
 *    eeprom memory address, the eeprom needs 3ms for each write to
 *    complete. Thus if you send 100 bytes it needs 300ms
 * 2. Write an array of bytes (called Page write). Up to 64 bytes 
 *    can be sent before the eeprom needs to save the data. 
 *    The save only needs 5ms thus saving considerable time. 
 * There is however a problem with method 2. The eeprom's memory is
 * split into 64byte chunks (or pages) and the page write can only write
 * within one page. Attempting to write past a page boundary will lead
 * to the the memory pointer wrapping back to the start of the page
 * and data at the beginning will be overwritten.
 *
 * The solution provided here calculates the remaining space in the
 * initial page that is being written to and breaks up the write into
 * separate writes to make the data fit the page boudaries.
 *
 * E.g. We want to write 78 bytes starting at memory location 55 (ending
 * at memory location 132). This overlaps into 3 pages.
 *
 *
 * 0                               64
 * |------------page----------------|
 * 64                              128
 * |------------page----------------|
 *
 * |------------------data-------------------|
 * 55                                       132 
 *
 *
 * The program below will split the write up as follows
 * - write 9 bytes  ( 55 to  63) - up to page boundary
 * - write 16 bytes ( 64 to  79)
 * - write 16 bytes ( 80 to  95)
 * - write 16 bytes ( 96 to 111)
 * - write 16 bytes (112 to 127) - up to page boundary
 * - write 5 bytes  (128 to 132)
 *
 * Why 16 bytes and not 32 or 64? Arduino uses a 32 byte buffer for sending
 * and receiving data so this is the maximum you can send. But this buffer
 * needs to include the memory location bytes which reduces the available 
 * space to 30 bytes. So 16 bytes is used as an easy divisor of 64.
 *
 * The overhead for these 6 writes              = 5ms x 6    = 30ms
 * The overhead for writing 78 individual bytes = 3.5ms x 78 = 273ms
*/

#include <Wire.h>
#define eeprom1 0x50    //Address of 24LC256 eeprom chip
#define WRITE_CNT 5

unsigned char rdata[32];

void setup(void)
{
  Serial.begin(9600);
  Wire.begin();  
 
  unsigned int i;
  // define large string of data to be written
  char str_data[]={"Hello-1234567890-and-abcdefghijklmnopqrstuvwxyz-Goodbye\n"};

  // Work out length of data
  char str_len=0;  
  do{ str_len++; } while(str_data[str_len]);  
  
  // Write out data several times consecutively starting at address 0
  for(i=0;i<WRITE_CNT;i++) writeEEPROM(eeprom1,i*str_len,str_data);

  // read back the data 28 bytes at a time
  // reading data doesn't suffer from the page boundary rules
  Serial.println("DATA READ");
  for(i=0;i<10;i++) {
    readEEPROM(eeprom1, (i*28), rdata, 28);
    Serial.write(rdata,28);
  }  

}
 
void loop(){
}
 
void writeEEPROM(int deviceaddress, unsigned int eeaddress, char* data) 
{
  // Uses Page Write for 24LC256
  // Allows for 64 byte page boundary
  // Splits string into max 16 byte writes
  unsigned char i=0, counter=0;
  unsigned int  address;
  unsigned int  page_space;
  unsigned int  page=0;
  unsigned int  num_writes;
  unsigned int  data_len=0;
  unsigned char first_write_size;
  unsigned char last_write_size;  
  unsigned char write_size;  
  
  // Calculate length of data
  do{ data_len++; } while(data[data_len]);   
   
  // Calculate space available in first page
  page_space = int(((eeaddress/64) + 1)*64)-eeaddress;

  // Calculate first write size
  if (page_space>16){
     first_write_size=page_space-((page_space/16)*16);
     if (first_write_size==0) first_write_size=16;
  }   
  else 
     first_write_size=page_space; 
    
  // calculate size of last write  
  if (data_len>first_write_size) 
     last_write_size = (data_len-first_write_size)%16;   
  
  // Calculate how many writes we need
  if (data_len>first_write_size)
     num_writes = ((data_len-first_write_size)/16)+2;
  else
     num_writes = 1;  
     
  i=0;   
  address=eeaddress;
  for(page=0;page<num_writes;page++) 
  {
     if(page==0) write_size=first_write_size;
     else if(page==(num_writes-1)) write_size=last_write_size;
     else write_size=16;
  
     Wire.beginTransmission(deviceaddress);
     Wire.write((int)((address) >> 8));   // MSB
     Wire.write((int)((address) & 0xFF)); // LSB
     counter=0;
     do{ 
        Wire.write((byte) data[i]);
        i++;
        counter++;
     } while((data[i]) && (counter<write_size));  
     Wire.endTransmission();
     address+=write_size;   // Increment address for next write
     
     delay(6);  // needs 5ms for page write
  }
}
 
void readEEPROM(int deviceaddress, unsigned int eeaddress,  
                 unsigned char* data, unsigned int num_chars) 
{
  unsigned char i=0;
  Wire.beginTransmission(deviceaddress);
  Wire.write((int)(eeaddress >> 8));   // MSB
  Wire.write((int)(eeaddress & 0xFF)); // LSB
  Wire.endTransmission();
 
  Wire.requestFrom(deviceaddress,num_chars);
 
  while(Wire.available()) data[i++] = Wire.read();

}

LINKI:   01

 

Comments are closed.