FastArduino v1.10
C++ library to build fast but small Arduino/AVR projects
Loading...
Searching...
No Matches
eeprom.h
Go to the documentation of this file.
1// Copyright 2016-2023 Jean-Francois Poilpret
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
16
21#ifndef EEPROM_H
22#define EEPROM_H
23
24#include "boards/board_traits.h"
25#include <avr/interrupt.h>
26#include <avr/eeprom.h>
27#include "interrupts.h"
28#include "utilities.h"
29#include "queue.h"
30
36#define REGISTER_EEPROM_ISR() \
37 ISR(EE_READY_vect) \
38 { \
39 eeprom::isr_handler::eeprom_ready(); \
40 }
41
53#define REGISTER_EEPROM_ISR_METHOD(HANDLER, CALLBACK) \
54 ISR(EE_READY_vect) \
55 { \
56 eeprom::isr_handler::eeprom_ready_method<HANDLER, CALLBACK>(); \
57 }
58
69#define REGISTER_EEPROM_ISR_FUNCTION(CALLBACK) \
70 ISR(EE_READY_vect) \
71 { \
72 eeprom::isr_handler::eeprom_ready_function<CALLBACK>(); \
73 }
74
81#define DECL_EEPROM_ISR_HANDLERS_FRIEND \
82 friend struct eeprom::isr_handler; \
83 friend void ::EE_READY_vect();
84
89namespace eeprom
90{
126 class EEPROM
127 {
128 public:
129 EEPROM() = default;
130 EEPROM(const EEPROM&) = delete;
131 EEPROM& operator=(const EEPROM&) = delete;
132
149 template<typename T> static bool read(const T* address, T& value)
150 {
151 return read((uint16_t) address, value);
152 }
153
169 template<typename T> static bool read(uint16_t address, T& value)
170 {
171 if (!check(address, sizeof(T))) return false;
172 uint8_t* pvalue = (uint8_t*) &value;
173 for (uint16_t i = 0; i < sizeof(T); ++i) blocked_read(address++, *pvalue++);
174 return true;
175 }
176
194 template<typename T> static bool read(const T* address, T* value, uint16_t count)
195 {
196 return read((uint16_t) address, value, count);
197 }
198
215 template<typename T> static bool read(uint16_t address, T* value, uint16_t count)
216 {
217 if (!check(address, count * sizeof(T))) return false;
218 uint8_t* pvalue = (uint8_t*) value;
219 for (uint16_t i = 0; i < (count * sizeof(T)); ++i) blocked_read(address++, *pvalue++);
220 return true;
221 }
222
235 static bool read(const uint8_t* address, uint8_t& value)
236 {
237 return read((uint16_t) address, value);
238 }
239
251 static bool read(uint16_t address, uint8_t& value)
252 {
253 if (!check(address, 1))
254 {
255 value = UINT8_MAX;
256 return false;
257 }
258 blocked_read(address, value);
259 return true;
260 }
261
278 template<typename T> static bool write(const T* address, const T& value)
279 {
280 return write((uint16_t) address, value);
281 }
282
298 template<typename T> static bool write(uint16_t address, const T& value)
299 {
300 if (!check(address, sizeof(T))) return false;
301 uint8_t* pvalue = (uint8_t*) &value;
302 for (uint8_t i = 0; i < sizeof(T); ++i) blocked_write(address++, *pvalue++);
303 return true;
304 }
305
323 template<typename T> static bool write(const T* address, const T* value, uint16_t count)
324 {
325 return write((uint16_t) address, value, count);
326 }
327
344 template<typename T> static bool write(uint16_t address, const T* value, uint16_t count)
345 {
346 if (!check(address, count * sizeof(T))) return false;
347 uint8_t* pvalue = (uint8_t*) value;
348 for (uint8_t i = 0; i < (count * sizeof(T)); ++i) blocked_write(address++, *pvalue++);
349 return true;
350 }
351
363 static bool write(const uint8_t* address, uint8_t value)
364 {
365 return write((uint16_t) address, value);
366 }
367
378 static bool write(uint16_t address, uint8_t value)
379 {
380 if (!check(address, 1)) return false;
381 blocked_write(address, value);
382 return true;
383 }
384
390 static void erase()
391 {
392 for (uint16_t address = 0; address < size(); ++address)
393 {
395 erase_address(address);
396 }
397 }
398
402 static constexpr uint16_t size()
403 {
404 return E2END + 1;
405 }
406
413 static void wait_until_ready()
414 {
415 EECR_.loop_until_bit_clear(EEPE);
416 }
417
418 protected:
420 static bool constexpr check(uint16_t address, uint16_t size)
421 {
422 return (size != 0) && (address <= E2END) && (size <= (E2END + 1)) && ((address + size) <= (E2END + 1));
423 }
424
425 static void blocked_read(uint16_t address, uint8_t& value)
426 {
428 read_byte(address, value);
429 }
430
431 static void read_byte(uint16_t address, uint8_t& value)
432 {
433 EEAR_ = address;
434 EECR_ = bits::BV8(EERE);
435 value = EEDR_;
436 }
437
438 static void blocked_write(uint16_t address, uint8_t value)
439 {
441 write_byte(address, value);
442 }
443
444 // In order to optimize write time we read current byte first, then compare it with new value
445 // Then we choose between erase, write and erase+write based on comparison
446 // This approach is detailed in ATmel note AVR103: Using the EEPROM Programming Modes
447 // http://www.atmel.com/images/doc2578.pdf
448 static void write_byte(uint16_t address, uint8_t value)
449 {
450 EEAR_ = address;
451 EECR_ = bits::BV8(EERE);
452 uint8_t old_value = EEDR_;
453 uint8_t diff = old_value ^ value;
454 if (diff & value)
455 {
456 // Some bits need to be erased (ie set to 1)
457 if (value == UINT8_MAX)
458 // Erase only
459 EECR_ = bits::BV8(EEPM0);
460 else
461 // Combine Erase/Write operation
462 EECR_ = 0;
463 }
464 else
465 {
466 // No bit to be erased
467 if (diff)
468 // Some bits to be programmed (set to 0): Write only
469 EECR_ = bits::BV8(EEPM1);
470 else
471 // old value == new value => do nothing
472 return;
473 }
474 EEDR_ = value;
475 synchronized
476 {
477 EECR_ |= bits::BV8(EEMPE);
478 EECR_ |= bits::BV8(EEPE);
479 }
480 }
481
482 static bool erase_address(uint16_t address)
483 {
484 EEAR_ = address;
485 EECR_ = bits::BV8(EERE);
486 uint8_t value = EEDR;
487 // Some bits need to be erased (ie set to 1)
488 if (value == UINT8_MAX) return false;
489 EECR_ = bits::BV8(EEPM0);
490 EEDR_ = UINT8_MAX;
491 synchronized
492 {
493 EECR_ |= bits::BV8(EEMPE);
494 EECR_ |= bits::BV8(EEPE);
495 }
496 return true;
497 }
499
500 private:
501 using REG8 = board_traits::REG8;
502 using REG16 = board_traits::REG16;
503 static constexpr const REG16 EEAR_{EEAR};
504 static constexpr const REG8 EECR_{EECR};
505 static constexpr const REG8 EEDR_{EEDR};
506 };
507
530 class QueuedWriter : private EEPROM
531 {
532 public:
540 template<uint16_t SIZE>
541 explicit QueuedWriter(uint8_t (&buffer)[SIZE]) : buffer_{buffer}
542 {
544 }
545
563 template<typename T> bool write(const T* address, const T& value)
564 {
565 return write((uint16_t) address, value);
566 }
567
584 template<typename T> bool write(uint16_t address, const T& value)
585 {
586 if (!check(address, sizeof(T))) return false;
587 synchronized return write_data(address, (uint8_t*) &value, sizeof(T));
588 }
589
608 template<typename T> bool write(const T* address, const T* value, uint16_t count)
609 {
610 return write((uint16_t) address, value, count);
611 }
612
630 template<typename T> bool write(uint16_t address, const T* value, uint16_t count)
631 {
632 if (!check(address, count * sizeof(T))) return false;
633 synchronized return write_data(address, (uint8_t*) value, count * sizeof(T));
634 }
635
648 bool write(const uint8_t* address, uint8_t value)
649 {
650 return write((uint16_t) address, value);
651 }
652
664 bool write(uint16_t address, uint8_t value)
665 {
666 if (!check(address, 1)) return false;
667 synchronized return write_data(address, &value, 1);
668 }
669
677 void erase()
678 {
679 // First remove all pending writes
680 synchronized
681 {
682 buffer_.clear_();
683 current_.size = 0;
684 }
685 // Wait until current byte write is finished
687 synchronized
688 {
689 // Start erase
690 erase_ = true;
691 done_ = false;
692 current_.address = 0;
693 current_.size = size();
694 // Start transmission if not done yet
695 EECR_ = bits::BV8(EERIE);
696 }
697 }
698
706 void wait_until_done() const
707 {
708 while (!done_)
709 ;
710 }
711
715 bool is_done() const
716 {
717 return done_;
718 }
719
720 private:
721 static const uint16_t ITEM_SIZE = 3;
722
723 bool on_ready()
724 {
725 if (erase_)
726 {
727 if (current_.size)
728 erase_next();
729 else
730 {
731 // All erases are finished
732 erase_ = false;
733 // Mark all EEPROM work as finished if no write is pending in the queue
734 if (buffer_.empty_())
735 {
736 done_ = true;
737 EECR_ = 0;
738 }
739 }
740 }
741 else if (current_.size)
742 // There is one item being currently written, write next byte
743 write_next();
744 else if (!buffer_.empty_())
745 {
746 // Current item is finished writing but there is another item to be written in the queue
747 // Get new item and start transmission of first byte
748 current_ = next_item();
749 write_next();
750 }
751 else
752 {
753 // All writes are finished
754 done_ = true;
755 EECR_ = 0;
756 }
757 return done_;
758 }
759
760 void write_next()
761 {
762 uint8_t value = 0;
763 buffer_.pull_(value);
764 EEPROM::write_byte(current_.address++, value);
765 --current_.size;
766 EECR_ |= bits::BV8(EERIE);
767 }
768
769 void erase_next()
770 {
771 EEPROM::erase_address(current_.address++);
772 --current_.size;
773 EECR_ |= bits::BV8(EERIE);
774 }
775
776 bool write_data(uint16_t address, uint8_t* value, uint16_t size)
777 {
778 // First check if there is enough space in buffer_ for this queued write
779 if ((buffer_.free_() < (size + ITEM_SIZE)) || (size == 0)) return false;
780 done_ = false;
781 // Add new queued write to buffer
782 buffer_.push_(WriteItem::value1(address, size));
783 buffer_.push_(WriteItem::value2(address, size));
784 buffer_.push_(WriteItem::value3(address, size));
785 for (uint16_t i = 0; i < size; ++i) buffer_.push_(*value++);
786
787 // Start transmission if not done yet
788 EECR_ = bits::BV8(EERIE);
789 return true;
790 }
791
792 struct WriteItem
793 {
794 WriteItem() = default;
795 WriteItem(uint8_t value1, uint8_t value2, uint8_t value3)
796 : address{uint16_t(uint16_t(value1 << 4) | uint8_t(value2 >> 4))},
797 size{uint16_t(utils::is_zero(((value2 & 0x0F) << 8) | value3, E2END + 1))} {}
798
799 static uint8_t value1(uint16_t address, uint16_t size UNUSED)
800 {
801 return address >> 4;
802 }
803 static uint8_t value2(uint16_t address, uint16_t size)
804 {
805 return (address << 4) | (size >> 8);
806 }
807 static uint8_t value3(uint16_t address UNUSED, uint16_t size)
808 {
809 return size;
810 }
811
812 uint16_t address = 0;
813 uint16_t size = 0;
814 };
815
816 WriteItem next_item()
817 {
818 uint8_t value1 = 0;
819 buffer_.pull_(value1);
820 uint8_t value2 = 0;
821 buffer_.pull_(value2);
822 uint8_t value3 = 0;
823 buffer_.pull_(value3);
824 return WriteItem{value1, value2, value3};
825 }
826
827 using REG8 = board_traits::REG8;
828 using REG16 = board_traits::REG16;
829 static constexpr const REG16 EEAR_{EEAR};
830 static constexpr const REG8 EECR_{EECR};
831 static constexpr const REG8 EEDR_{EEDR};
832
834 WriteItem current_;
835 volatile bool erase_ = false;
836 volatile bool done_ = true;
837
838 friend struct isr_handler;
839 };
840
842 struct isr_handler
843 {
844 static void eeprom_ready()
845 {
846 interrupt::HandlerHolder<eeprom::QueuedWriter>::handler()->on_ready();
847 }
848
849 template<void (*CALLBACK)()> static void eeprom_ready_function()
850 {
851 if (interrupt::HandlerHolder<eeprom::QueuedWriter>::handler()->on_ready())
852 interrupt::CallbackHandler<void (*)(), CALLBACK>::call();
853 }
854
855 template<typename HANDLER, void (HANDLER::*CALLBACK)()> static void eeprom_ready_method()
856 {
857 if (interrupt::HandlerHolder<eeprom::QueuedWriter>::handler()->on_ready())
858 interrupt::CallbackHandler<void (HANDLER::*)(), CALLBACK>::call();
859 }
860 };
862}
863
864#endif /* EEPROM_H */
Queue of type T_ items.
Definition: queue.h:59
void clear_()
Completely clear this queue.
Definition: queue.h:291
bool empty_() const
Tell if this queue is currently empty.
Definition: queue.h:233
bool pull_(T &item)
Pull an item from the beginning of this queue, if not empty, and copy it into item.
uint8_t free_() const
Tell the current number of available locations for items to be pushed to this queue.
Definition: queue.h:275
bool push_(TREF item)
Push item to the end of this queue, provided there is still available space in its ring buffer.
Collection of static methods to read or write the AVR EEPROM.
Definition: eeprom.h:127
static constexpr uint16_t size()
Return the size (in bytes) of the embedded EEPROM.
Definition: eeprom.h:402
static bool read(const T *address, T &value)
Read value of type T stored in EEPROM at address.
Definition: eeprom.h:149
static bool read(const uint8_t *address, uint8_t &value)
Read one byte stored in EEPROM at address.
Definition: eeprom.h:235
static bool write(uint16_t address, const T &value)
Write the content of value of type T to the EEPROM at address.
Definition: eeprom.h:298
static bool read(uint16_t address, T &value)
Read value of type T stored in EEPROM at address.
Definition: eeprom.h:169
static void wait_until_ready()
Block until the current EEPROM operation, whetever it is (e.g.
Definition: eeprom.h:413
static bool write(uint16_t address, uint8_t value)
Write one byte to the EEPROM at address.
Definition: eeprom.h:378
static bool write(const T *address, const T &value)
Write the content of value of type T to the EEPROM at address.
Definition: eeprom.h:278
static bool write(const uint8_t *address, uint8_t value)
Write one byte to the EEPROM at address.
Definition: eeprom.h:363
static bool write(const T *address, const T *value, uint16_t count)
Write value, an array of count values of type T to the EEPROM at address.
Definition: eeprom.h:323
static bool write(uint16_t address, const T *value, uint16_t count)
Write value, an array of count values of type T to the EEPROM at address.
Definition: eeprom.h:344
static bool read(uint16_t address, T *value, uint16_t count)
Read an array of count values of type T stored in EEPROM at address.
Definition: eeprom.h:215
static void erase()
Erase the full EEPROM content.
Definition: eeprom.h:390
static bool read(const T *address, T *value, uint16_t count)
Read an array of count values of type T stored in EEPROM at address.
Definition: eeprom.h:194
static bool read(uint16_t address, uint8_t &value)
Read one byte stored in EEPROM at address.
Definition: eeprom.h:251
API that allows asynchronous writing to EEPROM; this can be useful when you have large amount of data...
Definition: eeprom.h:531
QueuedWriter(uint8_t(&buffer)[SIZE])
Construct a QueuedWriter from a given buffer array.
Definition: eeprom.h:541
bool is_done() const
Tell if there is no queued, nor on-going write operation.
Definition: eeprom.h:715
bool write(const T *address, const T &value)
Write the content of value of type T to the EEPROM at address.
Definition: eeprom.h:563
bool write(const T *address, const T *value, uint16_t count)
Write value, an array of count values of type T to the EEPROM at address.
Definition: eeprom.h:608
bool write(uint16_t address, const T *value, uint16_t count)
Write value, an array of count values of type T to the EEPROM at address.
Definition: eeprom.h:630
bool write(uint16_t address, uint8_t value)
Write one byte to the EEPROM at address.
Definition: eeprom.h:664
bool write(uint16_t address, const T &value)
Write the content of value of type T to the EEPROM at address.
Definition: eeprom.h:584
void wait_until_done() const
Block until all pending operations (queued in the ring buffer) are complete.
Definition: eeprom.h:706
void erase()
Erase the full EEPROM content.
Definition: eeprom.h:677
bool write(const uint8_t *address, uint8_t value)
Write one byte to the EEPROM at address.
Definition: eeprom.h:648
#define UNUSED
Specific GCC attribute to declare an argument or variable unused, so that the compiler does not emit ...
Definition: defines.h:45
General API for handling AVR interrupt vectors.
static constexpr uint8_t BV8(uint8_t bit)
Create a uint8_t bitmask for the given bit number.
Definition: bits.h:41
Defines the API for accessing the EEPROM embedded in each AVR MCU.
Definition: eeprom.h:90
void register_handler(Handler &handler)
Register a class instance containing methods that shall be called back by an ISR.
Definition: interrupts.h:185
Contains all generic utility methods.
Definition: iterator.h:29
constexpr T is_zero(T value, T default_value)
Replace value by default_value if not "true" (also known as "Elvis operator").
Definition: utilities.h:339
Utility API to handle ring-buffer queue containers.
General utilities API that have broad application in programs.