- Added bunch of screens, fonts and images - Added script to read out frame buffer (function currently disabled in Firmware)
283 lines
9.4 KiB
C
283 lines
9.4 KiB
C
/*
|
|
* File: UI_Control.c
|
|
*
|
|
* Created: Created: Friday August 2025 10:21:23
|
|
* Author: Chris
|
|
*/
|
|
#include "UI_Control.h"
|
|
|
|
#include "EEPROM_M24C64.h"
|
|
|
|
#include <string.h>
|
|
|
|
|
|
// ============================================================================================
|
|
// Includes
|
|
|
|
|
|
// ============================================================================================
|
|
// Defines
|
|
#define SELECTOR_INC(__SELECTOR__, __STEP__, __MIN__, __MAX__, __CIRCLE__) if((*__SELECTOR__) <= (__MAX__ - __STEP__)) { (*__SELECTOR__)+=__STEP__; } else if(__CIRCLE__) { (*__SELECTOR__) = __MIN__; break; } else { break; }
|
|
#define SELECTOR_DEC(__SELECTOR__, __STEP__, __MIN__, __MAX__, __CIRCLE__) if((*__SELECTOR__) >= (__MIN__ + __STEP__)) { (*__SELECTOR__)-=__STEP__; } else if(__CIRCLE__) { (*__SELECTOR__) = __MAX__; break; } else { break; }
|
|
|
|
|
|
// ============================================================================================
|
|
// Datatypes
|
|
typedef struct {
|
|
// Configuration
|
|
const Encoder_Acceleration_Config* config;
|
|
|
|
// State tracking
|
|
uint8_t Speed_Counter; // Current speed counter
|
|
uint8_t Acceleration_Level; // Current acceleration level (0 = no accel)
|
|
uint32_t Last_Activity_Time; // Last encoder activity timestamp (ms)
|
|
|
|
// Statistics (for debugging)
|
|
uint32_t Total_Steps; // Total encoder steps processed
|
|
uint32_t Accelerated_Steps; // Steps that used acceleration
|
|
} Encoder_Acceleration_Instance;
|
|
|
|
|
|
// ============================================================================================
|
|
// Variables
|
|
|
|
// Default configuration
|
|
static const Encoder_Acceleration_Config _Default_Acceleration_Config = {
|
|
.Base_Step = 1,
|
|
.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) {
|
|
SELECTOR_INC(selector, _Acceleration_Instance.config->Base_Step, minimum, maximum, circle_around);
|
|
} else {
|
|
SELECTOR_DEC(selector, _Acceleration_Instance.config->Base_Step, 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) {
|
|
SELECTOR_DEC(selector, _Acceleration_Instance.config->Base_Step, minimum, maximum, circle_around);
|
|
} else {
|
|
SELECTOR_INC(selector, _Acceleration_Instance.config->Base_Step, 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;
|
|
}
|