Implement EEPROM and use it for saving the calibration

This commit is contained in:
Mario Hüttel 2021-01-08 18:39:54 +01:00
parent 0e233a257c
commit 3a07347f48
8 changed files with 339 additions and 10 deletions

View File

@ -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

View File

@ -0,0 +1,32 @@
/* Reflow Oven Controller
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef __SETTINGS_SETTINGS_EEPROM_H__
#define __SETTINGS_SETTINGS_EEPROM_H__
#include <stdbool.h>
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__ */

View File

@ -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__ */

View File

@ -22,6 +22,8 @@
#define __SETTINGS_SPI_EEPROM_H__
#include <stdint.h>
#include <stdbool.h>
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__ */

View File

@ -46,6 +46,7 @@
#include <reflow-controller/safety/safety-memory.h>
#include <reflow-controller/safety/fault.h>
#include <reflow-controller/updater/updater.h>
#include <reflow-controller/settings/spi-eeprom.h>
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);

View File

@ -0,0 +1,140 @@
/* Reflow Oven Controller
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <reflow-controller/settings/settings-eeprom.h>
#include <reflow-controller/settings/spi-eeprom.h>
#include <stm-periph/crc-unit.h>
#include <string.h>
#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;
}
}

View File

@ -20,19 +20,47 @@
#include <reflow-controller/settings/settings.h>
#include <reflow-controller/settings/settings-sd-card.h>
#include <reflow-controller/settings/settings-eeprom.h>
#include <reflow-controller/hw-version-detect.h>
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) */
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;
}

View File

@ -23,10 +23,23 @@
#include <reflow-controller/periph-config/spi-eeprom-hwcfg.h>
#include <stm-periph/rcc-manager.h>
#include <stm-periph/stm32-gpio-macros.h>
#include <stddef.h>
#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;
}