Skip to content

Commit 3aa463a

Browse files
authored
EEPROM library - emulated EEPROM in user option bytes (#103)
* Emulate EEPROM using the user select word storage area. Tested on CH32V003. * Added more info, styling. * Updated wording, typos and styling * Updated some more wording and typos * fixed typo
1 parent 96e1e59 commit 3aa463a

File tree

11 files changed

+825
-0
lines changed

11 files changed

+825
-0
lines changed

libraries/EEPROM/README.md

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
## **EEPROM Library** for Arduino CH32
2+
3+
### **What is the EEPROM library**
4+
5+
The EEPROM library provides an easy to use interface to interact with the internal non-volatile storage found in Arduino boards.
6+
This CH32 version of the library provides a familiar API to emulated EEPROM using the Option bytes area in flash memory.
7+
8+
Ported to CH32 by Maxint R&D, based on multiple sources:
9+
- Code from the Option Data example of CH32V003fun by @CNLOHR.
10+
- Arduino original copyright (c) 2006 David A. Mellis. All right reserved. New version by Christopher Andrews 2015.
11+
- ESP8266 version copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
12+
13+
## Table of contents
14+
- [CH32V003 emulated EEPROM](#ch32v003-emulated-eeprom)
15+
- [How to use this library](#how-to-use-this-library)
16+
- [Library functions](#library-functions)
17+
- [Features & limitations](#features--limitations)
18+
- [Disclaimer](#disclaimer)
19+
20+
### CH32V003 emulated EEPROM
21+
On the CH32V003 there are 2+24 bytes available. The first two bytes are Option bytes data0 and data1.
22+
All bytes are copied to a 26-byte long byte array in RAM. After changing a value the commit() method is used to write flash.
23+
24+
From the CH32V003 Data Sheet:
25+
> "Built-in 1920 bytes of system storage (System FLASH) for system bootloader storage (factory-cured
26+
bootloader) 64 bytes are used for the system non-volatile configuration information storage area and 64 bytes
27+
are used for the user select word storage area."
28+
29+
So next to 16kB of Code FLASH, the chip features 2kB Flash for boot, configuration and storage. That 64 bytes
30+
user select word storage area is the area we can use to emulate on chip EEPROM memory.
31+
32+
From the CH32V003 Reference Manual:
33+
> "The user-option bytes is solidified in FLASH and will be reloaded into the corresponding register after system
34+
reset, and can be erased and programmed by the user at will. The user option bytes information block has a
35+
total of 8 bytes (4 bytes for write protection, 1 byte for read protection, 1 byte for configuration options, and
36+
2 bytes for storing user data), and each bit has its inverse code bit for checksum during loading."
37+
38+
These 8 bytes and their inverse use 16 bytes of flash. The total storage area page is 64 bytes, leaving 48 bytes available,
39+
(including required inverse values). This gives 24 bytes that can be used plus the two data0 and data1 bytes.
40+
Layout for uint8_t _data[26]: { ob[4], ob[6], ob[16...62] ].
41+
42+
The first release of this library was made for the CH32V003 and only uses the user select word storage area.
43+
It was tested using Arduino IDE 2.3.2 and OpenWCH core 1.0.4.
44+
Future releases of this library may support other CH32 processors and allow for larger memory sizes.
45+
46+
### **How to use this library**
47+
To use the library in your sketch you'll need to reference the library header file. You do this by adding an include directive to the top of your sketch. The library provides a global object variable named `EEPROM`. You use this object to access the library functions that are listed below.
48+
This EEPROM library requires you to call EEPROM.begin() to initialize the object.
49+
50+
```Arduino
51+
#include <EEPROM.h>
52+
53+
void setup(){
54+
EEPROM.begin();
55+
}
56+
57+
void loop(){
58+
59+
}
60+
61+
```
62+
You can find complete examples [here](examples/).
63+
64+
---
65+
66+
67+
### **Library functions**
68+
69+
#### **`EEPROM.begin()`**
70+
71+
This method initializes the EEPROM object. It reads the current data from permanent storage into a RAM memory buffer to allow speedy access. After writing data to memory, EEPROM.commit() needs to be called to save the data into permanent storage.
72+
73+
This method does not return any value.
74+
75+
#### **`EEPROM.erase()`**
76+
77+
This method erases the RAM memory of the EEPROM object. Erased bytes have a default value of 255 (0xFF). After erasing the RAM memory, EEPROM.commit() needs to be called to erase the permanent storage.
78+
79+
This method does not return any value.
80+
81+
#### **`EEPROM.commit()`** [[_example_]](examples/eeprom_counter/eeprom_counter.ino)
82+
83+
The EEPROM object uses a RAM memory buffer to allow speedy access to its data. After writing data to memory, EEPROM.commit() needs to be called to save the data into permanent storage.
84+
85+
This method returns the `bool` true value to indicate success. The current implementations uses loops to wait for success.
86+
87+
#### **`EEPROM.length()`** [[_example_]](examples/eeprom_counter/eeprom_counter.ino)
88+
89+
This method returns an `size_t` containing the number of elements in the emulated EEPROM. On the CH32V003 there are 2+24 bytes available. _Future releases of this library may allow the begin() method to specify a larger size._
90+
91+
#### **`EEPROM.read( address )`** [[_example_]](examples/eeprom_read/eeprom_read.ino)
92+
93+
This method allows you to read a single byte of data from the EEPROM.
94+
Its only parameter is an `int` which should be set to the address you wish to read.
95+
96+
The method returns the byte (`unsigned char` / `uint8_t`) containing the value read.
97+
98+
#### **`EEPROM.write( address, value )`** [[_example_]](examples/eeprom_write/eeprom_write.ino)
99+
100+
The `write()` method allows you to write a single byte of data to the EEPROM.
101+
Two parameters are needed. The first is an `int` containing the address that is to be written, and the second is a the byte to be written (`unsigned char` / `uint8_t`). After writing data to memory, EEPROM.commit() needs to be called to save the data into permanent storage.
102+
103+
This method does not return any value.
104+
105+
#### **`EEPROM.get( address, variable )`** [[_example_]](examples/eeprom_get/eeprom_get.ino)
106+
107+
This method will retrieve any type of data from the EEPROM.
108+
Two parameters are needed to call this method. The first is an `int` containing the address that is to be written, and the second is the variable you would like to read.
109+
110+
This method returns a reference to the `variable` passed in. It does not need to be used and is only returned for convenience.
111+
112+
#### **`EEPROM.put( address, variable )`** [[_example_]](examples/eeprom_put/eeprom_put.ino)
113+
114+
This method will write any type of data to the EEPROM.
115+
Two parameters are needed to call this method. The first is an `int` containing the address that is to be written, and the second is the variable you would like to write. After writing data to memory, EEPROM.commit() needs to be called to save the data into permanent storage.
116+
117+
This method returns a reference to the `variable` passed in. It does not need to be used and is only returned for convenience.
118+
119+
#### **Subscript operator: `EEPROM[address]`** [[_example_]](examples/eeprom_crc/eeprom_crc.ino)
120+
121+
This operator allows using the `EEPROM` object like an array.
122+
EEPROM RAM elements can be read _and_ **_written_** directly using this method. After writing data to memory, EEPROM.commit() needs to be called to save the data into permanent storage.
123+
124+
This operator returns a reference to the EEPROM RAM elements.
125+
126+
```c++
127+
uint8_t val;
128+
129+
//Read first EEPROM RAM element.
130+
val = EEPROM[ 0 ];
131+
132+
//Write first EEPROM RAM element.
133+
EEPROM[ 0 ] = val;
134+
135+
//Compare contents
136+
if( val == EEPROM[ 0 ] ){
137+
//Do something...
138+
}
139+
```
140+
141+
#### **`EEPROM.ReadOptionBytes()`** [[_example_]](examples/eeprom_counter/eeprom_counter.ino)
142+
143+
This method is made available to show how the CH32 stores data in the user select word storage area.
144+
The 8 words in the user option bytes information block are reloaded into their corresponding register after system reset.
145+
For this reason the data0 and data1 bytes within that block can be read even before EEPROM.begin() is called.
146+
After calling EEPROM.begin() these data0 and data1 bytes are copied into addresses 0 and 1 of the EEPROM RAM buffer.
147+
148+
The method returns a `uint32_t` value, containing the data0 and data1 bytes and their inversed values.
149+
150+
---
151+
152+
## Features & limitations
153+
- The first release of this library was made only for the CH32V003 and has been tested on that MCU only. Other members of the CH32 may behave incorrectly or not work at all.
154+
- This EEPROM implementation for the CH32V003 has only 26 bytes available. When addressing more, things are likely to go wrong. A future release may allow using more pages from the flash memory.
155+
- Most CH32 EEPROM methods are the same as their equivalent on regular Arduino's. BEWARE: The begin() and end() methods are like their counterparts for ESP8266/ESP32, but are very different from the begin() and end() methods of EEPROM v2.0 by Christopher Andrews, who introduced them to support C++ iterators. This library follows the begin() convention introduced by the Serial and Wire classes, i.e. to initialize the object.
156+
157+
## Disclaimer
158+
- All code on this GitHub account, including this library is provided to you on an as-is basis without guarantees and with all liability dismissed. It may be used at your own risk. Unfortunately I have no means to provide support.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/***
2+
eeprom_counter example.
3+
Written by by Maxint R&D for CH32.
4+
5+
The purpose of this example is to demonstrate non-volatile storage of a simple counter that counts
6+
the times that the setup() function was called. It also shows the entire contents of the EEPROM.
7+
***/
8+
#include <EEPROM.h>
9+
10+
void setup()
11+
{
12+
Serial.begin(115200);
13+
delay(2000); // allow some time for Arduino IDE v2 serial console to show after recompiling
14+
Serial.println("\n--- EEPROM COUNTER EXAMPLE ---");
15+
Serial.printf("Option bytes: 0x%08x\r\n", EEPROM.ReadOptionBytes()); // usually FF FF to start with
16+
EEPROM.begin();
17+
18+
// Read the counter at EEPROM address 0, increment and write it back.
19+
uint8_t bootcnt=EEPROM.read(0);
20+
bootcnt++;
21+
EEPROM.write(0, bootcnt);
22+
Serial.printf("Boot count is %d [0x%02X]\n",bootcnt, bootcnt);
23+
24+
// Use EEPROM.commit() to write data to permanent storage
25+
EEPROM.commit();
26+
27+
// Show the contents of the entire EEPROM memory
28+
Serial.print("EEPROM contents:");
29+
for(int n=0; n<EEPROM.length(); n++)
30+
{
31+
if(n%8==0) Serial.println("");
32+
Serial.printf("%02X ", EEPROM[n]);
33+
}
34+
Serial.println(".");
35+
}
36+
37+
void loop()
38+
{ // nothing to do
39+
40+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/***
2+
eeprom_crc example.
3+
Written by Christopher Andrews. Modified by Maxint R&D for CH32.
4+
CRC algorithm generated by pycrc, MIT licence ( https://github.com/tpircher/pycrc ).
5+
6+
A CRC is a simple way of checking whether data has changed or become corrupted.
7+
This example calculates a CRC value directly on the EEPROM values.
8+
The purpose of this example is to highlight how the EEPROM object can be used just like an array.
9+
***/
10+
11+
#include <EEPROM.h>
12+
13+
void setup() {
14+
// Initialize serial and wait for port to open:
15+
Serial.begin(115200);
16+
while (!Serial) {
17+
; // wait for serial port to connect. Needed for native USB port only
18+
}
19+
20+
// Initialize EEPROM object
21+
EEPROM.begin();
22+
23+
//Print length of data to run CRC on.
24+
Serial.print("EEPROM length: ");
25+
Serial.println(EEPROM.length());
26+
27+
//Print the result of calling eeprom_crc()
28+
Serial.print("CRC32 of EEPROM data: 0x");
29+
Serial.println(eeprom_crc(), HEX);
30+
Serial.print("\n\nDone!");
31+
}
32+
33+
void loop() {
34+
/* Empty loop */
35+
}
36+
37+
unsigned long eeprom_crc(void) {
38+
39+
const unsigned long crc_table[16] = {
40+
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
41+
0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
42+
0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
43+
0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
44+
};
45+
46+
unsigned long crc = ~0L;
47+
48+
for (int index = 0 ; index < EEPROM.length() ; ++index) {
49+
crc = crc_table[(crc ^ EEPROM[index]) & 0x0f] ^ (crc >> 4);
50+
crc = crc_table[(crc ^ (EEPROM[index] >> 4)) & 0x0f] ^ (crc >> 4);
51+
crc = ~crc;
52+
}
53+
return crc;
54+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/***
2+
eeprom_get example.
3+
4+
This shows how to use the EEPROM.get() method.
5+
6+
To pre-set the EEPROM data, run the example sketch eeprom_put.
7+
This sketch will run without it, however, the values shown
8+
will be shown from what ever is already on the EEPROM.
9+
10+
This may cause the serial object to print out a large string
11+
of garbage if there is no null character inside one of the strings
12+
loaded.
13+
14+
Written by Christopher Andrews 2015. Modified by Maxint R&D for CH32.
15+
Released under MIT licence.
16+
***/
17+
18+
#include <EEPROM.h>
19+
20+
void setup() {
21+
22+
float f = 0.00f; //Variable to store data read from EEPROM.
23+
int eeAddress = 0; //EEPROM address to start reading from
24+
25+
Serial.begin(115200);
26+
while (!Serial) {
27+
; // wait for serial port to connect. Needed for native USB port only
28+
}
29+
Serial.print("Read float from EEPROM: ");
30+
31+
// initialize EEPROM object
32+
EEPROM.begin();
33+
34+
//Get the float data from the EEPROM at position 'eeAddress'
35+
EEPROM.get(eeAddress, f);
36+
Serial.println(f, 3); //This may print 'ovf, nan' if the data inside the EEPROM is not a valid float.
37+
38+
/***
39+
As get also returns a reference to 'f', you can use it inline.
40+
E.g: Serial.print( EEPROM.get( eeAddress, f ) );
41+
***/
42+
43+
/***
44+
Get can be used with custom structures too.
45+
I have separated this into an extra function.
46+
***/
47+
48+
secondTest(); //Run the next test.
49+
}
50+
51+
struct MyObject {
52+
float field1;
53+
byte field2;
54+
char name[10];
55+
};
56+
57+
void secondTest() {
58+
int eeAddress = sizeof(float); //Move address to the next byte after float 'f'.
59+
60+
MyObject customVar; //Variable to store custom object read from EEPROM.
61+
EEPROM.get(eeAddress, customVar);
62+
63+
Serial.println("Read custom object from EEPROM: ");
64+
Serial.println(customVar.field1);
65+
Serial.println(customVar.field2);
66+
Serial.println(customVar.name);
67+
}
68+
69+
void loop() {
70+
/* Empty loop */
71+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/***
2+
eeprom_put example.
3+
4+
This shows how to use the EEPROM.put() method.
5+
Also, this sketch will pre-set the EEPROM data for the
6+
example sketch eeprom_get.
7+
8+
Note, unlike the single byte version EEPROM.write(),
9+
the put method will use update semantics. As in a byte
10+
will only be written to the EEPROM if the data is actually
11+
different.
12+
13+
Written by Christopher Andrews 2015. Modified by Maxint R&D for CH32.
14+
Released under MIT licence.
15+
***/
16+
17+
#include <EEPROM.h>
18+
19+
struct MyObject {
20+
float field1;
21+
byte field2;
22+
char name[10];
23+
};
24+
25+
void setup() {
26+
float f = 123.456f; //Variable to store in EEPROM.
27+
int eeAddress = 0; //Location we want the data to be put.
28+
29+
Serial.begin(115200);
30+
while (!Serial) {
31+
; // wait for serial port to connect. Needed for native USB port only
32+
}
33+
34+
// initialize EEPROM object
35+
EEPROM.begin();
36+
37+
//One simple call, with the address first and the object second.
38+
EEPROM.put(eeAddress, f);
39+
40+
Serial.println("Written float data type!");
41+
42+
/** Put is designed for use with custom structures also. **/
43+
44+
//Data to store.
45+
MyObject customVar = {
46+
3.14f,
47+
42,
48+
"Working!"
49+
};
50+
51+
eeAddress += sizeof(float); //Move address to the next byte after float 'f'.
52+
53+
EEPROM.put(eeAddress, customVar);
54+
55+
// Use EEPROM.commit() to write data to permanent storage
56+
EEPROM.commit();
57+
58+
Serial.print("Written custom data type! \n\nView the example sketch eeprom_get to see how you can retrieve the values!");
59+
}
60+
61+
void loop() {
62+
/* Empty loop */
63+
}

0 commit comments

Comments
 (0)