@@ -48,6 +48,11 @@ static_assert(MIN_PULSE_WIDTH_US >= 10ULL,
4848static constexpr std::uint32_t kDeviceInfoAddr {0x0 };
4949static constexpr std::uint32_t kPulseCountAddr {0x800 };
5050
51+ // Number of 4-byte words used in the EEPROM for the pulse count. This is used
52+ // for wear leveling. Each word is rated for a million write cycles, so +1 here
53+ // adds a million to our upper count limit, essentially.
54+ static constexpr std::size_t kPulseCountWords {16 };
55+
5156// Board ID (this is set only once and stored here).
5257static char board_id[PICO_UNIQUE_BOARD_ID_SIZE_BYTES * 2 + 1 ];
5358
@@ -59,6 +64,7 @@ static AT24CM02 eeprom{I2C_INST, true};
5964
6065static volatile std::uint32_t pulsecount;
6166static std::uint32_t last_pulsecount;
67+ static std::uint32_t next_pulsecount_idx;
6268
6369[[noreturn]] static void Panic () noexcept {
6470 while (1 ) {
@@ -91,20 +97,67 @@ static void ValidateDeviceInfo() noexcept {
9197 }
9298}
9399
100+ inline constexpr std::uint32_t GetPulseCountAddr (std::uint32_t idx) noexcept {
101+ return kPulseCountAddr + idx * sizeof (std::uint32_t );
102+ }
103+
94104static void StorePulseCount (std::uint32_t pc) noexcept {
95- if (!eeprom.Write (kPulseCountAddr , reinterpret_cast <const std::uint8_t *>(&pc),
105+ const auto addr = GetPulseCountAddr (next_pulsecount_idx);
106+ next_pulsecount_idx = (next_pulsecount_idx + 1 ) % kPulseCountWords ;
107+ if (!eeprom.Write (addr, reinterpret_cast <const std::uint8_t *>(&pc),
96108 sizeof (pc))) {
97109 Panic ();
98110 }
99111}
100112
113+ static void ResetPulseCount () noexcept {
114+ const std::uint32_t dummy[kPulseCountWords ]{};
115+ if (!eeprom.Write (kPulseCountAddr ,
116+ reinterpret_cast <const std::uint8_t *>(dummy),
117+ sizeof (dummy))) {
118+ Panic ();
119+ }
120+ pulsecount = 0 ;
121+ last_pulsecount = 0 ;
122+ next_pulsecount_idx = 0 ;
123+ }
124+
101125static void LoadPulseCount () noexcept {
102- std::uint32_t pc ;
103- if (!eeprom.Read (kPulseCountAddr , reinterpret_cast <std::uint8_t *>(&pc ),
104- sizeof (pc ))) {
126+ std::uint32_t pcs[ kPulseCountWords ] ;
127+ if (!eeprom.Read (kPulseCountAddr , reinterpret_cast <std::uint8_t *>(pcs ),
128+ sizeof (pcs ))) {
105129 Panic ();
106130 }
107- pulsecount = pc;
131+
132+ // Validate the pulse counts in the array and if any are all FFs, reset them
133+ // to 0 (these are blank cells).
134+ bool modified{};
135+ for (std::uint32_t i{}; i < std::size (pcs); ++i) {
136+ if (pcs[i] == 0xFFFFFFFF ) {
137+ pcs[i] = 0 ;
138+ modified = true ;
139+ }
140+ }
141+
142+ if (modified) {
143+ if (!eeprom.Write (kPulseCountAddr ,
144+ reinterpret_cast <const std::uint8_t *>(pcs),
145+ sizeof (pcs))) {
146+ Panic ();
147+ }
148+ }
149+
150+ // Find the next index to write a pulse count to (i.e., the first one where
151+ // the value is less than the previous one). Also, store the highest value, as
152+ // that's the actual pulse count.
153+ for (std::uint32_t i{}; i < kPulseCountWords ; ++i) {
154+ const auto next_idx = (i + 1 ) % kPulseCountWords ;
155+ if (pcs[next_idx] <= pcs[i]) {
156+ next_pulsecount_idx = next_idx;
157+ pulsecount = pcs[i];
158+ break ;
159+ }
160+ }
108161}
109162
110163[[nodiscard]] static bool WriteLockEnabled () noexcept {
@@ -168,9 +221,7 @@ static void HandleSerialMessage(std::string_view message) noexcept {
168221 StoreDeviceInfo ();
169222 }
170223 } else if (header == " RESETCOUNT" sv) {
171- StorePulseCount (0 );
172- pulsecount = 0 ;
173- last_pulsecount = 0 ;
224+ ResetPulseCount ();
174225 }
175226 }
176227}
@@ -212,12 +263,8 @@ int main(void) {
212263 LoadDeviceInfo ();
213264 ValidateDeviceInfo ();
214265
266+ // This also checks for fresh EEPROM (FFs)
215267 LoadPulseCount ();
216- // check for blank EEPROM
217- if (pulsecount == 0xFFFFFFFF ) {
218- pulsecount = 0 ;
219- StorePulseCount (0 );
220- }
221268
222269 // Get the board ID (we only need to do this once)
223270 ::pico_get_unique_board_id_string (board_id, sizeof (board_id));
0 commit comments