mirror of
https://github.com/betaflight/betaflight.git
synced 2025-07-23 16:25:31 +03:00
Add option to display OSD menus over a solid gray background
Improves menu readability by changing the background from a transparent display of the camera image to a static opaque gray background. The behavior is controlled with the `osd_menu_background` parameter which defaults to `TRANSPARENT` to preserve the previous behavior. Other opaque options are available: ``` osd_menu_background = TRANSPARENT Allowed values: TRANSPARENT, BLACK, GRAY, LIGHT_GRAY ``` The background setting is available in the CMS OSD menu and the user can cycle through the various options with the display updating in real-time. Currently only the onboard MAX7456-based OSD is supported, but the implementation adds `displayPort` support so it can easily be extended to other OSD devices if those manufacturers want to add support. Also can be extended to other background types (like colors, varying transparency, etc.) for future device support. Makes use of the built-in MAX7456 feature to display all transparent pixels as "gray". The MAX7456 display area seems to be a few scan lines smaller than the actual camera video image so it's normal for some of the camera image to "leak" at the top/bottom of the display. The OSD display area can be adjusted up/down using the `vcd_v_offset` setting if desired.
This commit is contained in:
parent
38c792c1c5
commit
e7b9828c3b
12 changed files with 111 additions and 5 deletions
|
@ -510,6 +510,12 @@ static const char* const lookupTableMixerType[] = {
|
|||
"LEGACY", "LINEAR", "DYNAMIC",
|
||||
};
|
||||
|
||||
#ifdef USE_OSD
|
||||
const char * const lookupTableCMSMenuBackgroundType[] = {
|
||||
"TRANSPARENT", "BLACK", "GRAY", "LIGHT_GRAY"
|
||||
};
|
||||
#endif
|
||||
|
||||
#define LOOKUP_TABLE_ENTRY(name) { name, ARRAYLEN(name) }
|
||||
|
||||
const lookupTableEntry_t lookupTables[] = {
|
||||
|
@ -633,6 +639,9 @@ const lookupTableEntry_t lookupTables[] = {
|
|||
#endif
|
||||
LOOKUP_TABLE_ENTRY(lookupTableMixerType),
|
||||
LOOKUP_TABLE_ENTRY(lookupTableSimplifiedTuningPidsMode),
|
||||
#ifdef USE_OSD
|
||||
LOOKUP_TABLE_ENTRY(lookupTableCMSMenuBackgroundType),
|
||||
#endif
|
||||
};
|
||||
|
||||
#undef LOOKUP_TABLE_ENTRY
|
||||
|
@ -1465,6 +1474,7 @@ const clivalue_t valueTable[] = {
|
|||
{ "osd_camera_frame_width", VAR_UINT8 | MASTER_VALUE, .config.minmaxUnsigned = { OSD_CAMERA_FRAME_MIN_WIDTH, OSD_CAMERA_FRAME_MAX_WIDTH }, PG_OSD_CONFIG, offsetof(osdConfig_t, camera_frame_width) },
|
||||
{ "osd_camera_frame_height", VAR_UINT8 | MASTER_VALUE, .config.minmaxUnsigned = { OSD_CAMERA_FRAME_MIN_HEIGHT, OSD_CAMERA_FRAME_MAX_HEIGHT }, PG_OSD_CONFIG, offsetof(osdConfig_t, camera_frame_height) },
|
||||
{ "osd_task_frequency", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { OSD_TASK_FREQUENCY_MIN, OSD_TASK_FREQUENCY_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, task_frequency) },
|
||||
{ "osd_menu_background", VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_CMS_BACKGROUND }, PG_OSD_CONFIG, offsetof(osdConfig_t, cms_background_type) },
|
||||
#endif // end of #ifdef USE_OSD
|
||||
|
||||
// PG_SYSTEM_CONFIG
|
||||
|
|
|
@ -143,7 +143,9 @@ typedef enum {
|
|||
#endif
|
||||
TABLE_MIXER_TYPE,
|
||||
TABLE_SIMPLIFIED_TUNING_PIDS_MODE,
|
||||
|
||||
#ifdef USE_OSD
|
||||
TABLE_CMS_BACKGROUND,
|
||||
#endif
|
||||
LOOKUP_TABLE_COUNT
|
||||
} lookupTableIndex_e;
|
||||
|
||||
|
@ -265,3 +267,5 @@ extern const char * const lookupTableInterpolatedSetpoint[];
|
|||
extern const char * const lookupTableOffOn[];
|
||||
|
||||
extern const char * const lookupTableSimplifiedTuningPidsMode[];
|
||||
|
||||
extern const char * const lookupTableCMSMenuBackgroundType[];
|
||||
|
|
|
@ -840,6 +840,9 @@ void cmsMenuOpen(void)
|
|||
menuStackIdx = 0;
|
||||
setArmingDisabled(ARMING_DISABLED_CMS_MENU);
|
||||
displayLayerSelect(pCurrentDisplay, DISPLAYPORT_LAYER_FOREGROUND); // make sure the foreground layer is active
|
||||
if (osdConfig()->cms_background_type != DISPLAY_BACKGROUND_TRANSPARENT) {
|
||||
displaySetBackgroundType(pCurrentDisplay, (displayPortBackground_e)osdConfig()->cms_background_type); // set the background type if not transparent
|
||||
}
|
||||
} else {
|
||||
// Switch display
|
||||
displayPort_t *pNextDisplay = cmsDisplayPortSelectNext();
|
||||
|
@ -848,8 +851,10 @@ void cmsMenuOpen(void)
|
|||
// DisplayPort has been changed.
|
||||
// Convert cursorRow to absolute value
|
||||
currentCtx.cursorRow = cmsCursorAbsolute(pCurrentDisplay);
|
||||
displaySetBackgroundType(pCurrentDisplay, DISPLAY_BACKGROUND_TRANSPARENT); // reset previous displayPort to transparent
|
||||
displayRelease(pCurrentDisplay);
|
||||
pCurrentDisplay = pNextDisplay;
|
||||
displaySetBackgroundType(pCurrentDisplay, (displayPortBackground_e)osdConfig()->cms_background_type); // set the background type if not transparent
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
@ -930,6 +935,8 @@ const void *cmsMenuExit(displayPort_t *pDisplay, const void *ptr)
|
|||
|
||||
cmsInMenu = false;
|
||||
|
||||
displaySetBackgroundType(pCurrentDisplay, DISPLAY_BACKGROUND_TRANSPARENT); // reset the background to transparent
|
||||
|
||||
displayRelease(pDisplay);
|
||||
currentCtx.menu = NULL;
|
||||
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
|
||||
#include "build/version.h"
|
||||
|
||||
#include "cli/settings.h"
|
||||
|
||||
#include "cms/cms.h"
|
||||
#include "cms/cms_types.h"
|
||||
#include "cms/cms_menu_osd.h"
|
||||
|
@ -36,13 +38,17 @@
|
|||
#include "common/utils.h"
|
||||
|
||||
#include "config/feature.h"
|
||||
#include "pg/pg.h"
|
||||
#include "pg/pg_ids.h"
|
||||
|
||||
#include "drivers/display.h"
|
||||
|
||||
#include "io/displayport_max7456.h"
|
||||
|
||||
#include "osd/osd.h"
|
||||
#include "osd/osd_elements.h"
|
||||
|
||||
#include "pg/pg.h"
|
||||
#include "pg/pg_ids.h"
|
||||
|
||||
#include "sensors/battery.h"
|
||||
|
||||
#ifdef USE_EXTENDED_CMS_MENUS
|
||||
|
@ -306,6 +312,8 @@ static uint8_t displayPortProfileMax7456_whiteBrightness;
|
|||
static uint8_t osdConfig_osdProfileIndex;
|
||||
#endif
|
||||
|
||||
static displayPortBackground_e osdMenuBackgroundType;
|
||||
|
||||
static const void *cmsx_menuOsdOnEnter(displayPort_t *pDisp)
|
||||
{
|
||||
UNUSED(pDisp);
|
||||
|
@ -318,6 +326,7 @@ static const void *cmsx_menuOsdOnEnter(displayPort_t *pDisp)
|
|||
displayPortProfileMax7456_invert = displayPortProfileMax7456()->invert;
|
||||
displayPortProfileMax7456_blackBrightness = displayPortProfileMax7456()->blackBrightness;
|
||||
displayPortProfileMax7456_whiteBrightness = displayPortProfileMax7456()->whiteBrightness;
|
||||
osdMenuBackgroundType = osdConfig()->cms_background_type;
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
|
@ -350,6 +359,14 @@ static const void *cmsx_max7456Update(displayPort_t *pDisp, const void *self)
|
|||
}
|
||||
#endif // USE_MAX7456
|
||||
|
||||
static const void *cmsx_osdBackgroundUpdate(displayPort_t *pDisp, const void *self)
|
||||
{
|
||||
UNUSED(self);
|
||||
osdConfigMutable()->cms_background_type = osdMenuBackgroundType;
|
||||
displaySetBackgroundType(pDisp, osdMenuBackgroundType);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const OSD_Entry cmsx_menuOsdEntries[] =
|
||||
{
|
||||
{"---OSD---", OME_Label, NULL, NULL, 0},
|
||||
|
@ -366,6 +383,7 @@ const OSD_Entry cmsx_menuOsdEntries[] =
|
|||
{"BRT BLACK", OME_UINT8, cmsx_max7456Update, &(OSD_UINT8_t){&displayPortProfileMax7456_blackBrightness, 0, 3, 1}, 0},
|
||||
{"BRT WHITE", OME_UINT8, cmsx_max7456Update, &(OSD_UINT8_t){&displayPortProfileMax7456_whiteBrightness, 0, 3, 1}, 0},
|
||||
#endif
|
||||
{"BACKGROUND",OME_TAB, cmsx_osdBackgroundUpdate, &(OSD_TAB_t){&osdMenuBackgroundType, DISPLAY_BACKGROUND_COUNT - 1, lookupTableCMSMenuBackgroundType}, 0},
|
||||
{"BACK", OME_Back, NULL, NULL, 0},
|
||||
{NULL, OME_END, NULL, NULL, 0}
|
||||
};
|
||||
|
|
|
@ -153,6 +153,13 @@ bool displayWriteFontCharacter(displayPort_t *instance, uint16_t addr, const osd
|
|||
return false;
|
||||
}
|
||||
|
||||
void displaySetBackgroundType(displayPort_t *instance, displayPortBackground_e backgroundType)
|
||||
{
|
||||
if (instance->vTable->setBackgroundType) {
|
||||
instance->vTable->setBackgroundType(instance, backgroundType);
|
||||
}
|
||||
}
|
||||
|
||||
bool displayCheckReady(displayPort_t *instance, bool rescan)
|
||||
{
|
||||
if (instance->vTable->checkReady) {
|
||||
|
|
|
@ -41,6 +41,13 @@ typedef enum {
|
|||
DISPLAY_TRANSACTION_OPT_RESET_DRAWING = 1 << 1,
|
||||
} displayTransactionOption_e;
|
||||
|
||||
typedef enum {
|
||||
DISPLAY_BACKGROUND_TRANSPARENT,
|
||||
DISPLAY_BACKGROUND_BLACK,
|
||||
DISPLAY_BACKGROUND_GRAY,
|
||||
DISPLAY_BACKGROUND_LTGRAY,
|
||||
DISPLAY_BACKGROUND_COUNT // must be the last entry
|
||||
} displayPortBackground_e;
|
||||
|
||||
struct displayCanvas_s;
|
||||
struct osdCharacter_s;
|
||||
|
@ -85,6 +92,7 @@ typedef struct displayPortVTable_s {
|
|||
void (*beginTransaction)(displayPort_t *displayPort, displayTransactionOption_e opts);
|
||||
void (*commitTransaction)(displayPort_t *displayPort);
|
||||
bool (*getCanvas)(struct displayCanvas_s *canvas, const displayPort_t *displayPort);
|
||||
void (*setBackgroundType)(displayPort_t *displayPort, displayPortBackground_e backgroundType);
|
||||
} displayPortVTable_t;
|
||||
|
||||
void displayGrab(displayPort_t *instance);
|
||||
|
@ -111,4 +119,4 @@ void displayInit(displayPort_t *instance, const displayPortVTable_t *vTable);
|
|||
bool displayLayerSupported(displayPort_t *instance, displayPortLayer_e layer);
|
||||
bool displayLayerSelect(displayPort_t *instance, displayPortLayer_e layer);
|
||||
bool displayLayerCopy(displayPort_t *instance, displayPortLayer_e destLayer, displayPortLayer_e sourceLayer);
|
||||
|
||||
void displaySetBackgroundType(displayPort_t *instance, displayPortBackground_e backgroundType);
|
||||
|
|
|
@ -100,7 +100,7 @@
|
|||
#define BACKGROUND_BRIGHTNESS_42 0x06
|
||||
#define BACKGROUND_BRIGHTNESS_49 0x07
|
||||
|
||||
#define BACKGROUND_MODE_GRAY 0x40
|
||||
#define BACKGROUND_MODE_GRAY 0x80
|
||||
|
||||
// STAT register bits
|
||||
|
||||
|
@ -230,6 +230,8 @@ static bool fontIsLoading = false;
|
|||
|
||||
static uint8_t max7456DeviceType;
|
||||
|
||||
static displayPortBackground_e deviceBackgroundType = DISPLAY_BACKGROUND_TRANSPARENT;
|
||||
|
||||
// previous states initialized outside the valid range to force update on first call
|
||||
#define INVALID_PREVIOUS_REGISTER_STATE 255
|
||||
static uint8_t previousBlackWhiteRegister = INVALID_PREVIOUS_REGISTER_STATE;
|
||||
|
@ -369,6 +371,29 @@ void max7456_dma_irq_handler(dmaChannelDescriptor_t* descriptor)
|
|||
|
||||
#endif
|
||||
|
||||
static void max7456SetRegisterVM1(void)
|
||||
{
|
||||
uint8_t backgroundGray = BACKGROUND_BRIGHTNESS_28; // this is the device default background gray level
|
||||
uint8_t vm1Register = BLINK_TIME_1 | BLINK_DUTY_CYCLE_75_25; // device defaults
|
||||
if (deviceBackgroundType != DISPLAY_BACKGROUND_TRANSPARENT) {
|
||||
vm1Register |= BACKGROUND_MODE_GRAY;
|
||||
switch (deviceBackgroundType) {
|
||||
case DISPLAY_BACKGROUND_BLACK:
|
||||
backgroundGray = BACKGROUND_BRIGHTNESS_0;
|
||||
break;
|
||||
case DISPLAY_BACKGROUND_LTGRAY:
|
||||
backgroundGray = BACKGROUND_BRIGHTNESS_49;
|
||||
break;
|
||||
case DISPLAY_BACKGROUND_GRAY:
|
||||
default:
|
||||
backgroundGray = BACKGROUND_BRIGHTNESS_28;
|
||||
break;
|
||||
}
|
||||
}
|
||||
vm1Register |= (backgroundGray << 4);
|
||||
max7456Send(MAX7456ADD_VM1, vm1Register);
|
||||
}
|
||||
|
||||
uint8_t max7456GetRowsCount(void)
|
||||
{
|
||||
return (videoSignalReg & VIDEO_MODE_PAL) ? VIDEO_LINES_PAL : VIDEO_LINES_NTSC;
|
||||
|
@ -435,6 +460,7 @@ void max7456ReInit(void)
|
|||
max7456Send(MAX7456ADD_VM0, videoSignalReg);
|
||||
max7456Send(MAX7456ADD_HOS, hosRegValue);
|
||||
max7456Send(MAX7456ADD_VOS, vosRegValue);
|
||||
max7456SetRegisterVM1();
|
||||
|
||||
max7456Send(MAX7456ADD_DMM, displayMemoryModeReg | CLEAR_DISPLAY);
|
||||
__spiBusTransactionEnd(busdev);
|
||||
|
@ -459,6 +485,7 @@ max7456InitStatus_e max7456Init(const max7456Config_t *max7456Config, const vcdP
|
|||
{
|
||||
max7456SpiClock = spiCalculateDivider(MAX7456_MAX_SPI_CLK_HZ);
|
||||
max7456DeviceDetected = false;
|
||||
deviceBackgroundType = DISPLAY_BACKGROUND_TRANSPARENT;
|
||||
|
||||
// initialize all layers
|
||||
for (unsigned i = 0; i < MAX7456_SUPPORTED_LAYER_COUNT; i++) {
|
||||
|
@ -895,4 +922,14 @@ bool max7456IsDeviceDetected(void)
|
|||
{
|
||||
return max7456DeviceDetected;
|
||||
}
|
||||
|
||||
void max7456SetBackgroundType(displayPortBackground_e backgroundType)
|
||||
{
|
||||
deviceBackgroundType = backgroundType;
|
||||
|
||||
__spiBusTransactionBegin(busdev);
|
||||
max7456SetRegisterVM1();
|
||||
__spiBusTransactionEnd(busdev);
|
||||
}
|
||||
|
||||
#endif // USE_MAX7456
|
||||
|
|
|
@ -62,3 +62,4 @@ bool max7456LayerSupported(displayPortLayer_e layer);
|
|||
bool max7456LayerSelect(displayPortLayer_e layer);
|
||||
bool max7456LayerCopy(displayPortLayer_e destLayer, displayPortLayer_e sourceLayer);
|
||||
bool max7456IsDeviceDetected(void);
|
||||
void max7456SetBackgroundType(displayPortBackground_e backgroundType);
|
||||
|
|
|
@ -186,6 +186,12 @@ static bool checkReady(displayPort_t *displayPort, bool rescan)
|
|||
return true;
|
||||
}
|
||||
|
||||
void setBackgroundType(displayPort_t *displayPort, displayPortBackground_e backgroundType)
|
||||
{
|
||||
UNUSED(displayPort);
|
||||
max7456SetBackgroundType(backgroundType);
|
||||
}
|
||||
|
||||
static const displayPortVTable_t max7456VTable = {
|
||||
.grab = grab,
|
||||
.release = release,
|
||||
|
@ -204,6 +210,7 @@ static const displayPortVTable_t max7456VTable = {
|
|||
.layerCopy = layerCopy,
|
||||
.writeFontCharacter = writeFontCharacter,
|
||||
.checkReady = checkReady,
|
||||
.setBackgroundType = setBackgroundType,
|
||||
};
|
||||
|
||||
bool max7456DisplayPortInit(const vcdProfile_t *vcdProfile, displayPort_t **displayPort)
|
||||
|
|
|
@ -341,6 +341,7 @@ void pgResetFn_osdConfig(osdConfig_t *osdConfig)
|
|||
osdConfig->camera_frame_height = 11;
|
||||
|
||||
osdConfig->task_frequency = OSD_TASK_FREQUENCY_DEFAULT;
|
||||
osdConfig->cms_background_type = DISPLAY_BACKGROUND_TRANSPARENT;
|
||||
}
|
||||
|
||||
void pgResetFn_osdElementConfig(osdElementConfig_t *osdElementConfig)
|
||||
|
|
|
@ -297,6 +297,7 @@ typedef struct osdConfig_s {
|
|||
uint8_t camera_frame_width; // The width of the box for the camera frame element
|
||||
uint8_t camera_frame_height; // The height of the box for the camera frame element
|
||||
uint16_t task_frequency;
|
||||
uint8_t cms_background_type; // For supporting devices, determines whether the CMS background is transparent or opaque
|
||||
} osdConfig_t;
|
||||
|
||||
PG_DECLARE(osdConfig_t, osdConfig);
|
||||
|
|
|
@ -32,6 +32,11 @@ extern "C" {
|
|||
#include "cms/cms_types.h"
|
||||
#include "fc/rc_modes.h"
|
||||
#include "fc/runtime_config.h"
|
||||
#include "osd/osd.h"
|
||||
#include "pg/pg_ids.h"
|
||||
|
||||
PG_REGISTER(osdConfig_t, osdConfig, PG_OSD_CONFIG, 0);
|
||||
|
||||
void cmsMenuOpen(void);
|
||||
const void *cmsMenuBack(displayPort_t *pDisplay);
|
||||
uint16_t cmsHandleKey(displayPort_t *pDisplay, uint8_t key);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue