Add support for FrSky OSD (#9127)

Add support for FrSky OSD
This commit is contained in:
Michael Keller 2019-12-01 13:22:41 +13:00 committed by GitHub
commit 76d9cc3e38
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 2311 additions and 44 deletions

View File

@ -26,6 +26,7 @@ COMMON_SRC = \
drivers/bus_spi_pinconfig.c \
drivers/buttons.c \
drivers/display.c \
drivers/display_canvas.c \
drivers/dma_reqmap.c \
drivers/exti.c \
drivers/io.c \
@ -152,12 +153,14 @@ COMMON_SRC = \
drivers/vtx_common.c \
drivers/vtx_table.c \
io/dashboard.c \
io/displayport_frsky_osd.c \
io/displayport_max7456.c \
io/displayport_msp.c \
io/displayport_oled.c \
io/displayport_srxl.c \
io/displayport_crsf.c \
io/displayport_hott.c \
io/frsky_osd.c \
io/rcdevice_cam.c \
io/rcdevice.c \
io/gps.c \

View File

@ -1253,8 +1253,8 @@ static void cliSerial(char *cmdline)
ptr = nextArg(ptr);
if (ptr) {
val = atoi(ptr);
portConfig.functionMask = val & 0xFFFF;
val = strtoul(ptr, NULL, 10);
portConfig.functionMask = val;
validArgumentCount++;
}

View File

@ -484,7 +484,7 @@ static const char* const lookupTableDshotBitbangedTimer[] = {
};
static const char * const lookupTableOsdDisplayPortDevice[] = {
"NONE", "AUTO", "MAX7456", "MSP",
"NONE", "AUTO", "MAX7456", "MSP", "FRSKYOSD"
};
#ifdef USE_OSD

View File

@ -1,5 +1,5 @@
/*
* This file is part of INAV.
* This file is part of Cleanflight, Betaflight and INAV.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,

68
src/main/common/uvarint.c Normal file
View File

@ -0,0 +1,68 @@
/*
* This file is part of Cleanflight, Betaflight and INAV.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Alternatively, the contents of this file may be used under the terms
* of the GNU General Public License Version 3, as described below:
*
* This file is free software: you may copy, redistribute and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*
* @author Alberto Garcia Hierro <alberto@garciahierro.com>
*/
#include "platform.h"
#include "common/uvarint.h"
int uvarintEncode(uint32_t val, uint8_t *ptr, size_t size)
{
unsigned ii = 0;
while (val > 0x80) {
if (ii >= size) {
return -1;
}
ptr[ii] = (val & 0xFF) | 0x80;
val >>= 7;
ii++;
}
if (ii >= size) {
return -1;
}
ptr[ii] = val & 0xFF;
return ii + 1;
}
int uvarintDecode(uint32_t *val, const uint8_t *ptr, size_t size)
{
unsigned s = 0;
*val = 0;
for (size_t ii = 0; ii < size; ii++) {
uint8_t b = ptr[ii];
if (b < 0x80) {
if (ii > 5 || (ii == 5 && b > 1)) {
// uint32_t overflow
return -2;
}
*val |= ((uint32_t)b) << s;
return ii + 1;
}
*val |= ((uint32_t)(b & 0x7f)) << s;
s += 7;
}
// no value could be decoded and we have no data left
return -1;
}

33
src/main/common/uvarint.h Normal file
View File

@ -0,0 +1,33 @@
/*
* This file is part of Cleanflight, Betaflight and INAV.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Alternatively, the contents of this file may be used under the terms
* of the GNU General Public License Version 3, as described below:
*
* This file is free software: you may copy, redistribute and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*
* @author Alberto Garcia Hierro <alberto@garciahierro.com>
*/
#pragma once
#include <stddef.h>
#include <stdint.h>
int uvarintEncode(uint32_t val, uint8_t *ptr, size_t size);
int uvarintDecode(uint32_t *val, const uint8_t *ptr, size_t size);

View File

@ -26,6 +26,9 @@
#include "common/utils.h"
#include "drivers/display_canvas.h"
#include "drivers/osd.h"
#include "display.h"
void displayClearScreen(displayPort_t *instance)
@ -142,6 +145,54 @@ bool displayLayerCopy(displayPort_t *instance, displayPortLayer_e destLayer, dis
return false;
}
bool displayWriteFontCharacter(displayPort_t *instance, uint16_t addr, const osdCharacter_t *chr)
{
if (instance->vTable->writeFontCharacter) {
return instance->vTable->writeFontCharacter(instance, addr, chr);
}
return false;
}
bool displayIsReady(displayPort_t *instance)
{
if (instance->vTable->isReady) {
return instance->vTable->isReady(instance);
}
// Drivers that don't provide an isReady method are
// assumed to be immediately ready (either by actually
// begin ready very quickly or by blocking)
return true;
}
void displayBeginTransaction(displayPort_t *instance, displayTransactionOption_e opts)
{
if (instance->vTable->beginTransaction) {
instance->vTable->beginTransaction(instance, opts);
}
}
void displayCommitTransaction(displayPort_t *instance)
{
if (instance->vTable->commitTransaction) {
instance->vTable->commitTransaction(instance);
}
}
bool displayGetCanvas(displayCanvas_t *canvas, const displayPort_t *instance)
{
#if defined(USE_CANVAS)
if (canvas && instance->vTable->getCanvas && instance->vTable->getCanvas(canvas, instance)) {
canvas->gridElementWidth = canvas->width / instance->cols;
canvas->gridElementHeight = canvas->height / instance->rows;
return true;
}
#else
UNUSED(canvas);
UNUSED(instance);
#endif
return false;
}
void displayInit(displayPort_t *instance, const displayPortVTable_t *vTable)
{
instance->vTable = vTable;

View File

@ -35,6 +35,15 @@ typedef enum {
DISPLAYPORT_LAYER_COUNT,
} displayPortLayer_e;
typedef enum {
DISPLAY_TRANSACTION_OPT_NONE = 0,
DISPLAY_TRANSACTION_OPT_PROFILED = 1 << 0,
DISPLAY_TRANSACTION_OPT_RESET_DRAWING = 1 << 1,
} displayTransactionOption_e;
struct displayCanvas_s;
struct osdCharacter_s;
struct displayPortVTable_s;
typedef struct displayPort_s {
@ -68,6 +77,11 @@ typedef struct displayPortVTable_s {
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);
bool (*writeFontCharacter)(displayPort_t *instance, uint16_t addr, const struct osdCharacter_s *chr);
bool (*isReady)(displayPort_t *displayPort);
void (*beginTransaction)(displayPort_t *displayPort, displayTransactionOption_e opts);
void (*commitTransaction)(displayPort_t *displayPort);
bool (*getCanvas)(struct displayCanvas_s *canvas, const displayPort_t *displayPort);
} displayPortVTable_t;
void displayGrab(displayPort_t *instance);
@ -85,6 +99,11 @@ void displayHeartbeat(displayPort_t *instance);
void displayResync(displayPort_t *instance);
bool displayIsSynced(const displayPort_t *instance);
uint16_t displayTxBytesFree(const displayPort_t *instance);
bool displayWriteFontCharacter(displayPort_t *instance, uint16_t addr, const struct osdCharacter_s *chr);
bool displayIsReady(displayPort_t *instance);
void displayBeginTransaction(displayPort_t *instance, displayTransactionOption_e opts);
void displayCommitTransaction(displayPort_t *instance);
bool displayGetCanvas(struct displayCanvas_s *canvas, 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);

View File

@ -0,0 +1,277 @@
/*
* This file is part of Cleanflight, Betaflight and INAV.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Alternatively, the contents of this file may be used under the terms
* of the GNU General Public License Version 3, as described below:
*
* This file is free software: you may copy, redistribute and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*
* @author Alberto Garcia Hierro <alberto@garciahierro.com>
*/
#include "platform.h"
#include "drivers/display_canvas.h"
void displayCanvasSetStrokeColor(displayCanvas_t *displayCanvas, displayCanvasColor_e color)
{
if (displayCanvas->vTable->setStrokeColor) {
displayCanvas->vTable->setStrokeColor(displayCanvas, color);
}
}
void displayCanvasSetFillColor(displayCanvas_t *displayCanvas, displayCanvasColor_e color)
{
if (displayCanvas->vTable->setFillColor) {
displayCanvas->vTable->setFillColor(displayCanvas, color);
}
}
void displayCanvasSetStrokeAndFillColor(displayCanvas_t *displayCanvas, displayCanvasColor_e color)
{
if (displayCanvas->vTable->setStrokeAndFillColor) {
displayCanvas->vTable->setStrokeAndFillColor(displayCanvas, color);
} else {
displayCanvasSetStrokeColor(displayCanvas, color);
displayCanvasSetFillColor(displayCanvas, color);
}
}
void displayCanvasSetColorInversion(displayCanvas_t *displayCanvas, bool inverted)
{
if (displayCanvas->vTable->setColorInversion) {
displayCanvas->vTable->setColorInversion(displayCanvas, inverted);
}
}
void displayCanvasSetPixel(displayCanvas_t *displayCanvas, int x, int y, displayCanvasColor_e color)
{
if (displayCanvas->vTable->setPixel) {
displayCanvas->vTable->setPixel(displayCanvas, x, y, color);
}
}
void displayCanvasSetPixelToStrokeColor(displayCanvas_t *displayCanvas, int x, int y)
{
if (displayCanvas->vTable->setPixelToStrokeColor) {
displayCanvas->vTable->setPixelToStrokeColor(displayCanvas, x, y);
}
}
void displayCanvasSetPixelToFillColor(displayCanvas_t *displayCanvas, int x, int y)
{
if (displayCanvas->vTable->setPixelToFillColor) {
displayCanvas->vTable->setPixelToFillColor(displayCanvas, x, y);
}
}
void displayCanvasSetStrokeWidth(displayCanvas_t *displayCanvas, unsigned w)
{
if (displayCanvas->vTable->setStrokeWidth) {
displayCanvas->vTable->setStrokeWidth(displayCanvas, w);
}
}
void displayCanvasSetLineOutlineType(displayCanvas_t *displayCanvas, displayCanvasOutlineType_e outlineType)
{
if (displayCanvas->vTable->setLineOutlineType) {
displayCanvas->vTable->setLineOutlineType(displayCanvas, outlineType);
}
}
void displayCanvasSetLineOutlineColor(displayCanvas_t *displayCanvas, displayCanvasColor_e outlineColor)
{
if (displayCanvas->vTable->setLineOutlineColor) {
displayCanvas->vTable->setLineOutlineColor(displayCanvas, outlineColor);
}
}
void displayCanvasClipToRect(displayCanvas_t *displayCanvas, int x, int y, int w, int h)
{
if (displayCanvas->vTable->clipToRect) {
displayCanvas->vTable->clipToRect(displayCanvas, x, y, w, h);
}
}
void displayCanvasClearRect(displayCanvas_t *displayCanvas, int x, int y, int w, int h)
{
if (displayCanvas->vTable->clearRect) {
displayCanvas->vTable->clearRect(displayCanvas, x, y, w, h);
}
}
void displayCanvasResetDrawingState(displayCanvas_t *displayCanvas)
{
if (displayCanvas->vTable->resetDrawingState) {
displayCanvas->vTable->resetDrawingState(displayCanvas);
}
}
void displayCanvasDrawCharacter(displayCanvas_t *displayCanvas, int x, int y, uint16_t chr, displayCanvasBitmapOption_t opts)
{
if (displayCanvas->vTable->drawCharacter) {
displayCanvas->vTable->drawCharacter(displayCanvas, x, y, chr, opts);
}
}
void displayCanvasDrawCharacterMask(displayCanvas_t *displayCanvas, int x, int y, uint16_t chr, displayCanvasColor_e color, displayCanvasBitmapOption_t opts)
{
if (displayCanvas->vTable->drawCharacterMask) {
displayCanvas->vTable->drawCharacterMask(displayCanvas, x, y, chr, color, opts);
}
}
void displayCanvasDrawString(displayCanvas_t *displayCanvas, int x, int y, const char *s, displayCanvasBitmapOption_t opts)
{
if (displayCanvas->vTable->drawString) {
displayCanvas->vTable->drawString(displayCanvas, x, y, s, opts);
}
}
void displayCanvasDrawStringMask(displayCanvas_t *displayCanvas, int x, int y, const char *s, displayCanvasColor_e color, displayCanvasBitmapOption_t opts)
{
if (displayCanvas->vTable->drawStringMask) {
displayCanvas->vTable->drawStringMask(displayCanvas, x, y, s, color, opts);
}
}
void displayCanvasMoveToPoint(displayCanvas_t *displayCanvas, int x, int y)
{
if (displayCanvas->vTable->moveToPoint) {
displayCanvas->vTable->moveToPoint(displayCanvas, x, y);
}
}
void displayCanvasStrokeLineToPoint(displayCanvas_t *displayCanvas, int x, int y)
{
if (displayCanvas->vTable->strokeLineToPoint) {
displayCanvas->vTable->strokeLineToPoint(displayCanvas, x, y);
}
}
void displayCanvasStrokeTriangle(displayCanvas_t *displayCanvas, int x1, int y1, int x2, int y2, int x3, int y3)
{
if (displayCanvas->vTable->strokeTriangle) {
displayCanvas->vTable->strokeTriangle(displayCanvas, x1, y1, x2, y2, x3, y3);
}
}
void displayCanvasFillTriangle(displayCanvas_t *displayCanvas, int x1, int y1, int x2, int y2, int x3, int y3)
{
if (displayCanvas->vTable->fillTriangle) {
displayCanvas->vTable->fillTriangle(displayCanvas, x1, y1, x2, y2, x3, y3);
}
}
void displayCanvasFillStrokeTriangle(displayCanvas_t *displayCanvas, int x1, int y1, int x2, int y2, int x3, int y3)
{
if (displayCanvas->vTable->fillStrokeTriangle) {
displayCanvas->vTable->fillStrokeTriangle(displayCanvas, x1, y1, x2, y2, x3, y3);
}
}
void displayCanvasStrokeRect(displayCanvas_t *displayCanvas, int x, int y, int w, int h)
{
if (displayCanvas->vTable->strokeRect) {
displayCanvas->vTable->strokeRect(displayCanvas, x, y, w, h);
}
}
void displayCanvasFillRect(displayCanvas_t *displayCanvas, int x, int y, int w, int h)
{
if (displayCanvas->vTable->fillRect) {
displayCanvas->vTable->fillRect(displayCanvas, x, y, w, h);
}
}
void displayCanvasFillStrokeRect(displayCanvas_t *displayCanvas, int x, int y, int w, int h)
{
if (displayCanvas->vTable->fillStrokeRect) {
displayCanvas->vTable->fillStrokeRect(displayCanvas, x, y, w, h);
}
}
void displayCanvasStrokeEllipseInRect(displayCanvas_t *displayCanvas, int x, int y, int w, int h)
{
if (displayCanvas->vTable->strokeEllipseInRect) {
displayCanvas->vTable->strokeEllipseInRect(displayCanvas, x, y, w, h);
}
}
void displayCanvasFillEllipseInRect(displayCanvas_t *displayCanvas, int x, int y, int w, int h)
{
if (displayCanvas->vTable->fillEllipseInRect) {
displayCanvas->vTable->fillEllipseInRect(displayCanvas, x, y, w, h);
}
}
void displayCanvasFillStrokeEllipseInRect(displayCanvas_t *displayCanvas, int x, int y, int w, int h)
{
if (displayCanvas->vTable->fillStrokeEllipseInRect) {
displayCanvas->vTable->fillStrokeEllipseInRect(displayCanvas, x, y, w, h);
}
}
void displayCanvasCtmReset(displayCanvas_t *displayCanvas)
{
if (displayCanvas->vTable->ctmReset) {
displayCanvas->vTable->ctmReset(displayCanvas);
}
}
void displayCanvasCtmSet(displayCanvas_t *displayCanvas, float m11, float m12, float m21, float m22, float m31, float m32)
{
if (displayCanvas->vTable->ctmSet) {
displayCanvas->vTable->ctmSet(displayCanvas, m11, m12, m21, m22, m31, m32);
}
}
void displayCanvasCtmTranslate(displayCanvas_t *displayCanvas, float tx, float ty)
{
if (displayCanvas->vTable->ctmTranslate) {
displayCanvas->vTable->ctmTranslate(displayCanvas, tx, ty);
}
}
void displayCanvasCtmScale(displayCanvas_t *displayCanvas, float sx, float sy)
{
if (displayCanvas->vTable->ctmScale) {
displayCanvas->vTable->ctmScale(displayCanvas, sx, sy);
}
}
void displayCanvasCtmRotate(displayCanvas_t *displayCanvas, float r)
{
if (displayCanvas->vTable->ctmRotate) {
displayCanvas->vTable->ctmRotate(displayCanvas, r);
}
}
void displayCanvasContextPush(displayCanvas_t *displayCanvas)
{
if (displayCanvas->vTable->contextPush) {
displayCanvas->vTable->contextPush(displayCanvas);
}
}
void displayCanvasContextPop(displayCanvas_t *displayCanvas)
{
if (displayCanvas->vTable->contextPop) {
displayCanvas->vTable->contextPop(displayCanvas);
}
}

View File

@ -0,0 +1,143 @@
/*
* This file is part of Cleanflight, Betaflight and INAV.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Alternatively, the contents of this file may be used under the terms
* of the GNU General Public License Version 3, as described below:
*
* This file is free software: you may copy, redistribute and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*
* @author Alberto Garcia Hierro <alberto@garciahierro.com>
*/
#pragma once
#include <stdbool.h>
#include <stdint.h>
typedef enum {
DISPLAY_CANVAS_BITMAP_OPT_INVERT_COLORS = 1 << 0,
DISPLAY_CANVAS_BITMAP_OPT_SOLID_BACKGROUND = 1 << 1,
DISPLAY_CANVAS_BITMAP_OPT_ERASE_TRANSPARENT = 1 << 2,
} displayCanvasBitmapOption_t;
typedef enum {
DISPLAY_CANVAS_COLOR_BLACK = 0,
DISPLAY_CANVAS_COLOR_TRANSPARENT = 1,
DISPLAY_CANVAS_COLOR_WHITE = 2,
DISPLAY_CANVAS_COLOR_GRAY = 3,
} displayCanvasColor_e;
typedef enum {
DISPLAY_CANVAS_OUTLINE_TYPE_NONE = 0,
DISPLAY_CANVAS_OUTLINE_TYPE_TOP = 1 << 0,
DISPLAY_CANVAS_OUTLINE_TYPE_RIGHT = 1 << 1,
DISPLAY_CANVAS_OUTLINE_TYPE_BOTTOM = 1 << 2,
DISPLAY_CANVAS_OUTLINE_TYPE_LEFT = 1 << 3,
} displayCanvasOutlineType_e;
struct displayCanvasVTable_s;
typedef struct displayCanvas_s {
const struct displayCanvasVTable_s *vTable;
void *device;
uint16_t width;
uint16_t height;
uint8_t gridElementWidth;
uint8_t gridElementHeight;
} displayCanvas_t;
typedef struct displayCanvasVTable_s {
void (*setStrokeColor)(displayCanvas_t *displayCanvas, displayCanvasColor_e color);
void (*setFillColor)(displayCanvas_t *displayCanvas, displayCanvasColor_e color);
void (*setStrokeAndFillColor)(displayCanvas_t *displayCanvas, displayCanvasColor_e color);
void (*setColorInversion)(displayCanvas_t *displayCanvas, bool inverted);
void (*setPixel)(displayCanvas_t *displayCanvas, int x, int y, displayCanvasColor_e color);
void (*setPixelToStrokeColor)(displayCanvas_t *displayCanvas, int x, int y);
void (*setPixelToFillColor)(displayCanvas_t *displayCanvas, int x, int y);
void (*setStrokeWidth)(displayCanvas_t *displayCanvas, unsigned w);
void (*setLineOutlineType)(displayCanvas_t *displayCanvas, displayCanvasOutlineType_e outlineType);
void (*setLineOutlineColor)(displayCanvas_t *displayCanvas, displayCanvasColor_e outlineColor);
void (*clipToRect)(displayCanvas_t *displayCanvas, int x, int y, int w, int h);
void (*clearRect)(displayCanvas_t *displayCanvas, int x, int y, int w, int h);
void (*resetDrawingState)(displayCanvas_t *displayCanvas);
void (*drawCharacter)(displayCanvas_t *displayCanvas, int x, int y, uint16_t chr, displayCanvasBitmapOption_t opts);
void (*drawCharacterMask)(displayCanvas_t *displayCanvas, int x, int y, uint16_t chr, displayCanvasColor_e color, displayCanvasBitmapOption_t opts);
void (*drawString)(displayCanvas_t *displayCanvas, int x, int y, const char *s, displayCanvasBitmapOption_t opts);
void (*drawStringMask)(displayCanvas_t *displayCanvas, int x, int y, const char *s, displayCanvasColor_e color, displayCanvasBitmapOption_t opts);
void (*moveToPoint)(displayCanvas_t *displayCanvas, int x, int y);
void (*strokeLineToPoint)(displayCanvas_t *displayCanvas, int x, int y);
void (*strokeTriangle)(displayCanvas_t *displayCanvas, int x1, int y1, int x2, int y2, int x3, int y3);
void (*fillTriangle)(displayCanvas_t *displayCanvas, int x1, int y1, int x2, int y2, int x3, int y3);
void (*fillStrokeTriangle)(displayCanvas_t *displayCanvas, int x1, int y1, int x2, int y2, int x3, int y3);
void (*strokeRect)(displayCanvas_t *displayCanvas, int x, int y, int w, int h);
void (*fillRect)(displayCanvas_t *displayCanvas, int x, int y, int w, int h);
void (*fillStrokeRect)(displayCanvas_t *displayCanvas, int x, int y, int w, int h);
void (*strokeEllipseInRect)(displayCanvas_t *displayCanvas, int x, int y, int w, int h);
void (*fillEllipseInRect)(displayCanvas_t *displayCanvas, int x, int y, int w, int h);
void (*fillStrokeEllipseInRect)(displayCanvas_t *displayCanvas, int x, int y, int w, int h);
void (*ctmReset)(displayCanvas_t *displayCanvas);
void (*ctmSet)(displayCanvas_t *displayCanvas, float m11, float m12, float m21, float m22, float m31, float m32);
void (*ctmTranslate)(displayCanvas_t *displayCanvas, float tx, float ty);
void (*ctmScale)(displayCanvas_t *displayCanvas, float sx, float sy);
void (*ctmRotate)(displayCanvas_t *displayCanvas, float r);
void (*contextPush)(displayCanvas_t *displayCanvas);
void (*contextPop)(displayCanvas_t *displayCanvas);
} displayCanvasVTable_t;
void displayCanvasSetStrokeColor(displayCanvas_t *displayCanvas, displayCanvasColor_e color);
void displayCanvasSetFillColor(displayCanvas_t *displayCanvas, displayCanvasColor_e color);
void displayCanvasSetStrokeAndFillColor(displayCanvas_t *displayCanvas, displayCanvasColor_e color);
void displayCanvasSetColorInversion(displayCanvas_t *displayCanvas, bool inverted);
void displayCanvasSetPixel(displayCanvas_t *displayCanvas, int x, int y, displayCanvasColor_e);
void displayCanvasSetPixelToStrokeColor(displayCanvas_t *displayCanvas, int x, int y);
void displayCanvasSetPixelToFillColor(displayCanvas_t *displayCanvas, int x, int y);
void displayCanvasSetStrokeWidth(displayCanvas_t *displayCanvas, unsigned w);
void displayCanvasSetLineOutlineType(displayCanvas_t *displayCanvas, displayCanvasOutlineType_e outlineType);
void displayCanvasSetLineOutlineColor(displayCanvas_t *displayCanvas, displayCanvasColor_e outlineColor);
void displayCanvasClipToRect(displayCanvas_t *displayCanvas, int x, int y, int w, int h);
void displayCanvasClearRect(displayCanvas_t *displayCanvas, int x, int y, int w, int h);
void displayCanvasResetDrawingState(displayCanvas_t *displayCanvas);
void displayCanvasDrawCharacter(displayCanvas_t *displayCanvas, int x, int y, uint16_t chr, displayCanvasBitmapOption_t opts);
void displayCanvasDrawCharacterMask(displayCanvas_t *displayCanvas, int x, int y, uint16_t chr, displayCanvasColor_e color, displayCanvasBitmapOption_t opts);
void displayCanvasDrawString(displayCanvas_t *displayCanvas, int x, int y, const char *s, displayCanvasBitmapOption_t opts);
void displayCanvasDrawStringMask(displayCanvas_t *displayCanvas, int x, int y, const char *s, displayCanvasColor_e color, displayCanvasBitmapOption_t opts);
void displayCanvasMoveToPoint(displayCanvas_t *displayCanvas, int x, int y);
void displayCanvasStrokeLineToPoint(displayCanvas_t *displayCanvas, int x, int y);
void displayCanvasStrokeTriangle(displayCanvas_t *displayCanvas, int x1, int y1, int x2, int y2, int x3, int y3);
void displayCanvasFillTriangle(displayCanvas_t *displayCanvas, int x1, int y1, int x2, int y2, int x3, int y3);
void displayCanvasFillStrokeTriangle(displayCanvas_t *displayCanvas, int x1, int y1, int x2, int y2, int x3, int y3);
void displayCanvasStrokeRect(displayCanvas_t *displayCanvas, int x, int y, int w, int h);
void displayCanvasFillRect(displayCanvas_t *displayCanvas, int x, int y, int w, int h);
void displayCanvasFillStrokeRect(displayCanvas_t *displayCanvas, int x, int y, int w, int h);
void displayCanvasStrokeEllipseInRect(displayCanvas_t *displayCanvas, int x, int y, int w, int h);
void displayCanvasFillEllipseInRect(displayCanvas_t *displayCanvas, int x, int y, int w, int h);
void displayCanvasFillStrokeEllipseInRect(displayCanvas_t *displayCanvas, int x, int y, int w, int h);
void displayCanvasCtmReset(displayCanvas_t *displayCanvas);
void displayCanvasCtmSet(displayCanvas_t *displayCanvas, float m11, float m12, float m21, float m22, float m31, float m32);
void displayCanvasCtmTranslate(displayCanvas_t *displayCanvas, float tx, float ty);
void displayCanvasCtmScale(displayCanvas_t *displayCanvas, float sx, float sy);
void displayCanvasCtmRotate(displayCanvas_t *displayCanvas, float r);
void displayCanvasContextPush(displayCanvas_t *displayCanvas);
void displayCanvasContextPop(displayCanvas_t *displayCanvas);

View File

@ -36,8 +36,9 @@
#include "drivers/io.h"
#include "drivers/light_led.h"
#include "drivers/max7456.h"
#include "drivers/max7456_symbols.h"
#include "drivers/nvic.h"
#include "drivers/osd.h"
#include "drivers/osd_symbols.h"
#include "drivers/time.h"

60
src/main/drivers/osd.h Normal file
View File

@ -0,0 +1,60 @@
/*
* This file is part of Cleanflight, Betaflight and INAV
*
* Cleanflight, Betaflight and INAV are free software. You can
* redistribute this software and/or modify this software under
* the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* Cleanflight, Betaflight and INAV are distributed in the hope that
* they will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software.
*
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
#include "common/utils.h"
#define OSD_CHAR_WIDTH 12
#define OSD_CHAR_HEIGHT 18
#define OSD_CHAR_BITS_PER_PIXEL 2
#define OSD_CHAR_VISIBLE_BYTES (OSD_CHAR_WIDTH * OSD_CHAR_HEIGHT * OSD_CHAR_BITS_PER_PIXEL / 8)
// Only the first 54 bytes of a character represent visible data. However, some OSD drivers
// accept 64 bytes and use the extra 10 bytes for metadata.
#define OSD_CHAR_BYTES 64
#define OSD_CHARACTER_COLOR_BLACK 0
#define OSD_CHARACTER_COLOR_TRANSPARENT 1
#define OSD_CHARACTER_COLOR_WHITE 2
// 3 is unused but it's interpreted as transparent by all drivers
// Video Character Display parameters
typedef enum {
VIDEO_SYSTEM_AUTO = 0,
VIDEO_SYSTEM_PAL,
VIDEO_SYSTEM_NTSC
} videoSystem_e;
typedef enum {
OSD_DRIVER_NONE = 0,
OSD_DRIVER_MAX7456 = 1,
} osdDriver_e;
// osdCharacter_t represents the binary data for an OSD
// character. All OSD drivers use the same character format
// as defined by OSD_CHARACTER_WIDTH, OSD_CHARACTER_HEIGHT
// and OSD_CHARACTER_BITS_PER_PIXEL.
typedef struct osdCharacter_s {
uint8_t data[OSD_CHAR_BYTES];
} osdCharacter_t;

View File

@ -101,6 +101,7 @@
#include "io/asyncfatfs/asyncfatfs.h"
#include "io/beeper.h"
#include "io/dashboard.h"
#include "io/displayport_frsky_osd.h"
#include "io/displayport_max7456.h"
#include "io/displayport_msp.h"
#include "io/displayport_srxl.h"
@ -786,6 +787,18 @@ void init(void)
case OSD_DISPLAYPORT_DEVICE_AUTO:
FALLTHROUGH;
#if defined(USE_FRSKYOSD)
// Test OSD_DISPLAYPORT_DEVICE_FRSKYOSD first, since an FC could
// have a builtin MAX7456 but also an FRSKYOSD connected to an
// uart.
case OSD_DISPLAYPORT_DEVICE_FRSKYOSD:
osdDisplayPort = frskyOsdDisplayPortInit(vcdProfile()->video_system);
if (osdDisplayPort || device == OSD_DISPLAYPORT_DEVICE_FRSKYOSD) {
break;
}
FALLTHROUGH;
#endif
#if defined(USE_MAX7456)
case OSD_DISPLAYPORT_DEVICE_MAX7456:
// If there is a max7456 chip for the OSD configured and detectd then use it.

View File

@ -0,0 +1,503 @@
/*
* This file is part of Cleanflight, Betaflight and INAV
*
* Cleanflight, Betaflight and INAV are free software. You can
* redistribute this software and/or modify this software under
* the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* Cleanflight, Betaflight and INAV are distributed in the hope that
* they will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software.
*
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdbool.h>
#include <stdint.h>
#include "platform.h"
#if defined(USE_FRSKYOSD)
#include "common/utils.h"
#include "drivers/display.h"
#include "drivers/display_canvas.h"
#include "io/displayport_frsky_osd.h"
#include "io/frsky_osd.h"
static displayPort_t frskyOsdDisplayPort;
static int grab(displayPort_t *displayPort)
{
UNUSED(displayPort);
return 0;
}
static int release(displayPort_t *displayPort)
{
UNUSED(displayPort);
return 0;
}
static int clearScreen(displayPort_t *displayPort)
{
UNUSED(displayPort);
frskyOsdClearScreen();
return 0;
}
static int drawScreen(displayPort_t *displayPort)
{
UNUSED(displayPort);
frskyOsdUpdate();
return 0;
}
static int screenSize(const displayPort_t *displayPort)
{
UNUSED(displayPort);
return frskyOsdGetGridRows() * frskyOsdGetGridCols();
}
static int writeString(displayPort_t *displayPort, uint8_t x, uint8_t y, uint8_t attr, const char *s)
{
UNUSED(displayPort);
UNUSED(attr);
frskyOsdDrawStringInGrid(x, y, s);
return 0;
}
static int writeChar(displayPort_t *displayPort, uint8_t x, uint8_t y, uint8_t attr, uint8_t c)
{
UNUSED(displayPort);
UNUSED(attr);
frskyOsdDrawCharInGrid(x, y, c);
return 0;
}
static bool isTransferInProgress(const displayPort_t *displayPort)
{
UNUSED(displayPort);
return false;
}
static void updateGridSize(displayPort_t *displayPort)
{
displayPort->rows = frskyOsdGetGridRows();
displayPort->cols = frskyOsdGetGridCols();
}
static void resync(displayPort_t *displayPort)
{
UNUSED(displayPort);
// TODO(agh): Do we need to flush the screen here?
// MAX7456's driver does a full redraw in resync(),
// so some callers might be expecting that.
frskyOsdUpdate();
updateGridSize(displayPort);
}
static int heartbeat(displayPort_t *displayPort)
{
UNUSED(displayPort);
return 0;
}
static uint32_t txBytesFree(const displayPort_t *displayPort)
{
UNUSED(displayPort);
return UINT32_MAX;
}
static bool writeFontCharacter(displayPort_t *instance, uint16_t addr, const osdCharacter_t *chr)
{
UNUSED(instance);
return frskyOsdWriteFontCharacter(addr, chr);
}
static bool isReady(displayPort_t *instance)
{
if (frskyOsdIsReady()) {
updateGridSize(instance);
return true;
}
return false;
}
static void beginTransaction(displayPort_t *instance, displayTransactionOption_e opts)
{
UNUSED(instance);
frskyOsdTransactionOptions_e frskyOpts = 0;
if (opts & DISPLAY_TRANSACTION_OPT_PROFILED) {
frskyOpts |= FRSKY_OSD_TRANSACTION_OPT_PROFILED;
}
if (opts & DISPLAY_TRANSACTION_OPT_RESET_DRAWING) {
frskyOpts |= FRSKY_OSD_TRANSACTION_OPT_RESET_DRAWING;
}
frskyOsdBeginTransaction(frskyOpts);
}
static void commitTransaction(displayPort_t *instance)
{
UNUSED(instance);
frskyOsdCommitTransaction();
}
static frskyOsdColor_e frskyOsdGetColor(displayCanvasColor_e color)
{
switch (color)
{
case DISPLAY_CANVAS_COLOR_BLACK:
return FRSKY_OSD_COLOR_BLACK;
case DISPLAY_CANVAS_COLOR_TRANSPARENT:
return FRSKY_OSD_COLOR_TRANSPARENT;
case DISPLAY_CANVAS_COLOR_WHITE:
return FRSKY_OSD_COLOR_WHITE;
case DISPLAY_CANVAS_COLOR_GRAY:
return FRSKY_OSD_COLOR_GRAY;
}
return FRSKY_OSD_COLOR_BLACK;
}
static void setStrokeColor(displayCanvas_t *displayCanvas, displayCanvasColor_e color)
{
UNUSED(displayCanvas);
frskyOsdSetStrokeColor(frskyOsdGetColor(color));
}
static void setFillColor(displayCanvas_t *displayCanvas, displayCanvasColor_e color)
{
UNUSED(displayCanvas);
frskyOsdSetFillColor(frskyOsdGetColor(color));
}
static void setStrokeAndFillColor(displayCanvas_t *displayCanvas, displayCanvasColor_e color)
{
UNUSED(displayCanvas);
frskyOsdSetStrokeAndFillColor(frskyOsdGetColor(color));
}
static void setColorInversion(displayCanvas_t *displayCanvas, bool inverted)
{
UNUSED(displayCanvas);
frskyOsdSetColorInversion(inverted);
}
static void setPixel(displayCanvas_t *displayCanvas, int x, int y, displayCanvasColor_e color)
{
UNUSED(displayCanvas);
frskyOsdSetPixel(x, y, frskyOsdGetColor(color));
}
static void setPixelToStrokeColor(displayCanvas_t *displayCanvas, int x, int y)
{
UNUSED(displayCanvas);
frskyOsdSetPixelToStrokeColor(x, y);
}
static void setPixelToFillColor(displayCanvas_t *displayCanvas, int x, int y)
{
UNUSED(displayCanvas);
frskyOsdSetPixelToFillColor(x, y);
}
static void setStrokeWidth(displayCanvas_t *displayCanvas, unsigned w)
{
UNUSED(displayCanvas);
frskyOsdSetStrokeWidth(w);
}
static void setLineOutlineType(displayCanvas_t *displayCanvas, displayCanvasOutlineType_e outlineType)
{
UNUSED(displayCanvas);
frskyOsdSetLineOutlineType(outlineType);
}
static void setLineOutlineColor(displayCanvas_t *displayCanvas, displayCanvasColor_e outlineColor)
{
UNUSED(displayCanvas);
frskyOsdSetLineOutlineColor(outlineColor);
}
static void clipToRect(displayCanvas_t *displayCanvas, int x, int y, int w, int h)
{
UNUSED(displayCanvas);
frskyOsdClipToRect(x, y, w, h);
}
static void clearRect(displayCanvas_t *displayCanvas, int x, int y, int w, int h)
{
UNUSED(displayCanvas);
frskyOsdClearRect(x, y, w, h);
}
static void resetDrawingState(displayCanvas_t *displayCanvas)
{
UNUSED(displayCanvas);
frskyOsdResetDrawingState();
}
static void drawCharacter(displayCanvas_t *displayCanvas, int x, int y, uint16_t chr, displayCanvasBitmapOption_t opts)
{
UNUSED(displayCanvas);
frskyOsdDrawCharacter(x, y, chr, opts);
}
static void drawCharacterMask(displayCanvas_t *displayCanvas, int x, int y, uint16_t chr, displayCanvasColor_e color, displayCanvasBitmapOption_t opts)
{
UNUSED(displayCanvas);
frskyOsdDrawCharacterMask(x, y, chr, frskyOsdGetColor(color), opts);
}
static void drawString(displayCanvas_t *displayCanvas, int x, int y, const char *s, displayCanvasBitmapOption_t opts)
{
UNUSED(displayCanvas);
frskyOsdDrawString(x, y, s, opts);
}
static void drawStringMask(displayCanvas_t *displayCanvas, int x, int y, const char *s, displayCanvasColor_e color, displayCanvasBitmapOption_t opts)
{
UNUSED(displayCanvas);
frskyOsdDrawStringMask(x, y, s, frskyOsdGetColor(color), opts);
}
static void moveToPoint(displayCanvas_t *displayCanvas, int x, int y)
{
UNUSED(displayCanvas);
frskyOsdMoveToPoint(x, y);
}
static void strokeLineToPoint(displayCanvas_t *displayCanvas, int x, int y)
{
UNUSED(displayCanvas);
frskyOsdStrokeLineToPoint(x, y);
}
static void strokeTriangle(displayCanvas_t *displayCanvas, int x1, int y1, int x2, int y2, int x3, int y3)
{
UNUSED(displayCanvas);
frskyOsdStrokeTriangle(x1, y1, x2, y2, x3, y3);
}
static void fillTriangle(displayCanvas_t *displayCanvas, int x1, int y1, int x2, int y2, int x3, int y3)
{
UNUSED(displayCanvas);
frskyOsdFillTriangle(x1, y1, x2, y2, x3, y3);
}
static void fillStrokeTriangle(displayCanvas_t *displayCanvas, int x1, int y1, int x2, int y2, int x3, int y3)
{
UNUSED(displayCanvas);
frskyOsdFillStrokeTriangle(x1, y1, x2, y2, x3, y3);
}
static void strokeRect(displayCanvas_t *displayCanvas, int x, int y, int w, int h)
{
UNUSED(displayCanvas);
frskyOsdStrokeRect(x, y, w, h);
}
static void fillRect(displayCanvas_t *displayCanvas, int x, int y, int w, int h)
{
UNUSED(displayCanvas);
frskyOsdFillRect(x, y, w, h);
}
static void fillStrokeRect(displayCanvas_t *displayCanvas, int x, int y, int w, int h)
{
UNUSED(displayCanvas);
frskyOsdFillStrokeRect(x, y, w, h);
}
static void strokeEllipseInRect(displayCanvas_t *displayCanvas, int x, int y, int w, int h)
{
UNUSED(displayCanvas);
frskyOsdStrokeEllipseInRect(x, y, w, h);
}
static void fillEllipseInRect(displayCanvas_t *displayCanvas, int x, int y, int w, int h)
{
UNUSED(displayCanvas);
frskyOsdFillEllipseInRect(x, y, w, h);
}
static void fillStrokeEllipseInRect(displayCanvas_t *displayCanvas, int x, int y, int w, int h)
{
UNUSED(displayCanvas);
frskyOsdFillStrokeEllipseInRect(x, y, w, h);
}
static void ctmReset(displayCanvas_t *displayCanvas)
{
UNUSED(displayCanvas);
frskyOsdCtmReset();
}
static void ctmSet(displayCanvas_t *displayCanvas, float m11, float m12, float m21, float m22, float m31, float m32)
{
UNUSED(displayCanvas);
frskyOsdCtmSet(m11, m12, m21, m22, m31, m32);
}
static void ctmTranslate(displayCanvas_t *displayCanvas, float tx, float ty)
{
UNUSED(displayCanvas);
frskyOsdCtmTranslate(tx, ty);
}
static void ctmScale(displayCanvas_t *displayCanvas, float sx, float sy)
{
UNUSED(displayCanvas);
frskyOsdCtmScale(sx, sy);
}
static void ctmRotate(displayCanvas_t *displayCanvas, float r)
{
UNUSED(displayCanvas);
frskyOsdCtmRotate(r);
}
static void contextPush(displayCanvas_t *displayCanvas)
{
UNUSED(displayCanvas);
frskyOsdContextPush();
}
static void contextPop(displayCanvas_t *displayCanvas)
{
UNUSED(displayCanvas);
frskyOsdContextPop();
}
static const displayCanvasVTable_t frskyOsdCanvasVTable = {
.setStrokeColor = setStrokeColor,
.setFillColor = setFillColor,
.setStrokeAndFillColor = setStrokeAndFillColor,
.setColorInversion = setColorInversion,
.setPixel = setPixel,
.setPixelToStrokeColor = setPixelToStrokeColor,
.setPixelToFillColor = setPixelToFillColor,
.setStrokeWidth = setStrokeWidth,
.setLineOutlineType = setLineOutlineType,
.setLineOutlineColor = setLineOutlineColor,
.clipToRect = clipToRect,
.clearRect = clearRect,
.resetDrawingState = resetDrawingState,
.drawCharacter = drawCharacter,
.drawCharacterMask = drawCharacterMask,
.drawString = drawString,
.drawStringMask = drawStringMask,
.moveToPoint = moveToPoint,
.strokeLineToPoint = strokeLineToPoint,
.strokeTriangle = strokeTriangle,
.fillTriangle = fillTriangle,
.fillStrokeTriangle = fillStrokeTriangle,
.strokeRect = strokeRect,
.fillRect = fillRect,
.fillStrokeRect = fillStrokeRect,
.strokeEllipseInRect = strokeEllipseInRect,
.fillEllipseInRect = fillEllipseInRect,
.fillStrokeEllipseInRect = fillStrokeEllipseInRect,
.ctmReset = ctmReset,
.ctmSet = ctmSet,
.ctmTranslate = ctmTranslate,
.ctmScale = ctmScale,
.ctmRotate = ctmRotate,
.contextPush = contextPush,
.contextPop = contextPop,
};
static bool getCanvas(displayCanvas_t *canvas, const displayPort_t *instance)
{
UNUSED(instance);
canvas->vTable = &frskyOsdCanvasVTable;
canvas->width = frskyOsdGetPixelWidth();
canvas->height = frskyOsdGetPixelHeight();
return true;
}
static const displayPortVTable_t frskyOsdVTable = {
.grab = grab,
.release = release,
.clearScreen = clearScreen,
.drawScreen = drawScreen,
.screenSize = screenSize,
.writeString = writeString,
.writeChar = writeChar,
.isTransferInProgress = isTransferInProgress,
.heartbeat = heartbeat,
.resync = resync,
.txBytesFree = txBytesFree,
.writeFontCharacter = writeFontCharacter,
.isReady = isReady,
.beginTransaction = beginTransaction,
.commitTransaction = commitTransaction,
.getCanvas = getCanvas,
};
displayPort_t *frskyOsdDisplayPortInit(const videoSystem_e videoSystem)
{
if (frskyOsdInit(videoSystem)) {
displayInit(&frskyOsdDisplayPort, &frskyOsdVTable);
resync(&frskyOsdDisplayPort);
return &frskyOsdDisplayPort;
}
return NULL;
}
#endif // USE_FRSKYOSD

View File

@ -0,0 +1,26 @@
/*
* This file is part of Cleanflight, Betaflight and INAV
*
* Cleanflight, Betaflight and INAV are free software. You can
* redistribute this software and/or modify this software under
* the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* Cleanflight, Betaflight and INAV are distributed in the hope that
* they will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software.
*
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "drivers/osd.h"
typedef struct displayPort_s displayPort_t;
displayPort_t *frskyOsdDisplayPortInit(const videoSystem_e videoSystem);

View File

@ -29,6 +29,7 @@
#include "drivers/display.h"
#include "drivers/max7456.h"
#include "drivers/osd.h"
#include "config/config.h"
@ -156,6 +157,13 @@ static bool layerCopy(displayPort_t *displayPort, displayPortLayer_e destLayer,
return max7456LayerCopy(destLayer, sourceLayer);
}
static bool writeFontCharacter(displayPort_t *instance, uint16_t addr, const osdCharacter_t *chr)
{
UNUSED(instance);
return max7456WriteNvm(addr, (const uint8_t *)chr);
}
static const displayPortVTable_t max7456VTable = {
.grab = grab,
.release = release,
@ -172,6 +180,7 @@ static const displayPortVTable_t max7456VTable = {
.layerSupported = layerSupported,
.layerSelect = layerSelect,
.layerCopy = layerCopy,
.writeFontCharacter = writeFontCharacter,
};
displayPort_t *max7456DisplayPortInit(const vcdProfile_t *vcdProfile)

927
src/main/io/frsky_osd.c Normal file
View File

@ -0,0 +1,927 @@
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "platform.h"
#if defined(USE_FRSKYOSD)
#include "common/crc.h"
#include "common/maths.h"
#include "common/time.h"
#include "common/utils.h"
#include "common/uvarint.h"
#include "drivers/time.h"
#include "io/frsky_osd.h"
#include "io/serial.h"
#define FRSKY_OSD_BAUDRATE 115200
#define FRSKY_OSD_SUPPORTED_API_VERSION 1
#define FRSKY_OSD_PREAMBLE_BYTE_0 '$'
#define FRSKY_OSD_PREAMBLE_BYTE_1 'A'
#define FRSKY_OSD_GRID_BUFFER_CHAR_BITS 9
#define FRSKY_OSD_GRID_BUFFER_CHAR_MASK ((1 << FRSKY_OSD_GRID_BUFFER_CHAR_BITS) - 1)
#define FRSKY_OSD_GRID_BUFFER_ENCODE(chr, attr) ((chr & FRSKY_OSD_GRID_BUFFER_CHAR_MASK) | (attr << FRSKY_OSD_GRID_BUFFER_CHAR_BITS))
#define FRSKY_OSD_CHAR_ATTRIBUTE_COLOR_INVERSE (1 << 0)
#define FRSKY_OSD_CHAR_ATTRIBUTE_SOLID_BACKGROUND (1 << 1)
#define FRSKY_OSD_CHAR_DATA_BYTES 54
#define FRSKY_OSD_CHAR_METADATA_BYTES 10
#define FRSKY_OSD_CHAR_TOTAL_BYTES (FRSKY_OSD_CHAR_DATA_BYTES + FRSKY_OSD_CHAR_METADATA_BYTES)
#define FRSKY_OSD_SEND_BUFFER_SIZE 192
#define FRSKY_OSD_RECV_BUFFER_SIZE 128
#define FRSKY_OSD_CMD_RESPONSE_ERROR 0
#define FRSKY_OSD_INFO_INTERVAL_MS 1000
#define FRSKY_OSD_TRACE(...)
#define FRSKY_OSD_DEBUG(...)
#define FRSKY_OSD_ERROR(...)
#define FRSKY_OSD_ASSERT(x)
typedef enum
{
OSD_CMD_RESPONSE_ERROR = 0,
OSD_CMD_INFO = 1,
OSD_CMD_READ_FONT = 2,
OSD_CMD_WRITE_FONT = 3,
OSD_CMD_GET_CAMERA = 4,
OSD_CMD_SET_CAMERA = 5,
OSD_CMD_GET_ACTIVE_CAMERA = 6,
OSD_CMD_GET_OSD_ENABLED = 7,
OSD_CMD_SET_OSD_ENABLED = 8,
OSD_CMD_TRANSACTION_BEGIN = 16,
OSD_CMD_TRANSACTION_COMMIT = 17,
OSD_CMD_TRANSACTION_BEGIN_PROFILED = 18,
OSD_CMD_TRANSACTION_BEGIN_RESET_DRAWING = 19,
OSD_CMD_DRAWING_SET_STROKE_COLOR = 22,
OSD_CMD_DRAWING_SET_FILL_COLOR = 23,
OSD_CMD_DRAWING_SET_STROKE_AND_FILL_COLOR = 24,
OSD_CMD_DRAWING_SET_COLOR_INVERSION = 25,
OSD_CMD_DRAWING_SET_PIXEL = 26,
OSD_CMD_DRAWING_SET_PIXEL_TO_STROKE_COLOR = 27,
OSD_CMD_DRAWING_SET_PIXEL_TO_FILL_COLOR = 28,
OSD_CMD_DRAWING_SET_STROKE_WIDTH = 29,
OSD_CMD_DRAWING_SET_LINE_OUTLINE_TYPE = 30,
OSD_CMD_DRAWING_SET_LINE_OUTLINE_COLOR = 31,
OSD_CMD_DRAWING_CLIP_TO_RECT = 40,
OSD_CMD_DRAWING_CLEAR_SCREEN = 41,
OSD_CMD_DRAWING_CLEAR_RECT = 42,
OSD_CMD_DRAWING_RESET = 43,
OSD_CMD_DRAWING_DRAW_BITMAP = 44,
OSD_CMD_DRAWING_DRAW_BITMAP_MASK = 45,
OSD_CMD_DRAWING_DRAW_CHAR = 46,
OSD_CMD_DRAWING_DRAW_CHAR_MASK = 47,
OSD_CMD_DRAWING_DRAW_STRING = 48,
OSD_CMD_DRAWING_DRAW_STRING_MASK = 49,
OSD_CMD_DRAWING_MOVE_TO_POINT = 50,
OSD_CMD_DRAWING_STROKE_LINE_TO_POINT = 51,
OSD_CMD_DRAWING_STROKE_TRIANGLE = 52,
OSD_CMD_DRAWING_FILL_TRIANGLE = 53,
OSD_CMD_DRAWING_FILL_STROKE_TRIANGLE = 54,
OSD_CMD_DRAWING_STROKE_RECT = 55,
OSD_CMD_DRAWING_FILL_RECT = 56,
OSD_CMD_DRAWING_FILL_STROKE_RECT = 57,
OSD_CMD_DRAWING_STROKE_ELLIPSE_IN_RECT = 58,
OSD_CMD_DRAWING_FILL_ELLIPSE_IN_RECT = 59,
OSD_CMD_DRAWING_FILL_STROKE_ELLIPSE_IN_RECT = 60,
OSD_CMD_CTM_RESET = 80,
OSD_CMD_CTM_SET = 81,
OSD_CMD_CTM_TRANSLATE = 82,
OSD_CMD_CTM_SCALE = 83,
OSD_CMD_CTM_ROTATE = 84,
OSD_CMD_CTM_ROTATE_ABOUT = 85,
OSD_CMD_CTM_SHEAR = 86,
OSD_CMD_CTM_SHEAR_ABOUT = 87,
OSD_CMD_CTM_MULTIPLY = 88,
OSD_CMD_CONTEXT_PUSH = 100,
OSD_CMD_CONTEXT_POP = 101,
// MAX7456 emulation commands
OSD_CMD_DRAW_GRID_CHR = 110,
OSD_CMD_DRAW_GRID_STR = 111,
} osdCommand_e;
typedef enum {
RECV_STATE_NONE,
RECV_STATE_SYNC,
RECV_STATE_LENGTH,
RECV_STATE_DATA,
RECV_STATE_CHECKSUM,
RECV_STATE_DONE,
} frskyOsdRecvState_e;
typedef struct frskyOsdInfoResponse_s {
uint8_t magic[3];
uint8_t versionMajor;
uint8_t versionMinor;
uint8_t versionPatch;
uint8_t gridRows;
uint8_t gridColumns;
uint16_t pixelWidth;
uint16_t pixelHeight;
uint8_t tvStandard;
uint8_t hasDetectedCamera;
uint16_t maxFrameSize;
uint8_t contextStackSize;
} __attribute__((packed)) frskyOsdInfoResponse_t;
typedef struct frskyOsdFontCharacter_s {
uint16_t addr;
struct {
uint8_t bitmap[FRSKY_OSD_CHAR_DATA_BYTES]; // 12x18 2bpp
uint8_t metadata[FRSKY_OSD_CHAR_METADATA_BYTES];
} data;
} __attribute__((packed)) frskyOsdCharacter_t;
typedef struct frskyOsdDrawGridCharCmd_s {
uint8_t gx;
uint8_t gy;
uint16_t chr;
uint8_t opts;
} __attribute__((packed)) frskyOsdDrawGridCharCmd_t;
typedef struct frskyOsdDrawGridStrHeaderCmd_s {
uint8_t gx;
uint8_t gy;
uint8_t opts;
// uvarint with size and blob folow
} __attribute__((packed)) frskyOsdDrawGridStrHeaderCmd_t;
typedef struct frskyOsdPoint_s {
int x : 12;
int y : 12;
} __attribute__((packed)) frskyOsdPoint_t;
typedef struct frskyOsdSize_s {
int w : 12;
int h : 12;
} __attribute__((packed)) frskyOsdSize_t;
typedef struct frskyOsdRect_s {
frskyOsdPoint_t origin;
frskyOsdSize_t size;
} __attribute__((packed)) frskyOsdRect_t;
typedef struct frskyOsdTriangle_s {
frskyOsdPoint_t p1;
frskyOsdPoint_t p2;
frskyOsdPoint_t p3;
} __attribute__((packed)) frskyOsdTriangle_t;
typedef struct frskyOsdSetPixel_s {
frskyOsdPoint_t p;
uint8_t color;
} __attribute__((packed)) frskyOsdSetPixel_t;
typedef struct frskyOsdDrawCharacterCmd_s {
frskyOsdPoint_t p;
uint16_t chr;
uint8_t opts;
} __attribute__((packed)) frskyOsdDrawCharacterCmd_t;
typedef struct frskyOsdDrawCharacterMaskCmd_s {
frskyOsdDrawCharacterCmd_t dc;
uint8_t maskColor;
} __attribute__((packed)) frskyOsdDrawCharacterMaskCmd_t;
typedef struct frskyOsdDrawStrCommandHeaderCmd_s {
frskyOsdPoint_t p;
uint8_t opts;
// uvarint with size and blob follow
} __attribute__((packed)) frskyOsdDrawStrCommandHeaderCmd_t;
typedef struct frskyOsdDrawStrMaskCommandHeaderCmd_s {
frskyOsdPoint_t p;
uint8_t opts;
uint8_t maskColor;
// uvarint with size and blob follow
} __attribute__((packed)) frskyOsdDrawStrMaskCommandHeaderCmd_t;
typedef struct frskyOsdState_s {
struct {
uint8_t data[FRSKY_OSD_SEND_BUFFER_SIZE];
uint8_t pos;
} sendBuffer;
struct {
uint8_t state;
uint8_t crc;
uint16_t expected;
uint8_t expectedShift;
uint8_t data[FRSKY_OSD_RECV_BUFFER_SIZE];
uint8_t pos;
} recvBuffer;
struct {
uint8_t major;
uint8_t minor;
timeMs_t nextRequest;
struct {
uint8_t rows;
uint8_t columns;
} grid;
struct {
uint16_t width;
uint16_t height;
} viewport;
} info;
struct {
uint16_t addr;
osdCharacter_t *chr;
} recvOsdCharacter;
serialPort_t *port;
bool initialized;
timeMs_t nextInfoRequest;
} frskyOsdState_t;
static frskyOsdState_t state;
static uint8_t frskyOsdChecksum(uint8_t crc, uint8_t c)
{
return crc8_dvb_s2(crc, c);
}
static void frskyOsdResetReceiveBuffer(void)
{
state.recvBuffer.state = RECV_STATE_NONE;
state.recvBuffer.crc = 0;
state.recvBuffer.expected = 0;
state.recvBuffer.expectedShift = 0;
state.recvBuffer.pos = 0;
}
static void frskyOsdResetSendBuffer(void)
{
state.sendBuffer.pos = 0;
}
static void frskyOsdProcessCommandU8(uint8_t *crc, uint8_t c)
{
while (serialTxBytesFree(state.port) == 0) {
};
serialWrite(state.port, c);
if (crc) {
*crc = crc8_dvb_s2(*crc, c);
}
}
static void frskyOsdSendCommand(uint8_t cmd, const void *payload, size_t size)
{
int required = size + 1;
FRSKY_OSD_ASSERT(required <= sizeof(state.sendBuffer.data));
int rem = sizeof(state.sendBuffer.data) - state.sendBuffer.pos;
if (rem < required) {
frskyOsdFlushSendBuffer();
}
state.sendBuffer.data[state.sendBuffer.pos++] = cmd;
const uint8_t *ptr = payload;
for (size_t ii = 0; ii < size; ii++, ptr++) {
state.sendBuffer.data[state.sendBuffer.pos++] = *ptr;
}
}
static void frskyOsdStateReset(serialPort_t *port)
{
frskyOsdResetReceiveBuffer();
frskyOsdResetSendBuffer();
state.info.grid.rows = 0;
state.info.grid.columns = 0;
state.info.viewport.width = 0;
state.info.viewport.height = 0;
state.port = port;
state.initialized = false;
}
static void frskyOsdUpdateReceiveBuffer(void)
{
while (serialRxBytesWaiting(state.port) > 0) {
uint8_t c = serialRead(state.port);
switch ((frskyOsdRecvState_e)state.recvBuffer.state) {
case RECV_STATE_NONE:
if (c != FRSKY_OSD_PREAMBLE_BYTE_0) {
break;
}
state.recvBuffer.state = RECV_STATE_SYNC;
break;
case RECV_STATE_SYNC:
if (c != FRSKY_OSD_PREAMBLE_BYTE_1) {
frskyOsdResetReceiveBuffer();
break;
}
state.recvBuffer.state = RECV_STATE_LENGTH;
break;
case RECV_STATE_LENGTH:
state.recvBuffer.crc = frskyOsdChecksum(state.recvBuffer.crc, c);
state.recvBuffer.expected |= (c & 0x7F) << state.recvBuffer.expectedShift;
state.recvBuffer.expectedShift += 7;
if (c < 0x80) {
// Full uvarint decoded. Check against buffer size.
if (state.recvBuffer.expected > sizeof(state.recvBuffer.data)) {
FRSKY_OSD_ERROR("Can't handle payload of size %u with a buffer of size %u",
state.recvBuffer.expected, sizeof(state.recvBuffer.data));
frskyOsdResetReceiveBuffer();
break;
}
FRSKY_OSD_TRACE("Payload of size %u", state.recvBuffer.expected);
state.recvBuffer.state = state.recvBuffer.expected > 0 ? RECV_STATE_DATA : RECV_STATE_CHECKSUM;
}
break;
case RECV_STATE_DATA:
state.recvBuffer.data[state.recvBuffer.pos++] = c;
state.recvBuffer.crc = frskyOsdChecksum(state.recvBuffer.crc, c);
if (state.recvBuffer.pos == state.recvBuffer.expected) {
state.recvBuffer.state = RECV_STATE_CHECKSUM;
}
break;
case RECV_STATE_CHECKSUM:
if (c != state.recvBuffer.crc) {
FRSKY_OSD_DEBUG("Checksum error %u != %u. Discarding %u bytes",
c, state.recvBuffer.crc, state.recvBuffer.pos);
frskyOsdResetReceiveBuffer();
break;
}
state.recvBuffer.state = RECV_STATE_DONE;
break;
case RECV_STATE_DONE:
FRSKY_OSD_DEBUG("Received unexpected byte %u after data", c);
break;
}
}
}
static bool frskyOsdIsResponseAvailable(void)
{
return state.recvBuffer.state == RECV_STATE_DONE;
}
static bool frskyOsdHandleCommand(osdCommand_e cmd, const void *payload, size_t size)
{
switch (cmd) {
case OSD_CMD_RESPONSE_ERROR:
{
if (size >= 2) {
FRSKY_OSD_ERROR("Received an error %02x in response to command %u", *(ptr + 1), *ptr);
return true;
}
break;
}
case OSD_CMD_INFO:
{
if (size < sizeof(frskyOsdInfoResponse_t)) {
break;
}
const frskyOsdInfoResponse_t *resp = payload;
if (resp->magic[0] != 'A' || resp->magic[1] != 'G' || resp->magic[2] != 'H') {
FRSKY_OSD_ERROR("Invalid magic number %x %x %x, expecting AGH",
resp->magic[0], resp->magic[1], resp->magic[2]);
return false;
}
state.info.major = resp->versionMajor;
state.info.minor = resp->versionMinor;
state.info.grid.rows = resp->gridRows;
state.info.grid.columns = resp->gridColumns;
state.info.viewport.width = resp->pixelWidth;
state.info.viewport.height = resp->pixelHeight;
if (!state.initialized) {
FRSKY_OSD_DEBUG("FrSky OSD initialized. Version %u.%u.%u, pixels=%ux%u, grid=%ux%u",
resp->versionMajor, resp->versionMinor, resp->versionPatch,
resp->pixelWidth, resp->pixelHeight, resp->gridColumns, resp->gridRows);
state.initialized = true;
frskyOsdClearScreen();
frskyOsdResetDrawingState();
}
return true;
}
case OSD_CMD_READ_FONT:
{
if (!state.recvOsdCharacter.chr) {
FRSKY_OSD_DEBUG("Got unexpected font character");
break;
}
if (size < sizeof(uint16_t) + FRSKY_OSD_CHAR_TOTAL_BYTES) {
FRSKY_OSD_TRACE("Received buffer too small for a character: %u bytes", size);
break;
}
const frskyOsdCharacter_t *chr = payload;
state.recvOsdCharacter.addr = chr->addr;
FRSKY_OSD_TRACE("Received character %u", chr->addr);
// Skip character address
memcpy(state.recvOsdCharacter.chr->data, &chr->data, MIN(sizeof(state.recvOsdCharacter.chr->data), (size_t)FRSKY_OSD_CHAR_TOTAL_BYTES));
return true;
}
case OSD_CMD_WRITE_FONT:
{
// We only wait for the confirmation, we're not interested in the data
return true;
}
default:
break;
}
return false;
}
static bool frskyOsdDispatchResponse(void)
{
const uint8_t *payload = state.recvBuffer.data;
int remaining = (int)state.recvBuffer.pos;
bool ok = false;
if (remaining > 0) {
// OSD sends commands one by one, so we don't need to handle
// a frame with multiple ones.
uint8_t cmd = *payload;
payload++;
remaining--;
if (frskyOsdHandleCommand(cmd, payload, remaining)) {
ok = true;
} else {
FRSKY_OSD_DEBUG("Discarding buffer due to unhandled command %u (%d bytes remaining)", cmd, remaining);
}
}
frskyOsdResetReceiveBuffer();
return ok;
}
static void frskyOsdClearReceiveBuffer(void)
{
frskyOsdUpdateReceiveBuffer();
if (frskyOsdIsResponseAvailable()) {
frskyOsdDispatchResponse();
} else if (state.recvBuffer.pos > 0) {
FRSKY_OSD_DEBUG("Discarding receive buffer with %u bytes", state.recvBuffer.pos);
frskyOsdResetReceiveBuffer();
}
}
static void frskyOsdSendAsyncCommand(uint8_t cmd, const void *data, size_t size)
{
FRSKY_OSD_TRACE("Send async cmd %u", cmd);
frskyOsdSendCommand(cmd, data, size);
}
static bool frskyOsdSendSyncCommand(uint8_t cmd, const void *data, size_t size, timeMs_t timeout)
{
FRSKY_OSD_TRACE("Send sync cmd %u", cmd);
frskyOsdClearReceiveBuffer();
frskyOsdSendCommand(cmd, data, size);
frskyOsdFlushSendBuffer();
timeMs_t end = millis() + timeout;
while (millis() < end) {
frskyOsdUpdateReceiveBuffer();
if (frskyOsdIsResponseAvailable() && frskyOsdDispatchResponse()) {
FRSKY_OSD_DEBUG("Got sync response");
return true;
}
}
FRSKY_OSD_DEBUG("Sync response failed");
return false;
}
static bool frskyOsdShouldRequestInfo(void)
{
return !frskyOsdIsReady() || millis() > state.nextInfoRequest;
}
static void frskyOsdRequestInfo(void)
{
timeMs_t now = millis();
if (state.info.nextRequest < now) {
uint8_t version = FRSKY_OSD_SUPPORTED_API_VERSION;
frskyOsdSendAsyncCommand(OSD_CMD_INFO, &version, sizeof(version));
frskyOsdFlushSendBuffer();
state.info.nextRequest = now + FRSKY_OSD_INFO_INTERVAL_MS;
}
}
bool frskyOsdInit(videoSystem_e videoSystem)
{
UNUSED(videoSystem);
FRSKY_OSD_TRACE("frskyOsdInit()");
// TODO: Use videoSystem to set the signal standard when
// no input is detected.
const serialPortConfig_t *portConfig = findSerialPortConfig(FUNCTION_FRSKY_OSD);
if (portConfig) {
FRSKY_OSD_TRACE("FrSky OSD configured, trying to connect...");
portOptions_e portOptions = 0;
serialPort_t *port = openSerialPort(portConfig->identifier,
FUNCTION_FRSKY_OSD, NULL, NULL, FRSKY_OSD_BAUDRATE,
MODE_RXTX, portOptions);
if (port) {
frskyOsdStateReset(port);
frskyOsdRequestInfo();
return true;
}
}
return false;
}
bool frskyOsdIsReady(void)
{
return state.info.minor > 0 || state.info.major > 0;
}
void frskyOsdUpdate(void)
{
if (!state.port) {
return;
}
frskyOsdUpdateReceiveBuffer();
if (frskyOsdIsResponseAvailable()) {
frskyOsdDispatchResponse();
}
if (frskyOsdShouldRequestInfo()) {
frskyOsdRequestInfo();
}
}
void frskyOsdBeginTransaction(frskyOsdTransactionOptions_e opts)
{
if (opts & FRSKY_OSD_TRANSACTION_OPT_PROFILED) {
frskyOsdPoint_t p = { .x = 0, .y = 10};
frskyOsdSendAsyncCommand(OSD_CMD_TRANSACTION_BEGIN_PROFILED, &p, sizeof(p));
if (opts & FRSKY_OSD_TRANSACTION_OPT_RESET_DRAWING) {
frskyOsdResetDrawingState();
}
} else if (opts & FRSKY_OSD_TRANSACTION_OPT_RESET_DRAWING) {
frskyOsdSendAsyncCommand(OSD_CMD_TRANSACTION_BEGIN_RESET_DRAWING, NULL, 0);
} else {
frskyOsdSendAsyncCommand(OSD_CMD_TRANSACTION_BEGIN, NULL, 0);
}
}
void frskyOsdCommitTransaction(void)
{
// Check wether the only command in the queue is a transaction begin.
// In that, case, discard the send buffer since it will make generate
// an empty transaction.
if (state.sendBuffer.pos == 1) {
if (state.sendBuffer.data[0] == OSD_CMD_TRANSACTION_BEGIN ||
state.sendBuffer.data[0] == OSD_CMD_TRANSACTION_BEGIN_RESET_DRAWING) {
state.sendBuffer.pos = 0;
return;
}
}
frskyOsdSendAsyncCommand(OSD_CMD_TRANSACTION_COMMIT, NULL, 0);
frskyOsdFlushSendBuffer();
}
void frskyOsdFlushSendBuffer(void)
{
if (state.sendBuffer.pos > 0) {
frskyOsdProcessCommandU8(NULL, FRSKY_OSD_PREAMBLE_BYTE_0);
frskyOsdProcessCommandU8(NULL, FRSKY_OSD_PREAMBLE_BYTE_1);
uint8_t crc = 0;
uint8_t buffer[4];
int lengthSize = uvarintEncode(state.sendBuffer.pos, buffer, sizeof(buffer));
for (int ii = 0; ii < lengthSize; ii++) {
frskyOsdProcessCommandU8(&crc, buffer[ii]);
}
for (unsigned ii = 0; ii < state.sendBuffer.pos; ii++) {
frskyOsdProcessCommandU8(&crc, state.sendBuffer.data[ii]);
}
frskyOsdProcessCommandU8(NULL, crc);
state.sendBuffer.pos = 0;
}
}
bool frskyOsdReadFontCharacter(unsigned char_address, osdCharacter_t *chr)
{
uint16_t addr = char_address;
state.recvOsdCharacter.addr = UINT16_MAX;
state.recvOsdCharacter.chr = chr;
// 500ms should be more than enough to receive ~70 bytes @ 115200 bps
bool ok = frskyOsdSendSyncCommand(OSD_CMD_READ_FONT, &addr, sizeof(addr), 500);
state.recvOsdCharacter.chr = NULL;
if (ok && state.recvOsdCharacter.addr == addr) {
return true;
}
return false;
}
bool frskyOsdWriteFontCharacter(unsigned char_address, const osdCharacter_t *chr)
{
frskyOsdCharacter_t c;
STATIC_ASSERT(sizeof(*chr) == sizeof(c.data), invalid_character_size);
memcpy(&c.data, chr, sizeof(c.data));
c.addr = char_address;
FRSKY_OSD_TRACE("Writing font character %u", char_address);
frskyOsdSendSyncCommand(OSD_CMD_WRITE_FONT, &c, sizeof(c), 1000);
return true;
}
unsigned frskyOsdGetGridRows(void)
{
return state.info.grid.rows;
}
unsigned frskyOsdGetGridCols(void)
{
return state.info.grid.columns;
}
unsigned frskyOsdGetPixelWidth(void)
{
return state.info.viewport.width;
}
unsigned frskyOsdGetPixelHeight(void)
{
return state.info.viewport.height;
}
static void frskyOsdSendCharInGrid(unsigned x, unsigned y, uint16_t chr)
{
uint8_t payload[] = {
x,
y,
chr & 0xFF,
chr >> 8,
0,
};
frskyOsdSendAsyncCommand(OSD_CMD_DRAW_GRID_CHR, payload, sizeof(payload));
}
static void frskyOsdSendAsyncBlobCommand(uint8_t cmd, const void *header, size_t headerSize, const void *blob, size_t blobSize)
{
uint8_t payload[128];
memcpy(payload, header, headerSize);
int uvarintSize = uvarintEncode(blobSize, &payload[headerSize], sizeof(payload) - headerSize);
memcpy(&payload[headerSize + uvarintSize], blob, blobSize);
frskyOsdSendAsyncCommand(cmd, payload, headerSize + uvarintSize + blobSize);
}
void frskyOsdDrawStringInGrid(unsigned x, unsigned y, const char *buff)
{
frskyOsdDrawGridStrHeaderCmd_t cmd;
cmd.gx = x;
cmd.gy = y;
cmd.opts = 0;
frskyOsdSendAsyncBlobCommand(OSD_CMD_DRAW_GRID_STR, &cmd, sizeof(cmd), buff, strlen(buff) + 1);
}
void frskyOsdDrawCharInGrid(unsigned x, unsigned y, uint16_t chr)
{
frskyOsdSendCharInGrid(x, y, chr);
}
void frskyOsdClearScreen(void)
{
if (!frskyOsdIsReady()) {
return;
}
frskyOsdSendAsyncCommand(OSD_CMD_DRAWING_CLEAR_SCREEN, NULL, 0);
}
void frskyOsdSetStrokeColor(frskyOsdColor_e color)
{
uint8_t c = color;
frskyOsdSendAsyncCommand(OSD_CMD_DRAWING_SET_STROKE_COLOR, &c, sizeof(c));
}
void frskyOsdSetFillColor(frskyOsdColor_e color)
{
uint8_t c = color;
frskyOsdSendAsyncCommand(OSD_CMD_DRAWING_SET_FILL_COLOR, &c, sizeof(c));
}
void frskyOsdSetStrokeAndFillColor(frskyOsdColor_e color)
{
uint8_t c = color;
frskyOsdSendAsyncCommand(OSD_CMD_DRAWING_SET_STROKE_AND_FILL_COLOR, &c, sizeof(c));
}
void frskyOsdSetColorInversion(bool inverted)
{
uint8_t c = inverted ? 1 : 0;
frskyOsdSendAsyncCommand(OSD_CMD_DRAWING_SET_COLOR_INVERSION, &c, sizeof(c));
}
void frskyOsdSetPixel(int x, int y, frskyOsdColor_e color)
{
frskyOsdSetPixel_t sp = {.p = {.x = x, .y = y}, .color = color};
frskyOsdSendAsyncCommand(OSD_CMD_DRAWING_SET_PIXEL, &sp, sizeof(sp));
}
void frskyOsdSetPixelToStrokeColor(int x, int y)
{
frskyOsdPoint_t p = { .x = x, .y = y};
frskyOsdSendAsyncCommand(OSD_CMD_DRAWING_SET_PIXEL_TO_STROKE_COLOR, &p, sizeof(p));
}
void frskyOsdSetPixelToFillColor(int x, int y)
{
frskyOsdPoint_t p = { .x = x, .y = y};
frskyOsdSendAsyncCommand(OSD_CMD_DRAWING_SET_PIXEL_TO_FILL_COLOR, &p, sizeof(p));
}
void frskyOsdSetStrokeWidth(unsigned width)
{
uint8_t w = width;
frskyOsdSendAsyncCommand(OSD_CMD_DRAWING_SET_STROKE_WIDTH, &w, sizeof(w));
}
void frskyOsdSetLineOutlineType(frskyOsdLineOutlineType_e outlineType)
{
uint8_t type = outlineType;
frskyOsdSendAsyncCommand(OSD_CMD_DRAWING_SET_LINE_OUTLINE_TYPE, &type, sizeof(type));
}
void frskyOsdSetLineOutlineColor(frskyOsdColor_e outlineColor)
{
uint8_t color = outlineColor;
frskyOsdSendAsyncCommand(OSD_CMD_DRAWING_SET_LINE_OUTLINE_COLOR, &color, sizeof(color));
}
void frskyOsdClipToRect(int x, int y, int w, int h)
{
frskyOsdRect_t r = { .origin = { .x = x, .y = y}, .size = {.w = w, .h = h}};
frskyOsdSendAsyncCommand(OSD_CMD_DRAWING_CLIP_TO_RECT, &r, sizeof(r));
}
void frskyOsdClearRect(int x, int y, int w, int h)
{
frskyOsdRect_t r = { .origin = { .x = x, .y = y}, .size = {.w = w, .h = h}};
frskyOsdSendAsyncCommand(OSD_CMD_DRAWING_CLEAR_RECT, &r, sizeof(r));
}
void frskyOsdResetDrawingState(void)
{
frskyOsdSendAsyncCommand(OSD_CMD_DRAWING_RESET, NULL, 0);
}
void frskyOsdDrawCharacter(int x, int y, uint16_t chr, uint8_t opts)
{
frskyOsdDrawCharacterCmd_t dc = { .p = {.x = x, .y = y}, .chr = chr, .opts = opts};
frskyOsdSendAsyncCommand(OSD_CMD_DRAWING_DRAW_CHAR, &dc, sizeof(dc));
}
void frskyOsdDrawCharacterMask(int x, int y, uint16_t chr, frskyOsdColor_e color, uint8_t opts)
{
frskyOsdDrawCharacterMaskCmd_t dc = { .dc = { .p = {.x = x, .y = y}, .chr = chr, .opts = opts}, .maskColor = color};
frskyOsdSendAsyncCommand(OSD_CMD_DRAWING_DRAW_CHAR_MASK, &dc, sizeof(dc));
}
void frskyOsdDrawString(int x, int y, const char *s, uint8_t opts)
{
frskyOsdDrawStrCommandHeaderCmd_t cmd;
cmd.p.x = x;
cmd.p.y = y;
cmd.opts = opts;
frskyOsdSendAsyncBlobCommand(OSD_CMD_DRAWING_DRAW_STRING, &cmd, sizeof(cmd), s, strlen(s) + 1);
}
void frskyOsdDrawStringMask(int x, int y, const char *s, frskyOsdColor_e color, uint8_t opts)
{
frskyOsdDrawStrMaskCommandHeaderCmd_t cmd;
cmd.p.x = x;
cmd.p.y = y;
cmd.opts = opts;
cmd.maskColor = color;
frskyOsdSendAsyncBlobCommand(OSD_CMD_DRAWING_DRAW_STRING_MASK, &cmd, sizeof(cmd), s, strlen(s) + 1);
}
void frskyOsdMoveToPoint(int x, int y)
{
frskyOsdPoint_t p = { .x = x, .y = y};
frskyOsdSendAsyncCommand(OSD_CMD_DRAWING_MOVE_TO_POINT, &p, sizeof(p));
}
void frskyOsdStrokeLineToPoint(int x, int y)
{
frskyOsdPoint_t p = { .x = x, .y = y};
frskyOsdSendAsyncCommand(OSD_CMD_DRAWING_STROKE_LINE_TO_POINT, &p, sizeof(p));
}
void frskyOsdStrokeTriangle(int x1, int y1, int x2, int y2, int x3, int y3)
{
frskyOsdTriangle_t t = {.p1 = {.x = x1, .y = y1}, .p2 = {.x = x2, .y = y2}, .p3 = { .x = x3, .y = y3}};
frskyOsdSendAsyncCommand(OSD_CMD_DRAWING_STROKE_TRIANGLE, &t, sizeof(t));
}
void frskyOsdFillTriangle(int x1, int y1, int x2, int y2, int x3, int y3)
{
frskyOsdTriangle_t t = {.p1 = {.x = x1, .y = y1}, .p2 = {.x = x2, .y = y2}, .p3 = { .x = x3, .y = y3}};
frskyOsdSendAsyncCommand(OSD_CMD_DRAWING_FILL_TRIANGLE, &t, sizeof(t));
}
void frskyOsdFillStrokeTriangle(int x1, int y1, int x2, int y2, int x3, int y3)
{
frskyOsdTriangle_t t = {.p1 = {.x = x1, .y = y1}, .p2 = {.x = x2, .y = y2}, .p3 = { .x = x3, .y = y3}};
frskyOsdSendAsyncCommand(OSD_CMD_DRAWING_FILL_STROKE_TRIANGLE, &t, sizeof(t));
}
void frskyOsdStrokeRect(int x, int y, int w, int h)
{
frskyOsdRect_t r = { .origin = { .x = x, .y = y}, .size = {.w = w, .h = h}};
frskyOsdSendAsyncCommand(OSD_CMD_DRAWING_STROKE_RECT, &r, sizeof(r));
}
void frskyOsdFillRect(int x, int y, int w, int h)
{
frskyOsdRect_t r = { .origin = { .x = x, .y = y}, .size = {.w = w, .h = h}};
frskyOsdSendAsyncCommand(OSD_CMD_DRAWING_FILL_RECT, &r, sizeof(r));
}
void frskyOsdFillStrokeRect(int x, int y, int w, int h)
{
frskyOsdRect_t r = { .origin = { .x = x, .y = y}, .size = {.w = w, .h = h}};
frskyOsdSendAsyncCommand(OSD_CMD_DRAWING_FILL_STROKE_RECT, &r, sizeof(r));
}
void frskyOsdStrokeEllipseInRect(int x, int y, int w, int h)
{
frskyOsdRect_t r = { .origin = { .x = x, .y = y}, .size = {.w = w, .h = h}};
frskyOsdSendAsyncCommand(OSD_CMD_DRAWING_STROKE_ELLIPSE_IN_RECT, &r, sizeof(r));
}
void frskyOsdFillEllipseInRect(int x, int y, int w, int h)
{
frskyOsdRect_t r = { .origin = { .x = x, .y = y}, .size = {.w = w, .h = h}};
frskyOsdSendAsyncCommand(OSD_CMD_DRAWING_FILL_ELLIPSE_IN_RECT, &r, sizeof(r));
}
void frskyOsdFillStrokeEllipseInRect(int x, int y, int w, int h)
{
frskyOsdRect_t r = { .origin = { .x = x, .y = y}, .size = {.w = w, .h = h}};
frskyOsdSendAsyncCommand(OSD_CMD_DRAWING_FILL_STROKE_ELLIPSE_IN_RECT, &r, sizeof(r));
}
void frskyOsdCtmReset(void)
{
frskyOsdSendAsyncCommand(OSD_CMD_CTM_RESET, NULL, 0);
}
void frskyOsdCtmSet(float m11, float m12, float m21, float m22, float m31, float m32)
{
float values[] = {
m11, m12,
m21, m22,
m31, m32,
};
frskyOsdSendAsyncCommand(OSD_CMD_CTM_SET, values, sizeof(values));
}
void frskyOsdCtmTranslate(float tx, float ty)
{
float values[] = {
tx,
ty,
};
frskyOsdSendAsyncCommand(OSD_CMD_CTM_TRANSLATE, values, sizeof(values));
}
void frskyOsdCtmScale(float sx, float sy)
{
float values[] = {
sx,
sy,
};
frskyOsdSendAsyncCommand(OSD_CMD_CTM_SCALE, values, sizeof(values));
}
void frskyOsdCtmRotate(float r)
{
frskyOsdSendAsyncCommand(OSD_CMD_CTM_ROTATE, &r, sizeof(r));
}
void frskyOsdContextPush(void)
{
frskyOsdSendAsyncCommand(OSD_CMD_CONTEXT_PUSH, NULL, 0);
}
void frskyOsdContextPop(void)
{
frskyOsdSendAsyncCommand(OSD_CMD_CONTEXT_POP, NULL, 0);
}
#endif

86
src/main/io/frsky_osd.h Normal file
View File

@ -0,0 +1,86 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include "drivers/display.h"
#include "drivers/osd.h"
typedef enum {
FRSKY_OSD_TRANSACTION_OPT_PROFILED = 1 << 0,
FRSKY_OSD_TRANSACTION_OPT_RESET_DRAWING = 1 << 1,
} frskyOsdTransactionOptions_e;
typedef enum {
FRSKY_OSD_COLOR_BLACK = 0,
FRSKY_OSD_COLOR_TRANSPARENT = 1,
FRSKY_OSD_COLOR_WHITE = 2,
FRSKY_OSD_COLOR_GRAY = 3,
} frskyOsdColor_e;
typedef enum {
FRSKY_OSD_OUTLINE_TYPE_NONE = 0,
FRSKY_OSD_OUTLINE_TYPE_TOP = 1 << 0,
FRSKY_OSD_OUTLINE_TYPE_RIGHT = 1 << 1,
FRSKY_OSD_OUTLINE_TYPE_BOTTOM = 1 << 2,
FRSKY_OSD_OUTLINE_TYPE_LEFT = 1 << 3,
} frskyOsdLineOutlineType_e;
bool frskyOsdInit(videoSystem_e videoSystem);
bool frskyOsdIsReady(void);
void frskyOsdUpdate(void);
void frskyOsdBeginTransaction(frskyOsdTransactionOptions_e opts);
void frskyOsdCommitTransaction(void);
void frskyOsdFlushSendBuffer(void);
bool frskyOsdReadFontCharacter(unsigned char_address, osdCharacter_t *chr);
bool frskyOsdWriteFontCharacter(unsigned char_address, const osdCharacter_t *chr);
unsigned frskyOsdGetGridRows(void);
unsigned frskyOsdGetGridCols(void);
unsigned frskyOsdGetPixelWidth(void);
unsigned frskyOsdGetPixelHeight(void);
void frskyOsdDrawStringInGrid(unsigned x, unsigned y, const char *buff);
void frskyOsdDrawCharInGrid(unsigned x, unsigned y, uint16_t chr);
bool frskyOsdReadCharInGrid(unsigned x, unsigned y, uint16_t *c);
void frskyOsdClearScreen(void);
void frskyOsdSetStrokeColor(frskyOsdColor_e color);
void frskyOsdSetFillColor(frskyOsdColor_e color);
void frskyOsdSetStrokeAndFillColor(frskyOsdColor_e color);
void frskyOsdSetColorInversion(bool inverted);
void frskyOsdSetPixel(int x, int y, frskyOsdColor_e color);
void frskyOsdSetPixelToStrokeColor(int x, int y);
void frskyOsdSetPixelToFillColor(int x, int y);
void frskyOsdSetStrokeWidth(unsigned width);
void frskyOsdSetLineOutlineType(frskyOsdLineOutlineType_e outlineType);
void frskyOsdSetLineOutlineColor(frskyOsdColor_e outlineColor);
void frskyOsdClipToRect(int x, int y, int w, int h);
void frskyOsdClearRect(int x, int y, int w, int h);
void frskyOsdResetDrawingState(void);
void frskyOsdDrawCharacter(int x, int y, uint16_t chr, uint8_t opts);
void frskyOsdDrawCharacterMask(int x, int y, uint16_t chr, frskyOsdColor_e color, uint8_t opts);
void frskyOsdDrawString(int x, int y, const char *s, uint8_t opts);
void frskyOsdDrawStringMask(int x, int y, const char *s, frskyOsdColor_e color, uint8_t opts);
void frskyOsdMoveToPoint(int x, int y);
void frskyOsdStrokeLineToPoint(int x, int y);
void frskyOsdStrokeTriangle(int x1, int y1, int x2, int y2, int x3, int y3);
void frskyOsdFillTriangle(int x1, int y1, int x2, int y2, int x3, int y3);
void frskyOsdFillStrokeTriangle(int x1, int y1, int x2, int y2, int x3, int y3);
void frskyOsdStrokeRect(int x, int y, int w, int h);
void frskyOsdFillRect(int x, int y, int w, int h);
void frskyOsdFillStrokeRect(int x, int y, int w, int h);
void frskyOsdStrokeEllipseInRect(int x, int y, int w, int h);
void frskyOsdFillEllipseInRect(int x, int y, int w, int h);
void frskyOsdFillStrokeEllipseInRect(int x, int y, int w, int h);
void frskyOsdCtmReset(void);
void frskyOsdCtmSet(float m11, float m12, float m21, float m22, float m31, float m32);
void frskyOsdCtmTranslate(float tx, float ty);
void frskyOsdCtmScale(float sx, float sy);
void frskyOsdCtmRotate(float r);
void frskyOsdContextPush(void);
void frskyOsdContextPop(void);

View File

@ -119,7 +119,7 @@ serialPortConfig_t *serialFindPortConfigurationMutable(serialPortIdentifier_e id
return NULL;
}
PG_REGISTER_WITH_RESET_FN(serialConfig_t, serialConfig, PG_SERIAL_CONFIG, 0);
PG_REGISTER_WITH_RESET_FN(serialConfig_t, serialConfig, PG_SERIAL_CONFIG, 1);
void pgResetFn_serialConfig(serialConfig_t *serialConfig)
{

View File

@ -49,6 +49,7 @@ typedef enum {
FUNCTION_VTX_TRAMP = (1 << 13), // 8192
FUNCTION_RCDEVICE = (1 << 14), // 16384
FUNCTION_LIDAR_TF = (1 << 15), // 32768
FUNCTION_FRSKY_OSD = (1 << 16), // 65536
} serialPortFunction_e;
#define TELEMETRY_SHAREABLE_PORT_FUNCTIONS_MASK (FUNCTION_TELEMETRY_FRSKY_HUB | FUNCTION_TELEMETRY_LTM | FUNCTION_TELEMETRY_MAVLINK)
@ -113,7 +114,7 @@ serialPort_t *findSharedSerialPort(uint16_t functionMask, serialPortFunction_e s
// configuration
//
typedef struct serialPortConfig_s {
uint16_t functionMask;
uint32_t functionMask;
serialPortIdentifier_e identifier;
uint8_t msp_baudrateIndex;
uint8_t gps_baudrateIndex;

View File

@ -57,6 +57,7 @@
#include "drivers/io.h"
#include "drivers/max7456.h"
#include "drivers/motor.h"
#include "drivers/osd.h"
#include "drivers/pwm_output.h"
#include "drivers/sdcard.h"
#include "drivers/serial.h"
@ -3294,22 +3295,42 @@ static mspResult_e mspCommonProcessInCommand(mspDescriptor_t srcDesc, uint8_t cm
break;
case MSP_OSD_CHAR_WRITE:
#ifdef USE_MAX7456
{
uint8_t font_data[64];
const uint8_t addr = sbufReadU8(src);
for (int i = 0; i < 54; i++) {
font_data[i] = sbufReadU8(src);
osdCharacter_t chr;
size_t osdCharacterBytes;
uint16_t addr;
if (dataSize >= OSD_CHAR_VISIBLE_BYTES + 2) {
if (dataSize >= OSD_CHAR_BYTES + 2) {
// 16 bit address, full char with metadata
addr = sbufReadU16(src);
osdCharacterBytes = OSD_CHAR_BYTES;
} else if (dataSize >= OSD_CHAR_BYTES + 1) {
// 8 bit address, full char with metadata
addr = sbufReadU8(src);
osdCharacterBytes = OSD_CHAR_BYTES;
} else {
// 16 bit character address, only visible char bytes
addr = sbufReadU16(src);
osdCharacterBytes = OSD_CHAR_VISIBLE_BYTES;
}
} else {
// 8 bit character address, only visible char bytes
addr = sbufReadU8(src);
osdCharacterBytes = OSD_CHAR_VISIBLE_BYTES;
}
// !!TODO - replace this with a device independent implementation
if (!max7456WriteNvm(addr, font_data)) {
for (unsigned ii = 0; ii < MIN(osdCharacterBytes, sizeof(chr.data)); ii++) {
chr.data[ii] = sbufReadU8(src);
}
displayPort_t *osdDisplayPort = osdGetDisplayPort();
if (!osdDisplayPort) {
return MSP_RESULT_ERROR;
}
if (!displayWriteFontCharacter(osdDisplayPort, addr, &chr)) {
return MSP_RESULT_ERROR;
}
}
break;
#else
return MSP_RESULT_ERROR;
#endif
#endif // OSD
default:

View File

@ -54,7 +54,7 @@
#include "drivers/display.h"
#include "drivers/flash.h"
#include "drivers/max7456_symbols.h"
#include "drivers/osd_symbols.h"
#include "drivers/sdcard.h"
#include "drivers/time.h"
@ -126,6 +126,7 @@ static uint8_t armState;
static uint8_t osdProfile = 1;
#endif
static displayPort_t *osdDisplayPort;
static bool osdIsReady;
static bool suppressStatsDisplay = false;
static uint8_t osdStatsRowCount = 0;
@ -136,6 +137,8 @@ static bool backgroundLayerSupported = false;
escSensorData_t *osdEscDataCombined;
#endif
STATIC_ASSERT(OSD_POS_MAX == OSD_POS(31,31), OSD_POS_MAX_incorrect);
PG_REGISTER_WITH_RESET_FN(osdConfig_t, osdConfig, PG_OSD_CONFIG, 6);
// Controls the display order of the OSD post-flight statistics.
@ -354,26 +357,16 @@ static void osdDrawLogo(int x, int y)
}
}
void osdInit(displayPort_t *osdDisplayPortToUse)
static void osdCompleteInitialization(void)
{
if (!osdDisplayPortToUse) {
return;
}
STATIC_ASSERT(OSD_POS_MAX == OSD_POS(31,31), OSD_POS_MAX_incorrect);
osdDisplayPort = osdDisplayPortToUse;
#ifdef USE_CMS
cmsDisplayPortRegister(osdDisplayPort);
#endif
backgroundLayerSupported = displayLayerSupported(osdDisplayPort, DISPLAYPORT_LAYER_BACKGROUND);
displayLayerSelect(osdDisplayPort, DISPLAYPORT_LAYER_FOREGROUND);
armState = ARMING_FLAG(ARMED);
osdResetAlarms();
backgroundLayerSupported = displayLayerSupported(osdDisplayPort, DISPLAYPORT_LAYER_BACKGROUND);
displayLayerSelect(osdDisplayPort, DISPLAYPORT_LAYER_FOREGROUND);
displayBeginTransaction(osdDisplayPort, DISPLAY_TRANSACTION_OPT_RESET_DRAWING);
displayClearScreen(osdDisplayPort);
osdDrawLogo(3, 1);
@ -394,8 +387,6 @@ void osdInit(displayPort_t *osdDisplayPortToUse)
}
#endif
displayResync(osdDisplayPort);
resumeRefreshAt = micros() + (4 * REFRESH_1S);
#ifdef USE_OSD_PROFILES
setOsdProfile(osdConfig()->osdProfileIndex);
@ -403,6 +394,25 @@ void osdInit(displayPort_t *osdDisplayPortToUse)
osdElementsInit(backgroundLayerSupported);
osdAnalyzeActiveElements();
displayCommitTransaction(osdDisplayPort);
osdIsReady = true;
}
void osdInit(displayPort_t *osdDisplayPortToUse)
{
if (!osdDisplayPortToUse) {
return;
}
osdDisplayPort = osdDisplayPortToUse;
#ifdef USE_CMS
cmsDisplayPortRegister(osdDisplayPort);
#endif
if (displayIsReady(osdDisplayPort)) {
osdCompleteInitialization();
}
}
bool osdInitialized(void)
@ -838,6 +848,14 @@ STATIC_UNIT_TESTED void osdRefresh(timeUs_t currentTimeUs)
static bool osdStatsVisible = false;
static timeUs_t osdStatsRefreshTimeUs;
if (!osdIsReady) {
if (!displayIsReady(osdDisplayPort)) {
displayResync(osdDisplayPort);
return;
}
osdCompleteInitialization();
}
// detect arm/disarm
if (armState != ARMING_FLAG(ARMED)) {
if (ARMING_FLAG(ARMED)) {
@ -887,6 +905,8 @@ STATIC_UNIT_TESTED void osdRefresh(timeUs_t currentTimeUs)
}
lastTimeUs = currentTimeUs;
displayBeginTransaction(osdDisplayPort, DISPLAY_TRANSACTION_OPT_RESET_DRAWING);
if (resumeRefreshAt) {
if (cmp32(currentTimeUs, resumeRefreshAt) < 0) {
// in timeout period, check sticks for activity to resume display.
@ -929,6 +949,7 @@ STATIC_UNIT_TESTED void osdRefresh(timeUs_t currentTimeUs)
osdDrawElements(currentTimeUs);
displayHeartbeat(osdDisplayPort);
}
displayCommitTransaction(osdDisplayPort);
}
/*
@ -1012,4 +1033,9 @@ bool osdNeedsAccelerometer(void)
}
#endif // USE_ACC
displayPort_t *osdGetDisplayPort(void)
{
return osdDisplayPort;
}
#endif // USE_OSD

View File

@ -227,6 +227,7 @@ typedef enum {
OSD_DISPLAYPORT_DEVICE_AUTO,
OSD_DISPLAYPORT_DEVICE_MAX7456,
OSD_DISPLAYPORT_DEVICE_MSP,
OSD_DISPLAYPORT_DEVICE_FRSKYOSD,
} osdDisplayPortDevice_e;
// Make sure the number of warnings do not exceed the available 32bit storage
@ -319,3 +320,4 @@ bool osdElementVisible(uint16_t value);
bool osdGetVisualBeeperState(void);
statistic_t *osdGetStats(void);
bool osdNeedsAccelerometer(void);
struct displayPort_s *osdGetDisplayPort(void);

View File

@ -85,8 +85,8 @@
#include "config/feature.h"
#include "drivers/display.h"
#include "drivers/max7456_symbols.h"
#include "drivers/dshot.h"
#include "drivers/osd_symbols.h"
#include "drivers/time.h"
#include "drivers/vtx_common.h"

View File

@ -24,12 +24,6 @@
// Video Character Display parameters
enum VIDEO_SYSTEMS {
VIDEO_SYSTEM_AUTO = 0,
VIDEO_SYSTEM_PAL,
VIDEO_SYSTEM_NTSC
};
typedef struct vcdProfile_s {
uint8_t video_system;
int8_t h_offset;

View File

@ -318,7 +318,9 @@
#if (FLASH_SIZE > 256)
#define USE_AIRMODE_LPF
#define USE_CANVAS
#define USE_DASHBOARD
#define USE_FRSKYOSD
#define USE_GPS
#define USE_GPS_NMEA
#define USE_GPS_UBLOX

View File

@ -439,9 +439,11 @@ COMMON_FLAGS = \
ifeq ($(shell $(CC) -v 2>&1 | grep -q "clang version" && echo "clang"),clang)
COMMON_FLAGS += -fblocks
ifndef CYGWIN
ifndef MACOSX
LDFLAGS += -lBlocksRuntime
endif
endif
endif
USE_PTHREAD = YES
USE_COVERAGE = YES

View File

@ -33,7 +33,7 @@ extern "C" {
#include "common/printf.h"
#include "common/streambuf.h"
#include "drivers/max7456_symbols.h"
#include "drivers/osd_symbols.h"
#include "drivers/persistent.h"
#include "drivers/serial.h"
#include "drivers/system.h"

View File

@ -36,7 +36,7 @@ extern "C" {
#include "common/time.h"
#include "drivers/max7456_symbols.h"
#include "drivers/osd_symbols.h"
#include "drivers/persistent.h"
#include "drivers/serial.h"