atbetaflight/src/main/io/displayport_crsf.c

216 lines
5.4 KiB
C

/*
* This file is part of Cleanflight and Betaflight.
*
* Cleanflight and Betaflight 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 and Betaflight 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 <string.h>
#include "platform.h"
#if defined(USE_CRSF_CMS_TELEMETRY)
#include "cms/cms.h"
#include "common/maths.h"
#include "common/printf.h"
#include "common/time.h"
#include "drivers/display.h"
#include "drivers/time.h"
#include "displayport_crsf.h"
#define CRSF_DISPLAY_PORT_OPEN_DELAY_MS 400
#define CRSF_DISPLAY_PORT_CLEAR_DELAY_MS 45
static crsfDisplayPortScreen_t crsfScreen;
static timeMs_t delayTransportUntilMs = 0;
displayPort_t crsfDisplayPort;
static int crsfGrab(displayPort_t *displayPort)
{
return displayPort->grabCount = 1;
}
static int crsfClearScreen(displayPort_t *displayPort, displayClearOption_e options)
{
UNUSED(displayPort);
UNUSED(options);
memset(crsfScreen.buffer, ' ', sizeof(crsfScreen.buffer));
crsfScreen.updated = false;
crsfScreen.reset = true;
delayTransportUntilMs = millis() + CRSF_DISPLAY_PORT_CLEAR_DELAY_MS;
return 0;
}
static int crsfRelease(displayPort_t *displayPort)
{
displayPort->grabCount = 0;
return crsfClearScreen(displayPort, DISPLAY_CLEAR_WAIT);
}
static bool crsfDrawScreen(displayPort_t *displayPort)
{
UNUSED(displayPort);
return 0;
}
static int crsfScreenSize(const displayPort_t *displayPort)
{
return displayPort->rows * displayPort->cols;
}
static int crsfWriteString(displayPort_t *displayPort, uint8_t col, uint8_t row, uint8_t attr, const char *s)
{
UNUSED(displayPort);
UNUSED(attr);
if (row >= crsfScreen.rows || col >= crsfScreen.cols) {
return 0;
}
const uint8_t truncLen = MIN((int)strlen(s), crsfScreen.cols-col); // truncate at colCount
char *rowStart = &crsfScreen.buffer[row * crsfScreen.cols + col];
crsfScreen.updated |= memcmp(rowStart, s, truncLen);
if (crsfScreen.updated) {
memcpy(rowStart, s, truncLen);
}
return 0;
}
static int crsfWriteChar(displayPort_t *displayPort, uint8_t col, uint8_t row, uint8_t attr, uint8_t c)
{
char s[1];
tfp_sprintf(s, "%c", c);
return crsfWriteString(displayPort, col, row, attr, s);
}
static bool crsfIsTransferInProgress(const displayPort_t *displayPort)
{
UNUSED(displayPort);
return false;
}
static bool crsfIsSynced(const displayPort_t *displayPort)
{
UNUSED(displayPort);
return true;
}
static int crsfHeartbeat(displayPort_t *displayPort)
{
UNUSED(displayPort);
return 0;
}
static void crsfRedraw(displayPort_t *displayPort)
{
displayPort->rows = crsfScreen.rows;
displayPort->cols = crsfScreen.cols;
}
static uint32_t crsfTxBytesFree(const displayPort_t *displayPort)
{
UNUSED(displayPort);
return UINT32_MAX;
}
static const displayPortVTable_t crsfDisplayPortVTable = {
.grab = crsfGrab,
.release = crsfRelease,
.clearScreen = crsfClearScreen,
.drawScreen = crsfDrawScreen,
.screenSize = crsfScreenSize,
.writeString = crsfWriteString,
.writeChar = crsfWriteChar,
.isTransferInProgress = crsfIsTransferInProgress,
.heartbeat = crsfHeartbeat,
.redraw = crsfRedraw,
.isSynced = crsfIsSynced,
.txBytesFree = crsfTxBytesFree,
.layerSupported = NULL,
.layerSelect = NULL,
.layerCopy = NULL,
};
crsfDisplayPortScreen_t *crsfDisplayPortScreen(void)
{
return &crsfScreen;
}
void crsfDisplayPortMenuOpen(void)
{
if (cmsInMenu) {
return;
}
if (cmsDisplayPortSelect(&crsfDisplayPort)) {
cmsMenuOpen();
delayTransportUntilMs = millis() + CRSF_DISPLAY_PORT_OPEN_DELAY_MS;
}
}
void crsfDisplayPortMenuExit(void)
{
if (!cmsInMenu) {
return;
}
uint8_t exitMenu = CMS_EXIT;
cmsMenuExit(&crsfDisplayPort, &exitMenu);
}
void crsfDisplayPortSetDimensions(uint8_t rows, uint8_t cols)
{
crsfScreen.rows = MIN(rows, CRSF_DISPLAY_PORT_ROWS_MAX);
crsfScreen.cols = MIN(cols, CRSF_DISPLAY_PORT_COLS_MAX);
crsfRedraw(&crsfDisplayPort);
}
void crsfDisplayPortRefresh(void)
{
if (!cmsInMenu) {
crsfDisplayPortMenuOpen();
return;
}
crsfScreen.updated = true;
crsfScreen.reset = true;
delayTransportUntilMs = millis() + CRSF_DISPLAY_PORT_CLEAR_DELAY_MS;
}
bool crsfDisplayPortIsReady(void)
{
const timeMs_t currentTimeMs = millis();
const bool delayExpired = (currentTimeMs > delayTransportUntilMs);
const bool cmsReady = (cmsInMenu && (pCurrentDisplay == &crsfDisplayPort));
return (bool)(delayExpired && cmsReady);
}
static displayPort_t *displayPortCrsfInit()
{
crsfDisplayPortSetDimensions(CRSF_DISPLAY_PORT_ROWS_MAX, CRSF_DISPLAY_PORT_COLS_MAX);
displayInit(&crsfDisplayPort, &crsfDisplayPortVTable, DISPLAYPORT_DEVICE_TYPE_CRSF);
return &crsfDisplayPort;
}
void crsfDisplayportRegister(void)
{
cmsDisplayPortRegister(displayPortCrsfInit());
}
#endif