Project 6.01 EEPROM
The 32U4 and other Arduino microcontrollers have both volatile and non-volatile memory. So far we’ve used only the volatile memory. As soon as power is removed from the chip, the values in the volatile memory are lost. In this project we use the non-volatile memory, which is saved even after power is removed. The volatile memory is also called EEPROM (electronically erasable programmable read only memory). We also introduce an important concept: bit shifting. Bit shifting allows us to read and write the value of multi-byte variables like int (2 bytes) and long (4 bytes) types one byte at a time.
In this project, we compare volatile and non-volatile memory. Each time SW1 is pressed we add 1 to a variable in volatile memory and to a variable whose value is stored in EEPROM. When the Arno is disconnected from the USB cable and reconnected, the variable in non-volatile EEPROM keeps its value while the volatile variable is reset to zero. We press SW1 to reset the non-volatile values to zero.
Upload this sketch to the Arno and open the serial monitor. Press SW2 and watch the values of the non-volatile and volatile variables increase. Then close the serial monitor and disconnect the Arno from the USB cable. Wait a moment and reconnect it. Re-open the serial monitor and press SW2 again. The non-volatile variable should start with its last value before you disconnected it. The volatile variable will start back at 0. Pressing SW1 resets the volatile variable back to 0, too.
Concepts: bit shifting, EEPROM, non-volatile memory, serial monitor
Circuits:
At the very beginning of the sketch there’s a line we haven’t seen before. This line tells the compiler to include the EEPROM library in the program. The #include statement adds objects that aren’t part of the regular, core Arduino software. These functions are stored in the “library” folder of the Arduino software:
#include <EEPROM.h>
You can also add a library to a sketch using the IDE menu Sketch > Import Library…
In the setup() block, we use the new capabilities brought into the sketch by the #include statement. The #include statement creates an instance of the EEPROM object. The EEPROM.read method has one argument: the address of the EEPROM byte to read. The byte addresses start at 0 and go up to 512.
The variable eepromValue holds the value of the non-volatile variable. This variable is an int variable, which means it is composed on two bytes. So we need to read two bytes from EEPROM and put them together to create our variable. The two bytes are read in the setup() block so we know what the variable value was if the power was disconnected. First we read one byte:
eepromValue = EEPROM.read(0);
Now we have our first byte, but this byte includes the most significant bit of our variable eepromValue. The most significant bit is the bit with the highest value (or the left-most bit if you write out the numbers in 1’s and 0’s). By default, the byte we read from EEPROM is put into the right-most bits of eepromValue. We need to shift these bits left before we read the next byte. We do this with the << operator:
eepromValue = eepromValue << 8;
This line replaces the value of eepromValue with the value of eepromValue with the bits shifted 8 bits (1 byte) left.
Now we want to read the next byte to complete our int variable. This operation is a little strange at first. We use the or operator, |, to add the second byte:
eepromValue |= EEPROM.read(1);
These operations can be hard to wrap our heads around. Let’s look at an example. Say we have saved the number 555 to EEPROM. The 16-bit representation of this number is 00000010 00101011.
We start with eepromValue = 0. The 16 bit representation of zero is 00000000 00000000.
We reach the line:
eepromValue = EEPROM.read(0);
This loads the left-most 8 bits of 555 into eepromValue, but by default these bits are in the right-hand position in eepromValue. So now:
eepromValue = 00000000 00000010
Next is the bit-shift operator that moves all the bits to the left by 8 bits:
eepromValue = eepromValue << 8;
So now:
eepromValue = 00000010 00000000
We’re getting closer. Now we need to load the lower, right-hand 8 bits into our variable:
eepromValue |= EEPROM.read(1);
The | operator compared two bytes, bit by bit. It records a 1 if either bit is 1, and 0 otherwise. In our example the comparisons are:
00000010 00000000
00101011
00000010 00101011
Success! We’ve reassembled our int value from two separate bytes.
Back to the code. When SW2 is pressed, both eepromValue and volatileValue are incremented by 1:
if(digitalRead(SW2)==LOW){
eepromValue = eepromValue + 1;
volatileValue = volatileValue + 1;
Next, we save the new value of eepromValue to EEPROM. Now we need to do the reverse of the read operation. By default, EEPROM.write writes the right-most byte of a variable to a byte in EEPROM. To write our left-most byte to address 0, we need to right-shift the variable by eight bits using the >> operator:
EEPROM.write(0,eepromValue>>8);
We can now write the right-most byte to address 1 without the need for bit shifting:
EEPROM.write(1,eepromValue);
We finish the if block by writing the current values of the variables to the serial monitor:
Serial.print("EEPROM Value = ");
Serial.print(eepromValue);
Serial.print(" Volatile Value = ");
Serial.println(volatileValue);
When SW1 is pressed, we reset eepromValue to zero and save the new value to EEPROM. We debounce SW1 so that it can’t be pressed any more than once every two seconds:
if(digitalRead(SW1)==LOW and (millis()-now) > 2000){
eepromValue = 0;
now = millis();
EEPROM.write(0,eepromValue >> 8);
EEPROM.write(1,eepromValue);
Serial.println("EEPROM value set back to zero");
A final note: the EEPROM in the 32U4 chip is rated for 100,000 write cycles. This is a lot of cycles but you can wear it out if you use it a lot (such as writing a value every couple of millisecond for 5 minutes).
#include <EEPROM.h>
You can also add a library to a sketch using the IDE menu Sketch > Import Library…
In the setup() block, we use the new capabilities brought into the sketch by the #include statement. The #include statement creates an instance of the EEPROM object. The EEPROM.read method has one argument: the address of the EEPROM byte to read. The byte addresses start at 0 and go up to 512.
The variable eepromValue holds the value of the non-volatile variable. This variable is an int variable, which means it is composed on two bytes. So we need to read two bytes from EEPROM and put them together to create our variable. The two bytes are read in the setup() block so we know what the variable value was if the power was disconnected. First we read one byte:
eepromValue = EEPROM.read(0);
Now we have our first byte, but this byte includes the most significant bit of our variable eepromValue. The most significant bit is the bit with the highest value (or the left-most bit if you write out the numbers in 1’s and 0’s). By default, the byte we read from EEPROM is put into the right-most bits of eepromValue. We need to shift these bits left before we read the next byte. We do this with the << operator:
eepromValue = eepromValue << 8;
This line replaces the value of eepromValue with the value of eepromValue with the bits shifted 8 bits (1 byte) left.
Now we want to read the next byte to complete our int variable. This operation is a little strange at first. We use the or operator, |, to add the second byte:
eepromValue |= EEPROM.read(1);
These operations can be hard to wrap our heads around. Let’s look at an example. Say we have saved the number 555 to EEPROM. The 16-bit representation of this number is 00000010 00101011.
We start with eepromValue = 0. The 16 bit representation of zero is 00000000 00000000.
We reach the line:
eepromValue = EEPROM.read(0);
This loads the left-most 8 bits of 555 into eepromValue, but by default these bits are in the right-hand position in eepromValue. So now:
eepromValue = 00000000 00000010
Next is the bit-shift operator that moves all the bits to the left by 8 bits:
eepromValue = eepromValue << 8;
So now:
eepromValue = 00000010 00000000
We’re getting closer. Now we need to load the lower, right-hand 8 bits into our variable:
eepromValue |= EEPROM.read(1);
The | operator compared two bytes, bit by bit. It records a 1 if either bit is 1, and 0 otherwise. In our example the comparisons are:
00000010 00000000
00101011
00000010 00101011
Success! We’ve reassembled our int value from two separate bytes.
Back to the code. When SW2 is pressed, both eepromValue and volatileValue are incremented by 1:
if(digitalRead(SW2)==LOW){
eepromValue = eepromValue + 1;
volatileValue = volatileValue + 1;
Next, we save the new value of eepromValue to EEPROM. Now we need to do the reverse of the read operation. By default, EEPROM.write writes the right-most byte of a variable to a byte in EEPROM. To write our left-most byte to address 0, we need to right-shift the variable by eight bits using the >> operator:
EEPROM.write(0,eepromValue>>8);
We can now write the right-most byte to address 1 without the need for bit shifting:
EEPROM.write(1,eepromValue);
We finish the if block by writing the current values of the variables to the serial monitor:
Serial.print("EEPROM Value = ");
Serial.print(eepromValue);
Serial.print(" Volatile Value = ");
Serial.println(volatileValue);
When SW1 is pressed, we reset eepromValue to zero and save the new value to EEPROM. We debounce SW1 so that it can’t be pressed any more than once every two seconds:
if(digitalRead(SW1)==LOW and (millis()-now) > 2000){
eepromValue = 0;
now = millis();
EEPROM.write(0,eepromValue >> 8);
EEPROM.write(1,eepromValue);
Serial.println("EEPROM value set back to zero");
A final note: the EEPROM in the 32U4 chip is rated for 100,000 write cycles. This is a lot of cycles but you can wear it out if you use it a lot (such as writing a value every couple of millisecond for 5 minutes).