From e5a30b43095dde136de068fe5648fce84373aee4 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sat, 28 May 2022 13:55:18 -0700 Subject: [PATCH] Persistent memory check value verification, defaulting when fails. (#662) * Make default constructor for touch calibration * Add persistent memory check value and access abstraction. * Add persistent data_t default constructor with reasonable defaults. * serial_format_t default constructor. * Tidy up backlight timeout type. * Add persistent data struct version/checking. * Make range_t functions constexpr. * Move ui_config and functions into class. * Add backlight_config_t struct, separate enable and time settings. --- firmware/application/apps/ui_settings.cpp | 20 +- firmware/application/apps/ui_settings.hpp | 18 +- .../application/apps/ui_touch_calibration.cpp | 2 +- firmware/application/event_m0.cpp | 8 +- firmware/application/portapack.cpp | 3 + firmware/application/serializer.hpp | 8 + firmware/application/touch.cpp | 8 - firmware/application/touch.hpp | 11 +- .../common/portapack_persistent_memory.cpp | 410 +++++++++++++++--- .../common/portapack_persistent_memory.hpp | 77 +++- firmware/common/utility.hpp | 10 +- 11 files changed, 481 insertions(+), 94 deletions(-) diff --git a/firmware/application/apps/ui_settings.cpp b/firmware/application/apps/ui_settings.cpp index b5e033b3..93786c38 100644 --- a/firmware/application/apps/ui_settings.cpp +++ b/firmware/application/apps/ui_settings.cpp @@ -245,13 +245,9 @@ SetUIView::SetUIView(NavigationView& nav) { checkbox_showclock.set_value(!persistent_memory::hide_clock()); checkbox_guireturnflag.set_value(persistent_memory::show_gui_return_icon()); - uint32_t backlight_timer = persistent_memory::config_backlight_timer(); - if (backlight_timer) { - checkbox_bloff.set_value(true); - options_bloff.set_by_value(backlight_timer); - } else { - options_bloff.set_selected_index(0); - } + const auto backlight_config = persistent_memory::config_backlight_timer(); + checkbox_bloff.set_value(backlight_config.timeout_enabled()); + options_bloff.set_by_value(backlight_config.timeout_enum()); if (persistent_memory::clock_with_date()) { options_clockformat.set_selected_index(1); @@ -261,11 +257,11 @@ SetUIView::SetUIView(NavigationView& nav) { button_save.on_select = [&nav, this](Button&) { - if (checkbox_bloff.value()) - persistent_memory::set_config_backlight_timer(options_bloff.selected_index() + 1); - else - persistent_memory::set_config_backlight_timer(0); - + persistent_memory::set_config_backlight_timer({ + (persistent_memory::backlight_timeout_t)options_bloff.selected_index_value(), + checkbox_bloff.value() + }); + if (checkbox_showclock.value()){ if (options_clockformat.selected_index() == 1) persistent_memory::set_clock_with_date(true); diff --git a/firmware/application/apps/ui_settings.hpp b/firmware/application/apps/ui_settings.hpp index d1f3cb63..cbffdaae 100644 --- a/firmware/application/apps/ui_settings.hpp +++ b/firmware/application/apps/ui_settings.hpp @@ -27,6 +27,7 @@ #include "ui_menu.hpp" #include "ui_navigation.hpp" #include "ff.h" +#include "portapack_persistent_memory.hpp" #include @@ -210,6 +211,8 @@ private: SetFrequencyCorrectionModel form_collect(); }; +using portapack::persistent_memory::backlight_timeout_t; + class SetUIView : public View { public: SetUIView(NavigationView& nav); @@ -241,13 +244,14 @@ private: { 52, 7 * 16 + 8 }, 20, { - { "5 seconds", 5 }, - { "15 seconds", 15 }, - { "30 seconds", 30 }, - { "1 minute", 60 }, - { "3 minutes", 180 }, - { "5 minutes", 300 }, - { "10 minutes", 600 } + { "5 seconds", backlight_timeout_t::Timeout5Sec }, + { "15 seconds", backlight_timeout_t::Timeout15Sec }, + { "30 seconds", backlight_timeout_t::Timeout30Sec }, + { "1 minute", backlight_timeout_t::Timeout60Sec }, + { "3 minutes", backlight_timeout_t::Timeout180Sec }, + { "5 minutes", backlight_timeout_t::Timeout300Sec }, + { "10 minutes", backlight_timeout_t::Timeout600Sec }, + { "1 hour", backlight_timeout_t::Timeout3600Sec }, } }; diff --git a/firmware/application/apps/ui_touch_calibration.cpp b/firmware/application/apps/ui_touch_calibration.cpp index a5a7c348..26960106 100644 --- a/firmware/application/apps/ui_touch_calibration.cpp +++ b/firmware/application/apps/ui_touch_calibration.cpp @@ -31,7 +31,7 @@ namespace ui { TouchCalibrationView::TouchCalibrationView( NavigationView& nav ) : nav { nav }, - calibration { touch::default_calibration() } + calibration { touch::Calibration() } { add_children({ &image_calibrate_0, diff --git a/firmware/application/event_m0.cpp b/firmware/application/event_m0.cpp index 8c11b2fa..d15df9fe 100644 --- a/firmware/application/event_m0.cpp +++ b/firmware/application/event_m0.cpp @@ -226,15 +226,17 @@ void EventDispatcher::handle_rtc_tick() { portapack::temperature_logger.second_tick(); - uint32_t backlight_timer = portapack::persistent_memory::config_backlight_timer(); - if (backlight_timer) { - if (portapack::bl_tick_counter == backlight_timer) + const auto backlight_timer = portapack::persistent_memory::config_backlight_timer(); + if (backlight_timer.timeout_enabled()) { + if (portapack::bl_tick_counter == backlight_timer.timeout_seconds()) set_display_sleep(true); else portapack::bl_tick_counter++; } rtc_time::on_tick_second(); + + portapack::persistent_memory::cache::persist(); } ui::Widget* EventDispatcher::touch_widget(ui::Widget* const w, ui::TouchEvent event) { diff --git a/firmware/application/portapack.cpp b/firmware/application/portapack.cpp index 5d51f175..ec4800a7 100644 --- a/firmware/application/portapack.cpp +++ b/firmware/application/portapack.cpp @@ -457,6 +457,9 @@ bool init() { i2c0.start(i2c_config_fast_clock); chThdSleepMilliseconds(10); + /* Cache some configuration data from persistent memory. */ + persistent_memory::cache::init(); + touch::adc::init(); controls_init(); chThdSleepMilliseconds(10); diff --git a/firmware/application/serializer.hpp b/firmware/application/serializer.hpp index 3aedfb24..124dd69e 100644 --- a/firmware/application/serializer.hpp +++ b/firmware/application/serializer.hpp @@ -45,6 +45,14 @@ struct serial_format_t { parity_enum parity; uint8_t stop_bits; order_enum bit_order; + + constexpr serial_format_t() : + data_bits(7), + parity(parity_enum::EVEN), + stop_bits(1), + bit_order(order_enum::LSB_FIRST) + { + } }; size_t symbol_count(const serial_format_t& serial_format); diff --git a/firmware/application/touch.cpp b/firmware/application/touch.cpp index 63c7bb47..681c2518 100644 --- a/firmware/application/touch.cpp +++ b/firmware/application/touch.cpp @@ -76,14 +76,6 @@ ui::Point Calibration::translate(const DigitizerPoint& p) const { return { x_clipped, y_clipped }; } -const Calibration default_calibration() { - /* Values derived from one PortaPack H1 unit. */ - return { - { { { 256, 731 }, { 880, 432 }, { 568, 146 } } }, - { { { 32, 48 }, { 208, 168 }, { 120, 288 } } } - }; -}; - void Manager::feed(const Frame& frame) { // touch_debounce.feed(touch_raw); const auto touch_raw = frame.touch; diff --git a/firmware/application/touch.hpp b/firmware/application/touch.hpp index ebea3c92..7fa1c029 100644 --- a/firmware/application/touch.hpp +++ b/firmware/application/touch.hpp @@ -137,6 +137,15 @@ struct Calibration { { } + constexpr Calibration() : + Calibration( + /* Values derived from one PortaPack H1 unit. */ + { { { 256, 731 }, { 880, 432 }, { 568, 146 } } }, + { { { 32, 48 }, { 208, 168 }, { 120, 288 } } } + ) + { + } + ui::Point translate(const DigitizerPoint& p) const; private: @@ -149,8 +158,6 @@ private: int32_t f; }; -const Calibration default_calibration(); - template class Filter { public: diff --git a/firmware/common/portapack_persistent_memory.cpp b/firmware/common/portapack_persistent_memory.cpp index 35e2ad71..e226b4b7 100644 --- a/firmware/common/portapack_persistent_memory.cpp +++ b/firmware/common/portapack_persistent_memory.cpp @@ -28,7 +28,8 @@ #include "utility.hpp" #include "memory_map.hpp" -using portapack::memory::map::backup_ram; + +#include "crc.hpp" #include #include @@ -67,8 +68,193 @@ using clkout_freq_range_t = range_t; constexpr clkout_freq_range_t clkout_freq_range { 10, 60000 }; constexpr uint32_t clkout_freq_reset_value { 10000 }; +enum data_structure_version_enum : uint32_t { + VERSION_CURRENT = 0x10000002, +}; + +static const uint32_t TOUCH_CALIBRATION_MAGIC = 0x074af82f; + +struct ui_config_t { +private: + enum bits_t { + BacklightTimeoutLSB = 0, + BacklightTimeoutEnable = 3, + ClkoutFreqLSB = 4, + ShowGUIReturnIcon = 20, + LoadAppSettings = 21, + SaveAppSettings = 22, + ShowBiggerQRCode = 23, + DisableTouchscreen = 24, + HideClock = 25, + ClockWithDate = 26, + ClkOutEnabled = 27, + ConfigSpeaker = 28, + StealthMode = 29, + ConfigLogin = 30, + ConfigSplash = 31, + }; + + enum bits_mask_t : uint32_t { + BacklightTimeoutMask = ((1 << 3) - 1) << bits_t::BacklightTimeoutLSB, + ClkoutFreqMask = ((1 << 16) - 1) << bits_t::ClkoutFreqLSB, + }; + + uint32_t values; + + constexpr bool bit_read(const bits_t n) const { + return ((values >> n) & 1) != 0; + } + + constexpr void bit_write(const bits_t n, const bool v) { + if(bit_read(n) != v) { + values ^= 1 << n; + } + } + +public: + backlight_config_t config_backlight_timer() { + const auto timeout_enum = (backlight_timeout_t)((values & bits_mask_t::BacklightTimeoutMask) >> bits_t::BacklightTimeoutLSB); + const bool timeout_enabled = bit_read(bits_t::BacklightTimeoutEnable); + return backlight_config_t(timeout_enum, timeout_enabled); + } + + void set_config_backlight_timer(const backlight_config_t& new_value) { + values = (values & ~bits_mask_t::BacklightTimeoutMask) + | ((new_value.timeout_enum() << bits_t::BacklightTimeoutLSB) & bits_mask_t::BacklightTimeoutMask); + bit_write(bits_t::BacklightTimeoutEnable, new_value.timeout_enabled()); + } + + constexpr uint32_t clkout_freq() { + uint32_t freq = (values & bits_mask_t::ClkoutFreqMask) >> bits_t::ClkoutFreqLSB; + if(freq < clkout_freq_range.minimum || freq > clkout_freq_range.maximum) { + values = (values & ~bits_mask_t::ClkoutFreqMask) | (clkout_freq_reset_value << bits_t::ClkoutFreqLSB); + return clkout_freq_reset_value; + } + else { + return freq; + } + } + + constexpr void set_clkout_freq(uint32_t freq) { + values = (values & ~bits_mask_t::ClkoutFreqMask) | (clkout_freq_range.clip(freq) << bits_t::ClkoutFreqLSB); + } + + // ui_config is an uint32_t var storing information bitwise + // bits 0-2 store the backlight timer + // bits 4-19 (16 bits) store the clkout frequency + // bits 21-31 store the different single bit configs depicted below + // bit 20 store the display state of the gui return icon, hidden (0) or shown (1) + + constexpr bool show_gui_return_icon() const { // add return icon in touchscreen menue + return bit_read(bits_t::ShowGUIReturnIcon); + } + + constexpr void set_gui_return_icon(bool v) { + bit_write(bits_t::ShowGUIReturnIcon, v); + } + + constexpr bool load_app_settings() const { // load (last saved) app settings on startup of app + return bit_read(bits_t::LoadAppSettings); + } + + constexpr void set_load_app_settings(bool v) { + bit_write(bits_t::LoadAppSettings, v); + } + + constexpr bool save_app_settings() const { // save app settings when closing app + return bit_read(bits_t::SaveAppSettings); + } + + constexpr void set_save_app_settings(bool v) { + bit_write(bits_t::SaveAppSettings, v); + } + + constexpr bool show_bigger_qr_code() const { // show bigger QR code + return bit_read(bits_t::ShowBiggerQRCode); + } + + constexpr void set_show_bigger_qr_code(bool v) { + bit_write(bits_t::ShowBiggerQRCode, v); + } + + constexpr bool disable_touchscreen() const { // Option to disable touch screen + return bit_read(bits_t::DisableTouchscreen); + } + + constexpr void set_disable_touchscreen(bool v) { + bit_write(bits_t::DisableTouchscreen, v); + } + + constexpr bool hide_clock() const { // clock hidden from main menu + return bit_read(bits_t::HideClock); + } + + constexpr void set_clock_hidden(bool v) { + bit_write(bits_t::HideClock, v); + } + + constexpr bool clock_with_date() const { // show clock with date, if not hidden + return bit_read(bits_t::ClockWithDate); + } + + constexpr void set_clock_with_date(bool v) { + bit_write(bits_t::ClockWithDate, v); + } + + constexpr bool clkout_enabled() const { + return bit_read(bits_t::ClkOutEnabled); + } + + constexpr void set_clkout_enabled(bool v) { + bit_write(bits_t::ClkOutEnabled, v); + } + + constexpr bool config_speaker() const { + return bit_read(bits_t::ConfigSpeaker); + } + + constexpr void set_config_speaker(bool v) { + bit_write(bits_t::ConfigSpeaker, v); + } + + constexpr bool stealth_mode() const { + return bit_read(bits_t::StealthMode); + } + + constexpr void set_stealth_mode(bool v) { + bit_write(bits_t::StealthMode, v); + } + + constexpr bool config_login() const { + return bit_read(bits_t::ConfigLogin); + } + + constexpr void set_config_login(bool v) { + bit_write(bits_t::ConfigLogin, v); + } + + constexpr bool config_splash() const { + return bit_read(bits_t::ConfigSplash); + } + + constexpr void set_config_splash(bool v) { + bit_write(bits_t::ConfigSplash, v); + } + + constexpr ui_config_t() : + values( + (1 << ConfigSplash) + | (1 << ConfigSpeaker) + | (clkout_freq_reset_value << ClkoutFreqLSB) + | (7 << BacklightTimeoutLSB) + ) + { + } +}; + /* struct must pack the same way on M4 and M0 cores. */ struct data_t { + data_structure_version_enum structure_version; int64_t tuned_frequency; int32_t correction_ppb; uint32_t touch_calibration_magic; @@ -89,7 +275,7 @@ struct data_t { uint32_t playdead_sequence; // UI - uint32_t ui_config; + ui_config_t ui_config; uint32_t pocsag_last_address; uint32_t pocsag_ignore_address; @@ -98,11 +284,142 @@ struct data_t { // Hardware uint32_t hardware_config; + + constexpr data_t() : + structure_version(data_structure_version_enum::VERSION_CURRENT), + tuned_frequency(tuned_frequency_reset_value), + correction_ppb(ppb_reset_value), + touch_calibration_magic(TOUCH_CALIBRATION_MAGIC), + touch_calibration(touch::Calibration()), + + modem_def_index(0), // TODO: Unused? + serial_format(), + modem_bw(15000), // TODO: Unused? + afsk_mark_freq(afsk_mark_reset_value), + afsk_space_freq(afsk_space_reset_value), + modem_baudrate(modem_baudrate_reset_value), + modem_repeat(modem_repeat_reset_value), + + playdead_magic(), // TODO: Unused? + playing_dead(), // TODO: Unused? + playdead_sequence(), // TODO: Unused? + + ui_config(), + + pocsag_last_address(0), // TODO: A better default? + pocsag_ignore_address(0), // TODO: A better default? + + tone_mix(tone_mix_reset_value), + + hardware_config(0) + { + } }; -static_assert(sizeof(data_t) <= backup_ram.size(), "Persistent memory structure too large for VBAT-maintained region"); +struct backup_ram_t { +private: + uint32_t regfile[63]; + uint32_t check_value; -static data_t* const data = reinterpret_cast(backup_ram.base()); + static void copy(const backup_ram_t& src, backup_ram_t& dst) { + for(size_t i=0; i<63; i++) { + dst.regfile[i] = src.regfile[i]; + } + dst.check_value = src.check_value; + } + + static void copy_from_data_t(const data_t& src, backup_ram_t& dst) { + const uint32_t* const src_words = (uint32_t*)&src; + const size_t word_count = (sizeof(data_t) + 3) / 4; + for(size_t i=0; i<63; i++) { + if(i crc { 0x04c11db7, 0xffffffff, 0xffffffff }; + for(size_t i=0; i<63; i++) { + const auto word = regfile[i]; + crc.process_byte((word >> 0) & 0xff); + crc.process_byte((word >> 8) & 0xff); + crc.process_byte((word >> 16) & 0xff); + crc.process_byte((word >> 24) & 0xff); + } + return crc.checksum(); + } + +public: + /* default constructor */ + backup_ram_t() : + check_value(0) + { + const data_t defaults = data_t(); + copy_from_data_t(defaults, *this); + } + + /* copy-assignment operator */ + backup_ram_t& operator=(const backup_ram_t& src) { + copy(src, *this); + return *this; + } + + /* Calculate a check value from `this`, and check against + * the stored value. + */ + bool is_valid() { + return compute_check_value() == check_value; + } + + /* Assuming `this` contains valid data, update the checksum + * and copy to the destination. + */ + void persist_to(backup_ram_t& dst) { + check_value = compute_check_value(); + copy(*this, dst); + } +}; + +static_assert(sizeof(backup_ram_t) == memory::map::backup_ram.size()); +static_assert(sizeof(data_t) <= sizeof(backup_ram_t) - sizeof(uint32_t)); + +static backup_ram_t* const backup_ram = reinterpret_cast(memory::map::backup_ram.base()); + +static backup_ram_t cached_backup_ram; +static data_t* const data = reinterpret_cast(&cached_backup_ram); + +namespace cache { + +void defaults() { + cached_backup_ram = backup_ram_t(); +} + +void init() { + if(backup_ram->is_valid()) { + // Copy valid persistent data into cache. + cached_backup_ram = *backup_ram; + + // Check that structure data we copied into cache is the expected + // version. If not, initialize cache to defaults. + if(data->structure_version != data_structure_version_enum::VERSION_CURRENT) { + // TODO: Can provide version-to-version upgrade functions here, + // if we want to be fancy. + defaults(); + } + } else { + // Copy defaults into cache. + defaults(); + } +} + +void persist() { + cached_backup_ram.persist_to(*backup_ram); +} + +} /* namespace cache */ rf::Frequency tuned_frequency() { rf::tuning_range.reset_if_outside(data->tuned_frequency, tuned_frequency_reset_value); @@ -124,16 +441,14 @@ void set_correction_ppb(const ppb_t new_value) { portapack::clock_manager.set_reference_ppb(clipped_value); } -static constexpr uint32_t touch_calibration_magic = 0x074af82f; - void set_touch_calibration(const touch::Calibration& new_value) { data->touch_calibration = new_value; - data->touch_calibration_magic = touch_calibration_magic; + data->touch_calibration_magic = TOUCH_CALIBRATION_MAGIC; } const touch::Calibration& touch_calibration() { - if( data->touch_calibration_magic != touch_calibration_magic ) { - set_touch_calibration(touch::default_calibration()); + if( data->touch_calibration_magic != TOUCH_CALIBRATION_MAGIC ) { + set_touch_calibration(touch::Calibration()); } return data->touch_calibration; } @@ -200,122 +515,116 @@ void set_serial_format(const serial_format_t new_value) { data->serial_format = new_value; } -// ui_config is an uint32_t var storing information bitwise -// bits 0-2 store the backlight timer -// bits 4-19 (16 bits) store the clkout frequency -// bits 21-31 store the different single bit configs depicted below -// bit 20 store the display state of the gui return icon, hidden (0) or shown (1) - -bool show_gui_return_icon(){ // add return icon in touchscreen menue -return data->ui_config & (1 << 20); +bool show_gui_return_icon() { // add return icon in touchscreen menue + return data->ui_config.show_gui_return_icon(); } bool load_app_settings() { // load (last saved) app settings on startup of app - return data->ui_config & (1 << 21); + return data->ui_config.load_app_settings(); } bool save_app_settings() { // save app settings when closing app - return data->ui_config & (1 << 22); + return data->ui_config.save_app_settings(); } bool show_bigger_qr_code() { // show bigger QR code - return data->ui_config & (1 << 23); + return data->ui_config.show_bigger_qr_code(); } bool disable_touchscreen() { // Option to disable touch screen - return data->ui_config & (1 << 24); + return data->ui_config.disable_touchscreen(); } bool hide_clock() { // clock hidden from main menu - return data->ui_config & (1 << 25); + return data->ui_config.hide_clock(); } bool clock_with_date() { // show clock with date, if not hidden - return data->ui_config & (1 << 26); + return data->ui_config.clock_with_date(); } bool clkout_enabled() { - return data->ui_config & (1 << 27); + return data->ui_config.clkout_enabled(); } bool config_speaker() { - return data->ui_config & (1 << 28); + return data->ui_config.config_speaker(); } + bool stealth_mode() { - return data->ui_config & (1 << 29); + return data->ui_config.stealth_mode(); } bool config_login() { - return data->ui_config & (1 << 30); + return data->ui_config.config_login(); } bool config_splash() { - return data->ui_config & (1 << 31); + return data->ui_config.config_splash(); } uint8_t config_cpld() { return data->hardware_config; } -uint32_t config_backlight_timer() { - const uint32_t timer_seconds[8] = { 0, 5, 15, 30, 60, 180, 300, 600 }; - return timer_seconds[data->ui_config & 7]; //first three bits, 8 possible values +backlight_config_t config_backlight_timer() { + return data->ui_config.config_backlight_timer(); } void set_gui_return_icon(bool v) { - data->ui_config = (data->ui_config & ~(1 << 20)) | (v << 20); + data->ui_config.set_gui_return_icon(v); } void set_load_app_settings(bool v) { - data->ui_config = (data->ui_config & ~(1 << 21)) | (v << 21); + data->ui_config.set_load_app_settings(v); } void set_save_app_settings(bool v) { - data->ui_config = (data->ui_config & ~(1 << 22)) | (v << 22); + data->ui_config.set_save_app_settings(v); } void set_show_bigger_qr_code(bool v) { - data->ui_config = (data->ui_config & ~(1 << 23)) | (v << 23); + data->ui_config.set_show_bigger_qr_code(v); } void set_disable_touchscreen(bool v) { - data->ui_config = (data->ui_config & ~(1 << 24)) | (v << 24); + data->ui_config.set_disable_touchscreen(v); } void set_clock_hidden(bool v) { - data->ui_config = (data->ui_config & ~(1 << 25)) | (v << 25); + data->ui_config.set_clock_hidden(v); } void set_clock_with_date(bool v) { - data->ui_config = (data->ui_config & ~(1 << 26)) | (v << 26); + data->ui_config.set_clock_with_date(v); } void set_clkout_enabled(bool v) { - data->ui_config = (data->ui_config & ~(1 << 27)) | (v << 27); + data->ui_config.set_clkout_enabled(v); } void set_config_speaker(bool v) { - data->ui_config = (data->ui_config & ~(1 << 28)) | (v << 28); + data->ui_config.set_config_speaker(v); } void set_stealth_mode(bool v) { - data->ui_config = (data->ui_config & ~(1 << 29)) | (v << 29); + data->ui_config.set_stealth_mode(v); } void set_config_login(bool v) { - data->ui_config = (data->ui_config & ~(1 << 30)) | (v << 30); + data->ui_config.set_config_login(v); } void set_config_splash(bool v) { - data->ui_config = (data->ui_config & ~(1 << 31)) | (v << 31); + data->ui_config.set_config_splash(v); } void set_config_cpld(uint8_t i) { data->hardware_config = i; } -void set_config_backlight_timer(uint32_t i) { - data->ui_config = (data->ui_config & ~7) | (i & 7); +void set_config_backlight_timer(const backlight_config_t& new_value) { + data->ui_config.set_config_backlight_timer(new_value); } /*void set_config_textentry(uint8_t new_value) { @@ -347,18 +656,11 @@ void set_pocsag_ignore_address(uint32_t address) { } uint32_t clkout_freq() { - uint16_t freq = (data->ui_config & 0x000FFFF0) >> 4; - if(freq < clkout_freq_range.minimum || freq > clkout_freq_range.maximum) { - data->ui_config = (data->ui_config & ~0x000FFFF0) | clkout_freq_reset_value << 4; - return clkout_freq_reset_value; - } - else { - return freq; - } + return data->ui_config.clkout_freq(); } void set_clkout_freq(uint32_t freq) { - data->ui_config = (data->ui_config & ~0x000FFFF0) | (clkout_freq_range.clip(freq) << 4); + data->ui_config.set_clkout_freq(freq); } diff --git a/firmware/common/portapack_persistent_memory.hpp b/firmware/common/portapack_persistent_memory.hpp index 03d5cc91..f498bcae 100644 --- a/firmware/common/portapack_persistent_memory.hpp +++ b/firmware/common/portapack_persistent_memory.hpp @@ -25,6 +25,8 @@ #include +#include "optional.hpp" + #include "rf_path.hpp" #include "touch.hpp" #include "modems.hpp" @@ -36,6 +38,77 @@ using namespace serializer; namespace portapack { namespace persistent_memory { +enum backlight_timeout_t { + Timeout5Sec = 0, + Timeout15Sec = 1, + Timeout30Sec = 2, + Timeout60Sec = 3, + Timeout180Sec = 4, + Timeout300Sec = 5, + Timeout600Sec = 6, + Timeout3600Sec = 7, +}; + +struct backlight_config_t { +public: + backlight_config_t() : + _timeout_enum(backlight_timeout_t::Timeout600Sec), + _timeout_enabled(false) + { + } + + backlight_config_t( + backlight_timeout_t timeout_enum, + bool timeout_enabled + ) : + _timeout_enum(timeout_enum), + _timeout_enabled(timeout_enabled) + { + } + + bool timeout_enabled() const { + return _timeout_enabled; + } + + backlight_timeout_t timeout_enum() const { + return _timeout_enum; + } + + uint32_t timeout_seconds() const { + switch(timeout_enum()) { + case Timeout5Sec: return 5; + case Timeout15Sec: return 15; + case Timeout30Sec: return 30; + case Timeout60Sec: return 60; + case Timeout180Sec: return 180; + case Timeout300Sec: return 300; + default: + case Timeout600Sec: return 600; + case Timeout3600Sec: return 3600; + } + } + +private: + backlight_timeout_t _timeout_enum; + bool _timeout_enabled; +}; + +namespace cache { + +/* Set values in cache to sensible defaults. */ +void defaults(); + +/* Load cached settings from values in persistent RAM, replacing with defaults + * if persistent RAM contents appear to be invalid. */ +void init(); + +/* Calculate a check value for cached settings, and copy the check value and + * settings into persistent RAM. Intended to be called periodically to update + * persistent settings with current settings. */ +void persist(); + +} /* namespace cache */ + using ppb_t = int32_t; rf::Frequency tuned_frequency(); @@ -86,7 +159,7 @@ bool hide_clock(); bool clock_with_date(); bool config_login(); bool config_speaker(); -uint32_t config_backlight_timer(); +backlight_config_t config_backlight_timer(); bool disable_touchscreen(); void set_gui_return_icon(bool v); @@ -98,7 +171,7 @@ void set_clock_hidden(bool v); void set_clock_with_date(bool v); void set_config_login(bool v); void set_config_speaker(bool v); -void set_config_backlight_timer(uint32_t i); +void set_config_backlight_timer(const backlight_config_t& new_value); void set_disable_touchscreen(bool v); //uint8_t ui_config_textentry(); diff --git a/firmware/common/utility.hpp b/firmware/common/utility.hpp index 60d25fe0..2cd3fcc6 100644 --- a/firmware/common/utility.hpp +++ b/firmware/common/utility.hpp @@ -95,27 +95,27 @@ struct range_t { const T minimum; const T maximum; - const T& clip(const T& value) const { + constexpr const T& clip(const T& value) const { return std::max(std::min(value, maximum), minimum); } - void reset_if_outside(T& value, const T& reset_value) const { + constexpr void reset_if_outside(T& value, const T& reset_value) const { if( (value < minimum ) || (value > maximum ) ) { value = reset_value; } } - bool below_range(const T& value) const { + constexpr bool below_range(const T& value) const { return value < minimum; } - bool contains(const T& value) const { + constexpr bool contains(const T& value) const { // TODO: Subtle gotcha here! Range test doesn't include maximum! return (value >= minimum) && (value < maximum); } - bool out_of_range(const T& value) const { + constexpr bool out_of_range(const T& value) const { // TODO: Subtle gotcha here! Range test in contains() doesn't include maximum! return !contains(value); }