diff --git a/make/source.mk b/make/source.mk index 2d21c8d5be..c2707a169d 100644 --- a/make/source.mk +++ b/make/source.mk @@ -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 \ diff --git a/src/main/cli/cli.c b/src/main/cli/cli.c index 8902d83fc3..b7e3fad7b6 100644 --- a/src/main/cli/cli.c +++ b/src/main/cli/cli.c @@ -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++; } diff --git a/src/main/cli/settings.c b/src/main/cli/settings.c index e611f79d96..9159bc816f 100644 --- a/src/main/cli/settings.c +++ b/src/main/cli/settings.c @@ -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 diff --git a/src/main/common/time.c b/src/main/common/time.c index 707cad7c38..67d46e3060 100644 --- a/src/main/common/time.c +++ b/src/main/common/time.c @@ -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, diff --git a/src/main/common/uvarint.c b/src/main/common/uvarint.c new file mode 100644 index 0000000000..ecf782684b --- /dev/null +++ b/src/main/common/uvarint.c @@ -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 + */ + +#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; +} diff --git a/src/main/common/uvarint.h b/src/main/common/uvarint.h new file mode 100644 index 0000000000..cf2d40adab --- /dev/null +++ b/src/main/common/uvarint.h @@ -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 + */ + +#pragma once + +#include +#include + +int uvarintEncode(uint32_t val, uint8_t *ptr, size_t size); +int uvarintDecode(uint32_t *val, const uint8_t *ptr, size_t size); diff --git a/src/main/drivers/display.c b/src/main/drivers/display.c index 7182910826..ee905549b5 100644 --- a/src/main/drivers/display.c +++ b/src/main/drivers/display.c @@ -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; diff --git a/src/main/drivers/display.h b/src/main/drivers/display.h index fa2acf0f65..82e4a13b7b 100644 --- a/src/main/drivers/display.h +++ b/src/main/drivers/display.h @@ -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); diff --git a/src/main/drivers/display_canvas.c b/src/main/drivers/display_canvas.c new file mode 100644 index 0000000000..9ba47087bc --- /dev/null +++ b/src/main/drivers/display_canvas.c @@ -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 + */ + +#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); + } +} diff --git a/src/main/drivers/display_canvas.h b/src/main/drivers/display_canvas.h new file mode 100644 index 0000000000..e70ee1c96e --- /dev/null +++ b/src/main/drivers/display_canvas.h @@ -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 + */ + +#pragma once + +#include +#include + +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); diff --git a/src/main/drivers/max7456.c b/src/main/drivers/max7456.c index a3e783caec..78f179db0c 100644 --- a/src/main/drivers/max7456.c +++ b/src/main/drivers/max7456.c @@ -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" diff --git a/src/main/drivers/osd.h b/src/main/drivers/osd.h new file mode 100644 index 0000000000..46bb875410 --- /dev/null +++ b/src/main/drivers/osd.h @@ -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 . + */ + +#pragma once + +#include + +#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; diff --git a/src/main/drivers/max7456_symbols.h b/src/main/drivers/osd_symbols.h similarity index 100% rename from src/main/drivers/max7456_symbols.h rename to src/main/drivers/osd_symbols.h diff --git a/src/main/fc/init.c b/src/main/fc/init.c index b949695e2d..6e27eb7265 100644 --- a/src/main/fc/init.c +++ b/src/main/fc/init.c @@ -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. diff --git a/src/main/io/displayport_frsky_osd.c b/src/main/io/displayport_frsky_osd.c new file mode 100644 index 0000000000..9596d20f5e --- /dev/null +++ b/src/main/io/displayport_frsky_osd.c @@ -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 . + */ + +#include +#include + +#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 diff --git a/src/main/io/displayport_frsky_osd.h b/src/main/io/displayport_frsky_osd.h new file mode 100644 index 0000000000..fc004f761f --- /dev/null +++ b/src/main/io/displayport_frsky_osd.h @@ -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 . + */ +#pragma once + +#include "drivers/osd.h" + +typedef struct displayPort_s displayPort_t; + +displayPort_t *frskyOsdDisplayPortInit(const videoSystem_e videoSystem); diff --git a/src/main/io/displayport_max7456.c b/src/main/io/displayport_max7456.c index ea7108afdb..03db0ff5fc 100644 --- a/src/main/io/displayport_max7456.c +++ b/src/main/io/displayport_max7456.c @@ -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) diff --git a/src/main/io/frsky_osd.c b/src/main/io/frsky_osd.c new file mode 100644 index 0000000000..0ff6890fe4 --- /dev/null +++ b/src/main/io/frsky_osd.c @@ -0,0 +1,927 @@ +#include +#include +#include + +#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 diff --git a/src/main/io/frsky_osd.h b/src/main/io/frsky_osd.h new file mode 100644 index 0000000000..80dd427df9 --- /dev/null +++ b/src/main/io/frsky_osd.h @@ -0,0 +1,86 @@ +#pragma once + +#include +#include + +#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); diff --git a/src/main/io/serial.c b/src/main/io/serial.c index 06ff2b928a..e016650b99 100644 --- a/src/main/io/serial.c +++ b/src/main/io/serial.c @@ -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) { diff --git a/src/main/io/serial.h b/src/main/io/serial.h index 94eb170d41..8023e32ab9 100644 --- a/src/main/io/serial.h +++ b/src/main/io/serial.h @@ -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; diff --git a/src/main/msp/msp.c b/src/main/msp/msp.c index 3f882d3718..bfbeae2ece 100644 --- a/src/main/msp/msp.c +++ b/src/main/msp/msp.c @@ -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: diff --git a/src/main/osd/osd.c b/src/main/osd/osd.c index c0651e4046..fe59ec0da1 100644 --- a/src/main/osd/osd.c +++ b/src/main/osd/osd.c @@ -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 diff --git a/src/main/osd/osd.h b/src/main/osd/osd.h index 69db3d3dd6..f242ed3a9c 100644 --- a/src/main/osd/osd.h +++ b/src/main/osd/osd.h @@ -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); diff --git a/src/main/osd/osd_elements.c b/src/main/osd/osd_elements.c index 0b65dff968..ef7fe782a0 100644 --- a/src/main/osd/osd_elements.c +++ b/src/main/osd/osd_elements.c @@ -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" diff --git a/src/main/pg/vcd.h b/src/main/pg/vcd.h index df3ed8001d..63d95ff955 100644 --- a/src/main/pg/vcd.h +++ b/src/main/pg/vcd.h @@ -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; diff --git a/src/main/target/common_pre.h b/src/main/target/common_pre.h index 664cd05218..26d704112a 100644 --- a/src/main/target/common_pre.h +++ b/src/main/target/common_pre.h @@ -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 diff --git a/src/test/Makefile b/src/test/Makefile index 08589756a6..4e243a2088 100644 --- a/src/test/Makefile +++ b/src/test/Makefile @@ -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 diff --git a/src/test/unit/link_quality_unittest.cc b/src/test/unit/link_quality_unittest.cc index e8df023050..119c2ac0cf 100644 --- a/src/test/unit/link_quality_unittest.cc +++ b/src/test/unit/link_quality_unittest.cc @@ -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" diff --git a/src/test/unit/osd_unittest.cc b/src/test/unit/osd_unittest.cc index 7e54bcbc91..1636101af8 100644 --- a/src/test/unit/osd_unittest.cc +++ b/src/test/unit/osd_unittest.cc @@ -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"