diff --git a/firmware/application/apps/ui_settings.cpp b/firmware/application/apps/ui_settings.cpp index d0668342..228f9936 100644 --- a/firmware/application/apps/ui_settings.cpp +++ b/firmware/application/apps/ui_settings.cpp @@ -3,6 +3,7 @@ * Copyright (C) 2016 Furrtek * Copyright (C) 2023 gullradriel, Nilorea Studio Inc. * Copyright (C) 2023 Kyle Reed + * Copyright (C) 2024 Mark Thompson * Copyleft (ɔ) 2024 zxkmm under GPL license * * This file is part of PortaPack. @@ -680,13 +681,16 @@ void SetQRCodeView::focus() { SetEncoderDialView::SetEncoderDialView(NavigationView& nav) { add_children({&labels, &field_encoder_dial_sensitivity, + &field_encoder_rate_multiplier, &button_save, &button_cancel}); - field_encoder_dial_sensitivity.set_by_value(pmem::config_encoder_dial_sensitivity()); + field_encoder_dial_sensitivity.set_by_value(pmem::encoder_dial_sensitivity()); + field_encoder_rate_multiplier.set_value(pmem::encoder_rate_multiplier()); button_save.on_select = [&nav, this](Button&) { pmem::set_encoder_dial_sensitivity(field_encoder_dial_sensitivity.selected_index_value()); + pmem::set_encoder_rate_multiplier(field_encoder_rate_multiplier.value()); nav.pop(); }; diff --git a/firmware/application/apps/ui_settings.hpp b/firmware/application/apps/ui_settings.hpp index 4c858e76..0f50e101 100644 --- a/firmware/application/apps/ui_settings.hpp +++ b/firmware/application/apps/ui_settings.hpp @@ -3,6 +3,7 @@ * Copyright (C) 2016 Furrtek * Copyright (C) 2023 gullradriel, Nilorea Studio Inc. * Copyright (C) 2023 Kyle Reed + * Copyright (C) 2024 Mark Thompson * Copyleft (ɔ) 2024 zxkmm under GPL license * * This file is part of PortaPack. @@ -555,6 +556,7 @@ class SetQRCodeView : public View { }; using portapack::persistent_memory::encoder_dial_sensitivity; +using portapack::persistent_memory::encoder_rate_multiplier; class SetEncoderDialView : public View { public: @@ -566,18 +568,30 @@ class SetEncoderDialView : public View { private: Labels labels{ - {{1 * 8, 1 * 16}, "Adjusts how many steps to", Color::light_grey()}, - {{1 * 8, 2 * 16}, "change the encoder value.", Color::light_grey()}, - {{2 * 8, 4 * 16}, "Dial sensitivity:", Color::light_grey()}, + {{1 * 8, 1 * 16}, "Adjusts sensitivity to dial", Color::light_grey()}, + {{1 * 8, 2 * 16}, "rotation position (number of", Color::light_grey()}, + {{1 * 8, 3 * 16}, "steps per full rotation):", Color::light_grey()}, + {{2 * 8, 5 * 16}, "Dial sensitivity:", Color::light_grey()}, + {{1 * 8, 8 * 16}, "Adjusts sensitivity to dial", Color::light_grey()}, + {{1 * 8, 9 * 16}, "rotation rate (default 1", Color::light_grey()}, + {{1 * 8, 10 * 16}, "means no rate dependency):", Color::light_grey()}, + {{3 * 8, 12 * 16}, "Rate multiplier:", Color::light_grey()}, }; OptionsField field_encoder_dial_sensitivity{ - {20 * 8, 4 * 16}, + {20 * 8, 5 * 16}, 6, {{"LOW", encoder_dial_sensitivity::DIAL_SENSITIVITY_LOW}, {"NORMAL", encoder_dial_sensitivity::DIAL_SENSITIVITY_NORMAL}, {"HIGH", encoder_dial_sensitivity::DIAL_SENSITIVITY_HIGH}}}; + NumberField field_encoder_rate_multiplier{ + {20 * 8, 12 * 16}, + 2, + {1, 10}, + 1, + ' '}; + Button button_save{ {2 * 8, 16 * 16, 12 * 8, 32}, "Save"}; diff --git a/firmware/application/hw/debounce.cpp b/firmware/application/hw/debounce.cpp index 85ba6b6d..31d81237 100644 --- a/firmware/application/hw/debounce.cpp +++ b/firmware/application/hw/debounce.cpp @@ -24,6 +24,9 @@ #include "utility.hpp" +#include "portapack.hpp" +#include "portapack_persistent_memory.hpp" + uint8_t Debounce::state() { uint8_t v = state_to_report_; simulated_pulse_ = false; @@ -154,6 +157,10 @@ uint8_t EncoderDebounce::state() { return state_; } +uint8_t EncoderDebounce::rotation_rate() { + return last_rotation_rate_; +} + // Returns TRUE if encoder position phase bits changed (after debouncing) bool EncoderDebounce::feed(const uint8_t phase_bits) { history_ = (history_ << 2) | phase_bits; @@ -164,14 +171,21 @@ bool EncoderDebounce::feed(const uint8_t phase_bits) { // But, checking for equal seems to cause issues with at least 1 user's encoder, so we're treating the input // as "stable" if at least ONE input bit is consistent for 4 ticks... uint8_t diff = (history_ ^ expected_stable_history); - if ((diff == 0) || ((diff & 0b01010101) == 0) || ((diff & 0b10101010) == 0)) { + if (((diff & 0b01010101) == 0) || ((diff & 0b10101010) == 0)) { // Has the debounced input value changed? if (state_ != phase_bits) { state_ = phase_bits; + + // Rate multiplier is for larger delta increments when dial is rotated rapidly. + last_rotation_rate_ = rotation_rate_downcounter_; + rotation_rate_downcounter_ = portapack::persistent_memory::encoder_rate_multiplier(); return true; } } - // Unstable input, or no change + // Unstable input, or no change. + // Decrement rotation rate detector once per timer tick. + if (rotation_rate_downcounter_ > 1) + rotation_rate_downcounter_--; return false; } diff --git a/firmware/application/hw/debounce.hpp b/firmware/application/hw/debounce.hpp index 6bf1033a..1d042b1d 100644 --- a/firmware/application/hw/debounce.hpp +++ b/firmware/application/hw/debounce.hpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2024 Mark Thompson * * This file is part of PortaPack. * @@ -70,10 +71,15 @@ class EncoderDebounce { uint8_t state(); // returns debounced phase bits from encoder + uint8_t rotation_rate(); // returns last rotation rate + private: uint8_t history_{0}; // shift register of previous reads from encoder uint8_t state_{0}; // actual encoder output state (after debounce logic) + + uint8_t last_rotation_rate_{1}; + uint8_t rotation_rate_downcounter_{1}; // down-counter to estimate rotation speed }; #endif /*__DEBOUNCE_H__*/ diff --git a/firmware/application/hw/encoder.cpp b/firmware/application/hw/encoder.cpp index 89733393..352b70de 100644 --- a/firmware/application/hw/encoder.cpp +++ b/firmware/application/hw/encoder.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2024 Mark Thompson * * This file is part of PortaPack. * @@ -63,7 +64,7 @@ int_fast8_t Encoder::update(const uint_fast8_t phase_bits) { // Require 2 state changes in same direction to register movement -- for additional level of contact switch debouncing if (direction == prev_direction) { - if ((sensitivity_map[portapack::persistent_memory::config_encoder_dial_sensitivity()] & (1 << state)) == 0) + if ((sensitivity_map[portapack::persistent_memory::encoder_dial_sensitivity()] & (1 << state)) == 0) return 0; return direction; } diff --git a/firmware/application/hw/encoder.hpp b/firmware/application/hw/encoder.hpp index 8ce02b57..da3c269c 100644 --- a/firmware/application/hw/encoder.hpp +++ b/firmware/application/hw/encoder.hpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2024 Mark Thompson * * This file is part of PortaPack. * diff --git a/firmware/application/irq_controls.cpp b/firmware/application/irq_controls.cpp index db9d31ef..06a1d21f 100644 --- a/firmware/application/irq_controls.cpp +++ b/firmware/application/irq_controls.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2024 Mark Thompson * * This file is part of PortaPack. * @@ -166,7 +167,7 @@ static bool encoder_update(const uint8_t raw) { } static bool encoder_read() { - auto delta = encoder.update(encoder_debounce.state()); + auto delta = encoder.update(encoder_debounce.state()) * encoder_debounce.rotation_rate(); if (injected_encoder > 0) { if (injected_encoder == 1) delta = -1; diff --git a/firmware/application/irq_controls.hpp b/firmware/application/irq_controls.hpp index 79482fe7..cf0883c2 100644 --- a/firmware/application/irq_controls.hpp +++ b/firmware/application/irq_controls.hpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2024 Mark Thompson * * This file is part of PortaPack. * diff --git a/firmware/common/portapack_persistent_memory.cpp b/firmware/common/portapack_persistent_memory.cpp index bc587c3a..dd73a5c9 100644 --- a/firmware/common/portapack_persistent_memory.cpp +++ b/firmware/common/portapack_persistent_memory.cpp @@ -1,6 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2016 Furrtek + * Copyright (C) 2024 Mark Thompson * * This file is part of PortaPack. * @@ -226,7 +227,9 @@ struct data_t { // fake brightness level (not switch, switch is in another place) uint16_t fake_brightness_level : 4; - uint16_t UNUSED_8 : 8; + + // Encoder rotation rate multiplier for larger increments when rotated rapidly + uint16_t encoder_rate_multiplier : 8; // Headphone volume in centibels. int16_t headphone_volume_cb; @@ -292,7 +295,7 @@ struct data_t { encoder_dial_sensitivity(DIAL_SENSITIVITY_NORMAL), fake_brightness_level(BRIGHTNESS_50), - UNUSED_8(0), + encoder_rate_multiplier(1), headphone_volume_cb(-600), misc_config(), ui_config2(), @@ -399,6 +402,7 @@ void defaults() { cached_backup_ram = backup_ram_t(); // If the desired default is 0/false, then no need to set it here (buffer is initialized to 0) + // NB: This function is only called when pmem is reset; also see firmware upgrade handling below. set_config_backlight_timer(backlight_config_t{}); set_config_splash(true); set_config_disable_external_tcxo(false); @@ -449,9 +453,9 @@ void init() { } set_config_mode_storage_direct(config_mode_backup); - // Firmware upgrade handling - adjust newly defined fields where 0 is an unwanted default - if (fake_brightness_level() == 0) - set_fake_brightness_level(BRIGHTNESS_50); + // Firmware upgrade handling - adjust newly defined fields where 0 is an invalid default + if (fake_brightness_level() == 0) set_fake_brightness_level(BRIGHTNESS_50); + if (encoder_rate_multiplier() == 0) set_encoder_rate_multiplier(1); } void persist() { @@ -976,13 +980,18 @@ void set_config_freq_rx_correction(uint32_t v) { } // Rotary encoder dial settings - -uint8_t config_encoder_dial_sensitivity() { +uint8_t encoder_dial_sensitivity() { return data->encoder_dial_sensitivity; } void set_encoder_dial_sensitivity(uint8_t v) { data->encoder_dial_sensitivity = v; } +uint8_t encoder_rate_multiplier() { + return data->encoder_rate_multiplier; +} +void set_encoder_rate_multiplier(uint8_t v) { + data->encoder_rate_multiplier = v; +} // Recovery mode magic value storage static data_t* data_direct_access = reinterpret_cast(memory::map::backup_ram.base()); @@ -1137,7 +1146,7 @@ bool debug_dump() { pmem_dump_file.write_line("frequency_rx_correction: " + to_string_dec_uint(data->frequency_rx_correction)); pmem_dump_file.write_line("frequency_tx_correction: " + to_string_dec_uint(data->frequency_tx_correction)); pmem_dump_file.write_line("encoder_dial_sensitivity: " + to_string_dec_uint(data->encoder_dial_sensitivity)); - // pmem_dump_file.write_line("UNUSED_8: " + to_string_dec_uint(data->UNUSED_8)); + pmem_dump_file.write_line("encoder_rate_multiplier: " + to_string_dec_uint(data->encoder_rate_multiplier)); pmem_dump_file.write_line("headphone_volume_cb: " + to_string_dec_int(data->headphone_volume_cb)); pmem_dump_file.write_line("config_mode_storage: 0x" + to_string_hex(data->config_mode_storage, 8)); pmem_dump_file.write_line("dst_config: 0x" + to_string_hex((uint32_t)data->dst_config.v, 8)); diff --git a/firmware/common/portapack_persistent_memory.hpp b/firmware/common/portapack_persistent_memory.hpp index f55b49d0..0a1b42f8 100644 --- a/firmware/common/portapack_persistent_memory.hpp +++ b/firmware/common/portapack_persistent_memory.hpp @@ -1,6 +1,7 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2016 Furrtek + * Copyright (C) 2024 Mark Thompson * * This file is part of PortaPack. * @@ -246,8 +247,11 @@ void set_config_audio_mute(bool v); void set_config_speaker_disable(bool v); void set_config_backlight_timer(const backlight_config_t& new_value); void set_disable_touchscreen(bool v); -uint8_t config_encoder_dial_sensitivity(); + +uint8_t encoder_dial_sensitivity(); void set_encoder_dial_sensitivity(uint8_t v); +uint8_t encoder_rate_multiplier(); +void set_encoder_rate_multiplier(uint8_t v); uint32_t config_mode_storage_direct(); void set_config_mode_storage_direct(uint32_t v);