1
0
Fork 0
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:
Bruce Luckcuck 2021-01-02 18:53:04 -05:00
parent 38c792c1c5
commit e7b9828c3b
12 changed files with 111 additions and 5 deletions

View file

@ -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

View file

@ -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[];

View file

@ -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;

View file

@ -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}
};

View file

@ -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) {

View file

@ -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);

View file

@ -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

View file

@ -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);

View file

@ -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)

View file

@ -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)

View file

@ -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);

View file

@ -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);