From 3487cfb2520365df1a0daadab9c208f951193c0a Mon Sep 17 00:00:00 2001
From: kkoskivi <122452861+kkoskivi@users.noreply.github.com>
Date: Tue, 4 Mar 2025 16:06:08 +0200
Subject: [PATCH] eeprom functionalities that worked when testing (on my
 device)

---
 pico/CMakeLists.txt                |  10 ++-
 pico/inc/devices/eeprom/eeprom.h   |  24 +++++
 pico/inc/devices/eeprom/logger.hpp |  21 +++++
 pico/src/devices/eeprom/eeprom.c   | 132 +++++++++++++++++++++++++++
 pico/src/devices/eeprom/logger.cpp | 139 +++++++++++++++++++++++++++++
 5 files changed, 324 insertions(+), 2 deletions(-)
 create mode 100644 pico/inc/devices/eeprom/eeprom.h
 create mode 100644 pico/inc/devices/eeprom/logger.hpp
 create mode 100644 pico/src/devices/eeprom/eeprom.c
 create mode 100644 pico/src/devices/eeprom/logger.cpp

diff --git a/pico/CMakeLists.txt b/pico/CMakeLists.txt
index 7cee5b2..5ef4f12 100644
--- a/pico/CMakeLists.txt
+++ b/pico/CMakeLists.txt
@@ -27,6 +27,8 @@ add_executable(${PROJECT_NAME}
     src/devices/motor-control.cpp
     src/planet_finder/planet_finder.cpp
     src/planet_finder/date_utils.cpp
+    src/devices/eeprom/eeprom.c
+    src/devices/eeprom/logger.cpp
 )
 
 add_executable(${PROJECT_NAME}_test
@@ -42,6 +44,8 @@ add_executable(${PROJECT_NAME}_test
     src/commbridge.cpp
     src/planet_finder/planet_finder.cpp
     src/planet_finder/date_utils.cpp
+    src/devices/eeprom/eeprom.c
+    src/devices/eeprom/logger.cpp
 )
 
 add_library(message ${COMMON_DIR}/src/message.cpp)
@@ -67,6 +71,7 @@ target_include_directories(${PROJECT_NAME} PRIVATE
     src
     inc
     inc/devices
+    inc/devices/eeprom
     inc/hardware
     inc/hardware/uart
     ${COMMON_DIR}/inc
@@ -77,6 +82,7 @@ target_include_directories(${PROJECT_NAME}_test PRIVATE
     src
     inc
     inc/devices
+    inc/devices/eeprom
     inc/hardware
     inc/hardware/uart
     inc/planet_finder
@@ -120,8 +126,8 @@ ENDIF()
 pico_enable_stdio_usb(${PROJECT_NAME} 0)
 pico_enable_stdio_uart(${PROJECT_NAME} 1)
 
-pico_enable_stdio_usb(${PROJECT_NAME}_test 0)
-pico_enable_stdio_uart(${PROJECT_NAME}_test 1)
+pico_enable_stdio_usb(${PROJECT_NAME}_test 1)
+pico_enable_stdio_uart(${PROJECT_NAME}_test 0)
 
 pico_enable_stdio_usb(test_planet_finder 0)
 pico_enable_stdio_uart(test_planet_finder 1)
diff --git a/pico/inc/devices/eeprom/eeprom.h b/pico/inc/devices/eeprom/eeprom.h
new file mode 100644
index 0000000..fcaed77
--- /dev/null
+++ b/pico/inc/devices/eeprom/eeprom.h
@@ -0,0 +1,24 @@
+#ifndef EEPROM_H
+#define EEPROM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "pico/stdlib.h"
+#include "hardware/i2c.h"
+
+
+void eeprom_init_i2c(i2c_inst_t *i2c, uint sda_pin, uint scl_pin, uint baud, uint32_t write_cycle_max_ms);
+void eeprom_write_byte(uint16_t address, char c);
+char eeprom_read_byte(uint16_t address);
+void eeprom_write_page(uint16_t address, uint8_t *src, size_t size);
+void eeprom_read_page(uint16_t address, uint8_t *dst, size_t size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
\ No newline at end of file
diff --git a/pico/inc/devices/eeprom/logger.hpp b/pico/inc/devices/eeprom/logger.hpp
new file mode 100644
index 0000000..0556d9e
--- /dev/null
+++ b/pico/inc/devices/eeprom/logger.hpp
@@ -0,0 +1,21 @@
+#pragma once
+#include "eeprom.h"
+#include "structs.hpp"
+#include <memory>
+#include <cstring>
+#include <cstdint>
+
+class logger {
+  public:
+    logger(i2c_inst_t* i2c, uint sda_pin, uint scl_pin);
+    bool write(Command& command);
+    bool read(Command& command);
+    void clear_eeprom();
+  private:
+    bool write_page(uint16_t address, const uint8_t* data, size_t size);
+    i2c_inst_t* i2c;
+    uint16_t head = 0xFFFF;
+    uint16_t tail = 0xFFFF;
+};
+
+uint16_t crc16(const uint8_t *data, size_t length);
\ No newline at end of file
diff --git a/pico/src/devices/eeprom/eeprom.c b/pico/src/devices/eeprom/eeprom.c
new file mode 100644
index 0000000..c1a8372
--- /dev/null
+++ b/pico/src/devices/eeprom/eeprom.c
@@ -0,0 +1,132 @@
+#include "pico/stdlib.h"
+#include "hardware/i2c.h"
+#include <stdio.h>
+#include <stdbool.h>
+
+
+#define EEPROM_ADDRESS 0x50
+
+static uint64_t write_cycle_max = 0;
+static absolute_time_t write_init_time;
+
+/**
+ * Checks if the EEPROM write cycle duration has exceeded the maximum allowed time.
+ *
+ * @return true if the EEPROM write cycle duration exceeds the maximum, otherwise false.
+ */
+static inline bool eeprom_write_cycle_check() {
+    if (absolute_time_diff_us(write_init_time, get_absolute_time()) > write_cycle_max) {
+        return true;
+    }
+    return false;
+}
+
+/**
+ * Blocks the execution until the EEPROM write cycle duration falls below the maximum allowed time.
+ * Sleeps until the EEPROM write cycle duration is within the allowed limit.
+ */
+static inline void eeprom_write_cycle_block() {
+    if (!eeprom_write_cycle_check()) {
+        sleep_until(delayed_by_us(write_init_time, write_cycle_max));
+    }
+}
+
+/**
+ * Initializes the EEPROM module using the specified I2C interface, baud rate, and maximum write cycle duration.
+ *
+ * @param i2c              Pointer to the I2C interface (i2c_inst_t) to be used for EEPROM communication.
+ * @param baud             Baud rate for the I2C communication.
+ * @param write_cycle_max_ms Maximum allowed duration for EEPROM write cycles in milliseconds.
+ */
+void eeprom_init_i2c(i2c_inst_t *i2c,uint sda_pin, uint scl_pin, uint baud, uint32_t write_cycle_max_ms) {
+
+    gpio_set_function(sda_pin, GPIO_FUNC_I2C);
+    gpio_set_function(scl_pin, GPIO_FUNC_I2C);
+    gpio_set_dir(sda_pin, GPIO_OUT);
+    gpio_set_dir(scl_pin, GPIO_OUT);
+
+    i2c_init(i2c, baud);
+
+    write_cycle_max = (uint64_t) write_cycle_max_ms * 1000;
+
+    write_init_time = nil_time;
+}
+
+/**
+ * Writes the specified EEPROM address using the I2C interface after ensuring the EEPROM write cycle duration is within limits.
+ *
+ * @param address The address to be written to the EEPROM.
+ */
+void eeprom_write_address(uint16_t address) {
+    eeprom_write_cycle_block();
+
+    uint8_t out[2] = {address >> 8, address}; // shift by 8 not 4...
+    i2c_write_blocking(i2c0, EEPROM_ADDRESS, out, 2, true);
+}
+
+/**
+ * Writes a byte of data to the specified EEPROM address using the I2C interface after ensuring the EEPROM write cycle duration is within limits.
+ *
+ * @param address The address in the EEPROM where the byte will be written.
+ * @param c       The byte of data to be written.
+ */
+void eeprom_write_byte(uint16_t address, char c) {
+    uint8_t out[3] = {address >> 8, address, c};
+
+    eeprom_write_cycle_block();
+
+    i2c_write_blocking(i2c0, EEPROM_ADDRESS, out, 3, false);
+
+    write_init_time = get_absolute_time();
+}
+
+/**
+ * Writes a page of data to the specified EEPROM address using the I2C interface after ensuring the EEPROM write cycle duration is within limits.
+ *
+ * @param address The starting address in the EEPROM where the page will be written.
+ * @param src     Pointer to the source data to be written to the EEPROM.
+ * @param size    Size of the data (page size) to be written.
+ */
+void eeprom_write_page(uint16_t address, uint8_t *src, size_t size) {
+    uint8_t out[size + 2];
+    out[0] = address >> 8; // Upper bits of the address
+    out[1] = address; // Lower bits of the address
+    for (int i = 0; i < size; i++) {
+        out[i + 2] = src[i];
+    }
+
+    eeprom_write_cycle_block(); // Ensure EEPROM write cycle duration is within limits
+
+    i2c_write_blocking(i2c0, EEPROM_ADDRESS, out, size + 2, false);
+
+    write_init_time = get_absolute_time();
+}
+
+/**
+ * Reads a byte of data from the specified EEPROM address using the I2C interface.
+ *
+ * @param address The address in the EEPROM from where the byte will be read.
+ * @return The byte of data read from the EEPROM.
+ */
+char eeprom_read_byte(uint16_t address) {
+    char c = 0;
+
+    eeprom_write_address(address);
+
+    i2c_read_blocking(i2c0, EEPROM_ADDRESS, &c, 1, false);
+
+    return c;
+}
+
+/**
+ * Reads a page of data from the specified EEPROM address into the provided destination buffer using the I2C interface.
+ *
+ * @param address The starting address in the EEPROM from where the page will be read.
+ * @param dst     Pointer to the destination buffer to store the read data.
+ * @param size    Size of the data (page size) to be read.
+ */
+void eeprom_read_page(uint16_t address, uint8_t *dst, size_t size) {
+    eeprom_write_address(address);
+
+    i2c_read_blocking(i2c0, EEPROM_ADDRESS, dst, size, false);
+}
\ No newline at end of file
diff --git a/pico/src/devices/eeprom/logger.cpp b/pico/src/devices/eeprom/logger.cpp
new file mode 100644
index 0000000..eace50e
--- /dev/null
+++ b/pico/src/devices/eeprom/logger.cpp
@@ -0,0 +1,139 @@
+#include "logger.hpp"
+
+#define BAUD_RATE 1000000
+#define WRITE_CYCLE_MAX_MS 10
+#define EEPROM_SIZE 32768
+#define HEAD_ADDR 0
+#define TAIL_ADDR 2
+#define START_ADDR 64
+
+#include "debug.hpp"
+
+logger::logger(i2c_inst_t* i2c, uint sda_pin, uint scl_pin) {
+    this->i2c = i2c;
+    eeprom_init_i2c(i2c, sda_pin, scl_pin, BAUD_RATE, WRITE_CYCLE_MAX_MS);
+
+    eeprom_read_page(HEAD_ADDR, (uint8_t*)&head, sizeof(head));
+    sleep_ms(10);
+    eeprom_read_page(TAIL_ADDR, (uint8_t*)&tail, sizeof(tail));
+    sleep_ms(10);
+
+    if (head == 0xFFFF || tail == 0xFFFF || head >= EEPROM_SIZE || tail >= EEPROM_SIZE) {
+        head = START_ADDR;
+        tail = START_ADDR;
+        eeprom_write_page(HEAD_ADDR, (uint8_t*)&head, sizeof(head));
+        sleep_ms(10);
+        eeprom_write_page(TAIL_ADDR, (uint8_t*)&tail, sizeof(tail));
+        sleep_ms(10);
+    }
+}
+
+bool logger::write(Command& command) {
+    uint8_t buffer[sizeof(Command) + 2];
+
+    memcpy(buffer, &command, sizeof(Command));
+
+    uint16_t crc = crc16(buffer, sizeof(Command));
+    buffer[sizeof(Command)] = crc >> 8;
+    buffer[sizeof(Command) + 1] = crc & 0xFF;
+
+    size_t total_size = sizeof(buffer);
+
+    uint16_t new_head = head + total_size;
+    if (new_head % 64 != 0) {
+        new_head = ((new_head / 64) + 1) * 64;
+    }
+
+    if (new_head >= EEPROM_SIZE) {
+        new_head = START_ADDR;
+    }
+
+    if (new_head == tail) {
+        return false;
+    }
+
+    if (!write_page(head, buffer, total_size)) {
+        return false;
+    }
+
+    head = new_head;
+    eeprom_write_page(HEAD_ADDR, (uint8_t*)&head, sizeof(head));
+    sleep_ms(10);
+
+    return true;
+}
+
+bool logger::read(Command& command) {
+    if (tail == head) {
+        return false;
+    }
+
+    uint8_t buffer[sizeof(Command) + 2];
+
+    eeprom_read_page(tail, buffer, sizeof(buffer));
+    sleep_ms(10);
+
+    uint16_t stored_crc = (buffer[sizeof(Command)] << 8) | buffer[sizeof(Command) + 1];
+    uint16_t other_crc = crc16(buffer, sizeof(Command));
+
+    if (stored_crc != other_crc) {
+        return false;
+    }
+
+    memcpy(&command, buffer, sizeof(Command));
+
+    tail += sizeof(buffer);
+
+    tail = ((tail / 64) + 1) * 64;
+
+    if (tail >= EEPROM_SIZE) {
+        tail = START_ADDR;
+    }
+
+    eeprom_write_page(TAIL_ADDR, (uint8_t*)&tail, sizeof(tail));
+    sleep_ms(10);
+    return true;
+}
+
+void logger::clear_eeprom() {
+    uint8_t empty_data[64] = {0};
+
+    for (uint16_t addr = START_ADDR; addr < EEPROM_SIZE; addr += 64) {
+        if (!write_page(addr, empty_data, sizeof(empty_data))) {
+            DEBUG("Failed to clear EEPROM at address " + std::to_string(addr));
+            sleep_ms(10);
+            return;
+        }
+    }
+
+    head = START_ADDR;
+    tail = START_ADDR;
+    eeprom_write_page(HEAD_ADDR, (uint8_t*)&head, sizeof(head));
+    sleep_ms(10);
+    eeprom_write_page(TAIL_ADDR, (uint8_t*)&tail, sizeof(tail));
+    sleep_ms(10);
+}
+
+bool logger::write_page(uint16_t address, const uint8_t *data, size_t size) {
+    uint8_t buffer[size + 2];
+    buffer[0] = address >> 8;
+    buffer[1] = address & 0xFF;
+    memcpy(buffer + 2, data, size);
+
+    int result = i2c_write_blocking(i2c, 0x50, buffer, size + 2, false);
+    sleep_ms(10);
+    return (result == size + 2);
+}
+
+uint16_t crc16(const uint8_t *data, size_t length) {
+    uint8_t x;
+    uint16_t crc = 0xFFFF;
+
+    while (length--) {
+        x = crc >> 8 ^ *data++;
+        x ^= x >> 4;
+        crc =
+            (crc << 8) ^ (static_cast<uint16_t>(x << 12)) ^ (static_cast<uint16_t>(x << 5)) ^ static_cast<uint16_t>(x);
+    }
+    return crc;
+}
\ No newline at end of file
-- 
GitLab