From 55b162b0a0de7d5d243cf7d661c101dc3613b50d Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Fri, 11 Sep 2026 16:01:41 +0200 Subject: [PATCH] feat: Persistenz, Einschaltverhalten, Werksreset und Watchdog --- CMakeLists.txt | 2 ++ src/app_main.c | 74 ++++++++++++++++++++++++++++++++++---- src/config/nl_flash_w600.c | 32 ++++++++++++++--- 3 files changed, 96 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bcf99cc..5978588 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,8 @@ set(NL_FW_SOURCES src/light/nl_control.c src/light/nl_boot_state.c src/config/nl_settings.c + src/config/nl_state_log.c + src/config/nl_reset_counter.c src/config/nl_flash_w600.c src/drivers/nl_led.c src/drivers/nl_pwm_leds.c diff --git a/src/app_main.c b/src/app_main.c index 5eeabf3..9c7ef10 100644 --- a/src/app_main.c +++ b/src/app_main.c @@ -1,9 +1,14 @@ #include #include "wm_include.h" +#include "wm_watchdog.h" #include "board/nl_board.h" +#include "config/nl_flash.h" +#include "config/nl_flash_layout.h" +#include "config/nl_reset_counter.h" #include "config/nl_settings.h" +#include "config/nl_state_log.h" #include "drivers/nl_led.h" #include "light/nl_boot_state.h" #include "light/nl_control.h" @@ -32,12 +37,24 @@ nl_settings_t *nl_app_settings(void) { return &s_settings; } int nl_app_save_settings(void) { - return 0; /* persistence follows in task 12 */ + int r; + nl_app_lock(); + r = nl_settings_save(&s_settings); + nl_app_unlock(); + return r; } void nl_app_factory_reset(void) { - printf("factory reset: not implemented yet\r\n"); + printf("factory reset\r\n"); + nl_app_lock(); + nl_engine_blink(3); + nl_app_unlock(); + tls_os_time_delay(2 * HZ); /* 3 blinks = 1.8 s, the light task keeps running */ + nl_settings_erase(); + nl_state_log_erase(); + nl_resetcnt_clear(); + tls_sys_reset(); } static void led_out(const uint8_t ch[NL_CH_COUNT], void *ctx) @@ -65,29 +82,72 @@ static void light_task(void *arg) static void app_task(void *arg) { const nl_board_t *board = nl_board_get(); + uint32_t base = nl_flash_region_base(); nl_engine_cfg_t cfg; - nl_light_state_t initial; + nl_light_state_t last, initial, pending; + bool have_last, counter_cleared = false; + uint8_t boots; + uint32_t boot_ms, beat_seen; (void)arg; - nl_settings_defaults(&s_settings); + nl_resetcnt_init(base + NL_FL_RESETCNT); + boots = nl_resetcnt_boot(); + nl_settings_init(base + NL_FL_SETTINGS_A, base + NL_FL_SETTINGS_B); + nl_settings_load(&s_settings); + nl_state_log_init(base + NL_FL_STATE_0, base + NL_FL_STATE_1); + have_last = nl_state_log_load(&last); + printf("boot #%u, settings: pob=%u fade=%u/%u, last state: %s\r\n", boots, + s_settings.power_on_behaviour, s_settings.fade_in_ms, s_settings.fade_out_ms, + have_last ? "yes" : "no"); nl_led_init(board); cfg.ct_min = board->ct_min; cfg.ct_max = board->ct_max; cfg.output = led_out; cfg.ctx = NULL; + initial = nl_boot_state((nl_pob_t)s_settings.power_on_behaviour, have_last ? &last : NULL, board); nl_app_lock(); nl_engine_init(&cfg); nl_ctrl_init(board, s_settings.fade_in_ms, s_settings.fade_out_ms); - initial = nl_boot_default_state(board); nl_ctrl_boot(&initial); nl_app_unlock(); tls_os_task_create(NULL, "nl_light", light_task, NULL, (u8 *)s_light_stack, sizeof(s_light_stack), NL_LIGHT_PRIO, 0); + + if (boots >= NL_FACTORY_RESET_BOOTS) + nl_app_factory_reset(); /* does not return */ + + tls_watchdog_init(20u * 1000u * 1000u); /* 20 s */ nl_console_start(); - for (;;) - tls_os_time_delay(HZ); + boot_ms = nl_app_now_ms(); + beat_seen = s_light_beat; + for (;;) { + uint32_t now; + bool due; + + tls_os_time_delay(HZ / 10); /* 100 ms */ + now = nl_app_now_ms(); + + if (!counter_cleared && now - boot_ms >= NL_RESETCNT_CLEAR_MS) { + nl_resetcnt_clear(); + counter_cleared = true; + } + + nl_app_lock(); + due = nl_ctrl_persist_due(now, &pending); + nl_app_unlock(); + if (due && nl_state_log_append(&pending) == 0) { + nl_app_lock(); + nl_ctrl_persisted(&pending); + nl_app_unlock(); + } + + if (s_light_beat != beat_seen) { + beat_seen = s_light_beat; + tls_watchdog_clr(); + } + } } void UserMain(void) diff --git a/src/config/nl_flash_w600.c b/src/config/nl_flash_w600.c index 78a44e3..20d7518 100644 --- a/src/config/nl_flash_w600.c +++ b/src/config/nl_flash_w600.c @@ -1,7 +1,29 @@ +#include "wm_include.h" +#include "wm_flash_map.h" +#include "wm_internal_flash.h" + #include "config/nl_flash.h" -/* Temporary stub, replaced by the real W600 implementation in task 12. */ -int nl_flash_read(uint32_t addr, void *buf, uint32_t len) { (void)addr; (void)buf; (void)len; return -1; } -int nl_flash_write(uint32_t addr, const void *buf, uint32_t len) { (void)addr; (void)buf; (void)len; return -1; } -int nl_flash_erase_sector(uint32_t addr) { (void)addr; return -1; } -uint32_t nl_flash_region_base(void) { return 0; } +int nl_flash_read(uint32_t addr, void *buf, uint32_t len) +{ + return tls_fls_read(addr, (u8 *)buf, len) == TLS_FLS_STATUS_OK ? 0 : -1; +} + +int nl_flash_write(uint32_t addr, const void *buf, uint32_t len) +{ + /* tls_fls_write only erases when the target is not 0xFF; the stores always write erased areas. */ + return tls_fls_write(addr, (u8 *)buf, len) == TLS_FLS_STATUS_OK ? 0 : -1; +} + +int nl_flash_erase_sector(uint32_t addr) +{ + if (addr % NL_FLASH_SECTOR_SIZE) + return -1; + return tls_fls_erase(addr / INSIDE_FLS_SECTOR_SIZE) == TLS_FLS_STATUS_OK ? 0 : -1; +} + +uint32_t nl_flash_region_base(void) +{ + /* The first user sector is TLS_FLASH_PARAM_DEFAULT (SDK), ninlight starts one sector later. */ + return USER_ADDR_START + NL_FLASH_SECTOR_SIZE; +}