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