1
0
Fork 0
mirror of https://github.com/iNavFlight/inav.git synced 2025-07-24 00:35:34 +03:00

Merge pull request #2955 from iNavFlight/agh_osd_layouts

Add support for multiple OSD layouts
This commit is contained in:
Konstantin Sharlaimov 2018-04-26 20:27:15 +10:00 committed by GitHub
commit e3dafbf516
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 719 additions and 422 deletions

View file

@ -39,6 +39,7 @@
#include "cms/cms.h"
#include "cms/cms_menu_builtin.h"
#include "cms/cms_menu_osd.h"
#include "cms/cms_types.h"
#include "common/maths.h"
@ -94,6 +95,7 @@ static displayPort_t *pCurrentDisplay;
static displayPort_t *cmsDisplayPorts[CMS_MAX_DEVICE];
static int cmsDeviceCount;
static int cmsCurrentDevice = -1;
static timeMs_t cmsYieldUntil = 0;
bool cmsDisplayPortRegister(displayPort_t *pDisplay)
{
@ -349,21 +351,6 @@ static int cmsDrawMenuEntry(displayPort_t *pDisplay, const OSD_Entry *p, uint8_t
}
break;
#ifdef USE_OSD
case OME_VISIBLE:
if (IS_PRINTVALUE(p, screenRow) && p->data) {
uint16_t val = osdConfig()->item_pos[(int)p->data];
if (VISIBLE(val)) {
cnt = displayWrite(pDisplay, RIGHT_MENU_COLUMN(pDisplay), row, "YES");
} else {
cnt = displayWrite(pDisplay, RIGHT_MENU_COLUMN(pDisplay), row, "NO ");
}
CLR_PRINTVALUE(p, screenRow);
}
break;
#endif
case OME_UINT8:
if (IS_PRINTVALUE(p, screenRow) && p->data) {
const uint8_t *val;
@ -636,10 +623,8 @@ static void cmsMenuCountPage(displayPort_t *pDisplay)
STATIC_UNIT_TESTED long cmsMenuBack(displayPort_t *pDisplay); // Forward; will be resolved after merging
long cmsMenuChange(displayPort_t *pDisplay, const void *ptr)
long cmsMenuChange(displayPort_t *pDisplay, const CMS_Menu *pMenu, const OSD_Entry *from)
{
CMS_Menu *pMenu = (CMS_Menu *)ptr;
if (!pMenu) {
return 0;
}
@ -664,7 +649,7 @@ long cmsMenuChange(displayPort_t *pDisplay, const void *ptr)
currentCtx.menu = pMenu;
currentCtx.cursorRow = 0;
if (pMenu->onEnter && (pMenu->onEnter() == MENU_CHAIN_BACK)) {
if (pMenu->onEnter && (pMenu->onEnter(from) == MENU_CHAIN_BACK)) {
return cmsMenuBack(pDisplay);
}
@ -731,7 +716,7 @@ STATIC_UNIT_TESTED void cmsMenuOpen(void)
}
}
displayGrab(pCurrentDisplay); // grab the display for use by the CMS
cmsMenuChange(pCurrentDisplay, currentCtx.menu);
cmsMenuChange(pCurrentDisplay, currentCtx.menu, NULL);
}
static void cmsTraverseGlobalExit(const CMS_Menu *pMenu)
@ -743,7 +728,7 @@ static void cmsTraverseGlobalExit(const CMS_Menu *pMenu)
}
if (pMenu->onGlobalExit) {
pMenu->onGlobalExit();
pMenu->onGlobalExit(NULL);
}
}
@ -789,6 +774,12 @@ long cmsMenuExit(displayPort_t *pDisplay, const void *ptr)
return 0;
}
void cmsYieldDisplay(displayPort_t *pPort, timeMs_t duration)
{
cmsYieldUntil = millis() + duration;
displayRelease(pPort);
}
// Stick/key detection and key codes
#define IS_HI(X) (rcData[X] > 1750)
@ -855,7 +846,7 @@ STATIC_UNIT_TESTED uint16_t cmsHandleKey(displayPort_t *pDisplay, uint8_t key)
switch (p->type) {
case OME_Submenu:
if (key == KEY_RIGHT) {
cmsMenuChange(pDisplay, p->data);
cmsMenuChange(pDisplay, p->data, p);
res = BUTTON_PAUSE;
}
break;
@ -889,6 +880,9 @@ STATIC_UNIT_TESTED uint16_t cmsHandleKey(displayPort_t *pDisplay, uint8_t key)
else
*val = 0;
SET_PRINTVALUE(p, currentCtx.cursorRow);
if (p->func) {
p->func(pDisplay, p);
}
}
break;
@ -901,20 +895,6 @@ STATIC_UNIT_TESTED uint16_t cmsHandleKey(displayPort_t *pDisplay, uint8_t key)
}
break;
#ifdef USE_OSD
case OME_VISIBLE:
if (p->data) {
uint16_t *val = &osdConfigMutable()->item_pos[(int)p->data];
if (key == KEY_RIGHT)
*val |= VISIBLE_FLAG;
else
*val %= ~VISIBLE_FLAG;
SET_PRINTVALUE(p, currentCtx.cursorRow);
}
break;
#endif
case OME_UINT8:
case OME_FLOAT:
if (IS_READONLY(p)) {
@ -1107,6 +1087,97 @@ uint16_t cmsHandleKeyWithRepeat(displayPort_t *pDisplay, uint8_t key, int repeat
return ret;
}
static uint16_t cmsScanKeys(timeMs_t currentTimeMs, timeMs_t lastCalledMs, int16_t rcDelayMs)
{
static int holdCount = 1;
static int repeatCount = 1;
static int repeatBase = 0;
//
// Scan 'key' first
//
uint8_t key = KEY_NONE;
if (IS_MID(THROTTLE) && IS_LO(YAW) && IS_HI(PITCH) && !ARMING_FLAG(ARMED)) {
key = KEY_MENU;
}
else if (IS_HI(PITCH)) {
key = KEY_UP;
}
else if (IS_LO(PITCH)) {
key = KEY_DOWN;
}
else if (IS_LO(ROLL)) {
key = KEY_LEFT;
}
else if (IS_HI(ROLL)) {
key = KEY_RIGHT;
}
else if (IS_HI(YAW) || IS_LO(YAW))
{
key = KEY_ESC;
}
if (key == KEY_NONE) {
// No 'key' pressed, reset repeat control
holdCount = 1;
repeatCount = 1;
repeatBase = 0;
} else {
// The 'key' is being pressed; keep counting
++holdCount;
}
if (rcDelayMs > 0) {
rcDelayMs -= (currentTimeMs - lastCalledMs);
} else if (key) {
rcDelayMs = cmsHandleKeyWithRepeat(pCurrentDisplay, key, repeatCount);
// Key repeat effect is implemented in two phases.
// First phldase is to decrease rcDelayMs reciprocal to hold time.
// When rcDelayMs reached a certain limit (scheduling interval),
// repeat rate will not raise anymore, so we call key handler
// multiple times (repeatCount).
//
// XXX Caveat: Most constants are adjusted pragmatically.
// XXX Rewrite this someday, so it uses actual hold time instead
// of holdCount, which depends on the scheduling interval.
if (((key == KEY_LEFT) || (key == KEY_RIGHT)) && (holdCount > 20)) {
// Decrease rcDelayMs reciprocally
rcDelayMs /= (holdCount - 20);
// When we reach the scheduling limit,
if (rcDelayMs <= 50) {
// start calling handler multiple times.
if (repeatBase == 0)
repeatBase = holdCount;
if (holdCount < 100) {
repeatCount = repeatCount + (holdCount - repeatBase) / 5;
if (repeatCount > 5) {
repeatCount= 5;
}
} else {
repeatCount = repeatCount + holdCount - repeatBase;
if (repeatCount > 50) {
repeatCount = 50;
}
}
}
}
}
return rcDelayMs;
}
void cmsUpdate(uint32_t currentTimeUs)
{
#ifdef USE_RCDEVICE
@ -1116,126 +1187,35 @@ void cmsUpdate(uint32_t currentTimeUs)
#endif
static int16_t rcDelayMs = BUTTON_TIME;
static int holdCount = 1;
static int repeatCount = 1;
static int repeatBase = 0;
// e.g #define CMS_INJECTED_KEYS KEY_DOWN,KEY_RIGHT,KEY_DOWN,KEY_RIGHT,KEY_DOWN
#define CMS_INJECTED_KEYS_INTERVAL 800
#if defined CMS_INJECTED_KEYS
int cmsInjectedKeys[] = {KEY_NONE, CMS_INJECTED_KEYS};
static timeMs_t lastInjectedKeyMs = 0;
static unsigned lastInjectedKeyIndex = 0;
#endif
static uint32_t lastCalledMs = 0;
static timeMs_t lastCalledMs = 0;
static uint32_t lastCmsHeartBeatMs = 0;
const uint32_t currentTimeMs = currentTimeUs / 1000;
const timeMs_t currentTimeMs = currentTimeUs / 1000;
if (!cmsInMenu) {
// Detect menu invocation
#if defined(CMS_INJECTED_KEYS)
cmsMenuOpen();
rcDelayMs = 0;
#else
if (IS_MID(THROTTLE) && IS_LO(YAW) && IS_HI(PITCH) && !ARMING_FLAG(ARMED)) {
cmsMenuOpen();
rcDelayMs = BUTTON_PAUSE; // Tends to overshoot if BUTTON_TIME
}
#endif
} else {
//
// Scan 'key' first
//
uint8_t key = KEY_NONE;
if (IS_MID(THROTTLE) && IS_LO(YAW) && IS_HI(PITCH) && !ARMING_FLAG(ARMED)) {
key = KEY_MENU;
}
else if (IS_HI(PITCH)) {
key = KEY_UP;
}
else if (IS_LO(PITCH)) {
key = KEY_DOWN;
}
else if (IS_LO(ROLL)) {
key = KEY_LEFT;
}
else if (IS_HI(ROLL)) {
key = KEY_RIGHT;
}
else if (IS_HI(YAW) || IS_LO(YAW))
{
key = KEY_ESC;
// Check if we're yielding and its's time to stop it
if (cmsYieldUntil > 0 && currentTimeMs > cmsYieldUntil) {
cmsYieldUntil = 0;
displayGrab(pCurrentDisplay);
displayClearScreen(pCurrentDisplay);
}
#if defined(CMS_INJECTED_KEYS)
if (lastInjectedKeyMs < currentTimeMs - CMS_INJECTED_KEYS_INTERVAL) {
if (lastInjectedKeyIndex < ARRAYLEN(cmsInjectedKeys)) {
key = cmsInjectedKeys[lastInjectedKeyIndex++];
lastInjectedKeyMs = currentTimeMs;
// Only scan keys and draw if we're not yielding
if (cmsYieldUntil == 0) {
rcDelayMs = cmsScanKeys(currentTimeMs, lastCalledMs, rcDelayMs);
// Check again, the keypress might have produced a yield
if (cmsYieldUntil == 0) {
cmsDrawMenu(pCurrentDisplay, currentTimeUs);
}
}
#endif
if (key == KEY_NONE) {
// No 'key' pressed, reset repeat control
holdCount = 1;
repeatCount = 1;
repeatBase = 0;
} else {
// The 'key' is being pressed; keep counting
++holdCount;
}
if (rcDelayMs > 0) {
rcDelayMs -= (currentTimeMs - lastCalledMs);
} else if (key) {
rcDelayMs = cmsHandleKeyWithRepeat(pCurrentDisplay, key, repeatCount);
// Key repeat effect is implemented in two phases.
// First phldase is to decrease rcDelayMs reciprocal to hold time.
// When rcDelayMs reached a certain limit (scheduling interval),
// repeat rate will not raise anymore, so we call key handler
// multiple times (repeatCount).
//
// XXX Caveat: Most constants are adjusted pragmatically.
// XXX Rewrite this someday, so it uses actual hold time instead
// of holdCount, which depends on the scheduling interval.
if (((key == KEY_LEFT) || (key == KEY_RIGHT)) && (holdCount > 20)) {
// Decrease rcDelayMs reciprocally
rcDelayMs /= (holdCount - 20);
// When we reach the scheduling limit,
if (rcDelayMs <= 50) {
// start calling handler multiple times.
if (repeatBase == 0)
repeatBase = holdCount;
if (holdCount < 100) {
repeatCount = repeatCount + (holdCount - repeatBase) / 5;
if (repeatCount > 5) {
repeatCount= 5;
}
} else {
repeatCount = repeatCount + holdCount - repeatBase;
if (repeatCount > 50) {
repeatCount= 50;
}
}
}
}
}
cmsDrawMenu(pCurrentDisplay, currentTimeUs);
if (currentTimeMs > lastCmsHeartBeatMs + 500) {
// Heart beat for external CMS display device @ 500msec

View file

@ -4,6 +4,8 @@
#include "common/time.h"
#include "cms/cms_types.h"
extern bool cmsInMenu;
// Device management
@ -13,8 +15,9 @@ bool cmsDisplayPortRegister(displayPort_t *pDisplay);
void cmsInit(void);
void cmsHandler(timeUs_t currentTimeUs);
long cmsMenuChange(displayPort_t *pPort, const void *ptr);
long cmsMenuChange(displayPort_t *pPort, const CMS_Menu *menu, const OSD_Entry *from);
long cmsMenuExit(displayPort_t *pPort, const void *ptr);
void cmsYieldDisplay(displayPort_t *pPort, timeMs_t duration);
void cmsUpdate(uint32_t currentTimeUs);
#define CMS_STARTUP_HELP_TEXT1 "MENU: THR MID"

View file

@ -30,6 +30,8 @@
#include "build/version.h"
#include "common/utils.h"
#include "drivers/time.h"
#include "cms/cms.h"
@ -59,8 +61,10 @@ static char infoTargetName[] = __TARGET__;
#include "msp/msp_protocol.h" // XXX for FC identification... not available elsewhere
static long cmsx_InfoInit(void)
static long cmsx_InfoInit(const OSD_Entry *from)
{
UNUSED(from);
int i;
for ( i = 0 ; i < GIT_SHORT_REVISION_LENGTH ; i++) {
if (shortGitRevision[i] >= 'a' && shortGitRevision[i] <= 'f')
@ -142,8 +146,8 @@ static const OSD_Entry menuMainEntries[] =
OSD_SUBMENU_ENTRY("PID TUNING", &cmsx_menuImu),
OSD_SUBMENU_ENTRY("FEATURES", &menuFeatures),
#ifdef USE_OSD
OSD_SUBMENU_ENTRY("SCR LAYOUT", &cmsx_menuOsdLayout),
#if defined(USE_OSD) && defined(CMS_MENU_OSD)
OSD_SUBMENU_ENTRY("OSD LAYOUTS", &cmsx_menuOsdLayout),
OSD_SUBMENU_ENTRY("ALARMS", &cmsx_menuAlarms),
#endif
OSD_SUBMENU_ENTRY("FC&FW INFO", &menuInfo),

View file

@ -66,8 +66,10 @@ static void cmsx_WritebackPidFromArray(uint8_t *src, int pidIndex)
pidBankMutable()->pid[pidIndex].D = src[2];
}
static long cmsx_menuImu_onEnter(void)
static long cmsx_menuImu_onEnter(const OSD_Entry *from)
{
UNUSED(from);
profileIndex = getConfigProfile();
tmpProfileIndex = profileIndex + 1;
profileIndexString[1] = '0' + tmpProfileIndex;
@ -108,8 +110,10 @@ static long cmsx_PidRead(void)
return 0;
}
static long cmsx_PidOnEnter(void)
static long cmsx_PidOnEnter(const OSD_Entry *from)
{
UNUSED(from);
profileIndexString[1] = '0' + tmpProfileIndex;
cmsx_PidRead();
@ -164,8 +168,10 @@ static uint8_t cmsx_pidPosZ[3];
static uint8_t cmsx_pidVelZ[3];
static uint8_t cmsx_pidHead[3];
static long cmsx_menuPidAltMag_onEnter(void)
static long cmsx_menuPidAltMag_onEnter(const OSD_Entry *from)
{
UNUSED(from);
cmsx_ReadPidToArray(cmsx_pidPosZ, PID_POS_Z);
cmsx_ReadPidToArray(cmsx_pidVelZ, PID_VEL_Z);
cmsx_pidHead[0] = pidBank()->pid[PID_HEADING].P;
@ -217,8 +223,10 @@ static const CMS_Menu cmsx_menuPidAltMag = {
static uint8_t cmsx_pidPosXY[3];
static uint8_t cmsx_pidVelXY[3];
static long cmsx_menuPidGpsnav_onEnter(void)
static long cmsx_menuPidGpsnav_onEnter(const OSD_Entry *from)
{
UNUSED(from);
cmsx_ReadPidToArray(cmsx_pidPosXY, PID_POS_XY);
cmsx_ReadPidToArray(cmsx_pidVelXY, PID_VEL_XY);

View file

@ -21,7 +21,9 @@
#include "platform.h"
#if defined(USE_OSD) && defined(USE_CMS)
#if defined(USE_OSD) && defined(USE_CMS) && defined(CMS_MENU_OSD)
#include "build/debug.h"
#include "common/utils.h"
@ -33,8 +35,21 @@
#include "io/osd.h"
static const OSD_Entry cmsx_menuAlarmsEntries[] =
{
#define OSD_ITEM_ENTRY(label, item_id) ((OSD_Entry){ label, OME_Submenu, (void *)item_id, &cmsx_menuOsdElementActions, 0 })
#define OSD_ITEM_GET_ID(entry) ((int)entry->func)
static int osdCurrentLayout = -1;
static int osdCurrentItem = -1;
static uint8_t osdCurrentElementRow = 0;
static uint8_t osdCurrentElementColumn = 0;
static uint8_t osdCurrentElementVisible = 0;
static long osdElementsOnEnter(const OSD_Entry *from);
static long osdElementsOnExit(const OSD_Entry *from);
static long osdElemActionsOnEnter(const OSD_Entry *from);
static const OSD_Entry cmsx_menuAlarmsEntries[] = {
OSD_LABEL_ENTRY("--- ALARMS ---"),
OSD_SETTING_ENTRY_STEP("RSSI", SETTING_OSD_RSSI_ALARM, 5),
@ -56,62 +71,167 @@ const CMS_Menu cmsx_menuAlarms = {
.entries = cmsx_menuAlarmsEntries,
};
#define OSD_OSD_ELEMENT_ENTRY(name, osd_item_id) {name, OME_VISIBLE, NULL, (void *)osd_item_id, 0}
static const OSD_Entry menuOsdActiveElemsEntries[] =
static long cmsx_osdElementOnChange(displayPort_t *displayPort, const void *ptr)
{
OSD_LABEL_ENTRY("--- ACTIV ELEM ---"),
UNUSED(ptr);
OSD_OSD_ELEMENT_ENTRY("RSSI", OSD_RSSI_VALUE),
OSD_OSD_ELEMENT_ENTRY("MAIN BATTERY", OSD_MAIN_BATT_VOLTAGE),
OSD_OSD_ELEMENT_ENTRY("HORIZON", OSD_ARTIFICIAL_HORIZON),
OSD_OSD_ELEMENT_ENTRY("HORIZON SIDEBARS", OSD_HORIZON_SIDEBARS),
OSD_OSD_ELEMENT_ENTRY("UPTIME", OSD_ONTIME),
OSD_OSD_ELEMENT_ENTRY("FLY TIME", OSD_FLYTIME),
OSD_OSD_ELEMENT_ENTRY("FLY MODE", OSD_FLYMODE),
OSD_OSD_ELEMENT_ENTRY("NAME", OSD_CRAFT_NAME),
OSD_OSD_ELEMENT_ENTRY("THROTTLE", OSD_THROTTLE_POS),
#ifdef VTX
OSD_OSD_ELEMENT_ENTRY("VTX CHAN", OSD_VTX_CHANNEL),
#endif // VTX
OSD_OSD_ELEMENT_ENTRY("CURRENT (A)", OSD_CURRENT_DRAW),
OSD_OSD_ELEMENT_ENTRY("USED MAH", OSD_MAH_DRAWN),
#ifdef USE_GPS
OSD_OSD_ELEMENT_ENTRY("HOME DIR.", OSD_HOME_DIR),
OSD_OSD_ELEMENT_ENTRY("HOME DIST.", OSD_HOME_DIST),
OSD_OSD_ELEMENT_ENTRY("GPS SPEED", OSD_GPS_SPEED),
OSD_OSD_ELEMENT_ENTRY("GPS SATS.", OSD_GPS_SATS),
OSD_OSD_ELEMENT_ENTRY("GPS LAT", OSD_GPS_LAT),
OSD_OSD_ELEMENT_ENTRY("GPS LON.", OSD_GPS_LON),
OSD_OSD_ELEMENT_ENTRY("HEADING", OSD_HEADING),
#endif // GPS
#if defined(USE_BARO) || defined(USE_GPS)
OSD_OSD_ELEMENT_ENTRY("VARIO", OSD_VARIO),
OSD_OSD_ELEMENT_ENTRY("VARIO NUM", OSD_VARIO_NUM),
#endif // defined
OSD_OSD_ELEMENT_ENTRY("ALTITUDE", OSD_ALTITUDE),
OSD_OSD_ELEMENT_ENTRY("AIR SPEED", OSD_AIR_SPEED),
uint16_t *pos = &osdConfigMutable()->item_pos[osdCurrentLayout][osdCurrentItem];
*pos = OSD_POS(osdCurrentElementColumn, osdCurrentElementRow);
if (osdCurrentElementVisible) {
*pos |= OSD_VISIBLE_FLAG;
}
cmsYieldDisplay(displayPort, 500);
return 0;
}
static long osdElementPreview(displayPort_t *displayPort, const void *ptr)
{
UNUSED(ptr);
cmsYieldDisplay(displayPort, 2000);
return 0;
}
static const OSD_Entry menuOsdElemActionsEntries[] = {
OSD_BOOL_CALLBACK_ENTRY("ENABLED", cmsx_osdElementOnChange, &osdCurrentElementVisible),
OSD_UINT8_CALLBACK_ENTRY("ROW", cmsx_osdElementOnChange, (&(const OSD_UINT8_t){ &osdCurrentElementRow, 0, OSD_X(OSD_POS_MAX), 1 })),
OSD_UINT8_CALLBACK_ENTRY("COLUMN", cmsx_osdElementOnChange, (&(const OSD_UINT8_t){ &osdCurrentElementColumn, 0, OSD_Y(OSD_POS_MAX), 1 })),
OSD_FUNC_CALL_ENTRY("PREVIEW", osdElementPreview),
OSD_BACK_ENTRY,
OSD_END_ENTRY,
};
const CMS_Menu menuOsdActiveElems = {
static const OSD_Entry menuOsdFixedElemActionsEntries[] = {
OSD_BOOL_CALLBACK_ENTRY("ENABLED", cmsx_osdElementOnChange, &osdCurrentElementVisible),
OSD_FUNC_CALL_ENTRY("PREVIEW", osdElementPreview),
OSD_BACK_ENTRY,
OSD_END_ENTRY,
};
static CMS_Menu cmsx_menuOsdElementActions = {
#ifdef CMS_MENU_DEBUG
.GUARD_text = "MENUOSDACT",
.GUARD_text = "MENUOSDELEM",
.GUARD_type = OME_MENU,
#endif
.onEnter = NULL,
.onEnter = osdElemActionsOnEnter,
.onExit = NULL,
.onGlobalExit = NULL,
.entries = menuOsdActiveElemsEntries
.entries = menuOsdElemActionsEntries,
};
static long osdElemActionsOnEnter(const OSD_Entry *from)
{
osdCurrentItem = OSD_ITEM_GET_ID(from);
uint16_t pos = osdConfig()->item_pos[osdCurrentLayout][osdCurrentItem];
osdCurrentElementColumn = OSD_X(pos);
osdCurrentElementRow = OSD_Y(pos);
osdCurrentElementVisible = OSD_VISIBLE(pos) ? 1 : 0;
if (osdItemIsFixed(osdCurrentItem)) {
cmsx_menuOsdElementActions.entries = menuOsdFixedElemActionsEntries;
} else {
cmsx_menuOsdElementActions.entries = menuOsdElemActionsEntries;
}
return 0;
}
#define OSD_ELEMENT_ENTRY(name, osd_item_id) OSD_ITEM_ENTRY(name, osd_item_id)
static const OSD_Entry menuOsdElemsEntries[] =
{
OSD_LABEL_ENTRY("--- OSD ---"),
OSD_ELEMENT_ENTRY("RSSI", OSD_RSSI_VALUE),
OSD_ELEMENT_ENTRY("MAIN BATTERY", OSD_MAIN_BATT_VOLTAGE),
OSD_ELEMENT_ENTRY("CELL VOLTAGE", OSD_MAIN_BATT_CELL_VOLTAGE),
OSD_ELEMENT_ENTRY("CROSSHAIRS", OSD_CROSSHAIRS),
OSD_ELEMENT_ENTRY("HORIZON", OSD_ARTIFICIAL_HORIZON),
OSD_ELEMENT_ENTRY("HORIZON SIDEBARS", OSD_HORIZON_SIDEBARS),
OSD_ELEMENT_ENTRY("ON TIME", OSD_ONTIME),
OSD_ELEMENT_ENTRY("FLY TIME", OSD_FLYTIME),
OSD_ELEMENT_ENTRY("ON/FLY TIME", OSD_ONTIME_FLYTIME),
OSD_ELEMENT_ENTRY("TIME (HOUR)", OSD_RTC_TIME),
OSD_ELEMENT_ENTRY("FLY MODE", OSD_FLYMODE),
OSD_ELEMENT_ENTRY("NAME", OSD_CRAFT_NAME),
OSD_ELEMENT_ENTRY("THR. (MANU)", OSD_THROTTLE_POS),
OSD_ELEMENT_ENTRY("THR. (MANU/AUTO)", OSD_THROTTLE_POS_AUTO_THR),
OSD_ELEMENT_ENTRY("SYS MESSAGES", OSD_MESSAGES),
#ifdef VTX_COMMON
OSD_ELEMENT_ENTRY("VTX CHAN", OSD_VTX_CHANNEL),
#endif // VTX
OSD_ELEMENT_ENTRY("CURRENT (A)", OSD_CURRENT_DRAW),
OSD_ELEMENT_ENTRY("POWER", OSD_POWER),
OSD_ELEMENT_ENTRY("USED MAH", OSD_MAH_DRAWN),
OSD_ELEMENT_ENTRY("USED WH", OSD_WH_DRAWN),
OSD_ELEMENT_ENTRY("EFF/KM (AH)", OSD_EFFICIENCY_MAH_PER_KM),
OSD_ELEMENT_ENTRY("EFF/KM (WH)", OSD_EFFICIENCY_WH_PER_KM),
OSD_ELEMENT_ENTRY("BATT CAP REM", OSD_BATTERY_REMAINING_CAPACITY),
OSD_ELEMENT_ENTRY("BATT % REM", OSD_BATTERY_REMAINING_PERCENT),
#ifdef USE_GPS
OSD_ELEMENT_ENTRY("HOME DIR", OSD_HOME_DIR),
OSD_ELEMENT_ENTRY("HOME DIST", OSD_HOME_DIST),
OSD_ELEMENT_ENTRY("TRIP DIST", OSD_TRIP_DIST),
OSD_ELEMENT_ENTRY("GPS SPEED", OSD_GPS_SPEED),
OSD_ELEMENT_ENTRY("GPS SATS", OSD_GPS_SATS),
OSD_ELEMENT_ENTRY("GPS LAT", OSD_GPS_LAT),
OSD_ELEMENT_ENTRY("GPS LON", OSD_GPS_LON),
OSD_ELEMENT_ENTRY("GPS HDOP", OSD_GPS_HDOP),
#endif // GPS
OSD_ELEMENT_ENTRY("HEADING", OSD_HEADING),
OSD_ELEMENT_ENTRY("HEADING GR.", OSD_HEADING_GRAPH),
#if defined(USE_BARO) || defined(USE_GPS)
OSD_ELEMENT_ENTRY("VARIO", OSD_VARIO),
OSD_ELEMENT_ENTRY("VARIO NUM", OSD_VARIO_NUM),
#endif // defined
OSD_ELEMENT_ENTRY("ALTITUDE", OSD_ALTITUDE),
#if defined(USE_PITOT)
OSD_ELEMENT_ENTRY("AIR SPEED", OSD_AIR_SPEED),
#endif
OSD_ELEMENT_ENTRY("ROLL PIDS", OSD_ROLL_PIDS),
OSD_ELEMENT_ENTRY("PITCH PIDS", OSD_PITCH_PIDS),
OSD_ELEMENT_ENTRY("YAW PIDS", OSD_YAW_PIDS),
OSD_BACK_ENTRY,
OSD_END_ENTRY,
};
#if defined(VTX_COMMON) && defined(USE_GPS) && defined(USE_BARO) && defined(USE_PITOT)
// All CMS OSD elements should be enabled in this case
_Static_assert(ARRAYLEN(menuOsdElemsEntries) - 3 == OSD_ITEM_COUNT, "missing OSD elements in CMS");
#endif
const CMS_Menu menuOsdElements = {
#ifdef CMS_MENU_DEBUG
.GUARD_text = "MENUOSDELEMS",
.GUARD_type = OME_MENU,
#endif
.onEnter = osdElementsOnEnter,
.onExit = osdElementsOnExit,
.onGlobalExit = NULL,
.entries = menuOsdElemsEntries,
};
#define OSD_LAYOUT_SUBMENU_ENTRY(label) OSD_SUBMENU_ENTRY(label, &menuOsdElements)
static const OSD_Entry cmsx_menuOsdLayoutEntries[] =
{
OSD_LABEL_ENTRY("---SCREEN LAYOUT---"),
OSD_SUBMENU_ENTRY("ACTIVE ELEM", &menuOsdActiveElems),
OSD_LAYOUT_SUBMENU_ENTRY("DEFAULT"),
#if OSD_ALTERNATE_LAYOUT_COUNT > 0
OSD_LAYOUT_SUBMENU_ENTRY("ALTERNATE 1"),
#if OSD_ALTERNATE_LAYOUT_COUNT > 1
OSD_LAYOUT_SUBMENU_ENTRY("ALTERNATE 2"),
#if OSD_ALTERNATE_LAYOUT_COUNT > 2
OSD_LAYOUT_SUBMENU_ENTRY("ALTERNATE 3"),
#endif
#endif
#endif
OSD_BACK_ENTRY,
OSD_END_ENTRY,
@ -125,6 +245,25 @@ const CMS_Menu cmsx_menuOsdLayout = {
.onEnter = NULL,
.onExit = NULL,
.onGlobalExit = NULL,
.entries = cmsx_menuOsdLayoutEntries
.entries = cmsx_menuOsdLayoutEntries,
};
static long osdElementsOnEnter(const OSD_Entry *from)
{
// First entry is the label. Store the current layout
// and override it on the OSD so previews so this layout.
osdCurrentLayout = from - cmsx_menuOsdLayoutEntries - 1;
osdOverrideLayout(osdCurrentLayout);
return 0;
}
static long osdElementsOnExit(const OSD_Entry *from)
{
UNUSED(from);
// Stop overriding OSD layout
osdOverrideLayout(-1);
return 0;
}
#endif // CMS

View file

@ -365,7 +365,7 @@ static const char * const saCmsPitFModeNames[] = {
static const OSD_TAB_t saCmsEntPitFMode = { &saCmsPitFMode, 1, saCmsPitFModeNames };
static long sacms_SetupTopMenu(void); // Forward
static long sacms_SetupTopMenu(const OSD_Entry *from); // Forward
static long saCmsConfigFreqModeByGvar(displayPort_t *pDisp, const void *self)
{
@ -385,7 +385,7 @@ static long saCmsConfigFreqModeByGvar(displayPort_t *pDisp, const void *self)
}
}
sacms_SetupTopMenu();
sacms_SetupTopMenu(NULL);
return 0;
}
@ -421,8 +421,10 @@ static long saCmsCommence(displayPort_t *pDisp, const void *self)
return MENU_CHAIN_BACK;
}
static long saCmsSetPORFreqOnEnter(void)
static long saCmsSetPORFreqOnEnter(const OSD_Entry *from)
{
UNUSED(from);
saCmsORFreqNew = saCmsORFreq;
return 0;
@ -456,8 +458,10 @@ static char *saCmsUserFreqGetString(void)
return pbuf;
}
static long saCmsSetUserFreqOnEnter(void)
static long saCmsSetUserFreqOnEnter(const OSD_Entry *from)
{
UNUSED(from);
saCmsUserFreqNew = saCmsUserFreq;
return 0;
@ -612,8 +616,10 @@ static const OSD_Entry saCmsMenuOfflineEntries[] =
CMS_Menu cmsx_menuVtxSmartAudio; // Forward
static long sacms_SetupTopMenu(void)
static long sacms_SetupTopMenu(const OSD_Entry *from)
{
UNUSED(from);
if (saCmsDeviceStatus) {
if (saCmsFselMode == 0)
cmsx_menuVtxSmartAudio.entries = saCmsMenuChanModeEntries;

View file

@ -186,8 +186,10 @@ static void trampCmsInitSettings(void)
}
}
static long trampCmsOnEnter(void)
static long trampCmsOnEnter(const OSD_Entry *from)
{
UNUSED(from);
trampCmsInitSettings();
return 0;
}

View file

@ -46,9 +46,6 @@ typedef enum
OME_FLOAT, //only up to 255 value and cant be 2.55 or 25.5, just for PID's
OME_Setting,
//wlasciwosci elementow
#ifdef USE_OSD
OME_VISIBLE,
#endif
OME_TAB,
OME_END,
@ -81,9 +78,10 @@ typedef struct
#define OSD_LABEL_DATA_DYN_ENTRY(label, data) ((OSD_Entry){ label, OME_Label, NULL, data, DYNAMIC })
#define OSD_LABEL_FUNC_DYN_ENTRY(label, fn) ((OSD_Entry){ label, OME_LabelFunc, NULL, fn, DYNAMIC })
#define OSD_BACK_ENTRY ((OSD_Entry){ "BACK", OME_Back, NULL, NULL, 0 })
#define OSD_SUBMENU_ENTRY(label, menu) ((OSD_Entry){ label, OME_Submenu, cmsMenuChange, menu, 0 })
#define OSD_SUBMENU_ENTRY(label, menu) ((OSD_Entry){ label, OME_Submenu, NULL, menu, 0 })
#define OSD_FUNC_CALL_ENTRY(label, fn) ((OSD_Entry){ label, OME_Funcall, fn, NULL, 0 })
#define OSD_BOOL_ENTRY(label, val) ((OSD_Entry){ label, OME_Bool, NULL, val, 0 })
#define OSD_BOOL_CALLBACK_ENTRY(label, cb, val) ((OSD_Entry){ label, OME_Bool, cb, val, 0 })
#define OSD_BOOL_FUNC_ENTRY(label, fn) ((OSD_Entry){ label, OME_BoolFunc, NULL, fn, 0 })
#define OSD_UINT8_ENTRY(label, val) ((OSD_Entry){ label, OME_UINT8, NULL, val, 0 })
#define OSD_UINT8_CALLBACK_ENTRY(label, cb, val)((OSD_Entry){ label, OME_UINT8, cb, val, 0 })
@ -109,7 +107,7 @@ typedef enum {
// Use a function and data type to make sure switches are exhaustive
static inline CMSDataType_e CMS_DATA_TYPE(const OSD_Entry *entry) { return entry->flags & 0xF0; }
typedef long (*CMSMenuFuncPtr)(void);
typedef long (*CMSMenuFuncPtr)(const OSD_Entry *from);
// Special return value(s) for function chaining by CMSMenuFuncPtr
#define MENU_CHAIN_BACK (-1) // Causes automatic cmsMenuBack

View file

@ -1643,6 +1643,127 @@ static void cliFlashRead(char *cmdline)
#endif
#endif
#ifdef USE_OSD
static void printOsdLayout(uint8_t dumpMask, const osdConfig_t *osdConfig, const osdConfig_t *osdConfigDefault, int layout, int item)
{
// "<layout> <item> <col> <row> <visible>"
const char *format = "osd_layout %d %d %d %d %c";
for (int ii = 0; ii < OSD_LAYOUT_COUNT; ii++) {
if (layout >= 0 && layout != ii) {
continue;
}
const uint16_t *layoutItems = osdConfig->item_pos[ii];
const uint16_t *defaultLayoutItems = osdConfigDefault->item_pos[ii];
for (int jj = 0; jj < OSD_ITEM_COUNT; jj++) {
if (item >= 0 && item != jj) {
continue;
}
bool equalsDefault = layoutItems[jj] == defaultLayoutItems[jj];
cliDefaultPrintLinef(dumpMask, equalsDefault, format,
ii, jj,
OSD_X(defaultLayoutItems[jj]),
OSD_Y(defaultLayoutItems[jj]),
OSD_VISIBLE(defaultLayoutItems[jj]) ? 'V' : 'H');
cliDumpPrintLinef(dumpMask, equalsDefault, format,
ii, jj,
OSD_X(layoutItems[jj]),
OSD_Y(layoutItems[jj]),
OSD_VISIBLE(layoutItems[jj]) ? 'V' : 'H');
}
}
}
static void cliOsdLayout(char *cmdline)
{
char * saveptr;
int layout = -1;
int item = -1;
int col = 0;
int row = 0;
bool visible = false;
char *tok = strtok_r(cmdline, " ", &saveptr);
int ii;
for (ii = 0; tok != NULL; ii++, tok = strtok_r(NULL, " ", &saveptr)) {
switch (ii) {
case 0:
layout = fastA2I(tok);
if (layout < 0 || layout >= OSD_LAYOUT_COUNT) {
cliShowParseError();
return;
}
break;
case 1:
item = fastA2I(tok);
if (item < 0 || item >= OSD_ITEM_COUNT) {
cliShowParseError();
return;
}
break;
case 2:
col = fastA2I(tok);
if (col < 0 || col > OSD_X(OSD_POS_MAX)) {
cliShowParseError();
return;
}
break;
case 3:
row = fastA2I(tok);
if (row < 0 || row > OSD_Y(OSD_POS_MAX)) {
cliShowParseError();
return;
}
break;
case 4:
switch (*tok) {
case 'H':
visible = false;
break;
case 'V':
visible = true;
break;
default:
cliShowParseError();
return;
}
break;
default:
cliShowParseError();
return;
}
}
switch (ii) {
case 0:
FALLTHROUGH;
case 1:
FALLTHROUGH;
case 2:
// No args, or just layout or layout and item. If any of them not provided,
// it will be the -1 that we used during initialization, so printOsdLayout()
// won't use them for filtering.
printOsdLayout(DUMP_MASTER, osdConfig(), osdConfig(), layout, item);
break;
case 4:
// No visibility provided. Keep the previous one.
visible = OSD_VISIBLE(osdConfig()->item_pos[layout][item]);
FALLTHROUGH;
case 5:
// Layout, item, pos and visibility. Set the item.
osdConfigMutable()->item_pos[layout][item] = OSD_POS(col, row) | (visible ? OSD_VISIBLE_FLAG : 0);
break;
default:
// Unhandled
cliShowParseError();
return;
}
}
#endif
static void printFeature(uint8_t dumpMask, const featureConfig_t *featureConfig, const featureConfig_t *featureConfigDefault)
{
uint32_t mask = featureConfig->enabledFeatures;
@ -2541,6 +2662,11 @@ static void printConfig(const char *cmdline, bool doDiff)
cliPrintHashLine("rxrange");
printRxRange(dumpMask, rxChannelRangeConfigs_CopyArray, rxChannelRangeConfigs(0));
#ifdef USE_OSD
cliPrintHashLine("osd_layout");
printOsdLayout(dumpMask, &osdConfig_Copy, osdConfig(), -1, -1);
#endif
cliPrintHashLine("master");
dumpAllValues(MASTER_VALUE, dumpMask);
@ -2685,6 +2811,9 @@ const clicmd_t cmdTable[] = {
CLI_COMMAND_DEF("tasks", "show task stats", NULL, cliTasks),
#endif
CLI_COMMAND_DEF("version", "show version", NULL, cliVersion),
#ifdef USE_OSD
CLI_COMMAND_DEF("osd_layout", "get or set the layout of OSD items", "[<layout> [<item> [<col> <row> [<visible>]]]]", cliOsdLayout),
#endif
};
static void cliHelp(char *cmdline)

View file

@ -968,7 +968,7 @@ static bool mspFcProcessOutCommand(uint16_t cmdMSP, sbuf_t *dst, mspPostProcessF
sbufWriteU16(dst, osdConfig()->dist_alarm);
sbufWriteU16(dst, osdConfig()->neg_alt_alarm);
for (int i = 0; i < OSD_ITEM_COUNT; i++) {
sbufWriteU16(dst, osdConfig()->item_pos[i]);
sbufWriteU16(dst, osdConfig()->item_pos[0][i]);
}
#else
sbufWriteU8(dst, 0); // OSD not supported
@ -1305,6 +1305,38 @@ static bool mspFcProcessOutCommand(uint16_t cmdMSP, sbuf_t *dst, mspPostProcessF
sbufWriteU8(dst, MAX_SUPPORTED_SERVOS);
break;
#if defined(USE_OSD)
case MSP2_INAV_OSD_LAYOUTS:
sbufWriteU8(dst, OSD_LAYOUT_COUNT);
sbufWriteU8(dst, OSD_ITEM_COUNT);
for (unsigned ii = 0; ii < OSD_LAYOUT_COUNT; ii++) {
for (unsigned jj = 0; jj < OSD_ITEM_COUNT; jj++) {
sbufWriteU16(dst, osdConfig()->item_pos[ii][jj]);
}
}
break;
case MSP2_INAV_OSD_ALARMS:
sbufWriteU8(dst, osdConfig()->rssi_alarm);
sbufWriteU16(dst, osdConfig()->time_alarm);
sbufWriteU16(dst, osdConfig()->alt_alarm);
sbufWriteU16(dst, osdConfig()->dist_alarm);
sbufWriteU16(dst, osdConfig()->neg_alt_alarm);
break;
case MSP2_INAV_OSD_PREFERENCES:
sbufWriteU8(dst, osdConfig()->video_system);
sbufWriteU8(dst, osdConfig()->main_voltage_decimals);
sbufWriteU8(dst, osdConfig()->ahi_reverse_roll);
sbufWriteU8(dst, osdConfig()->crosshairs_style);
sbufWriteU8(dst, osdConfig()->left_sidebar_scroll);
sbufWriteU8(dst, osdConfig()->right_sidebar_scroll);
sbufWriteU8(dst, osdConfig()->sidebar_scroll_arrows);
sbufWriteU8(dst, osdConfig()->units);
sbufWriteU8(dst, osdConfig()->stats_energy_unit);
break;
#endif
default:
return false;
}
@ -2018,7 +2050,7 @@ static mspResult_e mspFcProcessInCommand(uint16_t cmdMSP, sbuf_t *src)
} else {
// set a position setting
if ((dataSize >= 3) && (tmp_u8 < OSD_ITEM_COUNT)) // tmp_u8 == addr
osdConfigMutable()->item_pos[tmp_u8] = sbufReadU16(src);
osdConfigMutable()->item_pos[0][tmp_u8] = sbufReadU16(src);
else
return MSP_RESULT_ERROR;
}
@ -2389,6 +2421,52 @@ static mspResult_e mspFcProcessInCommand(uint16_t cmdMSP, sbuf_t *src)
mixerUpdateStateFlags();
break;
#if defined(USE_OSD)
case MSP2_INAV_OSD_SET_LAYOUT_ITEM:
{
uint8_t layout;
if (!sbufReadU8Safe(&layout, src)) {
return MSP_RESULT_ERROR;
}
uint8_t item;
if (!sbufReadU8Safe(&item, src)) {
return MSP_RESULT_ERROR;
}
if (!sbufReadU16Safe(&osdConfigMutable()->item_pos[layout][item], src)) {
return MSP_RESULT_ERROR;
}
osdStartFullRedraw();
}
break;
case MSP2_INAV_OSD_SET_ALARMS:
{
sbufReadU8Safe(&osdConfigMutable()->rssi_alarm, src);
sbufReadU16Safe(&osdConfigMutable()->time_alarm, src);
sbufReadU16Safe(&osdConfigMutable()->alt_alarm, src);
sbufReadU16Safe(&osdConfigMutable()->dist_alarm, src);
sbufReadU16Safe(&osdConfigMutable()->neg_alt_alarm, src);
osdStartFullRedraw();
}
break;
case MSP2_INAV_OSD_SET_PREFERENCES:
{
sbufReadU8Safe(&osdConfigMutable()->video_system, src);
sbufReadU8Safe(&osdConfigMutable()->main_voltage_decimals, src);
sbufReadU8Safe(&osdConfigMutable()->ahi_reverse_roll, src);
sbufReadU8Safe(&osdConfigMutable()->crosshairs_style, src);
sbufReadU8Safe(&osdConfigMutable()->left_sidebar_scroll, src);
sbufReadU8Safe(&osdConfigMutable()->right_sidebar_scroll, src);
sbufReadU8Safe(&osdConfigMutable()->sidebar_scroll_arrows, src);
sbufReadU8Safe(&osdConfigMutable()->units, src);
sbufReadU8Safe(&osdConfigMutable()->stats_energy_unit, src);
osdStartFullRedraw();
}
break;
#endif
default:
return MSP_RESULT_ERROR;
}

View file

@ -30,6 +30,8 @@
#include "fc/fc_msp_box.h"
#include "fc/runtime_config.h"
#include "io/osd.h"
#include "sensors/diagnostics.h"
#include "sensors/sensors.h"
@ -72,6 +74,9 @@ static const box_t boxes[CHECKBOX_ITEM_COUNT + 1] = {
{ BOXCAMERA1, "CAMERA CONTROL 1", 39 },
{ BOXCAMERA2, "CAMERA CONTROL 2", 40 },
{ BOXCAMERA3, "CAMERA CONTROL 3", 41 },
{ BOXOSDALT1, "OSD ALT 1", 42 },
{ BOXOSDALT2, "OSD ALT 2", 43 },
{ BOXOSDALT3, "OSD ALT 3", 43 },
{ CHECKBOX_ITEM_COUNT, NULL, 0xFF }
};
@ -240,6 +245,18 @@ void initActiveBoxIds(void)
activeBoxIds[activeBoxIdCount++] = BOXCAMERA2;
activeBoxIds[activeBoxIdCount++] = BOXCAMERA3;
#endif
#if defined(USE_OSD) && defined(OSD_LAYOUT_COUNT)
#if OSD_LAYOUT_COUNT > 0
activeBoxIds[activeBoxIdCount++] = BOXOSDALT1;
#if OSD_LAYOUT_COUNT > 1
activeBoxIds[activeBoxIdCount++] = BOXOSDALT2;
#if OSD_LAYOUT_COUNT > 2
activeBoxIds[activeBoxIdCount++] = BOXOSDALT3;
#endif
#endif
#endif
#endif
}
#define IS_ENABLED(mask) (mask == 0 ? 0 : 1)
@ -289,6 +306,9 @@ void packBoxModeFlags(boxBitmask_t * mspBoxModeFlags)
CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXCAMERA1)), BOXCAMERA1);
CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXCAMERA2)), BOXCAMERA2);
CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXCAMERA3)), BOXCAMERA3);
CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXOSDALT1)), BOXOSDALT1);
CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXOSDALT2)), BOXOSDALT2);
CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXOSDALT3)), BOXOSDALT3);
memset(mspBoxModeFlags, 0, sizeof(boxBitmask_t));
for (uint32_t i = 0; i < activeBoxIdCount; i++) {

View file

@ -41,7 +41,15 @@ static bool isUsingNAVModes = false;
#endif
boxBitmask_t rcModeActivationMask; // one bit per mode defined in boxId_e
STATIC_ASSERT(CHECKBOX_ITEM_COUNT <= 32, too_many_box_modes);
// TODO(alberto): It looks like we can now safely remove this assert, since everything
// but BB is able to handle more than 32 boxes and all the definitions use
// CHECKBOX_ITEM_COUNT rather than hardcoded values. Note, however, that BB will only
// log the first 32 flight modes, so the ones affecting actual flight should be <= 32.
//
// Leaving the assert commented for now, just in case there are some unexpected issues
// and someone else has to debug it.
// STATIC_ASSERT(CHECKBOX_ITEM_COUNT <= 32, too_many_box_modes);
PG_REGISTER_ARRAY(modeActivationCondition_t, MAX_MODE_ACTIVATION_CONDITION_COUNT, modeActivationConditions, PG_MODE_ACTIVATION_PROFILE, 0);
PG_REGISTER(modeActivationOperatorConfig_t, modeActivationOperatorConfig, PG_MODE_ACTIVATION_OPERATOR_CONFIG, 0);

View file

@ -56,6 +56,9 @@ typedef enum {
BOXCAMERA1 = 29,
BOXCAMERA2 = 30,
BOXCAMERA3 = 31,
BOXOSDALT1 = 32,
BOXOSDALT2 = 33,
BOXOSDALT3 = 34,
CHECKBOX_ITEM_COUNT
} boxId_e;

View file

@ -1454,158 +1454,10 @@ groups:
- name: osd_sidebar_scroll_arrows
field: sidebar_scroll_arrows
type: bool
- name: osd_main_voltage_pos
field: item_pos[OSD_MAIN_BATT_VOLTAGE]
min: 0
max: OSD_POS_MAX_CLI
- name: osd_main_voltage_decimals
field: main_voltage_decimals
min: 1
max: 2
- name: osd_rssi_pos
field: item_pos[OSD_RSSI_VALUE]
min: 0
max: OSD_POS_MAX_CLI
- name: osd_flytimer_pos
field: item_pos[OSD_FLYTIME]
min: 0
max: OSD_POS_MAX_CLI
- name: osd_ontime_pos
field: item_pos[OSD_ONTIME]
min: 0
max: OSD_POS_MAX_CLI
- name: osd_flymode_pos
field: item_pos[OSD_FLYMODE]
min: 0
max: OSD_POS_MAX_CLI
- name: osd_throttle_pos
field: item_pos[OSD_THROTTLE_POS]
min: 0
max: OSD_POS_MAX_CLI
- name: osd_vtx_channel_pos
field: item_pos[OSD_VTX_CHANNEL]
min: 0
max: OSD_POS_MAX_CLI
- name: osd_crosshairs_pos
field: item_pos[OSD_CROSSHAIRS]
min: 0
max: OSD_POS_MAX_CLI
- name: osd_artificial_horizon_pos
field: item_pos[OSD_ARTIFICIAL_HORIZON]
min: 0
max: OSD_POS_MAX_CLI
- name: osd_horizon_sidebars_pos
field: item_pos[OSD_HORIZON_SIDEBARS]
min: 0
max: OSD_POS_MAX_CLI
- name: osd_current_draw_pos
field: item_pos[OSD_CURRENT_DRAW]
min: 0
max: OSD_POS_MAX_CLI
- name: osd_mah_drawn_pos
field: item_pos[OSD_MAH_DRAWN]
min: 0
max: OSD_POS_MAX_CLI
- name: osd_wh_drawn_pos
field: item_pos[OSD_WH_DRAWN]
min: 0
max: OSD_POS_MAX_CLI
- name: osd_bat_remaining_capacity_pos
field: item_pos[OSD_BATTERY_REMAINING_CAPACITY]
min: 0
max: OSD_POS_MAX_CLI
- name: osd_bat_remaining_percent_pos
field: item_pos[OSD_BATTERY_REMAINING_PERCENT]
min: 0
max: OSD_POS_MAX_CLI
- name: osd_craft_name_pos
field: item_pos[OSD_CRAFT_NAME]
min: 0
max: OSD_POS_MAX_CLI
- name: osd_gps_speed_pos
field: item_pos[OSD_GPS_SPEED]
min: 0
max: OSD_POS_MAX_CLI
- name: osd_gps_sats_pos
field: item_pos[OSD_GPS_SATS]
min: 0
max: OSD_POS_MAX_CLI
- name: osd_gps_lon_pos
field: item_pos[OSD_GPS_LON]
min: 0
max: OSD_POS_MAX_CLI
- name: osd_gps_lat_pos
field: item_pos[OSD_GPS_LAT]
min: 0
max: OSD_POS_MAX_CLI
- name: osd_home_dir_pos
field: item_pos[OSD_HOME_DIR]
min: 0
max: OSD_POS_MAX_CLI
- name: osd_home_dist_pos
field: item_pos[OSD_HOME_DIST]
min: 0
max: OSD_POS_MAX_CLI
- name: osd_altitude_pos
field: item_pos[OSD_ALTITUDE]
min: 0
max: OSD_POS_MAX_CLI
- name: osd_vario_pos
field: item_pos[OSD_VARIO]
min: 0
max: OSD_POS_MAX_CLI
- name: osd_vario_num_pos
field: item_pos[OSD_VARIO_NUM]
min: 0
max: OSD_POS_MAX_CLI
- name: osd_pid_roll_pos
field: item_pos[OSD_ROLL_PIDS]
min: 0
max: OSD_POS_MAX_CLI
- name: osd_pid_pitch_pos
field: item_pos[OSD_PITCH_PIDS]
min: 0
max: OSD_POS_MAX_CLI
- name: osd_pid_yaw_pos
field: item_pos[OSD_YAW_PIDS]
min: 0
max: OSD_POS_MAX_CLI
- name: osd_power_pos
field: item_pos[OSD_POWER]
min: 0
max: OSD_POS_MAX_CLI
- name: osd_air_speed_pos
field: item_pos[OSD_AIR_SPEED]
min: 0
max: OSD_POS_MAX_CLI
- name: osd_ontime_flytime_pos
field: item_pos[OSD_ONTIME_FLYTIME]
max: OSD_POS_MAX_CLI
- name: osd_rtc_time_pos
field: item_pos[OSD_RTC_TIME]
max: OSD_POS_MAX_CLI
- name: osd_messages_pos
field: item_pos[OSD_MESSAGES]
max: OSD_POS_MAX_CLI
- name: osd_gps_hdop_pos
field: item_pos[OSD_GPS_HDOP]
max: OSD_POS_MAX_CLI
- name: osd_main_cell_voltage_pos
field: item_pos[OSD_MAIN_BATT_CELL_VOLTAGE]
max: OSD_POS_MAX_CLI
- name: osd_throttle_auto_thr_pos
field: item_pos[OSD_THROTTLE_POS_AUTO_THR]
max: OSD_POS_MAX_CLI
- name: osd_heading_graph_pos
field: item_pos[OSD_HEADING_GRAPH]
max: OSD_POS_MAX_CLI
- name: osd_efficiency_mah_pos
field: item_pos[OSD_EFFICIENCY_MAH_PER_KM]
max: OSD_POS_MAX_CLI
- name: osd_efficiency_wh_pos
field: item_pos[OSD_EFFICIENCY_WH_PER_KM]
max: OSD_POS_MAX_CLI
- name: PG_SYSTEM_CONFIG
type: systemConfig_t

View file

@ -86,11 +86,6 @@
#define VIDEO_BUFFER_CHARS_PAL 480
#define IS_DISPLAY_PAL (displayScreenSize(osdDisplayPort) == VIDEO_BUFFER_CHARS_PAL)
// Character coordinate and attributes
#define OSD_POS(x,y) (x | (y << 5))
#define OSD_X(x) (x & 0x001F)
#define OSD_Y(x) ((x >> 5) & 0x001F)
#define CENTIMETERS_TO_CENTIFEET(cm) (cm * (328 / 100.0))
#define CENTIMETERS_TO_FEET(cm) (cm * (328 / 10000.0))
#define CENTIMETERS_TO_METERS(cm) (cm / 100)
@ -120,6 +115,8 @@
})
static timeUs_t flyTime = 0;
static unsigned currentLayout = 0;
static int layoutOverride = -1;
typedef struct statistic_s {
uint16_t max_speed;
@ -161,7 +158,7 @@ static displayPort_t *osdDisplayPort;
#define AH_SIDEBAR_WIDTH_POS 7
#define AH_SIDEBAR_HEIGHT_POS 3
PG_REGISTER_WITH_RESET_FN(osdConfig_t, osdConfig, PG_OSD_CONFIG, 0);
PG_REGISTER_WITH_RESET_FN(osdConfig_t, osdConfig, PG_OSD_CONFIG, 1);
static int digitCount(int32_t value)
{
@ -265,7 +262,7 @@ static int digitCount(int32_t value)
*/
static void osdFormatDistanceSymbol(char *buff, int32_t dist)
{
switch (osdConfig()->units) {
switch ((osd_unit_e)osdConfig()->units) {
case OSD_UNIT_IMPERIAL:
if (osdFormatCentiNumber(buff + 1, CENTIMETERS_TO_CENTIFEET(dist), FEET_PER_MILE, 0, 3, 3)) {
buff[0] = SYM_DIST_MI;
@ -292,7 +289,7 @@ static void osdFormatDistanceSymbol(char *buff, int32_t dist)
static void osdFormatDistanceStr(char *buff, int32_t dist)
{
int32_t centifeet;
switch (osdConfig()->units) {
switch ((osd_unit_e)osdConfig()->units) {
case OSD_UNIT_IMPERIAL:
centifeet = CENTIMETERS_TO_CENTIFEET(dist);
if (abs(centifeet) < FEET_PER_MILE * 100 / 2) {
@ -325,7 +322,7 @@ static void osdFormatDistanceSymbol(char *buff, int32_t dist)
*/
static int32_t osdConvertVelocityToUnit(int32_t vel)
{
switch (osdConfig()->units) {
switch ((osd_unit_e)osdConfig()->units) {
case OSD_UNIT_UK:
FALLTHROUGH;
case OSD_UNIT_IMPERIAL:
@ -343,7 +340,7 @@ static int32_t osdConvertVelocityToUnit(int32_t vel)
*/
static void osdFormatVelocityStr(char* buff, int32_t vel)
{
switch (osdConfig()->units) {
switch ((osd_unit_e)osdConfig()->units) {
case OSD_UNIT_UK:
FALLTHROUGH;
case OSD_UNIT_IMPERIAL:
@ -362,7 +359,7 @@ static void osdFormatVelocityStr(char* buff, int32_t vel)
*/
static void osdFormatAltitudeSymbol(char *buff, int32_t alt)
{
switch (osdConfig()->units) {
switch ((osd_unit_e)osdConfig()->units) {
case OSD_UNIT_IMPERIAL:
if (osdFormatCentiNumber(buff + 1, CENTIMETERS_TO_CENTIFEET(alt), 1000, 0, 2, 3)) {
// Scaled to kft
@ -394,7 +391,7 @@ static void osdFormatAltitudeSymbol(char *buff, int32_t alt)
static void osdFormatAltitudeStr(char *buff, int32_t alt)
{
int32_t value;
switch (osdConfig()->units) {
switch ((osd_unit_e)osdConfig()->units) {
case OSD_UNIT_IMPERIAL:
value = CENTIMETERS_TO_FEET(alt);
tfp_sprintf(buff, "%d%c", value, SYM_FT);
@ -818,12 +815,13 @@ static int16_t osdGetHeading(void)
static bool osdDrawSingleElement(uint8_t item)
{
if (!VISIBLE(osdConfig()->item_pos[item])) {
uint16_t pos = osdConfig()->item_pos[currentLayout][item];
if (!OSD_VISIBLE(pos)) {
return false;
}
uint8_t elemPosX = OSD_X(osdConfig()->item_pos[item]);
uint8_t elemPosY = OSD_Y(osdConfig()->item_pos[item]);
uint8_t elemPosX = OSD_X(pos);
uint8_t elemPosY = OSD_Y(pos);
textAttributes_t elemAttr = TEXT_ATTRIBUTES_NONE;
char buff[32];
@ -1082,7 +1080,7 @@ static bool osdDrawSingleElement(uint8_t item)
case OSD_CROSSHAIRS:
osdCrosshairsBounds(&elemPosX, &elemPosY, NULL);
switch (osdConfig()->crosshairs_style) {
switch ((osd_crosshairs_style_e)osdConfig()->crosshairs_style) {
case OSD_CROSSHAIRS_STYLE_DEFAULT:
buff[0] = SYM_AH_CENTER_LINE;
buff[1] = SYM_AH_CENTER;
@ -1128,7 +1126,7 @@ static bool osdDrawSingleElement(uint8_t item)
// Convert pitchAngle to y compensation value
pitchAngle = ((pitchAngle * 25) / AH_MAX_PITCH) - 41; // 41 = 4 * 9 + 5
crosshairsVisible = VISIBLE(osdConfig()->item_pos[OSD_CROSSHAIRS]);
crosshairsVisible = OSD_VISIBLE(osdConfig()->item_pos[currentLayout][OSD_CROSSHAIRS]);
if (crosshairsVisible) {
uint8_t cx, cy, cl;
osdCrosshairsBounds(&cx, &cy, &cl);
@ -1271,7 +1269,7 @@ static bool osdDrawSingleElement(uint8_t item)
{
int16_t value = getEstimatedActualVelocity(Z);
char sym;
switch (osdConfig()->units) {
switch ((osd_unit_e)osdConfig()->units) {
case OSD_UNIT_IMPERIAL:
// Convert to centifeet/s
value = CENTIMETERS_TO_CENTIFEET(value);
@ -1603,67 +1601,75 @@ static uint8_t osdIncElementIndex(uint8_t elementIndex)
void osdDrawNextElement(void)
{
static uint8_t elementIndex = 0;
// Prevent infinite loop when no elements are enabled
uint8_t index = elementIndex;
do {
elementIndex = osdIncElementIndex(elementIndex);
} while(!osdDrawSingleElement(elementIndex));
} while(!osdDrawSingleElement(elementIndex) && index != elementIndex);
}
void pgResetFn_osdConfig(osdConfig_t *osdConfig)
{
osdConfig->item_pos[OSD_ALTITUDE] = OSD_POS(1, 0) | VISIBLE_FLAG;
osdConfig->item_pos[OSD_MAIN_BATT_VOLTAGE] = OSD_POS(12, 0) | VISIBLE_FLAG;
osdConfig->item_pos[OSD_RSSI_VALUE] = OSD_POS(23, 0) | VISIBLE_FLAG;
osdConfig->item_pos[0][OSD_ALTITUDE] = OSD_POS(1, 0) | OSD_VISIBLE_FLAG;
osdConfig->item_pos[0][OSD_MAIN_BATT_VOLTAGE] = OSD_POS(12, 0) | OSD_VISIBLE_FLAG;
osdConfig->item_pos[0][OSD_RSSI_VALUE] = OSD_POS(23, 0) | OSD_VISIBLE_FLAG;
//line 2
osdConfig->item_pos[OSD_HOME_DIST] = OSD_POS(1, 1);
osdConfig->item_pos[OSD_TRIP_DIST] = OSD_POS(1, 2);
osdConfig->item_pos[OSD_MAIN_BATT_CELL_VOLTAGE] = OSD_POS(12, 1);
osdConfig->item_pos[OSD_GPS_SPEED] = OSD_POS(23, 1);
osdConfig->item_pos[0][OSD_HOME_DIST] = OSD_POS(1, 1);
osdConfig->item_pos[0][OSD_TRIP_DIST] = OSD_POS(1, 2);
osdConfig->item_pos[0][OSD_MAIN_BATT_CELL_VOLTAGE] = OSD_POS(12, 1);
osdConfig->item_pos[0][OSD_GPS_SPEED] = OSD_POS(23, 1);
osdConfig->item_pos[OSD_THROTTLE_POS] = OSD_POS(1, 2) | VISIBLE_FLAG;
osdConfig->item_pos[OSD_THROTTLE_POS_AUTO_THR] = OSD_POS(6, 2);
osdConfig->item_pos[OSD_HEADING] = OSD_POS(12, 2);
osdConfig->item_pos[OSD_HEADING_GRAPH] = OSD_POS(18, 2);
osdConfig->item_pos[OSD_CURRENT_DRAW] = OSD_POS(1, 3) | VISIBLE_FLAG;
osdConfig->item_pos[OSD_MAH_DRAWN] = OSD_POS(1, 4) | VISIBLE_FLAG;
osdConfig->item_pos[OSD_WH_DRAWN] = OSD_POS(1, 5);
osdConfig->item_pos[OSD_BATTERY_REMAINING_CAPACITY] = OSD_POS(1, 6);
osdConfig->item_pos[OSD_BATTERY_REMAINING_PERCENT] = OSD_POS(1, 7);
osdConfig->item_pos[0][OSD_THROTTLE_POS] = OSD_POS(1, 2) | OSD_VISIBLE_FLAG;
osdConfig->item_pos[0][OSD_THROTTLE_POS_AUTO_THR] = OSD_POS(6, 2);
osdConfig->item_pos[0][OSD_HEADING] = OSD_POS(12, 2);
osdConfig->item_pos[0][OSD_HEADING_GRAPH] = OSD_POS(18, 2);
osdConfig->item_pos[0][OSD_CURRENT_DRAW] = OSD_POS(1, 3) | OSD_VISIBLE_FLAG;
osdConfig->item_pos[0][OSD_MAH_DRAWN] = OSD_POS(1, 4) | OSD_VISIBLE_FLAG;
osdConfig->item_pos[0][OSD_WH_DRAWN] = OSD_POS(1, 5);
osdConfig->item_pos[0][OSD_BATTERY_REMAINING_CAPACITY] = OSD_POS(1, 6);
osdConfig->item_pos[0][OSD_BATTERY_REMAINING_PERCENT] = OSD_POS(1, 7);
osdConfig->item_pos[OSD_EFFICIENCY_MAH_PER_KM] = OSD_POS(1, 5);
osdConfig->item_pos[OSD_EFFICIENCY_WH_PER_KM] = OSD_POS(1, 5);
osdConfig->item_pos[0][OSD_EFFICIENCY_MAH_PER_KM] = OSD_POS(1, 5);
osdConfig->item_pos[0][OSD_EFFICIENCY_WH_PER_KM] = OSD_POS(1, 5);
// avoid OSD_VARIO under OSD_CROSSHAIRS
osdConfig->item_pos[OSD_VARIO] = OSD_POS(23, 5);
osdConfig->item_pos[0][OSD_VARIO] = OSD_POS(23, 5);
// OSD_VARIO_NUM at the right of OSD_VARIO
osdConfig->item_pos[OSD_VARIO_NUM] = OSD_POS(24, 7);
osdConfig->item_pos[OSD_HOME_DIR] = OSD_POS(14, 11);
osdConfig->item_pos[OSD_ARTIFICIAL_HORIZON] = OSD_POS(8, 6) | VISIBLE_FLAG;
osdConfig->item_pos[OSD_HORIZON_SIDEBARS] = OSD_POS(8, 6) | VISIBLE_FLAG;
osdConfig->item_pos[0][OSD_VARIO_NUM] = OSD_POS(24, 7);
osdConfig->item_pos[0][OSD_HOME_DIR] = OSD_POS(14, 11);
osdConfig->item_pos[0][OSD_ARTIFICIAL_HORIZON] = OSD_POS(8, 6) | OSD_VISIBLE_FLAG;
osdConfig->item_pos[0][OSD_HORIZON_SIDEBARS] = OSD_POS(8, 6) | OSD_VISIBLE_FLAG;
osdConfig->item_pos[OSD_CRAFT_NAME] = OSD_POS(20, 2);
osdConfig->item_pos[OSD_VTX_CHANNEL] = OSD_POS(8, 6);
osdConfig->item_pos[0][OSD_CRAFT_NAME] = OSD_POS(20, 2);
osdConfig->item_pos[0][OSD_VTX_CHANNEL] = OSD_POS(8, 6);
osdConfig->item_pos[OSD_ONTIME] = OSD_POS(23, 8);
osdConfig->item_pos[OSD_FLYTIME] = OSD_POS(23, 9);
osdConfig->item_pos[OSD_ONTIME_FLYTIME] = OSD_POS(23, 11) | VISIBLE_FLAG;
osdConfig->item_pos[OSD_RTC_TIME] = OSD_POS(23, 12);
osdConfig->item_pos[0][OSD_ONTIME] = OSD_POS(23, 8);
osdConfig->item_pos[0][OSD_FLYTIME] = OSD_POS(23, 9);
osdConfig->item_pos[0][OSD_ONTIME_FLYTIME] = OSD_POS(23, 11) | OSD_VISIBLE_FLAG;
osdConfig->item_pos[0][OSD_RTC_TIME] = OSD_POS(23, 12);
osdConfig->item_pos[OSD_GPS_SATS] = OSD_POS(0, 11) | VISIBLE_FLAG;
osdConfig->item_pos[OSD_GPS_HDOP] = OSD_POS(0, 10);
osdConfig->item_pos[0][OSD_GPS_SATS] = OSD_POS(0, 11) | OSD_VISIBLE_FLAG;
osdConfig->item_pos[0][OSD_GPS_HDOP] = OSD_POS(0, 10);
osdConfig->item_pos[OSD_GPS_LAT] = OSD_POS(0, 12);
osdConfig->item_pos[OSD_FLYMODE] = OSD_POS(12, 12) | VISIBLE_FLAG;
osdConfig->item_pos[OSD_GPS_LON] = OSD_POS(18, 12);
osdConfig->item_pos[0][OSD_GPS_LAT] = OSD_POS(0, 12);
osdConfig->item_pos[0][OSD_FLYMODE] = OSD_POS(12, 12) | OSD_VISIBLE_FLAG;
osdConfig->item_pos[0][OSD_GPS_LON] = OSD_POS(18, 12);
osdConfig->item_pos[OSD_ROLL_PIDS] = OSD_POS(2, 10);
osdConfig->item_pos[OSD_PITCH_PIDS] = OSD_POS(2, 11);
osdConfig->item_pos[OSD_YAW_PIDS] = OSD_POS(2, 12);
osdConfig->item_pos[OSD_POWER] = OSD_POS(15, 1);
osdConfig->item_pos[0][OSD_ROLL_PIDS] = OSD_POS(2, 10);
osdConfig->item_pos[0][OSD_PITCH_PIDS] = OSD_POS(2, 11);
osdConfig->item_pos[0][OSD_YAW_PIDS] = OSD_POS(2, 12);
osdConfig->item_pos[0][OSD_POWER] = OSD_POS(15, 1);
osdConfig->item_pos[OSD_AIR_SPEED] = OSD_POS(3, 5);
osdConfig->item_pos[0][OSD_AIR_SPEED] = OSD_POS(3, 5);
// Under OSD_FLYMODE. TODO: Might not be visible on NTSC?
osdConfig->item_pos[OSD_MESSAGES] = OSD_POS(1, 13) | VISIBLE_FLAG;
osdConfig->item_pos[0][OSD_MESSAGES] = OSD_POS(1, 13) | OSD_VISIBLE_FLAG;
for (unsigned ii = 1; ii < OSD_LAYOUT_COUNT; ii++) {
for (unsigned jj = 0; jj < ARRAYLEN(osdConfig->item_pos[0]); jj++) {
osdConfig->item_pos[ii][jj] = osdConfig->item_pos[0][jj] & ~OSD_VISIBLE_FLAG;
}
}
osdConfig->rssi_alarm = 20;
osdConfig->time_alarm = 10;
@ -2001,6 +2007,34 @@ void osdUpdate(timeUs_t currentTimeUs)
return;
}
#if defined(OSD_ALTERNATE_LAYOUT_COUNT) && OSD_ALTERNATE_LAYOUT_COUNT > 0
// Check if the layout has changed. Higher numbered
// boxes take priority.
unsigned activeLayout;
if (layoutOverride >= 0) {
activeLayout = layoutOverride;
} else {
#if OSD_ALTERNATE_LAYOUT_COUNT > 2
if (IS_RC_MODE_ACTIVE(BOXOSDALT3))
activeLayout = 3;
else
#endif
#if OSD_ALTERNATE_LAYOUT_COUNT > 1
if (IS_RC_MODE_ACTIVE(BOXOSDALT2))
activeLayout = 2;
else
#endif
if (IS_RC_MODE_ACTIVE(BOXOSDALT1))
activeLayout = 1;
else
activeLayout = 0;
}
if (currentLayout != activeLayout) {
currentLayout = activeLayout;
osdStartFullRedraw();
}
#endif
#define DRAW_FREQ_DENOM 4
#define STATS_FREQ_DENOM 50
counter++;
@ -2032,4 +2066,16 @@ void osdStartFullRedraw(void)
fullRedraw = true;
}
void osdOverrideLayout(int layout)
{
layoutOverride = constrain(layout, -1, ARRAYLEN(osdConfig()->item_pos) - 1);
}
bool osdItemIsFixed(osd_items_e item)
{
return item == OSD_CROSSHAIRS ||
item == OSD_ARTIFICIAL_HORIZON ||
item == OSD_HORIZON_SIDEBARS;
}
#endif // OSD

View file

@ -20,10 +20,18 @@
#include "common/time.h"
#include "config/parameter_group.h"
#define VISIBLE_FLAG 0x0800
#define VISIBLE(x) (x & VISIBLE_FLAG)
#define OSD_POS_MAX 0x3FF
#define OSD_POS_MAX_CLI (OSD_POS_MAX | VISIBLE_FLAG)
#ifndef OSD_ALTERNATE_LAYOUT_COUNT
#define OSD_ALTERNATE_LAYOUT_COUNT 3
#endif
#define OSD_LAYOUT_COUNT (OSD_ALTERNATE_LAYOUT_COUNT + 1)
#define OSD_VISIBLE_FLAG 0x0800
#define OSD_VISIBLE(x) ((x) & OSD_VISIBLE_FLAG)
#define OSD_POS(x,y) ((x) | ((y) << 5))
#define OSD_X(x) ((x) & 0x001F)
#define OSD_Y(x) (((x) >> 5) & 0x001F)
#define OSD_POS_MAX 0x3FF
#define OSD_POS_MAX_CLI (OSD_POS_MAX | OSD_VISIBLE_FLAG)
typedef enum {
OSD_RSSI_VALUE,
@ -94,7 +102,8 @@ typedef enum {
} osd_sidebar_scroll_e;
typedef struct osdConfig_s {
uint16_t item_pos[OSD_ITEM_COUNT];
// Layouts
uint16_t item_pos[OSD_LAYOUT_COUNT][OSD_ITEM_COUNT];
// Alarms
uint8_t rssi_alarm; // rssi %
@ -109,13 +118,13 @@ typedef struct osdConfig_s {
// Preferences
uint8_t main_voltage_decimals;
uint8_t ahi_reverse_roll;
osd_crosshairs_style_e crosshairs_style;
osd_sidebar_scroll_e left_sidebar_scroll;
osd_sidebar_scroll_e right_sidebar_scroll;
uint8_t crosshairs_style; // from osd_crosshairs_style_e
uint8_t left_sidebar_scroll; // from osd_sidebar_scroll_e
uint8_t right_sidebar_scroll; // from osd_sidebar_scroll_e
uint8_t sidebar_scroll_arrows;
osd_unit_e units;
osd_stats_energy_unit_e stats_energy_unit;
uint8_t units; // from osd_unit_e
uint8_t stats_energy_unit; // from osd_stats_energy_unit_e
} osdConfig_t;
PG_DECLARE(osdConfig_t, osdConfig);
@ -124,3 +133,7 @@ struct displayPort_s;
void osdInit(struct displayPort_s *osdDisplayPort);
void osdUpdate(timeUs_t currentTimeUs);
void osdStartFullRedraw(void);
// Sets a fixed OSD layout ignoring the RC input. Set it
// to -1 to disable the override.
void osdOverrideLayout(int layout);
bool osdItemIsFixed(osd_items_e item);

View file

@ -31,3 +31,10 @@
#define MSP2_INAV_MIXER 0x2010
#define MSP2_INAV_SET_MIXER 0x2011
#define MSP2_INAV_OSD_LAYOUTS 0x2012
#define MSP2_INAV_OSD_SET_LAYOUT_ITEM 0x2013
#define MSP2_INAV_OSD_ALARMS 0x2014
#define MSP2_INAV_OSD_SET_ALARMS 0x2015
#define MSP2_INAV_OSD_PREFERENCES 0x2016
#define MSP2_INAV_OSD_SET_PREFERENCES 0x2017

View file

@ -69,6 +69,7 @@
#define USE_DTERM_NOTCH
#define USE_ACC_NOTCH
#define USE_CMS
#define CMS_MENU_OSD
#define USE_DASHBOARD
#define USE_OLED_UG2864
#define USE_MSP_DISPLAYPORT