/* * File: UI_Control.c * * Created: Created: Friday August 2025 10:21:23 * Author: Chris */ #include "UI_Control.h" #include // ============================================================================================ // Includes // ============================================================================================ // Defines #define SELECTOR_INC(__SELECTOR__, __MIN__, __MAX__, __CIRCLE__) if((*__SELECTOR__) < __MAX__) { (*__SELECTOR__)++; } else if(__CIRCLE__) { (*__SELECTOR__) = __MIN__; break; } else { break; } #define SELECTOR_DEC(__SELECTOR__, __MIN__, __MAX__, __CIRCLE__) if((*__SELECTOR__) > __MIN__) { (*__SELECTOR__)--; } else if(__CIRCLE__) { (*__SELECTOR__) = __MAX__; break; } else { break; } // ============================================================================================ // Variables // Default configuration static const Encoder_Acceleration_Config _Default_Acceleration_Config = { .Base_Threshold = 5, // Requires more steps to trigger .Level_Step = 3, // Bigger jumps between levels .Max_Level = 3, // Fewer levels .Timeout_ms = 100, // Longer timeout .Multipliers = {1, 2, 3, 5, 7, 10, 13, 16, 20, 25} }; static Encoder_Acceleration_Instance _Acceleration_Instance; static bool _Acceleration_Initialized = false; static bool _Acceleration_Enabled = false; static uint32_t _Current_Time_MS = 0; // ============================================================================================ // Function Declarations uint8_t UI_Control_Acceleration_Process_Step(uint32_t current_time_ms); /******************************************************************* Functions *******************************************************************/ void UI_Control_Init(void) { UI_Control_Acceleration_Init_Custom(&_Default_Acceleration_Config); } void UI_Control_Selector_Inc(int32_t* selector, int32_t minimum, int32_t maximum, bool circle_around) { if (selector == NULL) { return; } _Current_Time_MS = CURRENT_TIME_ms; // Get acceleration multiplier (1 if not initialized or disabled) uint8_t Increment = UI_Control_Acceleration_Process_Step(_Current_Time_MS); // Apply increment with bounds checking for (uint8_t i = 0; i < Increment; i++) { // if(_EEPROM_Content.Device_Configuration.Reverse_List_Scrolling == 0) { if(true) { SELECTOR_INC(selector, minimum, maximum, circle_around); } else { SELECTOR_DEC(selector, minimum, maximum, circle_around); } } } void UI_Control_Selector_Dec(int32_t* selector, int32_t minimum, int32_t maximum, bool circle_around) { if (selector == NULL) { return; } _Current_Time_MS = CURRENT_TIME_ms; // Get acceleration multiplier (1 if not initialized or disabled) uint8_t Decrement = UI_Control_Acceleration_Process_Step(_Current_Time_MS); // Apply decrement with bounds checking for (uint8_t i = 0; i < Decrement; i++) { // if(_EEPROM_Content.Device_Configuration.Reverse_List_Scrolling == 0) { if(true) { SELECTOR_DEC(selector, minimum, maximum, circle_around); } else { SELECTOR_INC(selector, minimum, maximum, circle_around); } } } void UI_Control_Acceleration_Init_Custom(const Encoder_Acceleration_Config* config) { if (config == NULL) { config = &_Default_Acceleration_Config; } // Clear the instance memset(&_Acceleration_Instance, 0, sizeof(Encoder_Acceleration_Instance)); // Set configuration _Acceleration_Instance.config = config; // Initialize state _Acceleration_Instance.Speed_Counter = 0; _Acceleration_Instance.Acceleration_Level = 0; _Acceleration_Instance.Last_Activity_Time = 0; _Acceleration_Instance.Total_Steps = 0; _Acceleration_Instance.Accelerated_Steps = 0; _Acceleration_Initialized = true; _Acceleration_Enabled = false; } void UI_Control_Acceleration_Set_Enabled(bool enable) { _Acceleration_Enabled = enable; // Reset state when disabling if (!enable && _Acceleration_Initialized) { UI_Control_Acceleration_Reset(); } } void UI_Control_Acceleration_Reset(void) { if (!_Acceleration_Initialized) { return; } _Acceleration_Instance.Speed_Counter = 0; _Acceleration_Instance.Acceleration_Level = 0; // Don't reset timestamp - let natural timeout handle it } void UI_Control_Set_Current_Time(uint32_t current_time_ms) { _Current_Time_MS = current_time_ms; } void UI_Control_Selector_Inc_Accelerated(int32_t* selector, int32_t minimum, int32_t maximum, bool circle_around, uint32_t current_time_ms) { // This function is now redundant - just call the main function with time set UI_Control_Set_Current_Time(current_time_ms); UI_Control_Selector_Inc(selector, minimum, maximum, circle_around); } void UI_Control_Selector_Dec_Accelerated(int32_t* selector, int32_t minimum, int32_t maximum, bool circle_around, uint32_t current_time_ms) { // This function is now redundant - just call the main function with time set UI_Control_Set_Current_Time(current_time_ms); UI_Control_Selector_Dec(selector, minimum, maximum, circle_around); } void UI_Control_Acceleration_Update(uint32_t current_time_ms) { if (!_Acceleration_Initialized || _Acceleration_Instance.config == NULL) { return; } // Check for timeout if (_Acceleration_Instance.Last_Activity_Time > 0) { uint32_t time_since_activity = current_time_ms - _Acceleration_Instance.Last_Activity_Time; if (time_since_activity >= _Acceleration_Instance.config->Timeout_ms) { // Reset acceleration due to timeout _Acceleration_Instance.Speed_Counter = 0; _Acceleration_Instance.Acceleration_Level = 0; // Keep last_activity_time for future timeout calculations } } } uint8_t UI_Control_Acceleration_Get_Level(void) { if (!_Acceleration_Initialized) { return 0; } return _Acceleration_Instance.Acceleration_Level; } bool UI_Control_Acceleration_Is_Active(void) { if (!_Acceleration_Initialized || !_Acceleration_Enabled) { return false; } return (_Acceleration_Instance.Acceleration_Level > 0); } void UI_Control_Acceleration_Get_Stats(uint32_t* total_steps, uint32_t* accelerated_steps) { if (!_Acceleration_Initialized) { if (total_steps) *total_steps = 0; if (accelerated_steps) *accelerated_steps = 0; return; } if (total_steps) { *total_steps = _Acceleration_Instance.Total_Steps; } if (accelerated_steps) { *accelerated_steps = _Acceleration_Instance.Accelerated_Steps; } } /******************************************************************* Internal Functions *******************************************************************/ uint8_t UI_Control_Acceleration_Process_Step(uint32_t current_time_ms) { if (!_Acceleration_Initialized || !_Acceleration_Enabled || _Acceleration_Instance.config == NULL) { return 1; // Fallback to normal increment } // Use provided time or stored time uint32_t Time_To_Use = (current_time_ms > 0) ? current_time_ms : _Current_Time_MS; const Encoder_Acceleration_Config* Config = _Acceleration_Instance.config; // Update activity timestamp _Acceleration_Instance.Last_Activity_Time = Time_To_Use; // Increment speed counter _Acceleration_Instance.Speed_Counter++; _Acceleration_Instance.Total_Steps++; // Calculate current acceleration level if (_Acceleration_Instance.Speed_Counter >= Config->Base_Threshold) { // Calculate level based on how many steps beyond threshold uint8_t excess_steps = _Acceleration_Instance.Speed_Counter - Config->Base_Threshold; _Acceleration_Instance.Acceleration_Level = 1 + (excess_steps / Config->Level_Step); // Clamp to maximum level if (_Acceleration_Instance.Acceleration_Level > Config->Max_Level) { _Acceleration_Instance.Acceleration_Level = Config->Max_Level; } } else { _Acceleration_Instance.Acceleration_Level = 0; } // Get multiplier for current level uint8_t increment = 1; if (_Acceleration_Instance.Acceleration_Level > 0) { // Array is 0-indexed, but level 1 = index 1, level 0 = index 0 increment = Config->Multipliers[_Acceleration_Instance.Acceleration_Level]; _Acceleration_Instance.Accelerated_Steps++; } return increment; }