diff --git a/docs/Settings.md b/docs/Settings.md index 4e577bbedd5..95c9e80446a 100644 --- a/docs/Settings.md +++ b/docs/Settings.md @@ -4842,6 +4842,16 @@ Force OSD to work in grid mode even if the OSD device supports pixel level acces --- +### osd_framerate_hz + +Target refresh rate for OSD elements in Hz. Each element is redrawn at approximately this rate. Values above 10 Hz provide no visible improvement for typical flight data but increase CPU load. Artificial horizon and telemetry are always updated every cycle regardless of this setting. Set to -1 for legacy behavior (one element per frame). + +| Default | Min | Max | +| --- | --- | --- | +| -1 | -1 | 15 | + +--- + ### osd_gforce_alarm Value above which the OSD g force indicator will blink (g) diff --git a/src/main/build/debug.h b/src/main/build/debug.h index 0bb74bac1ac..b33868af8b2 100644 --- a/src/main/build/debug.h +++ b/src/main/build/debug.h @@ -79,6 +79,7 @@ typedef enum { DEBUG_GPS, DEBUG_LULU, DEBUG_SBUS2, + DEBUG_OSD_REFRESH, DEBUG_COUNT // also update debugModeNames in cli.c } debugType_e; diff --git a/src/main/fc/cli.c b/src/main/fc/cli.c index 6c60f08c6ed..4546a6abf27 100644 --- a/src/main/fc/cli.c +++ b/src/main/fc/cli.c @@ -219,7 +219,8 @@ static const char *debugModeNames[DEBUG_COUNT] = { "HEADTRACKER", "GPS", "LULU", - "SBUS2" + "SBUS2", + "OSD_REFRESH" }; /* Sensor names (used in lookup tables for *_hardware settings and in status diff --git a/src/main/fc/settings.yaml b/src/main/fc/settings.yaml index 1e1932531e5..ff510762bff 100644 --- a/src/main/fc/settings.yaml +++ b/src/main/fc/settings.yaml @@ -84,7 +84,7 @@ tables: "VIBE", "CRUISE", "REM_FLIGHT_TIME", "SMARTAUDIO", "ACC", "NAV_YAW", "PCF8574", "DYN_GYRO_LPF", "AUTOLEVEL", "ALTITUDE", "AUTOTRIM", "AUTOTUNE", "RATE_DYNAMICS", "LANDING", "POS_EST", - "ADAPTIVE_FILTER", "HEADTRACKER", "GPS", "LULU", "SBUS2"] + "ADAPTIVE_FILTER", "HEADTRACKER", "GPS", "LULU", "SBUS2", "OSD_REFRESH"] - name: aux_operator values: ["OR", "AND"] enum: modeActivationOperator_e @@ -3329,6 +3329,13 @@ groups: max: 600 type: int16_t field: msp_displayport_fullframe_interval + - name: osd_framerate_hz + description: "Target refresh rate for OSD elements in Hz. Each element is redrawn at approximately this rate. Values above 10 Hz provide no visible improvement for typical flight data but increase CPU load. Artificial horizon and telemetry are always updated every cycle regardless of this setting. Set to -1 for legacy behavior (one element per frame)." + default_value: -1 + min: -1 + max: 15 + type: int8_t + field: osd_framerate_hz - name: osd_units description: "IMPERIAL, METRIC, UK" default_value: "METRIC" diff --git a/src/main/io/osd.c b/src/main/io/osd.c index 6ad55632c17..f138be9a523 100644 --- a/src/main/io/osd.c +++ b/src/main/io/osd.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "platform.h" @@ -4245,15 +4246,48 @@ uint8_t osdIncElementIndex(uint8_t elementIndex) void osdDrawNextElement(void) { static uint8_t elementIndex = 0; - // Flag for end of loop, also prevents infinite loop when no elements are enabled - uint8_t index = elementIndex; - do { - elementIndex = osdIncElementIndex(elementIndex); - } while (!osdDrawSingleElement(elementIndex) && index != elementIndex); + static uint8_t activeElements = 0; + static unsigned lastLayout = UINT_MAX; + + // Recount visible elements on layout change + if (currentLayout != lastLayout) { + lastLayout = currentLayout; + activeElements = 0; + uint8_t idx = 0; + do { + idx = osdIncElementIndex(idx); + if (OSD_VISIBLE(osdLayoutsConfig()->item_pos[currentLayout][idx])) { + activeElements++; + } + } while (idx > 0); + } + + int8_t framerate_hz = osdConfig()->osd_framerate_hz; + + uint8_t elementsPerCycle; + if (framerate_hz <= 0 || activeElements == 0) { + elementsPerCycle = 1; // legacy: one element per cycle + } else { + elementsPerCycle = ((uint16_t)activeElements * framerate_hz * 2 + 124) / 125; + if (elementsPerCycle < 1) elementsPerCycle = 1; + if (elementsPerCycle > activeElements) elementsPerCycle = activeElements; + } + + DEBUG_SET(DEBUG_OSD_REFRESH, 0, elementsPerCycle); + DEBUG_SET(DEBUG_OSD_REFRESH, 1, activeElements); + DEBUG_SET(DEBUG_OSD_REFRESH, 2, elementIndex); + DEBUG_SET(DEBUG_OSD_REFRESH, 3, framerate_hz); + + for (uint8_t i = 0; i < elementsPerCycle; i++) { + uint8_t index = elementIndex; + do { + elementIndex = osdIncElementIndex(elementIndex); + } while (!osdDrawSingleElement(elementIndex) && index != elementIndex); + } // Draw artificial horizon + tracking telemetry last osdDrawSingleElement(OSD_ARTIFICIAL_HORIZON); - if (osdConfig()->telemetry>0){ + if (osdConfig()->telemetry > 0) { osdDisplayTelemetry(); } } @@ -4304,6 +4338,7 @@ PG_RESET_TEMPLATE(osdConfig_t, osdConfig, .video_system = SETTING_OSD_VIDEO_SYSTEM_DEFAULT, .row_shiftdown = SETTING_OSD_ROW_SHIFTDOWN_DEFAULT, .msp_displayport_fullframe_interval = SETTING_OSD_MSP_DISPLAYPORT_FULLFRAME_INTERVAL_DEFAULT, + .osd_framerate_hz = SETTING_OSD_FRAMERATE_HZ_DEFAULT, .ahi_reverse_roll = SETTING_OSD_AHI_REVERSE_ROLL_DEFAULT, .ahi_max_pitch = SETTING_OSD_AHI_MAX_PITCH_DEFAULT, diff --git a/src/main/io/osd.h b/src/main/io/osd.h index bbaa68f862d..d644d841a01 100644 --- a/src/main/io/osd.h +++ b/src/main/io/osd.h @@ -451,6 +451,7 @@ typedef struct osdConfig_s { videoSystem_e video_system; uint8_t row_shiftdown; int16_t msp_displayport_fullframe_interval; + int8_t osd_framerate_hz; // Preferences uint8_t main_voltage_decimals;