216 lines
5.4 KiB
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
|