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:
commit
8548189bc6
15 changed files with 305 additions and 49 deletions
|
@ -720,6 +720,7 @@ void cmsMenuOpen(void)
|
|||
currentCtx = (cmsCtx_t){ &menuMain, 0, 0 };
|
||||
menuStackIdx = 0;
|
||||
setArmingDisabled(ARMING_DISABLED_CMS_MENU);
|
||||
displayLayerSelect(pCurrentDisplay, DISPLAYPORT_LAYER_FOREGROUND); // make sure the foreground layer is active
|
||||
} else {
|
||||
// Switch display
|
||||
displayPort_t *pNextDisplay = cmsDisplayPortSelectNext();
|
||||
|
|
|
@ -115,6 +115,33 @@ uint16_t displayTxBytesFree(const displayPort_t *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)
|
||||
{
|
||||
instance->vTable = vTable;
|
||||
|
|
|
@ -20,6 +20,12 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
typedef enum {
|
||||
DISPLAYPORT_LAYER_FOREGROUND,
|
||||
DISPLAYPORT_LAYER_BACKGROUND,
|
||||
DISPLAYPORT_LAYER_COUNT,
|
||||
} displayPortLayer_e;
|
||||
|
||||
struct displayPortVTable_s;
|
||||
|
||||
typedef struct displayPort_s {
|
||||
|
@ -52,6 +58,9 @@ typedef struct displayPortVTable_s {
|
|||
void (*resync)(displayPort_t *displayPort);
|
||||
bool (*isSynced)(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;
|
||||
|
||||
typedef struct displayPortProfile_s {
|
||||
|
@ -81,3 +90,7 @@ void displayResync(displayPort_t *instance);
|
|||
bool displayIsSynced(const displayPort_t *instance);
|
||||
uint16_t displayTxBytesFree(const displayPort_t *instance);
|
||||
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);
|
||||
|
||||
|
|
|
@ -181,6 +181,15 @@
|
|||
#define __spiBusTransactionEnd(busdev) {IOHi((busdev)->busdev_u.spi.csnPin);spiSetDivisor((busdev)->busdev_u.spi.instance, MAX7456_RESTORE_CLK);}
|
||||
#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 *busdev = &max7456BusDevice;
|
||||
|
||||
|
@ -188,11 +197,10 @@ static uint16_t max7456SpiClock = MAX7456_SPI_CLK;
|
|||
|
||||
uint16_t maxScreenSize = VIDEO_BUFFER_CHARS_PAL;
|
||||
|
||||
// We write everything in screenBuffer and then compare
|
||||
// screenBuffer with shadowBuffer to upgrade only changed chars.
|
||||
// We write everything to the active layer and then compare
|
||||
// it with shadowBuffer to update only changed chars.
|
||||
// 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];
|
||||
|
||||
//Max chars to update in one idle
|
||||
|
@ -222,6 +230,16 @@ static uint8_t previousInvertRegister = INVALID_PREVIOUS_REGISTER_STATE;
|
|||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
// initialize all layers
|
||||
for (unsigned i = 0; i < MAX7456_SUPPORTED_LAYER_COUNT; i++) {
|
||||
max7456ClearLayer(i);
|
||||
}
|
||||
|
||||
max7456HardwareReset();
|
||||
|
||||
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)
|
||||
{
|
||||
memset(screenBuffer, 0x20, VIDEO_BUFFER_CHARS_PAL);
|
||||
}
|
||||
|
||||
uint8_t* max7456GetScreenBuffer(void)
|
||||
{
|
||||
return screenBuffer;
|
||||
max7456ClearLayer(activeLayer);
|
||||
}
|
||||
|
||||
void max7456WriteChar(uint8_t x, uint8_t y, uint8_t c)
|
||||
{
|
||||
uint8_t *buffer = getActiveLayerBuffer();
|
||||
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)
|
||||
{
|
||||
if (y < VIDEO_LINES_PAL) {
|
||||
uint8_t *buffer = getActiveLayerBuffer();
|
||||
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)
|
||||
{
|
||||
#ifdef MAX7456_DMA_CHANNEL_TX
|
||||
|
@ -607,7 +665,7 @@ bool max7456DmaInProgress(void)
|
|||
bool max7456BuffersSynced(void)
|
||||
{
|
||||
for (int i = 0; i < maxScreenSize; i++) {
|
||||
if (screenBuffer[i] != shadowBuffer[i]) {
|
||||
if (displayLayers[DISPLAYPORT_LAYER_FOREGROUND].buffer[i] != shadowBuffer[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -679,16 +737,18 @@ void max7456DrawScreen(void)
|
|||
|
||||
max7456ReInitIfRequired(false);
|
||||
|
||||
uint8_t *buffer = getActiveLayerBuffer();
|
||||
|
||||
int buff_len = 0;
|
||||
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++] = pos >> 8;
|
||||
spiBuff[buff_len++] = MAX7456ADD_DMAL;
|
||||
spiBuff[buff_len++] = pos & 0xff;
|
||||
spiBuff[buff_len++] = MAX7456ADD_DMDI;
|
||||
spiBuff[buff_len++] = screenBuffer[pos];
|
||||
shadowBuffer[pos] = screenBuffer[pos];
|
||||
spiBuff[buff_len++] = buffer[pos];
|
||||
shadowBuffer[pos] = buffer[pos];
|
||||
}
|
||||
|
||||
if (++pos >= maxScreenSize) {
|
||||
|
@ -712,23 +772,24 @@ void max7456DrawScreen(void)
|
|||
static void max7456DrawScreenSlow(void)
|
||||
{
|
||||
bool escapeCharFound;
|
||||
uint8_t *buffer = getActiveLayerBuffer();
|
||||
|
||||
__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.
|
||||
max7456Send(MAX7456ADD_DMAH, 0);
|
||||
max7456Send(MAX7456ADD_DMAL, 0);
|
||||
max7456Send(MAX7456ADD_DMM, displayMemoryModeReg | 1);
|
||||
|
||||
for (int xx = 0; xx < maxScreenSize; xx++) {
|
||||
if (screenBuffer[xx] == END_STRING) {
|
||||
if (buffer[xx] == END_STRING) {
|
||||
escapeCharFound = true;
|
||||
max7456Send(MAX7456ADD_DMDI, ' '); // replace the 0xFF character with a blank in the first pass to avoid terminating auto-increment
|
||||
} else {
|
||||
max7456Send(MAX7456ADD_DMDI, screenBuffer[xx]);
|
||||
max7456Send(MAX7456ADD_DMDI, buffer[xx]);
|
||||
}
|
||||
shadowBuffer[xx] = screenBuffer[xx];
|
||||
shadowBuffer[xx] = buffer[xx];
|
||||
}
|
||||
|
||||
max7456Send(MAX7456ADD_DMDI, END_STRING);
|
||||
|
@ -738,7 +799,7 @@ static void max7456DrawScreenSlow(void)
|
|||
// to update them with direct addressing
|
||||
if (escapeCharFound) {
|
||||
for (int xx = 0; xx < maxScreenSize; xx++) {
|
||||
if (screenBuffer[xx] == END_STRING) {
|
||||
if (buffer[xx] == END_STRING) {
|
||||
max7456Send(MAX7456ADD_DMAH, xx >> 8);
|
||||
max7456Send(MAX7456ADD_DMAL, xx & 0xFF);
|
||||
max7456Send(MAX7456ADD_DMDI, END_STRING);
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "drivers/display.h"
|
||||
|
||||
/** PAL or NTSC, value is number of chars total */
|
||||
#define VIDEO_BUFFER_CHARS_NTSC 390
|
||||
#define VIDEO_BUFFER_CHARS_PAL 480
|
||||
|
@ -27,7 +29,6 @@
|
|||
#define VIDEO_LINES_PAL 16
|
||||
|
||||
extern uint16_t maxScreenSize;
|
||||
|
||||
struct vcdProfile_s;
|
||||
void max7456HardwareReset(void);
|
||||
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 max7456ClearScreen(void);
|
||||
void max7456RefreshAll(void);
|
||||
uint8_t* max7456GetScreenBuffer(void);
|
||||
bool max7456DmaInProgress(void);
|
||||
bool max7456BuffersSynced(void);
|
||||
bool max7456LayerSupported(displayPortLayer_e layer);
|
||||
bool max7456LayerSelect(displayPortLayer_e layer);
|
||||
bool max7456LayerCopy(displayPortLayer_e destLayer, displayPortLayer_e sourceLayer);
|
||||
|
|
|
@ -138,7 +138,10 @@ static const displayPortVTable_t crsfDisplayPortVTable = {
|
|||
.heartbeat = crsfHeartbeat,
|
||||
.resync = crsfResync,
|
||||
.isSynced = crsfIsSynced,
|
||||
.txBytesFree = crsfTxBytesFree
|
||||
.txBytesFree = crsfTxBytesFree,
|
||||
.layerSupported = NULL,
|
||||
.layerSelect = NULL,
|
||||
.layerCopy = NULL,
|
||||
};
|
||||
|
||||
crsfDisplayPortScreen_t *crsfDisplayPortScreen(void)
|
||||
|
|
|
@ -119,7 +119,10 @@ static const displayPortVTable_t hottVTable = {
|
|||
.isTransferInProgress = hottIsTransferInProgress,
|
||||
.heartbeat = hottHeartbeat,
|
||||
.resync = hottResync,
|
||||
.txBytesFree = hottTxBytesFree
|
||||
.txBytesFree = hottTxBytesFree,
|
||||
.layerSupported = NULL,
|
||||
.layerSelect = NULL,
|
||||
.layerCopy = NULL,
|
||||
};
|
||||
|
||||
displayPort_t *displayPortHottInit()
|
||||
|
|
|
@ -148,6 +148,24 @@ static uint32_t txBytesFree(const displayPort_t *displayPort)
|
|||
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 = {
|
||||
.grab = grab,
|
||||
.release = release,
|
||||
|
@ -161,6 +179,9 @@ static const displayPortVTable_t max7456VTable = {
|
|||
.resync = resync,
|
||||
.isSynced = isSynced,
|
||||
.txBytesFree = txBytesFree,
|
||||
.layerSupported = layerSupported,
|
||||
.layerSelect = layerSelect,
|
||||
.layerCopy = layerCopy,
|
||||
};
|
||||
|
||||
displayPort_t *max7456DisplayPortInit(const vcdProfile_t *vcdProfile)
|
||||
|
|
|
@ -167,7 +167,10 @@ static const displayPortVTable_t mspDisplayPortVTable = {
|
|||
.heartbeat = heartbeat,
|
||||
.resync = resync,
|
||||
.isSynced = isSynced,
|
||||
.txBytesFree = txBytesFree
|
||||
.txBytesFree = txBytesFree,
|
||||
.layerSupported = NULL,
|
||||
.layerSelect = NULL,
|
||||
.layerCopy = NULL,
|
||||
};
|
||||
|
||||
displayPort_t *displayPortMspInit(void)
|
||||
|
|
|
@ -114,7 +114,10 @@ static const displayPortVTable_t oledVTable = {
|
|||
.heartbeat = oledHeartbeat,
|
||||
.resync = oledResync,
|
||||
.isSynced = oledIsSynced,
|
||||
.txBytesFree = oledTxBytesFree
|
||||
.txBytesFree = oledTxBytesFree,
|
||||
.layerSupported = NULL,
|
||||
.layerSelect = NULL,
|
||||
.layerCopy = NULL,
|
||||
};
|
||||
|
||||
displayPort_t *displayPortOledInit(void *device)
|
||||
|
|
|
@ -130,7 +130,10 @@ static const displayPortVTable_t srxlVTable = {
|
|||
.heartbeat = srxlHeartbeat,
|
||||
.resync = srxlResync,
|
||||
.isSynced = srxlIsSynced,
|
||||
.txBytesFree = srxlTxBytesFree
|
||||
.txBytesFree = srxlTxBytesFree,
|
||||
.layerSupported = NULL,
|
||||
.layerSelect = NULL,
|
||||
.layerCopy = NULL,
|
||||
};
|
||||
|
||||
displayPort_t *displayPortSrxlInit()
|
||||
|
|
|
@ -124,6 +124,8 @@ static displayPort_t *osdDisplayPort;
|
|||
static bool suppressStatsDisplay = false;
|
||||
static uint8_t osdStatsRowCount = 0;
|
||||
|
||||
static bool backgroundLayerSupported = false;
|
||||
|
||||
#ifdef USE_ESC_SENSOR
|
||||
escSensorData_t *osdEscDataCombined;
|
||||
#endif
|
||||
|
@ -225,15 +227,30 @@ void changeOsdProfileIndex(uint8_t profileIndex)
|
|||
}
|
||||
#endif
|
||||
|
||||
void osdAnalyzeActiveElements(void)
|
||||
{
|
||||
osdAddActiveElements();
|
||||
osdDrawActiveElementsBackground(osdDisplayPort);
|
||||
}
|
||||
|
||||
static void osdDrawElements(timeUs_t currentTimeUs)
|
||||
{
|
||||
displayClearScreen(osdDisplayPort);
|
||||
|
||||
// Hide OSD when OSDSW mode is active
|
||||
if (IS_RC_MODE_ACTIVE(BOXOSD)) {
|
||||
displayClearScreen(osdDisplayPort);
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -342,6 +359,9 @@ void osdInit(displayPort_t *osdDisplayPortToUse)
|
|||
cmsDisplayPortRegister(osdDisplayPort);
|
||||
#endif
|
||||
|
||||
backgroundLayerSupported = displayLayerSupported(osdDisplayPort, DISPLAYPORT_LAYER_BACKGROUND);
|
||||
displayLayerSelect(osdDisplayPort, DISPLAYPORT_LAYER_FOREGROUND);
|
||||
|
||||
armState = ARMING_FLAG(ARMED);
|
||||
|
||||
osdResetAlarms();
|
||||
|
@ -372,6 +392,8 @@ void osdInit(displayPort_t *osdDisplayPortToUse)
|
|||
#ifdef USE_OSD_PROFILES
|
||||
setOsdProfile(osdConfig()->osdProfileIndex);
|
||||
#endif
|
||||
|
||||
osdElementsInit(backgroundLayerSupported);
|
||||
osdAnalyzeActiveElements();
|
||||
}
|
||||
|
||||
|
@ -921,7 +943,6 @@ void osdUpdate(timeUs_t currentTimeUs)
|
|||
#else
|
||||
#define DRAW_FREQ_DENOM 10 // MWOSD @ 115200 baud (
|
||||
#endif
|
||||
#define STATS_FREQ_DENOM 50
|
||||
|
||||
if (counter % DRAW_FREQ_DENOM == 0) {
|
||||
osdRefresh(currentTimeUs);
|
||||
|
|
|
@ -311,6 +311,7 @@ void osdWarnSetState(uint8_t warningIndex, bool enabled);
|
|||
bool osdWarnGetState(uint8_t warningIndex);
|
||||
void osdSuppressStats(bool flag);
|
||||
|
||||
void osdAnalyzeActiveElements(void);
|
||||
uint8_t getCurrentOsdProfileIndex(void);
|
||||
void changeOsdProfileIndex(uint8_t profileIndex);
|
||||
bool osdElementVisible(uint16_t value);
|
||||
|
|
|
@ -28,14 +28,31 @@
|
|||
|
||||
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
|
||||
to the osdAnalyzeActiveElements() function instead.
|
||||
to the osdAddActiveElements() function instead.
|
||||
|
||||
Create the function to "draw" the element. It should be named like "osdElementSomething()"
|
||||
where the "Something" describes the element.
|
||||
Create the function to "draw" 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
|
||||
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.
|
||||
|
||||
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 uint8_t activeOsdElementArray[OSD_ITEM_COUNT];
|
||||
static bool backgroundLayerSupported = false;
|
||||
|
||||
// Blink control
|
||||
static bool blinkState = true;
|
||||
|
@ -578,7 +596,7 @@ static void osdElementCoreTemperature(osdElementParms_t *element)
|
|||
}
|
||||
#endif // USE_ADC_INTERNAL
|
||||
|
||||
static void osdElementCraftName(osdElementParms_t *element)
|
||||
static void osdBackgroundCraftName(osdElementParms_t *element)
|
||||
{
|
||||
if (strlen(pilotConfig()->name) == 0) {
|
||||
strcpy(element->buff, "CRAFT_NAME");
|
||||
|
@ -639,7 +657,7 @@ static void osdElementCrashFlipArrow(osdElementParms_t *element)
|
|||
}
|
||||
#endif // USE_ACC
|
||||
|
||||
static void osdElementCrosshairs(osdElementParms_t *element)
|
||||
static void osdBackgroundCrosshairs(osdElementParms_t *element)
|
||||
{
|
||||
element->buff[0] = SYM_AH_CENTER_LINE;
|
||||
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) {
|
||||
strcpy(element->buff, "DISPLAY_NAME");
|
||||
|
@ -862,7 +880,7 @@ static void osdElementGpsSpeed(osdElementParms_t *element)
|
|||
}
|
||||
#endif // USE_GPS
|
||||
|
||||
static void osdElementHorizonSidebars(osdElementParms_t *element)
|
||||
static void osdBackgroundHorizonSidebars(osdElementParms_t *element)
|
||||
{
|
||||
// Draw AH sides
|
||||
const int8_t hudwidth = AH_SIDEBAR_WIDTH_POS;
|
||||
|
@ -1078,7 +1096,7 @@ static void osdElementRssiDbm(osdElementParms_t *element)
|
|||
#endif // USE_RX_RSSI_DBM
|
||||
|
||||
#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 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
|
||||
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
|
||||
// ones if their character positions overlap
|
||||
// Elements that need special runtime conditional processing should be added
|
||||
// to osdAnalyzeActiveElements()
|
||||
// to osdAddActiveElements()
|
||||
|
||||
static const uint8_t osdElementDisplayOrder[] = {
|
||||
OSD_MAIN_BATT_VOLTAGE,
|
||||
|
@ -1487,15 +1513,15 @@ static const uint8_t osdElementDisplayOrder[] = {
|
|||
const osdElementDrawFn osdElementDrawFunction[OSD_ITEM_COUNT] = {
|
||||
[OSD_RSSI_VALUE] = osdElementRssi,
|
||||
[OSD_MAIN_BATT_VOLTAGE] = osdElementMainBatteryVoltage,
|
||||
[OSD_CROSSHAIRS] = osdElementCrosshairs,
|
||||
[OSD_CROSSHAIRS] = NULL, // only has background
|
||||
#ifdef USE_ACC
|
||||
[OSD_ARTIFICIAL_HORIZON] = osdElementArtificialHorizon,
|
||||
#endif
|
||||
[OSD_HORIZON_SIDEBARS] = osdElementHorizonSidebars,
|
||||
[OSD_HORIZON_SIDEBARS] = NULL, // only has background
|
||||
[OSD_ITEM_TIMER_1] = osdElementTimer,
|
||||
[OSD_ITEM_TIMER_2] = osdElementTimer,
|
||||
[OSD_FLYMODE] = osdElementFlymode,
|
||||
[OSD_CRAFT_NAME] = osdElementCraftName,
|
||||
[OSD_CRAFT_NAME] = NULL, // only has background
|
||||
[OSD_THROTTLE_POS] = osdElementThrottlePosition,
|
||||
#ifdef USE_VTX_COMMON
|
||||
[OSD_VTX_CHANNEL] = osdElementVtxChannel,
|
||||
|
@ -1571,7 +1597,7 @@ const osdElementDrawFn osdElementDrawFunction[OSD_ITEM_COUNT] = {
|
|||
[OSD_STICK_OVERLAY_LEFT] = osdElementStickOverlay,
|
||||
[OSD_STICK_OVERLAY_RIGHT] = osdElementStickOverlay,
|
||||
#endif
|
||||
[OSD_DISPLAY_NAME] = osdElementDisplayName,
|
||||
[OSD_DISPLAY_NAME] = NULL, // only has background
|
||||
#if defined(USE_DSHOT_TELEMETRY) || defined(USE_ESC_SENSOR)
|
||||
[OSD_ESC_RPM_FREQ] = osdElementEscRpmFreq,
|
||||
#endif
|
||||
|
@ -1588,6 +1614,20 @@ const osdElementDrawFn osdElementDrawFunction[OSD_ITEM_COUNT] = {
|
|||
[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)
|
||||
{
|
||||
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)
|
||||
// ones to speed up rendering.
|
||||
|
||||
void osdAnalyzeActiveElements(void)
|
||||
void osdAddActiveElements(void)
|
||||
{
|
||||
activeOsdElementCount = 0;
|
||||
|
||||
|
@ -1638,10 +1678,14 @@ void osdAnalyzeActiveElements(void)
|
|||
#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)) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
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) {
|
||||
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)
|
||||
|
@ -1681,10 +1749,33 @@ void osdDrawActiveElements(displayPort_t *osdDisplayPort, timeUs_t currentTimeUs
|
|||
blinkState = (currentTimeUs / 200000) % 2;
|
||||
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
memset(blinkBits, 0, sizeof(blinkBits));
|
||||
|
|
|
@ -45,8 +45,10 @@ char osdGetMetersToSelectedUnitSymbol(void);
|
|||
int32_t osdGetSpeedToSelectedUnit(int32_t value);
|
||||
char osdGetSpeedToSelectedUnitSymbol(void);
|
||||
char osdGetTemperatureSymbolForSelectedUnit(void);
|
||||
void osdAnalyzeActiveElements(void);
|
||||
void osdAddActiveElements(void);
|
||||
void osdDrawActiveElements(displayPort_t *osdDisplayPort, timeUs_t currentTimeUs);
|
||||
void osdDrawActiveElementsBackground(displayPort_t *osdDisplayPort);
|
||||
void osdElementsInit(bool backgroundLayerFlag);
|
||||
void osdResetAlarms(void);
|
||||
void osdUpdateAlarms(void);
|
||||
bool osdElementsNeedAccelerometer(void);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue