2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
26 #if defined(USE_CRSF_CMS_TELEMETRY)
30 #include "common/maths.h"
31 #include "common/printf.h"
32 #include "common/time.h"
34 #include "drivers/display.h"
35 #include "drivers/time.h"
37 #include "displayport_crsf.h"
39 #define CRSF_DISPLAY_PORT_OPEN_DELAY_MS 400
40 #define CRSF_DISPLAY_PORT_CLEAR_DELAY_MS 45
42 static crsfDisplayPortScreen_t crsfScreen
;
43 static timeMs_t delayTransportUntilMs
= 0;
45 displayPort_t crsfDisplayPort
;
47 static int crsfGrab(displayPort_t
*displayPort
)
49 return displayPort
->grabCount
= 1;
52 static int crsfClearScreen(displayPort_t
*displayPort
, displayClearOption_e options
)
56 memset(crsfScreen
.buffer
, ' ', sizeof(crsfScreen
.buffer
));
57 crsfScreen
.updated
= false;
58 crsfScreen
.reset
= true;
59 delayTransportUntilMs
= millis() + CRSF_DISPLAY_PORT_CLEAR_DELAY_MS
;
63 static int crsfRelease(displayPort_t
*displayPort
)
65 displayPort
->grabCount
= 0;
66 return crsfClearScreen(displayPort
, DISPLAY_CLEAR_WAIT
);
69 static bool crsfDrawScreen(displayPort_t
*displayPort
)
75 static int crsfScreenSize(const displayPort_t
*displayPort
)
77 return displayPort
->rows
* displayPort
->cols
;
81 static int crsfWriteString(displayPort_t
*displayPort
, uint8_t col
, uint8_t row
, uint8_t attr
, const char *s
)
85 if (row
>= crsfScreen
.rows
|| col
>= crsfScreen
.cols
) {
88 const size_t truncLen
= MIN((int)strlen(s
), crsfScreen
.cols
-col
); // truncate at colCount
89 char *rowStart
= &crsfScreen
.buffer
[row
* crsfScreen
.cols
+ col
];
90 crsfScreen
.updated
|= memcmp(rowStart
, s
, truncLen
);
91 if (crsfScreen
.updated
) {
92 memcpy(rowStart
, s
, truncLen
);
97 static int crsfWriteChar(displayPort_t
*displayPort
, uint8_t col
, uint8_t row
, uint8_t attr
, uint8_t c
)
100 tfp_sprintf(s
, "%c", c
);
101 return crsfWriteString(displayPort
, col
, row
, attr
, s
);
104 static bool crsfIsTransferInProgress(const displayPort_t
*displayPort
)
110 static bool crsfIsSynced(const displayPort_t
*displayPort
)
116 static int crsfHeartbeat(displayPort_t
*displayPort
)
122 static void crsfRedraw(displayPort_t
*displayPort
)
124 displayPort
->rows
= crsfScreen
.rows
;
125 displayPort
->cols
= crsfScreen
.cols
;
128 static uint32_t crsfTxBytesFree(const displayPort_t
*displayPort
)
134 static const displayPortVTable_t crsfDisplayPortVTable
= {
136 .release
= crsfRelease
,
137 .clearScreen
= crsfClearScreen
,
138 .drawScreen
= crsfDrawScreen
,
139 .screenSize
= crsfScreenSize
,
140 .writeString
= crsfWriteString
,
141 .writeChar
= crsfWriteChar
,
142 .isTransferInProgress
= crsfIsTransferInProgress
,
143 .heartbeat
= crsfHeartbeat
,
144 .redraw
= crsfRedraw
,
145 .isSynced
= crsfIsSynced
,
146 .txBytesFree
= crsfTxBytesFree
,
147 .layerSupported
= NULL
,
152 crsfDisplayPortScreen_t
*crsfDisplayPortScreen(void)
157 void crsfDisplayPortMenuOpen(void)
162 if (cmsDisplayPortSelect(&crsfDisplayPort
)) {
164 delayTransportUntilMs
= millis() + CRSF_DISPLAY_PORT_OPEN_DELAY_MS
;
168 void crsfDisplayPortMenuExit(void)
173 uint8_t exitMenu
= CMS_EXIT
;
174 cmsMenuExit(&crsfDisplayPort
, &exitMenu
);
177 void crsfDisplayPortSetDimensions(uint8_t rows
, uint8_t cols
)
179 crsfScreen
.rows
= MIN(rows
, CRSF_DISPLAY_PORT_ROWS_MAX
);
180 crsfScreen
.cols
= MIN(cols
, CRSF_DISPLAY_PORT_COLS_MAX
);
181 crsfRedraw(&crsfDisplayPort
);
184 void crsfDisplayPortRefresh(void)
187 crsfDisplayPortMenuOpen();
190 crsfScreen
.updated
= true;
191 crsfScreen
.reset
= true;
192 delayTransportUntilMs
= millis() + CRSF_DISPLAY_PORT_CLEAR_DELAY_MS
;
195 bool crsfDisplayPortIsReady(void)
197 const timeMs_t currentTimeMs
= millis();
198 const bool delayExpired
= (currentTimeMs
> delayTransportUntilMs
);
199 const bool cmsReady
= (cmsInMenu
&& (pCurrentDisplay
== &crsfDisplayPort
));
200 return (bool)(delayExpired
&& cmsReady
);
203 static displayPort_t
*displayPortCrsfInit()
205 crsfDisplayPortSetDimensions(CRSF_DISPLAY_PORT_ROWS_MAX
, CRSF_DISPLAY_PORT_COLS_MAX
);
206 displayInit(&crsfDisplayPort
, &crsfDisplayPortVTable
, DISPLAYPORT_DEVICE_TYPE_CRSF
);
208 return &crsfDisplayPort
;
211 void crsfDisplayportRegister(void)
213 cmsDisplayPortRegister(displayPortCrsfInit());