1
0
Fork 0
mirror of https://github.com/betaflight/betaflight.git synced 2025-07-15 12:25:20 +03:00

Add OSD background support for the static portions of display e… (#9129)

Add OSD background support for the static portions of display elements
This commit is contained in:
Michael Keller 2019-10-31 00:10:01 +13:00 committed by GitHub
commit 8548189bc6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 305 additions and 49 deletions

View file

@ -720,6 +720,7 @@ void cmsMenuOpen(void)
currentCtx = (cmsCtx_t){ &menuMain, 0, 0 }; currentCtx = (cmsCtx_t){ &menuMain, 0, 0 };
menuStackIdx = 0; menuStackIdx = 0;
setArmingDisabled(ARMING_DISABLED_CMS_MENU); setArmingDisabled(ARMING_DISABLED_CMS_MENU);
displayLayerSelect(pCurrentDisplay, DISPLAYPORT_LAYER_FOREGROUND); // make sure the foreground layer is active
} else { } else {
// Switch display // Switch display
displayPort_t *pNextDisplay = cmsDisplayPortSelectNext(); displayPort_t *pNextDisplay = cmsDisplayPortSelectNext();

View file

@ -115,6 +115,33 @@ uint16_t displayTxBytesFree(const displayPort_t *instance)
return instance->vTable->txBytesFree(instance); return instance->vTable->txBytesFree(instance);
} }
bool displayLayerSupported(displayPort_t *instance, displayPortLayer_e layer)
{
if (layer == DISPLAYPORT_LAYER_FOREGROUND) {
// Every device must support the foreground (default) layer
return true;
} else if (layer < DISPLAYPORT_LAYER_COUNT && instance->vTable->layerSupported) {
return instance->vTable->layerSupported(instance, layer);
}
return false;
}
bool displayLayerSelect(displayPort_t *instance, displayPortLayer_e layer)
{
if (instance->vTable->layerSelect) {
return instance->vTable->layerSelect(instance, layer);
}
return false;
}
bool displayLayerCopy(displayPort_t *instance, displayPortLayer_e destLayer, displayPortLayer_e sourceLayer)
{
if (instance->vTable->layerCopy && sourceLayer != destLayer) {
return instance->vTable->layerCopy(instance, destLayer, sourceLayer);
}
return false;
}
void displayInit(displayPort_t *instance, const displayPortVTable_t *vTable) void displayInit(displayPort_t *instance, const displayPortVTable_t *vTable)
{ {
instance->vTable = vTable; instance->vTable = vTable;

View file

@ -20,6 +20,12 @@
#pragma once #pragma once
typedef enum {
DISPLAYPORT_LAYER_FOREGROUND,
DISPLAYPORT_LAYER_BACKGROUND,
DISPLAYPORT_LAYER_COUNT,
} displayPortLayer_e;
struct displayPortVTable_s; struct displayPortVTable_s;
typedef struct displayPort_s { typedef struct displayPort_s {
@ -52,6 +58,9 @@ typedef struct displayPortVTable_s {
void (*resync)(displayPort_t *displayPort); void (*resync)(displayPort_t *displayPort);
bool (*isSynced)(const displayPort_t *displayPort); bool (*isSynced)(const displayPort_t *displayPort);
uint32_t (*txBytesFree)(const displayPort_t *displayPort); uint32_t (*txBytesFree)(const displayPort_t *displayPort);
bool (*layerSupported)(displayPort_t *displayPort, displayPortLayer_e layer);
bool (*layerSelect)(displayPort_t *displayPort, displayPortLayer_e layer);
bool (*layerCopy)(displayPort_t *displayPort, displayPortLayer_e destLayer, displayPortLayer_e sourceLayer);
} displayPortVTable_t; } displayPortVTable_t;
typedef struct displayPortProfile_s { typedef struct displayPortProfile_s {
@ -81,3 +90,7 @@ void displayResync(displayPort_t *instance);
bool displayIsSynced(const displayPort_t *instance); bool displayIsSynced(const displayPort_t *instance);
uint16_t displayTxBytesFree(const displayPort_t *instance); uint16_t displayTxBytesFree(const displayPort_t *instance);
void displayInit(displayPort_t *instance, const displayPortVTable_t *vTable); 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);

View file

@ -181,6 +181,15 @@
#define __spiBusTransactionEnd(busdev) {IOHi((busdev)->busdev_u.spi.csnPin);spiSetDivisor((busdev)->busdev_u.spi.instance, MAX7456_RESTORE_CLK);} #define __spiBusTransactionEnd(busdev) {IOHi((busdev)->busdev_u.spi.csnPin);spiSetDivisor((busdev)->busdev_u.spi.instance, MAX7456_RESTORE_CLK);}
#endif #endif
#define MAX7456_SUPPORTED_LAYER_COUNT (DISPLAYPORT_LAYER_BACKGROUND + 1)
typedef struct max7456Layer_s {
uint8_t buffer[VIDEO_BUFFER_CHARS_PAL];
} max7456Layer_t;
static max7456Layer_t displayLayers[MAX7456_SUPPORTED_LAYER_COUNT];
static displayPortLayer_e activeLayer = DISPLAYPORT_LAYER_FOREGROUND;
busDevice_t max7456BusDevice; busDevice_t max7456BusDevice;
busDevice_t *busdev = &max7456BusDevice; busDevice_t *busdev = &max7456BusDevice;
@ -188,11 +197,10 @@ static uint16_t max7456SpiClock = MAX7456_SPI_CLK;
uint16_t maxScreenSize = VIDEO_BUFFER_CHARS_PAL; uint16_t maxScreenSize = VIDEO_BUFFER_CHARS_PAL;
// We write everything in screenBuffer and then compare // We write everything to the active layer and then compare
// screenBuffer with shadowBuffer to upgrade only changed chars. // it with shadowBuffer to update only changed chars.
// This solution is faster then redrawing entire screen. // This solution is faster then redrawing entire screen.
static uint8_t screenBuffer[VIDEO_BUFFER_CHARS_PAL+40]; // For faster writes we use memcpy so we need some space to don't overwrite buffer
static uint8_t shadowBuffer[VIDEO_BUFFER_CHARS_PAL]; static uint8_t shadowBuffer[VIDEO_BUFFER_CHARS_PAL];
//Max chars to update in one idle //Max chars to update in one idle
@ -222,6 +230,16 @@ static uint8_t previousInvertRegister = INVALID_PREVIOUS_REGISTER_STATE;
static void max7456DrawScreenSlow(void); static void max7456DrawScreenSlow(void);
static uint8_t *getLayerBuffer(displayPortLayer_e layer)
{
return displayLayers[layer].buffer;
}
static uint8_t *getActiveLayerBuffer(void)
{
return getLayerBuffer(activeLayer);
}
static uint8_t max7456Send(uint8_t add, uint8_t data) static uint8_t max7456Send(uint8_t add, uint8_t data)
{ {
spiTransferByte(busdev->busdev_u.spi.instance, add); spiTransferByte(busdev->busdev_u.spi.instance, add);
@ -349,11 +367,21 @@ uint8_t max7456GetRowsCount(void)
return (videoSignalReg & VIDEO_MODE_PAL) ? VIDEO_LINES_PAL : VIDEO_LINES_NTSC; return (videoSignalReg & VIDEO_MODE_PAL) ? VIDEO_LINES_PAL : VIDEO_LINES_NTSC;
} }
// When clearing the shadow buffer we fill with 0 so that the characters will
// be flagged as changed when compared to the 0x20 used in the layer buffers.
static void max7456ClearShadowBuffer(void) static void max7456ClearShadowBuffer(void)
{ {
memset(shadowBuffer, 0, maxScreenSize); memset(shadowBuffer, 0, maxScreenSize);
} }
// Buffer is filled with the whitespace character (0x20)
static void max7456ClearLayer(displayPortLayer_e layer)
{
memset(getLayerBuffer(layer), 0x20, VIDEO_BUFFER_CHARS_PAL);
}
void max7456ReInit(void) void max7456ReInit(void)
{ {
uint8_t srdata = 0; uint8_t srdata = 0;
@ -422,6 +450,11 @@ void max7456PreInit(const max7456Config_t *max7456Config)
bool max7456Init(const max7456Config_t *max7456Config, const vcdProfile_t *pVcdProfile, bool cpuOverclock) bool max7456Init(const max7456Config_t *max7456Config, const vcdProfile_t *pVcdProfile, bool cpuOverclock)
{ {
// initialize all layers
for (unsigned i = 0; i < MAX7456_SUPPORTED_LAYER_COUNT; i++) {
max7456ClearLayer(i);
}
max7456HardwareReset(); max7456HardwareReset();
if (!max7456Config->csTag || !max7456Config->spiDevice) { if (!max7456Config->csTag || !max7456Config->spiDevice) {
@ -568,33 +601,58 @@ void max7456Brightness(uint8_t black, uint8_t white)
} }
} }
//just fill with spaces with some tricks
void max7456ClearScreen(void) void max7456ClearScreen(void)
{ {
memset(screenBuffer, 0x20, VIDEO_BUFFER_CHARS_PAL); max7456ClearLayer(activeLayer);
}
uint8_t* max7456GetScreenBuffer(void)
{
return screenBuffer;
} }
void max7456WriteChar(uint8_t x, uint8_t y, uint8_t c) void max7456WriteChar(uint8_t x, uint8_t y, uint8_t c)
{ {
uint8_t *buffer = getActiveLayerBuffer();
if (x < CHARS_PER_LINE && y < VIDEO_LINES_PAL) { if (x < CHARS_PER_LINE && y < VIDEO_LINES_PAL) {
screenBuffer[y * CHARS_PER_LINE + x] = c; buffer[y * CHARS_PER_LINE + x] = c;
} }
} }
void max7456Write(uint8_t x, uint8_t y, const char *buff) void max7456Write(uint8_t x, uint8_t y, const char *buff)
{ {
if (y < VIDEO_LINES_PAL) { if (y < VIDEO_LINES_PAL) {
uint8_t *buffer = getActiveLayerBuffer();
for (int i = 0; buff[i] && x + i < CHARS_PER_LINE; i++) { for (int i = 0; buff[i] && x + i < CHARS_PER_LINE; i++) {
screenBuffer[y * CHARS_PER_LINE + x + i] = buff[i]; buffer[y * CHARS_PER_LINE + x + i] = buff[i];
} }
} }
} }
bool max7456LayerSupported(displayPortLayer_e layer)
{
if (layer == DISPLAYPORT_LAYER_FOREGROUND || layer == DISPLAYPORT_LAYER_BACKGROUND) {
return true;
} else {
return false;
}
}
bool max7456LayerSelect(displayPortLayer_e layer)
{
if (max7456LayerSupported(layer)) {
activeLayer = layer;
return true;
} else {
return false;
}
}
bool max7456LayerCopy(displayPortLayer_e destLayer, displayPortLayer_e sourceLayer)
{
if ((sourceLayer != destLayer) && max7456LayerSupported(sourceLayer) && max7456LayerSupported(destLayer)) {
memcpy(getLayerBuffer(destLayer), getLayerBuffer(sourceLayer), VIDEO_BUFFER_CHARS_PAL);
return true;
} else {
return false;
}
}
bool max7456DmaInProgress(void) bool max7456DmaInProgress(void)
{ {
#ifdef MAX7456_DMA_CHANNEL_TX #ifdef MAX7456_DMA_CHANNEL_TX
@ -607,7 +665,7 @@ bool max7456DmaInProgress(void)
bool max7456BuffersSynced(void) bool max7456BuffersSynced(void)
{ {
for (int i = 0; i < maxScreenSize; i++) { for (int i = 0; i < maxScreenSize; i++) {
if (screenBuffer[i] != shadowBuffer[i]) { if (displayLayers[DISPLAYPORT_LAYER_FOREGROUND].buffer[i] != shadowBuffer[i]) {
return false; return false;
} }
} }
@ -679,16 +737,18 @@ void max7456DrawScreen(void)
max7456ReInitIfRequired(false); max7456ReInitIfRequired(false);
uint8_t *buffer = getActiveLayerBuffer();
int buff_len = 0; int buff_len = 0;
for (int k = 0; k < MAX_CHARS2UPDATE; k++) { for (int k = 0; k < MAX_CHARS2UPDATE; k++) {
if (screenBuffer[pos] != shadowBuffer[pos]) { if (buffer[pos] != shadowBuffer[pos]) {
spiBuff[buff_len++] = MAX7456ADD_DMAH; spiBuff[buff_len++] = MAX7456ADD_DMAH;
spiBuff[buff_len++] = pos >> 8; spiBuff[buff_len++] = pos >> 8;
spiBuff[buff_len++] = MAX7456ADD_DMAL; spiBuff[buff_len++] = MAX7456ADD_DMAL;
spiBuff[buff_len++] = pos & 0xff; spiBuff[buff_len++] = pos & 0xff;
spiBuff[buff_len++] = MAX7456ADD_DMDI; spiBuff[buff_len++] = MAX7456ADD_DMDI;
spiBuff[buff_len++] = screenBuffer[pos]; spiBuff[buff_len++] = buffer[pos];
shadowBuffer[pos] = screenBuffer[pos]; shadowBuffer[pos] = buffer[pos];
} }
if (++pos >= maxScreenSize) { if (++pos >= maxScreenSize) {
@ -712,23 +772,24 @@ void max7456DrawScreen(void)
static void max7456DrawScreenSlow(void) static void max7456DrawScreenSlow(void)
{ {
bool escapeCharFound; bool escapeCharFound;
uint8_t *buffer = getActiveLayerBuffer();
__spiBusTransactionBegin(busdev); __spiBusTransactionBegin(busdev);
// Enable auto-increment mode and update every character in the screenBuffer. // Enable auto-increment mode and update every character in the active buffer.
// The "escape" character 0xFF must be skipped as it causes the MAX7456 to exit auto-increment mode. // The "escape" character 0xFF must be skipped as it causes the MAX7456 to exit auto-increment mode.
max7456Send(MAX7456ADD_DMAH, 0); max7456Send(MAX7456ADD_DMAH, 0);
max7456Send(MAX7456ADD_DMAL, 0); max7456Send(MAX7456ADD_DMAL, 0);
max7456Send(MAX7456ADD_DMM, displayMemoryModeReg | 1); max7456Send(MAX7456ADD_DMM, displayMemoryModeReg | 1);
for (int xx = 0; xx < maxScreenSize; xx++) { for (int xx = 0; xx < maxScreenSize; xx++) {
if (screenBuffer[xx] == END_STRING) { if (buffer[xx] == END_STRING) {
escapeCharFound = true; escapeCharFound = true;
max7456Send(MAX7456ADD_DMDI, ' '); // replace the 0xFF character with a blank in the first pass to avoid terminating auto-increment max7456Send(MAX7456ADD_DMDI, ' '); // replace the 0xFF character with a blank in the first pass to avoid terminating auto-increment
} else { } else {
max7456Send(MAX7456ADD_DMDI, screenBuffer[xx]); max7456Send(MAX7456ADD_DMDI, buffer[xx]);
} }
shadowBuffer[xx] = screenBuffer[xx]; shadowBuffer[xx] = buffer[xx];
} }
max7456Send(MAX7456ADD_DMDI, END_STRING); max7456Send(MAX7456ADD_DMDI, END_STRING);
@ -738,7 +799,7 @@ static void max7456DrawScreenSlow(void)
// to update them with direct addressing // to update them with direct addressing
if (escapeCharFound) { if (escapeCharFound) {
for (int xx = 0; xx < maxScreenSize; xx++) { for (int xx = 0; xx < maxScreenSize; xx++) {
if (screenBuffer[xx] == END_STRING) { if (buffer[xx] == END_STRING) {
max7456Send(MAX7456ADD_DMAH, xx >> 8); max7456Send(MAX7456ADD_DMAH, xx >> 8);
max7456Send(MAX7456ADD_DMAL, xx & 0xFF); max7456Send(MAX7456ADD_DMAL, xx & 0xFF);
max7456Send(MAX7456ADD_DMDI, END_STRING); max7456Send(MAX7456ADD_DMDI, END_STRING);

View file

@ -20,6 +20,8 @@
#pragma once #pragma once
#include "drivers/display.h"
/** PAL or NTSC, value is number of chars total */ /** PAL or NTSC, value is number of chars total */
#define VIDEO_BUFFER_CHARS_NTSC 390 #define VIDEO_BUFFER_CHARS_NTSC 390
#define VIDEO_BUFFER_CHARS_PAL 480 #define VIDEO_BUFFER_CHARS_PAL 480
@ -27,7 +29,6 @@
#define VIDEO_LINES_PAL 16 #define VIDEO_LINES_PAL 16
extern uint16_t maxScreenSize; extern uint16_t maxScreenSize;
struct vcdProfile_s; struct vcdProfile_s;
void max7456HardwareReset(void); void max7456HardwareReset(void);
struct max7456Config_s; struct max7456Config_s;
@ -42,6 +43,8 @@ void max7456Write(uint8_t x, uint8_t y, const char *buff);
void max7456WriteChar(uint8_t x, uint8_t y, uint8_t c); void max7456WriteChar(uint8_t x, uint8_t y, uint8_t c);
void max7456ClearScreen(void); void max7456ClearScreen(void);
void max7456RefreshAll(void); void max7456RefreshAll(void);
uint8_t* max7456GetScreenBuffer(void);
bool max7456DmaInProgress(void); bool max7456DmaInProgress(void);
bool max7456BuffersSynced(void); bool max7456BuffersSynced(void);
bool max7456LayerSupported(displayPortLayer_e layer);
bool max7456LayerSelect(displayPortLayer_e layer);
bool max7456LayerCopy(displayPortLayer_e destLayer, displayPortLayer_e sourceLayer);

View file

@ -138,7 +138,10 @@ static const displayPortVTable_t crsfDisplayPortVTable = {
.heartbeat = crsfHeartbeat, .heartbeat = crsfHeartbeat,
.resync = crsfResync, .resync = crsfResync,
.isSynced = crsfIsSynced, .isSynced = crsfIsSynced,
.txBytesFree = crsfTxBytesFree .txBytesFree = crsfTxBytesFree,
.layerSupported = NULL,
.layerSelect = NULL,
.layerCopy = NULL,
}; };
crsfDisplayPortScreen_t *crsfDisplayPortScreen(void) crsfDisplayPortScreen_t *crsfDisplayPortScreen(void)

View file

@ -119,7 +119,10 @@ static const displayPortVTable_t hottVTable = {
.isTransferInProgress = hottIsTransferInProgress, .isTransferInProgress = hottIsTransferInProgress,
.heartbeat = hottHeartbeat, .heartbeat = hottHeartbeat,
.resync = hottResync, .resync = hottResync,
.txBytesFree = hottTxBytesFree .txBytesFree = hottTxBytesFree,
.layerSupported = NULL,
.layerSelect = NULL,
.layerCopy = NULL,
}; };
displayPort_t *displayPortHottInit() displayPort_t *displayPortHottInit()

View file

@ -148,6 +148,24 @@ static uint32_t txBytesFree(const displayPort_t *displayPort)
return UINT32_MAX; return UINT32_MAX;
} }
static bool layerSupported(displayPort_t *displayPort, displayPortLayer_e layer)
{
UNUSED(displayPort);
return max7456LayerSupported(layer);
}
static bool layerSelect(displayPort_t *displayPort, displayPortLayer_e layer)
{
UNUSED(displayPort);
return max7456LayerSelect(layer);
}
static bool layerCopy(displayPort_t *displayPort, displayPortLayer_e destLayer, displayPortLayer_e sourceLayer)
{
UNUSED(displayPort);
return max7456LayerCopy(destLayer, sourceLayer);
}
static const displayPortVTable_t max7456VTable = { static const displayPortVTable_t max7456VTable = {
.grab = grab, .grab = grab,
.release = release, .release = release,
@ -161,6 +179,9 @@ static const displayPortVTable_t max7456VTable = {
.resync = resync, .resync = resync,
.isSynced = isSynced, .isSynced = isSynced,
.txBytesFree = txBytesFree, .txBytesFree = txBytesFree,
.layerSupported = layerSupported,
.layerSelect = layerSelect,
.layerCopy = layerCopy,
}; };
displayPort_t *max7456DisplayPortInit(const vcdProfile_t *vcdProfile) displayPort_t *max7456DisplayPortInit(const vcdProfile_t *vcdProfile)

View file

@ -167,7 +167,10 @@ static const displayPortVTable_t mspDisplayPortVTable = {
.heartbeat = heartbeat, .heartbeat = heartbeat,
.resync = resync, .resync = resync,
.isSynced = isSynced, .isSynced = isSynced,
.txBytesFree = txBytesFree .txBytesFree = txBytesFree,
.layerSupported = NULL,
.layerSelect = NULL,
.layerCopy = NULL,
}; };
displayPort_t *displayPortMspInit(void) displayPort_t *displayPortMspInit(void)

View file

@ -114,7 +114,10 @@ static const displayPortVTable_t oledVTable = {
.heartbeat = oledHeartbeat, .heartbeat = oledHeartbeat,
.resync = oledResync, .resync = oledResync,
.isSynced = oledIsSynced, .isSynced = oledIsSynced,
.txBytesFree = oledTxBytesFree .txBytesFree = oledTxBytesFree,
.layerSupported = NULL,
.layerSelect = NULL,
.layerCopy = NULL,
}; };
displayPort_t *displayPortOledInit(void *device) displayPort_t *displayPortOledInit(void *device)

View file

@ -130,7 +130,10 @@ static const displayPortVTable_t srxlVTable = {
.heartbeat = srxlHeartbeat, .heartbeat = srxlHeartbeat,
.resync = srxlResync, .resync = srxlResync,
.isSynced = srxlIsSynced, .isSynced = srxlIsSynced,
.txBytesFree = srxlTxBytesFree .txBytesFree = srxlTxBytesFree,
.layerSupported = NULL,
.layerSelect = NULL,
.layerCopy = NULL,
}; };
displayPort_t *displayPortSrxlInit() displayPort_t *displayPortSrxlInit()

View file

@ -124,6 +124,8 @@ static displayPort_t *osdDisplayPort;
static bool suppressStatsDisplay = false; static bool suppressStatsDisplay = false;
static uint8_t osdStatsRowCount = 0; static uint8_t osdStatsRowCount = 0;
static bool backgroundLayerSupported = false;
#ifdef USE_ESC_SENSOR #ifdef USE_ESC_SENSOR
escSensorData_t *osdEscDataCombined; escSensorData_t *osdEscDataCombined;
#endif #endif
@ -225,15 +227,30 @@ void changeOsdProfileIndex(uint8_t profileIndex)
} }
#endif #endif
void osdAnalyzeActiveElements(void)
{
osdAddActiveElements();
osdDrawActiveElementsBackground(osdDisplayPort);
}
static void osdDrawElements(timeUs_t currentTimeUs) static void osdDrawElements(timeUs_t currentTimeUs)
{ {
displayClearScreen(osdDisplayPort);
// Hide OSD when OSDSW mode is active // Hide OSD when OSDSW mode is active
if (IS_RC_MODE_ACTIVE(BOXOSD)) { if (IS_RC_MODE_ACTIVE(BOXOSD)) {
displayClearScreen(osdDisplayPort);
return; return;
} }
if (backgroundLayerSupported) {
// Background layer is supported, overlay it onto the foreground
// so that we only need to draw the active parts of the elements.
displayLayerCopy(osdDisplayPort, DISPLAYPORT_LAYER_FOREGROUND, DISPLAYPORT_LAYER_BACKGROUND);
} else {
// Background layer not supported, just clear the foreground in preparation
// for drawing the elements including their backgrounds.
displayClearScreen(osdDisplayPort);
}
osdDrawActiveElements(osdDisplayPort, currentTimeUs); osdDrawActiveElements(osdDisplayPort, currentTimeUs);
} }
@ -342,6 +359,9 @@ void osdInit(displayPort_t *osdDisplayPortToUse)
cmsDisplayPortRegister(osdDisplayPort); cmsDisplayPortRegister(osdDisplayPort);
#endif #endif
backgroundLayerSupported = displayLayerSupported(osdDisplayPort, DISPLAYPORT_LAYER_BACKGROUND);
displayLayerSelect(osdDisplayPort, DISPLAYPORT_LAYER_FOREGROUND);
armState = ARMING_FLAG(ARMED); armState = ARMING_FLAG(ARMED);
osdResetAlarms(); osdResetAlarms();
@ -372,6 +392,8 @@ void osdInit(displayPort_t *osdDisplayPortToUse)
#ifdef USE_OSD_PROFILES #ifdef USE_OSD_PROFILES
setOsdProfile(osdConfig()->osdProfileIndex); setOsdProfile(osdConfig()->osdProfileIndex);
#endif #endif
osdElementsInit(backgroundLayerSupported);
osdAnalyzeActiveElements(); osdAnalyzeActiveElements();
} }
@ -921,7 +943,6 @@ void osdUpdate(timeUs_t currentTimeUs)
#else #else
#define DRAW_FREQ_DENOM 10 // MWOSD @ 115200 baud ( #define DRAW_FREQ_DENOM 10 // MWOSD @ 115200 baud (
#endif #endif
#define STATS_FREQ_DENOM 50
if (counter % DRAW_FREQ_DENOM == 0) { if (counter % DRAW_FREQ_DENOM == 0) {
osdRefresh(currentTimeUs); osdRefresh(currentTimeUs);

View file

@ -311,6 +311,7 @@ void osdWarnSetState(uint8_t warningIndex, bool enabled);
bool osdWarnGetState(uint8_t warningIndex); bool osdWarnGetState(uint8_t warningIndex);
void osdSuppressStats(bool flag); void osdSuppressStats(bool flag);
void osdAnalyzeActiveElements(void);
uint8_t getCurrentOsdProfileIndex(void); uint8_t getCurrentOsdProfileIndex(void);
void changeOsdProfileIndex(uint8_t profileIndex); void changeOsdProfileIndex(uint8_t profileIndex);
bool osdElementVisible(uint16_t value); bool osdElementVisible(uint16_t value);

View file

@ -28,14 +28,31 @@
Next add the element to the osdElementDisplayOrder array defined in this file. Next add the element to the osdElementDisplayOrder array defined in this file.
If the element needs special runtime conditional processing then it should be added If the element needs special runtime conditional processing then it should be added
to the osdAnalyzeActiveElements() function instead. to the osdAddActiveElements() function instead.
Create the function to "draw" the element. It should be named like "osdElementSomething()" Create the function to "draw" the element.
where the "Something" describes the element. ------------------------------------------
It should be named like "osdElementSomething()" where the "Something" describes
the element. The drawing function should only render the dynamic portions of the
element. If the element has static (unchanging) portions then those should be
rendered in the background function. The exception to this is elements that are
expected to blink (have a warning associated). In this case the entire element
must be handled in the main draw function and you can't use the background capability.
Add the mapping from the element ID added in the first step to the function Add the mapping from the element ID added in the first step to the function
created in the third step to the osdElementDrawFunction array. created in the third step to the osdElementDrawFunction array.
Create the function to draw the element's static (background) portion.
---------------------------------------------------------------------
If an element has static (unchanging) portions then create a function to draw only those
parts. It should be named like "osdBackgroundSomething()" where the "Something" matches
the related element function.
Add the mapping for the element ID to the background drawing function to the
osdElementBackgroundFunction array.
Accelerometer reqirement:
-------------------------
If the new element utilizes the accelerometer, add it to the osdElementsNeedAccelerometer() function. If the new element utilizes the accelerometer, add it to the osdElementsNeedAccelerometer() function.
Finally add a CLI parameter for the new element in cli/settings.c. Finally add a CLI parameter for the new element in cli/settings.c.
@ -152,6 +169,7 @@ static const char compassBar[] = {
static unsigned activeOsdElementCount = 0; static unsigned activeOsdElementCount = 0;
static uint8_t activeOsdElementArray[OSD_ITEM_COUNT]; static uint8_t activeOsdElementArray[OSD_ITEM_COUNT];
static bool backgroundLayerSupported = false;
// Blink control // Blink control
static bool blinkState = true; static bool blinkState = true;
@ -578,7 +596,7 @@ static void osdElementCoreTemperature(osdElementParms_t *element)
} }
#endif // USE_ADC_INTERNAL #endif // USE_ADC_INTERNAL
static void osdElementCraftName(osdElementParms_t *element) static void osdBackgroundCraftName(osdElementParms_t *element)
{ {
if (strlen(pilotConfig()->name) == 0) { if (strlen(pilotConfig()->name) == 0) {
strcpy(element->buff, "CRAFT_NAME"); strcpy(element->buff, "CRAFT_NAME");
@ -639,7 +657,7 @@ static void osdElementCrashFlipArrow(osdElementParms_t *element)
} }
#endif // USE_ACC #endif // USE_ACC
static void osdElementCrosshairs(osdElementParms_t *element) static void osdBackgroundCrosshairs(osdElementParms_t *element)
{ {
element->buff[0] = SYM_AH_CENTER_LINE; element->buff[0] = SYM_AH_CENTER_LINE;
element->buff[1] = SYM_AH_CENTER; element->buff[1] = SYM_AH_CENTER;
@ -665,7 +683,7 @@ static void osdElementDisarmed(osdElementParms_t *element)
} }
} }
static void osdElementDisplayName(osdElementParms_t *element) static void osdBackgroundDisplayName(osdElementParms_t *element)
{ {
if (strlen(pilotConfig()->displayName) == 0) { if (strlen(pilotConfig()->displayName) == 0) {
strcpy(element->buff, "DISPLAY_NAME"); strcpy(element->buff, "DISPLAY_NAME");
@ -862,7 +880,7 @@ static void osdElementGpsSpeed(osdElementParms_t *element)
} }
#endif // USE_GPS #endif // USE_GPS
static void osdElementHorizonSidebars(osdElementParms_t *element) static void osdBackgroundHorizonSidebars(osdElementParms_t *element)
{ {
// Draw AH sides // Draw AH sides
const int8_t hudwidth = AH_SIDEBAR_WIDTH_POS; const int8_t hudwidth = AH_SIDEBAR_WIDTH_POS;
@ -1078,7 +1096,7 @@ static void osdElementRssiDbm(osdElementParms_t *element)
#endif // USE_RX_RSSI_DBM #endif // USE_RX_RSSI_DBM
#ifdef USE_OSD_STICK_OVERLAY #ifdef USE_OSD_STICK_OVERLAY
static void osdElementStickOverlay(osdElementParms_t *element) static void osdBackgroundStickOverlay(osdElementParms_t *element)
{ {
const uint8_t xpos = element->elemPosX; const uint8_t xpos = element->elemPosX;
const uint8_t ypos = element->elemPosY; const uint8_t ypos = element->elemPosY;
@ -1097,6 +1115,14 @@ static void osdElementStickOverlay(osdElementParms_t *element)
} }
} }
element->drawElement = false; // element already drawn
}
static void osdElementStickOverlay(osdElementParms_t *element)
{
const uint8_t xpos = element->elemPosX;
const uint8_t ypos = element->elemPosY;
// Now draw the cursor // Now draw the cursor
rc_alias_e vertical_channel, horizontal_channel; rc_alias_e vertical_channel, horizontal_channel;
@ -1410,7 +1436,7 @@ static void osdElementWarnings(osdElementParms_t *element)
// Elements positioned later in the list will overlay the earlier // Elements positioned later in the list will overlay the earlier
// ones if their character positions overlap // ones if their character positions overlap
// Elements that need special runtime conditional processing should be added // Elements that need special runtime conditional processing should be added
// to osdAnalyzeActiveElements() // to osdAddActiveElements()
static const uint8_t osdElementDisplayOrder[] = { static const uint8_t osdElementDisplayOrder[] = {
OSD_MAIN_BATT_VOLTAGE, OSD_MAIN_BATT_VOLTAGE,
@ -1487,15 +1513,15 @@ static const uint8_t osdElementDisplayOrder[] = {
const osdElementDrawFn osdElementDrawFunction[OSD_ITEM_COUNT] = { const osdElementDrawFn osdElementDrawFunction[OSD_ITEM_COUNT] = {
[OSD_RSSI_VALUE] = osdElementRssi, [OSD_RSSI_VALUE] = osdElementRssi,
[OSD_MAIN_BATT_VOLTAGE] = osdElementMainBatteryVoltage, [OSD_MAIN_BATT_VOLTAGE] = osdElementMainBatteryVoltage,
[OSD_CROSSHAIRS] = osdElementCrosshairs, [OSD_CROSSHAIRS] = NULL, // only has background
#ifdef USE_ACC #ifdef USE_ACC
[OSD_ARTIFICIAL_HORIZON] = osdElementArtificialHorizon, [OSD_ARTIFICIAL_HORIZON] = osdElementArtificialHorizon,
#endif #endif
[OSD_HORIZON_SIDEBARS] = osdElementHorizonSidebars, [OSD_HORIZON_SIDEBARS] = NULL, // only has background
[OSD_ITEM_TIMER_1] = osdElementTimer, [OSD_ITEM_TIMER_1] = osdElementTimer,
[OSD_ITEM_TIMER_2] = osdElementTimer, [OSD_ITEM_TIMER_2] = osdElementTimer,
[OSD_FLYMODE] = osdElementFlymode, [OSD_FLYMODE] = osdElementFlymode,
[OSD_CRAFT_NAME] = osdElementCraftName, [OSD_CRAFT_NAME] = NULL, // only has background
[OSD_THROTTLE_POS] = osdElementThrottlePosition, [OSD_THROTTLE_POS] = osdElementThrottlePosition,
#ifdef USE_VTX_COMMON #ifdef USE_VTX_COMMON
[OSD_VTX_CHANNEL] = osdElementVtxChannel, [OSD_VTX_CHANNEL] = osdElementVtxChannel,
@ -1571,7 +1597,7 @@ const osdElementDrawFn osdElementDrawFunction[OSD_ITEM_COUNT] = {
[OSD_STICK_OVERLAY_LEFT] = osdElementStickOverlay, [OSD_STICK_OVERLAY_LEFT] = osdElementStickOverlay,
[OSD_STICK_OVERLAY_RIGHT] = osdElementStickOverlay, [OSD_STICK_OVERLAY_RIGHT] = osdElementStickOverlay,
#endif #endif
[OSD_DISPLAY_NAME] = osdElementDisplayName, [OSD_DISPLAY_NAME] = NULL, // only has background
#if defined(USE_DSHOT_TELEMETRY) || defined(USE_ESC_SENSOR) #if defined(USE_DSHOT_TELEMETRY) || defined(USE_ESC_SENSOR)
[OSD_ESC_RPM_FREQ] = osdElementEscRpmFreq, [OSD_ESC_RPM_FREQ] = osdElementEscRpmFreq,
#endif #endif
@ -1588,6 +1614,20 @@ const osdElementDrawFn osdElementDrawFunction[OSD_ITEM_COUNT] = {
[OSD_RC_CHANNELS] = osdElementRcChannels, [OSD_RC_CHANNELS] = osdElementRcChannels,
}; };
// Define the mapping between the OSD element id and the function to draw its background (static part)
// Only necessary to define the entries that actually have a background function
const osdElementDrawFn osdElementBackgroundFunction[OSD_ITEM_COUNT] = {
[OSD_CROSSHAIRS] = osdBackgroundCrosshairs,
[OSD_HORIZON_SIDEBARS] = osdBackgroundHorizonSidebars,
[OSD_CRAFT_NAME] = osdBackgroundCraftName,
#ifdef USE_OSD_STICK_OVERLAY
[OSD_STICK_OVERLAY_LEFT] = osdBackgroundStickOverlay,
[OSD_STICK_OVERLAY_RIGHT] = osdBackgroundStickOverlay,
#endif
[OSD_DISPLAY_NAME] = osdBackgroundDisplayName,
};
static void osdAddActiveElement(osd_items_e element) static void osdAddActiveElement(osd_items_e element)
{ {
if (VISIBLE(osdConfig()->item_pos[element])) { if (VISIBLE(osdConfig()->item_pos[element])) {
@ -1598,7 +1638,7 @@ static void osdAddActiveElement(osd_items_e element)
// Examine the elements and build a list of only the active (enabled) // Examine the elements and build a list of only the active (enabled)
// ones to speed up rendering. // ones to speed up rendering.
void osdAnalyzeActiveElements(void) void osdAddActiveElements(void)
{ {
activeOsdElementCount = 0; activeOsdElementCount = 0;
@ -1638,10 +1678,14 @@ void osdAnalyzeActiveElements(void)
#endif #endif
} }
static bool osdDrawSingleElement(displayPort_t *osdDisplayPort, uint8_t item) static void osdDrawSingleElement(displayPort_t *osdDisplayPort, uint8_t item)
{ {
if (!osdElementDrawFunction[item]) {
// Element has no drawing function
return;
}
if (BLINK(item)) { if (BLINK(item)) {
return false; return;
} }
uint8_t elemPosX = OSD_X(osdConfig()->item_pos[item]); uint8_t elemPosX = OSD_X(osdConfig()->item_pos[item]);
@ -1661,8 +1705,32 @@ static bool osdDrawSingleElement(displayPort_t *osdDisplayPort, uint8_t item)
if (element.drawElement) { if (element.drawElement) {
displayWrite(osdDisplayPort, elemPosX, elemPosY, buff); displayWrite(osdDisplayPort, elemPosX, elemPosY, buff);
} }
}
return true; static void osdDrawSingleElementBackground(displayPort_t *osdDisplayPort, uint8_t item)
{
if (!osdElementBackgroundFunction[item]) {
// Element has no background drawing function
return;
}
uint8_t elemPosX = OSD_X(osdConfig()->item_pos[item]);
uint8_t elemPosY = OSD_Y(osdConfig()->item_pos[item]);
char buff[OSD_ELEMENT_BUFFER_LENGTH] = "";
osdElementParms_t element;
element.item = item;
element.elemPosX = elemPosX;
element.elemPosY = elemPosY;
element.buff = (char *)&buff;
element.osdDisplayPort = osdDisplayPort;
element.drawElement = true;
// Call the element background drawing function
osdElementBackgroundFunction[item](&element);
if (element.drawElement) {
displayWrite(osdDisplayPort, elemPosX, elemPosY, buff);
}
} }
void osdDrawActiveElements(displayPort_t *osdDisplayPort, timeUs_t currentTimeUs) void osdDrawActiveElements(displayPort_t *osdDisplayPort, timeUs_t currentTimeUs)
@ -1681,10 +1749,33 @@ void osdDrawActiveElements(displayPort_t *osdDisplayPort, timeUs_t currentTimeUs
blinkState = (currentTimeUs / 200000) % 2; blinkState = (currentTimeUs / 200000) % 2;
for (unsigned i = 0; i < activeOsdElementCount; i++) { for (unsigned i = 0; i < activeOsdElementCount; i++) {
if (!backgroundLayerSupported) {
// If the background layer isn't supported then we
// have to draw the element's static layer as well.
osdDrawSingleElementBackground(osdDisplayPort, activeOsdElementArray[i]);
}
osdDrawSingleElement(osdDisplayPort, activeOsdElementArray[i]); osdDrawSingleElement(osdDisplayPort, activeOsdElementArray[i]);
} }
} }
void osdDrawActiveElementsBackground(displayPort_t *osdDisplayPort)
{
if (backgroundLayerSupported) {
displayLayerSelect(osdDisplayPort, DISPLAYPORT_LAYER_BACKGROUND);
displayClearScreen(osdDisplayPort);
for (unsigned i = 0; i < activeOsdElementCount; i++) {
osdDrawSingleElementBackground(osdDisplayPort, activeOsdElementArray[i]);
}
displayLayerSelect(osdDisplayPort, DISPLAYPORT_LAYER_FOREGROUND);
}
}
void osdElementsInit(bool backgroundLayerFlag)
{
backgroundLayerSupported = backgroundLayerFlag;
activeOsdElementCount = 0;
}
void osdResetAlarms(void) void osdResetAlarms(void)
{ {
memset(blinkBits, 0, sizeof(blinkBits)); memset(blinkBits, 0, sizeof(blinkBits));

View file

@ -45,8 +45,10 @@ char osdGetMetersToSelectedUnitSymbol(void);
int32_t osdGetSpeedToSelectedUnit(int32_t value); int32_t osdGetSpeedToSelectedUnit(int32_t value);
char osdGetSpeedToSelectedUnitSymbol(void); char osdGetSpeedToSelectedUnitSymbol(void);
char osdGetTemperatureSymbolForSelectedUnit(void); char osdGetTemperatureSymbolForSelectedUnit(void);
void osdAnalyzeActiveElements(void); void osdAddActiveElements(void);
void osdDrawActiveElements(displayPort_t *osdDisplayPort, timeUs_t currentTimeUs); void osdDrawActiveElements(displayPort_t *osdDisplayPort, timeUs_t currentTimeUs);
void osdDrawActiveElementsBackground(displayPort_t *osdDisplayPort);
void osdElementsInit(bool backgroundLayerFlag);
void osdResetAlarms(void); void osdResetAlarms(void);
void osdUpdateAlarms(void); void osdUpdateAlarms(void);
bool osdElementsNeedAccelerometer(void); bool osdElementsNeedAccelerometer(void);