From 3a07347f48f65be573c1bc35e2384c2ae2e8c0c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20H=C3=BCttel?= Date: Fri, 8 Jan 2021 18:39:54 +0100 Subject: [PATCH] Implement EEPROM and use it for saving the calibration --- stm-firmware/Makefile | 2 +- .../settings/settings-eeprom.h | 32 ++++ .../reflow-controller/settings/settings.h | 2 + .../reflow-controller/settings/spi-eeprom.h | 6 + stm-firmware/main.c | 10 +- stm-firmware/settings/settings-eeprom.c | 140 ++++++++++++++++++ stm-firmware/settings/settings.c | 34 ++++- stm-firmware/settings/spi-eeprom.c | 123 ++++++++++++++- 8 files changed, 339 insertions(+), 10 deletions(-) create mode 100644 stm-firmware/include/reflow-controller/settings/settings-eeprom.h create mode 100644 stm-firmware/settings/settings-eeprom.c diff --git a/stm-firmware/Makefile b/stm-firmware/Makefile index 918285f..99985fe 100644 --- a/stm-firmware/Makefile +++ b/stm-firmware/Makefile @@ -45,7 +45,7 @@ CFILES += rotary-encoder.c button.c CFILES += ui/lcd.c ui/menu.c ui/gui.c CFILES += fatfs/diskio.c fatfs/ff.c fatfs/ffsystem.c fatfs/ffunicode.c fatfs/shimatta_sdio_driver/shimatta_sdio.c CFILES += pid-controller.c oven-driver.c -CFILES += settings/settings.c settings/settings-sd-card.c settings/spi-eeprom.c +CFILES += settings/settings.c settings/settings-sd-card.c settings/spi-eeprom.c settings/settings-eeprom.c CFILES += stm-periph/crc-unit.c CFILES += safety/safety-adc.c safety/safety-controller.c safety/watchdog.c safety/fault.c safety/safety-memory.c safety/stack-check.c CFILES += hw-version-detect.c diff --git a/stm-firmware/include/reflow-controller/settings/settings-eeprom.h b/stm-firmware/include/reflow-controller/settings/settings-eeprom.h new file mode 100644 index 0000000..7fc0153 --- /dev/null +++ b/stm-firmware/include/reflow-controller/settings/settings-eeprom.h @@ -0,0 +1,32 @@ +/* Reflow Oven Controller +* +* Copyright (C) 2020 Mario Hüttel +* +* This file is part of the Reflow Oven Controller Project. +* +* The reflow oven controller is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* The Reflow Oven Control Firmware is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with the reflow oven controller project. +* If not, see . +*/ + +#ifndef __SETTINGS_SETTINGS_EEPROM_H__ +#define __SETTINGS_SETTINGS_EEPROM_H__ + +#include + +bool settings_eeprom_detect_and_prepare(void); + +int settings_eeprom_save_calibration(float sens, float offset, bool active); + +int settings_eeprom_load_calibration(float *sens, float *offset, bool *active); + +#endif /* __SETTINGS_SETTINGS_EEPROM_H__ */ diff --git a/stm-firmware/include/reflow-controller/settings/settings.h b/stm-firmware/include/reflow-controller/settings/settings.h index 8fe4c9c..56faf50 100644 --- a/stm-firmware/include/reflow-controller/settings/settings.h +++ b/stm-firmware/include/reflow-controller/settings/settings.h @@ -53,4 +53,6 @@ int settings_load_calibration(float *sens_dev, float *offset); enum settings_load_result settings_load_pid_oven_parameters(struct oven_pid_settings *settings); +void settings_setup(void); + #endif /* __SETTINGS_SETTINGS_H__ */ diff --git a/stm-firmware/include/reflow-controller/settings/spi-eeprom.h b/stm-firmware/include/reflow-controller/settings/spi-eeprom.h index 515f428..92bcbcb 100644 --- a/stm-firmware/include/reflow-controller/settings/spi-eeprom.h +++ b/stm-firmware/include/reflow-controller/settings/spi-eeprom.h @@ -22,6 +22,8 @@ #define __SETTINGS_SPI_EEPROM_H__ #include +#include + int spi_eeprom_init(); @@ -29,8 +31,12 @@ void spi_eeprom_deinit(); int spi_eeprom_read(uint32_t addr, uint8_t *rx_buff, uint32_t count); +bool spi_eeprom_write_in_progress(void); + int spi_eeprom_write(uint32_t addr, const uint8_t *data, uint32_t count); uint8_t spi_eeprom_read_status_reg(void); +bool spi_eeprom_check_connected(void); + #endif /* __SETTINGS_SPI_EEPROM_H__ */ diff --git a/stm-firmware/main.c b/stm-firmware/main.c index c0c4273..7c90768 100644 --- a/stm-firmware/main.c +++ b/stm-firmware/main.c @@ -46,6 +46,7 @@ #include #include #include + #include static void setup_nvic_priorities(void) @@ -179,7 +180,7 @@ static inline void setup_system(void) loudspeaker_setup(); gui_init(); uart_gpio_config(); - spi_eeprom_init(); + settings_setup(); handle_boot_status(); @@ -210,8 +211,15 @@ int main(void) shellmatta_handle_t shell_handle; int menu_wait_request; uint64_t quarter_sec_timestamp = 0ULL; + setup_system(); + /* Try load the calibration. This will only succeed if there's an EEPROM */ + status = settings_load_calibration(&sens, &offset); + if (!status) { + adc_pt1000_set_resistance_calibration(offset, sens, true); + } + shell_handle = shell_init(write_shell_callback); shell_print_motd(shell_handle); diff --git a/stm-firmware/settings/settings-eeprom.c b/stm-firmware/settings/settings-eeprom.c new file mode 100644 index 0000000..99171bb --- /dev/null +++ b/stm-firmware/settings/settings-eeprom.c @@ -0,0 +1,140 @@ +/* Reflow Oven Controller +* +* Copyright (C) 2020 Mario Hüttel +* +* This file is part of the Reflow Oven Controller Project. +* +* The reflow oven controller is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* The Reflow Oven Control Firmware is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with the reflow oven controller project. +* If not, see . +*/ + +#include +#include +#include +#include + +#define EEPROM_HEADER_MAGIC 0xA5 +#define EEPROM_HEADER_COMP_VER 0xFF + +static const uint8_t expected_header[2] = {EEPROM_HEADER_MAGIC, EEPROM_HEADER_COMP_VER}; + +#define EEPROM_CALIBRATION_BASE_ADDR 0x2 +struct eeprom_calibration_settings { + uint32_t active; + float offset; + float sens_dev; + uint32_t crc; +}; + +static bool check_eeprom_header(void) +{ + uint8_t header[2] = {0}; + + /* Try to read the magic header and the compatible version */ + spi_eeprom_read(0x0, header, 2); + if (memcmp(header, expected_header, 2)) + return false; + else + return true; +} + +static void settings_eeprom_zero() +{ + settings_eeprom_save_calibration(0.0f, 0.0f, false); +} + +bool settings_eeprom_detect_and_prepare(void) +{ + bool eeprom_ready = false;; + + int res; + + crc_unit_init(); + res = spi_eeprom_init(); + if (res) + goto ret_deinit_crc; + + if (!spi_eeprom_check_connected()) + goto ret_deinit_crc; + + if (check_eeprom_header() == false) { + /* Try to write a new header and check it again */ + spi_eeprom_write(0, expected_header, sizeof(expected_header)); + while (spi_eeprom_write_in_progress()); + if (check_eeprom_header() == false) { + goto ret_deinit_crc; + } else { + /* Sucessfully written new header + * Zero out the rest of the settings + */ + settings_eeprom_zero(); + eeprom_ready = true; + } + } else { + eeprom_ready = true; + } + + goto exit; + +ret_deinit_crc: + crc_unit_deinit(); + +exit: + return eeprom_ready; +} + +int settings_eeprom_save_calibration(float sens, float offset, bool active) +{ + int res; + + struct eeprom_calibration_settings sett; + + sett.active = (active ? 0xAABBCCDD : 0); + sett.offset = offset; + sett.sens_dev = sens; + + crc_unit_reset(); + crc_unit_input_array((const uint32_t *)&sett, (sizeof(sett) / 4)-1); + sett.crc = crc_unit_get_crc(); + + res = spi_eeprom_write(EEPROM_CALIBRATION_BASE_ADDR, (uint8_t *)&sett, sizeof(sett)); + + if (res) + return -2; + else + return 0; +} + +int settings_eeprom_load_calibration(float *sens, float *offset, bool *active) +{ + struct eeprom_calibration_settings sett; + int res; + + res = spi_eeprom_read(EEPROM_CALIBRATION_BASE_ADDR, (uint8_t *)&sett, sizeof(sett)); + if (res) + return -2; + + crc_unit_reset(); + crc_unit_input_array((const uint32_t *)&sett, sizeof(sett) / 4 - 1); + if (crc_unit_get_crc() == sett.crc) { + if (sens) + *sens = sett.sens_dev; + if (offset) + *offset = sett.offset; + if (active) + *active = (sett.active ? true : false); + return 0; + } else { + return -1; + } +} diff --git a/stm-firmware/settings/settings.c b/stm-firmware/settings/settings.c index 91db21c..7583e45 100644 --- a/stm-firmware/settings/settings.c +++ b/stm-firmware/settings/settings.c @@ -20,19 +20,47 @@ #include #include +#include +#include + +bool settings_use_eeprom; int settings_save_calibration(float sens_deviation, float offset, bool active) { - /* There is no other configuration location besides the SD card (yet) */ - return sd_card_settings_save_calibration(sens_deviation, offset, active); + if (settings_use_eeprom) + return settings_eeprom_save_calibration(sens_deviation, offset, active); + else + return sd_card_settings_save_calibration(sens_deviation, offset, active); } int settings_load_calibration(float *sens_dev, float *offset) { - return sd_card_settings_try_load_calibration(sens_dev, offset); + bool active; + int res; + + if (settings_use_eeprom) { + res =settings_eeprom_load_calibration(sens_dev, offset, &active); + if (!res && !active) + res = -1; + } else { + res = -1; + } + + if (res) + res = sd_card_settings_try_load_calibration(sens_dev, offset); + + return res; } enum settings_load_result settings_load_pid_oven_parameters(struct oven_pid_settings *settings) { return sd_card_settings_load_pid_oven_parameters(settings); } + +void settings_setup(void) +{ + if (get_pcb_hardware_version() >= HW_REV_V1_3) + settings_use_eeprom = settings_eeprom_detect_and_prepare(); + else + settings_use_eeprom = false; +} diff --git a/stm-firmware/settings/spi-eeprom.c b/stm-firmware/settings/spi-eeprom.c index 7c4e748..6a5ad63 100644 --- a/stm-firmware/settings/spi-eeprom.c +++ b/stm-firmware/settings/spi-eeprom.c @@ -23,10 +23,23 @@ #include #include #include +#include #define EEPROM_SIZE 0x200 #define EEPROM_PAGE_SIZE 16 +#define EEPROM_STATUS_REG_WIP (0x1U) +#define EEPROM_STATUS_REG_WEL (0x1U << 1) +#define EEPROM_STATUS_REG_BP0 (0x1U << 2) +#define EEPROM_STATUS_REG_BP1 (0x1U << 3) + +#define EEPROM_CMD_READ_STATUS 0x05 +#define EEPROM_CMD_WRITE_STATUS 0x01 +#define EEPROM_CMD_READ_DATA 0x3 +#define EEPROM_CMD_WRITE_DATA 0x2 +#define EEPROM_CMD_WREN 0x6 +#define EEPROM_CMD_WRDI 0x4 + static stm_spi_handle eeprom_spi_handle; static void eeprom_cs_activate(void) @@ -63,7 +76,7 @@ int spi_eeprom_init() settings.cs_deactivate = eeprom_cs_deactivate; settings.master = true; settings.msb_first = true; - settings.prescaler = SPI_PRSC_DIV64; + settings.prescaler = SPI_PRSC_DIV16; eeprom_spi_handle = spi_init(&spi_dev, SPI1, &settings); if (eeprom_spi_handle) @@ -91,9 +104,24 @@ uint8_t spi_eeprom_read_status_reg(void) return buff[1]; } +static void spi_eeprom_set_write_enable_latch(bool status) +{ + uint8_t cmd; + + if (status) + cmd = EEPROM_CMD_WREN; + else + cmd = EEPROM_CMD_WRDI; + + (void)spi_transfer(eeprom_spi_handle, &cmd, NULL, 1, true); +} + int spi_eeprom_read(uint32_t addr, uint8_t *rx_buff, uint32_t count) { int ret = 0; + int retry = 250; + uint8_t status_reg; + uint8_t cmd[2]; if (!rx_buff || !count) return -1000; @@ -101,15 +129,100 @@ int spi_eeprom_read(uint32_t addr, uint8_t *rx_buff, uint32_t count) if (addr >= EEPROM_SIZE) return -1001; + do { + status_reg = spi_eeprom_read_status_reg(); + } while ((status_reg & EEPROM_STATUS_REG_WIP) && retry--); + if (status_reg & EEPROM_STATUS_REG_WIP) + return -1; + + cmd[0] = EEPROM_CMD_READ_DATA; + if (addr & (1<<8)) + cmd[0] |= (1U<<3); + cmd[1] = (uint8_t)(addr & 0xFF); eeprom_cs_activate(); - - - + spi_transfer(eeprom_spi_handle, cmd, NULL, 2, false); + spi_transfer(eeprom_spi_handle, NULL, rx_buff, count, false); eeprom_cs_deactivate(); return ret; } -int spi_eeprom_write(uint32_t addr, const uint8_t *data, uint32_t count); +bool spi_eeprom_write_in_progress(void) +{ + uint8_t status_reg; + + status_reg = spi_eeprom_read_status_reg(); + + if (status_reg & EEPROM_STATUS_REG_WIP) + return true; + else + return false; +} + +static void spi_eeprom_do_write_page(uint32_t addr, const uint8_t *data, uint8_t len) +{ + uint8_t cmd[2]; + + /* Wait for the previous write to finish */ + while (spi_eeprom_write_in_progress()); + /* Set the write enable latch */ + spi_eeprom_set_write_enable_latch(true); + + cmd[0] = EEPROM_CMD_WRITE_DATA | ((addr & (1<<8)) ? (1<<4) : 0); + cmd[1] = (uint8_t)(addr & 0xFF); + + eeprom_cs_activate(); + spi_transfer(eeprom_spi_handle, cmd, NULL, 2, false); + spi_transfer(eeprom_spi_handle, data, NULL, len, false); + eeprom_cs_deactivate(); +} + +int spi_eeprom_write(uint32_t addr, const uint8_t *data, uint32_t count) +{ + const uint8_t *ptr = data; + uint32_t transfer_len; + + if (!data || !count) + return -1000; + + if (addr >= EEPROM_SIZE) + return -1001; + + while (count > 0) { + /* Calculate size for current page transfer */ + transfer_len = EEPROM_PAGE_SIZE - (addr % EEPROM_PAGE_SIZE); + if (transfer_len > count) + transfer_len = count; + + spi_eeprom_do_write_page(addr, ptr, transfer_len); + count -= transfer_len; + addr += transfer_len; + ptr += transfer_len; + } + + return 0; +} + +bool spi_eeprom_check_connected(void) +{ + uint8_t status_reg; + + /* Try to set write enable latch */ + spi_eeprom_set_write_enable_latch(true); + + /* Read back status register */ + status_reg = spi_eeprom_read_status_reg(); + if (!(status_reg & EEPROM_STATUS_REG_WEL)) + return false; + + /* Clear the latch */ + spi_eeprom_set_write_enable_latch(false); + /* Read back status register */ + status_reg = spi_eeprom_read_status_reg(); + if ((status_reg & EEPROM_STATUS_REG_WEL)) + return false; + + return true; +}