Skip to content

Commit 96d5980

Browse files
author
Victor Chavez
committed
First commit
0 parents  commit 96d5980

File tree

6 files changed

+1049
-0
lines changed

6 files changed

+1049
-0
lines changed

LICENSE

Lines changed: 674 additions & 0 deletions
Large diffs are not rendered by default.

Readme.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# NoBlockEEPROM
2+
3+
NoBlockEEPROM is a library intended to avoid blocking code execution when Writing/Reading to the integrated EEPROM peripheral of some AVR MCUs supported by the Arduino SDK.
4+
5+
### Supported Boards
6+
7+
- Arduino Uno/Nano
8+
9+
Note: In theory any AVR board with an EEPROM peripheral is supported as ATMEL MCUs have typically the same register sets. If you find that any other board works with this library you can add it to the list through a pull-request.
10+
11+
### Motivation
12+
13+
The Arduino SDK uses the avr-libc EEPROM API which in turn are blocking functions. This library uses the interrupt vector to write to the EEPROM asynchronously. Whenever a write operation is done, a callback can be set to notify of this event.
14+
15+
16+
17+
### How to Use
18+
19+
Check `NoBlockEEPROM\examples\WriteRead` for an example which Writes and Reads in non-blocking mode.
20+
21+
### Contribution
22+
23+
If you find any issue or have made improvements to this library you can submit a pull-request.
24+
25+
26+
27+
### License
28+
29+
GPLV3

examples/WriteRead/WriteRead.ino

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#include <EEPROM.h>
2+
#include "NoBlkEEPROM.hpp"
3+
4+
bool EEPROM_WriteFinished = false;
5+
6+
void EEPROMFinishedCB(void * args)
7+
{
8+
EEPROM_WriteFinished = true;
9+
}
10+
constexpr int buffer_size = 100;
11+
uint8_t write_buffer[buffer_size];
12+
constexpr uint8_t write_addr = 0;
13+
uint32_t write_start;
14+
15+
void setup() {
16+
Serial.begin(115200);
17+
NoBlkEEPROM.Begin();
18+
NoBlkEEPROM.SetCallback(EEPROMFinishedCB, nullptr);
19+
//Initialize buffer before writing with random values
20+
randomSeed(analogRead(0));
21+
for (auto i = 0; i < buffer_size; i++)
22+
{
23+
write_buffer[i] = random(0, 0xFF);
24+
}
25+
Serial.println("Write started");
26+
write_start = millis();
27+
28+
NoBlkEEPROMClass::EEPROMResult result;
29+
result = NoBlkEEPROM.Write(write_addr, write_buffer, buffer_size);
30+
if (result != NoBlkEEPROMClass::eeprom_ok)
31+
{
32+
Serial.print("1st EEPROM Write failed: ");
33+
Serial.println(result, HEX);
34+
while(1);
35+
}
36+
//Second write should fail as eeprom is busy
37+
result = NoBlkEEPROM.Write(write_addr, random(0, 0xFF));
38+
if (result != NoBlkEEPROMClass::eeprom_busy)
39+
{
40+
Serial.print("2nd EEPROM Write failed: ");
41+
Serial.println(result, HEX);
42+
while(1);
43+
}
44+
}
45+
46+
void loop() {
47+
48+
if (EEPROM_WriteFinished == true)
49+
{
50+
EEPROM_WriteFinished = false;
51+
uint32_t write_end = millis();
52+
Serial.print("Operation took :");
53+
Serial.print(write_end - write_start);
54+
Serial.println(" ms");
55+
uint8_t read_buff[buffer_size];
56+
Serial.println("Reading buffer");
57+
NoBlkEEPROM.Read(0, read_buff, buffer_size);
58+
int compare_res = memcmp(read_buff, write_buffer, buffer_size);
59+
if (compare_res != 0)
60+
{
61+
Serial.println("Error Write Operation failed");
62+
}
63+
else
64+
{
65+
Serial.println("Write/Read Ok");
66+
}
67+
}
68+
69+
}

library.properties

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
name=NoBlockEEPROM
2+
version=0.0.1
3+
author=Victor Chavez
4+
maintainer=Victor Chavez <[email protected]>
5+
sentence=Non Blocking EEPROM Library for Arduino
6+
paragraph=Library that does not use blocking methods to write/read to eeprom
7+
category=Other
8+
includes=NoBlockEEPROM.hpp
9+
url=https://github.com/vChavezB/NoBlockEEPROM
10+
architectures=avr

src/NoBlockEEPROM.cpp

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/*
2+
Copyright (C) 2022 Victor Chavez
3+
This file is part of NoBlockEEPROM
4+
5+
NoBlockEEPROM is free software: you can redistribute it and/or modify
6+
it under the terms of the GNU General Public License as published by
7+
the Free Software Foundation, either version 3 of the License, or
8+
(at your option) any later version.
9+
IOLink Device Generator is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
You should have received a copy of the GNU General Public License
14+
along with IOLink Device Generator. If not, see <https://www.gnu.org/licenses/>.
15+
16+
*/
17+
18+
#include "NoBlockEEPROM.hpp"
19+
#include "Arduino.h"
20+
#include <util/atomic.h>
21+
22+
#define MAX_ADR (1023) /*!<Max EEPROM Address for Arduino UNO */
23+
24+
NoBlkEEPROMClass& NoBlkEEPROM = NoBlkEEPROMClass::instance();
25+
26+
void NoBlkEEPROMClass::Begin()
27+
{
28+
EECR = (static_cast<uint8_t>(EraseWrite) << EEPM0); //Programming mode Write+Erase
29+
30+
}
31+
void NoBlkEEPROMClass::SetCallback(WriteCallback cb, void * args)
32+
{
33+
if (cb != nullptr)
34+
{
35+
write_cb = cb;
36+
cb_args = args;
37+
}
38+
}
39+
40+
void NoBlkEEPROMClass::WriteByte(uint16_t addr, uint8_t data)
41+
{
42+
EEAR = addr ;
43+
EEDR = data;
44+
EECR |= (1 << EEMPE); //Write Enable
45+
EECR |= ( (1U << EEPE) //Start eeprom write
46+
| (1U << EERIE) ); //Enable Interrupt
47+
}
48+
49+
uint8_t NoBlkEEPROMClass::ReadByte(uint16_t addr)
50+
{
51+
EEAR = addr ;
52+
/* Start eeprom read by writing EERE */
53+
EECR |= (1 << EERE);
54+
return EEDR;
55+
}
56+
57+
NoBlkEEPROMClass::EEPROMResult NoBlkEEPROMClass::Write(uint16_t addr, const uint8_t * buffer, uint8_t len)
58+
{
59+
EEPROMResult result;
60+
do
61+
{
62+
ATOMIC_BLOCK(ATOMIC_FORCEON) //Do not allow concurrent calls
63+
{
64+
if (addr > MAX_ADR || (addr + len > MAX_ADR))
65+
{
66+
result = EEPROMResult::eeprom_addr_ovflw;
67+
break;
68+
}
69+
if ( EECR & (1 << EEPE) )
70+
{
71+
result = EEPROMResult::eeprom_busy;
72+
break;
73+
}
74+
transfer.len = len;
75+
transfer.cnt = 0;
76+
transfer.buff = buffer;
77+
transfer.addr = addr;
78+
WriteByte(transfer.addr, *transfer.buff);
79+
result = EEPROMResult::eeprom_ok;
80+
}
81+
} while (0);
82+
return result;
83+
}
84+
85+
86+
NoBlkEEPROMClass::EEPROMResult NoBlkEEPROMClass::Write(uint16_t addr, uint8_t data)
87+
{
88+
return Write(addr, &data, 1);
89+
}
90+
91+
92+
NoBlkEEPROMClass::EEPROMResult NoBlkEEPROMClass::Read(uint16_t addr, uint8_t * buff_out)
93+
{
94+
return Read(addr, buff_out, 1);
95+
}
96+
97+
NoBlkEEPROMClass::EEPROMResult NoBlkEEPROMClass::Read(uint16_t addr, uint8_t * buff_out, uint8_t len)
98+
{
99+
100+
EEPROMResult result;
101+
do
102+
{
103+
ATOMIC_BLOCK(ATOMIC_FORCEON) //Do not allow concurrent calls
104+
{
105+
if (addr > MAX_ADR || (addr + len > MAX_ADR))
106+
{
107+
result = EEPROMResult::eeprom_addr_ovflw;
108+
break;
109+
}
110+
if ( EECR & (1 << EEPE) )
111+
{
112+
result = EEPROMResult::eeprom_busy;
113+
break;
114+
}
115+
for (auto i = 0; i < len; i++)
116+
{
117+
buff_out[i] = ReadByte(addr + i);
118+
}
119+
result = EEPROMResult::eeprom_ok;
120+
}
121+
} while (0);
122+
return result;
123+
}
124+
125+
ISR(EE_READY_vect)
126+
{
127+
NoBlkEEPROM.transfer.cnt++;
128+
if (NoBlkEEPROM.transfer.cnt == NoBlkEEPROM.transfer.len)
129+
{
130+
//Transfer finished
131+
EECR &= ~(1U << EERIE); //Disable interrupt
132+
if (NoBlkEEPROM.write_cb != nullptr)
133+
{
134+
NoBlkEEPROM.write_cb(NoBlkEEPROM.cb_args);
135+
}
136+
}
137+
else
138+
{
139+
//Continue writing to eeprom
140+
NoBlkEEPROM.transfer.addr++;
141+
NoBlkEEPROM.transfer.buff++;
142+
NoBlkEEPROM.WriteByte(NoBlkEEPROM.transfer.addr, *NoBlkEEPROM.transfer.buff);
143+
}
144+
}

src/NoBlockEEPROM.hpp

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
Copyright (C) 2022 Victor Chavez
3+
This file is part of NoBlockEEPROM
4+
5+
NoBlockEEPROM is free software: you can redistribute it and/or modify
6+
it under the terms of the GNU General Public License as published by
7+
the Free Software Foundation, either version 3 of the License, or
8+
(at your option) any later version.
9+
IOLink Device Generator is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
You should have received a copy of the GNU General Public License
14+
along with IOLink Device Generator. If not, see <https://www.gnu.org/licenses/>.
15+
16+
*/
17+
18+
#ifndef NoBlkEEPROM_H
19+
#define NoBlkEEPROM_H
20+
21+
#include <stdint.h>
22+
#include <avr/interrupt.h>
23+
24+
extern "C" void EE_READY_vect(void);
25+
26+
class NoBlkEEPROMClass
27+
{
28+
public:
29+
/*!< Results for using eeprom public operation API */
30+
enum EEPROMResult
31+
{
32+
eeprom_ok, /*!< EEPROM operation ok */
33+
eeprom_busy, /*!< EEPROM is already busy with a transaction */
34+
eeprom_addr_ovflw, /*!< Combination of address + len would cause overflow */
35+
eeprom_addr_range_err /*!< Addres not within range */
36+
};
37+
/*!< Allow only one instance */
38+
static NoBlkEEPROMClass & instance()
39+
{
40+
static NoBlkEEPROMClass NoBlkEEPROM;
41+
return NoBlkEEPROM;
42+
}
43+
/*!< Callback function type*/
44+
typedef void (*WriteCallback)(void *);
45+
/* @brief Initializes the EEPROM Peripheral
46+
@note Should be only called once
47+
*/
48+
void Begin();
49+
/* @brief Sets callback for asynch events from EEPROM
50+
@details Whenever a EEPROM operation is finished,
51+
the callback that has been passed to this function
52+
will be called asynchronously.
53+
@param[in] cb The callback function to be set
54+
@param[in] args Pointer to address of arguments to be passed to the callback
55+
@note Keep in mind that the callback is called in an ISR, so all
56+
best practices regarding code execution in an ISR should be followed.
57+
*/
58+
void SetCallback(WriteCallback cb, void * args);
59+
/* @brief Write a byte to the EEPROM
60+
@param[in] addr EEPROM Address
61+
@param[in] data 8 bit data to write to eeprom
62+
@retval eeprom_ok: Operation was succesful, operation done notified via callback @ref SetCallback
63+
*/
64+
EEPROMResult Write(uint16_t addr, uint8_t data);
65+
/* @brief Write a buffer to the EEPROM
66+
@param[in] addr EEPROM Address
67+
@param[in] buffer Pointer to data buffer to write
68+
@param[in] len Length of the buffer
69+
@note The buffer used to intiate the write operation shall not change while the operation is not finished.
70+
The reason is that there is no internal buffer implemented
71+
@retval eeprom_ok: Operation was succesful, operation done notified via callback @ref SetCallback
72+
*/
73+
EEPROMResult Write(uint16_t addr, const uint8_t * buffer, uint8_t len);
74+
/* @brief Read n bytes from the EEPROM
75+
@param[in] addr EEPROM Address to read
76+
@param[out] buff_out Pointer to buffer where read operations will be saved
77+
@param[in] len Total bytes to read
78+
@note This function is non-blocking, in case the eeprom is busy it will return eeprom_busy
79+
@retval eeprom_ok: Operation was succesful, operation done notified via callback @ref SetCallback
80+
*/
81+
EEPROMResult Read(uint16_t addr,uint8_t * buff_out,uint8_t len);
82+
/* @brief Read one byte of data from the EEPROM
83+
@param[in] addr EEPROM Address to read
84+
@param[out] buff_out Pointer to to 8 bit buffer to store read operation
85+
@note This function is non-blocking, in case the eeprom is busy it will return eeprom_busy
86+
@retval eeprom_ok: Operation was succesful, operation done notified via callback @ref SetCallback
87+
*/
88+
EEPROMResult Read(uint16_t addr,uint8_t * buff_out);
89+
private:
90+
NoBlkEEPROMClass() {};
91+
/* @brief Write a byte to the EEPROM peripheralusing the MCU registers
92+
@param[in] addr EEPROM Address
93+
@param[in] data 8 bit data to write to eeprom
94+
*/
95+
void WriteByte(uint16_t addr, uint8_t data);
96+
97+
uint8_t ReadByte(uint16_t addr);
98+
/*!< Pointer to callback arguments */
99+
void * cb_args;
100+
/*!< Callback function used when write operations are done */
101+
WriteCallback write_cb;
102+
/*!< Data required for EEPROM non blocking transfers */
103+
struct Transfer
104+
{
105+
const uint8_t * buff; /*!< Pointer to address that holds the buffer to write */
106+
uint16_t addr; /*!< EEPROM */
107+
uint8_t cnt;
108+
uint8_t len;
109+
};
110+
Transfer transfer;
111+
/*!<EEPROM Programming modes according to Table 7-1 datasheet */
112+
enum PRGMode
113+
{
114+
EraseWrite = 0,
115+
EraseOnly = 1,
116+
WriteOnly = 2
117+
};
118+
/*!< Vector table function for EEPROM Ready interrupt */
119+
friend void EE_READY_vect(void);
120+
};
121+
122+
extern NoBlkEEPROMClass & NoBlkEEPROM;
123+
#endif

0 commit comments

Comments
 (0)