1
0
Fork 0
mirror of https://github.com/betaflight/betaflight.git synced 2025-07-23 16:25:31 +03:00

Merge pull request #11340 from SteveCEvans/osd_peak_task

Increase number of element groups and use peak hold task estimation for OSD
This commit is contained in:
haslinghuis 2022-01-30 02:29:12 +01:00 committed by GitHub
commit 8701d9141f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 108 additions and 80 deletions

View file

@ -635,7 +635,7 @@ void spiSequenceStart(const extDevice_t *dev)
} }
// Use DMA if possible // Use DMA if possible
if (bus->useDMA && dmaSafe && ((segmentCount > 1) || (xferLen > 8))) { if (bus->useDMA && dmaSafe && ((segmentCount > 1) || (xferLen >= 8))) {
// Intialise the init structures for the first transfer // Intialise the init structures for the first transfer
spiInternalInitStream(dev, false); spiInternalInitStream(dev, false);

View file

@ -367,7 +367,7 @@ void spiSequenceStart(const extDevice_t *dev)
xferLen += checkSegment->len; xferLen += checkSegment->len;
} }
// Use DMA if possible // Use DMA if possible
if (bus->useDMA && dmaSafe && ((segmentCount > 1) || (xferLen > 8))) { if (bus->useDMA && dmaSafe && ((segmentCount > 1) || (xferLen >= 8))) {
// Intialise the init structures for the first transfer // Intialise the init structures for the first transfer
spiInternalInitStream(dev, false); spiInternalInitStream(dev, false);

View file

@ -206,7 +206,9 @@ static uint8_t shadowBuffer[VIDEO_BUFFER_CHARS_PAL];
//Max bytes to update in one call to max7456DrawScreen() //Max bytes to update in one call to max7456DrawScreen()
#define MAX_BYTES2SEND 250 #define MAX_BYTES2SEND 250
#define MAX_BYTES2SEND_POLLED 20 #define MAX_BYTES2SEND_POLLED 12
#define MAX_ENCODE_US 20
#define MAX_ENCODE_US_POLLED 10
static DMA_DATA uint8_t spiBuf[MAX_BYTES2SEND]; static DMA_DATA uint8_t spiBuf[MAX_BYTES2SEND];
@ -622,11 +624,13 @@ bool max7456DrawScreen(void)
uint8_t *buffer = getActiveLayerBuffer(); uint8_t *buffer = getActiveLayerBuffer();
int spiBufIndex = 0; int spiBufIndex = 0;
int maxSpiBufStartIndex; int maxSpiBufStartIndex;
timeDelta_t maxEncodeTime;
bool setAddress = true; bool setAddress = true;
bool autoInc = false; bool autoInc = false;
int posLimit = pos + (maxScreenSize / 2); int posLimit = pos + (maxScreenSize / 2);
maxSpiBufStartIndex = spiUseMOSI_DMA(dev) ? MAX_BYTES2SEND : MAX_BYTES2SEND_POLLED; maxSpiBufStartIndex = spiUseMOSI_DMA(dev) ? MAX_BYTES2SEND : MAX_BYTES2SEND_POLLED;
maxEncodeTime = spiUseMOSI_DMA(dev) ? MAX_ENCODE_US : MAX_ENCODE_US_POLLED;
// Abort for now if the bus is still busy // Abort for now if the bus is still busy
if (spiIsBusy(dev)) { if (spiIsBusy(dev)) {
@ -634,11 +638,13 @@ bool max7456DrawScreen(void)
return true; return true;
} }
// Allow for 8 bytes followed by an ESCAPE and reset of DMM at end of buffer timeUs_t startTime = micros();
maxSpiBufStartIndex -= 12;
// Allow for an ESCAPE, a reset of DMM and a two byte MAX7456ADD_DMM command at end of buffer
maxSpiBufStartIndex -= 4;
// Initialise the transfer buffer // Initialise the transfer buffer
while ((spiBufIndex < maxSpiBufStartIndex) && (pos < posLimit)) { while ((spiBufIndex < maxSpiBufStartIndex) && (pos < posLimit) && (cmpTimeUs(micros(), startTime) < maxEncodeTime)) {
if (buffer[pos] != shadowBuffer[pos]) { if (buffer[pos] != shadowBuffer[pos]) {
if (buffer[pos] == 0xff) { if (buffer[pos] == 0xff) {
buffer[pos] = ' '; buffer[pos] = ' ';

View file

@ -182,7 +182,7 @@ bool taskUpdateRxMainInProgress()
static void taskUpdateRxMain(timeUs_t currentTimeUs) static void taskUpdateRxMain(timeUs_t currentTimeUs)
{ {
static timeDelta_t rxStateDurationFracUs[RX_STATE_COUNT]; static timeDelta_t rxStateDurationFractionUs[RX_STATE_COUNT];
timeDelta_t executeTimeUs; timeDelta_t executeTimeUs;
rxState_e oldRxState = rxState; rxState_e oldRxState = rxState;
timeDelta_t anticipatedExecutionTime; timeDelta_t anticipatedExecutionTime;
@ -230,23 +230,23 @@ static void taskUpdateRxMain(timeUs_t currentTimeUs)
// If the scheduler has reduced the anticipatedExecutionTime due to task aging, pick that up // If the scheduler has reduced the anticipatedExecutionTime due to task aging, pick that up
anticipatedExecutionTime = schedulerGetNextStateTime(); anticipatedExecutionTime = schedulerGetNextStateTime();
if (anticipatedExecutionTime != (rxStateDurationFracUs[oldRxState] >> RX_TASK_DECAY_SHIFT)) { if (anticipatedExecutionTime != (rxStateDurationFractionUs[oldRxState] >> RX_TASK_DECAY_SHIFT)) {
rxStateDurationFracUs[oldRxState] = anticipatedExecutionTime << RX_TASK_DECAY_SHIFT; rxStateDurationFractionUs[oldRxState] = anticipatedExecutionTime << RX_TASK_DECAY_SHIFT;
} }
if (executeTimeUs > (rxStateDurationFracUs[oldRxState] >> RX_TASK_DECAY_SHIFT)) { if (executeTimeUs > (rxStateDurationFractionUs[oldRxState] >> RX_TASK_DECAY_SHIFT)) {
rxStateDurationFracUs[oldRxState] = executeTimeUs << RX_TASK_DECAY_SHIFT; rxStateDurationFractionUs[oldRxState] = executeTimeUs << RX_TASK_DECAY_SHIFT;
} else { } else {
// Slowly decay the max time // Slowly decay the max time
rxStateDurationFracUs[oldRxState]--; rxStateDurationFractionUs[oldRxState]--;
} }
} }
if (debugMode == DEBUG_RX_STATE_TIME) { if (debugMode == DEBUG_RX_STATE_TIME) {
debug[oldRxState] = rxStateDurationFracUs[oldRxState] >> RX_TASK_DECAY_SHIFT; debug[oldRxState] = rxStateDurationFractionUs[oldRxState] >> RX_TASK_DECAY_SHIFT;
} }
schedulerSetNextStateTime(rxStateDurationFracUs[rxState] >> RX_TASK_DECAY_SHIFT); schedulerSetNextStateTime(rxStateDurationFractionUs[rxState] >> RX_TASK_DECAY_SHIFT);
} }

View file

@ -766,7 +766,7 @@ void gpsUpdate(timeUs_t currentTimeUs)
{ {
static gpsState_e gpsStateDurationUs[GPS_STATE_COUNT]; static gpsState_e gpsStateDurationUs[GPS_STATE_COUNT];
timeUs_t executeTimeUs; timeUs_t executeTimeUs;
gpsState_e gpsCurState = gpsData.state; gpsState_e gpsCurrentState = gpsData.state;
// read out available GPS bytes // read out available GPS bytes
if (gpsPort) { if (gpsPort) {
@ -861,8 +861,8 @@ void gpsUpdate(timeUs_t currentTimeUs)
executeTimeUs = micros() - currentTimeUs; executeTimeUs = micros() - currentTimeUs;
if (executeTimeUs > gpsStateDurationUs[gpsCurState]) { if (executeTimeUs > gpsStateDurationUs[gpsCurrentState]) {
gpsStateDurationUs[gpsCurState] = executeTimeUs; gpsStateDurationUs[gpsCurrentState] = executeTimeUs;
} }
schedulerSetNextStateTime(gpsStateDurationUs[gpsData.state]); schedulerSetNextStateTime(gpsStateDurationUs[gpsData.state]);

View file

@ -188,16 +188,19 @@ const osd_stats_e osdStatsDisplayOrder[OSD_STAT_COUNT] = {
}; };
// Group elements in a number of groups to reduce task scheduling overhead // Group elements in a number of groups to reduce task scheduling overhead
#define OSD_GROUP_COUNT 20 #define OSD_GROUP_COUNT OSD_ITEM_COUNT
// Aim to render a group of elements within a target time // Aim to render a group of elements within a target time
#define OSD_ELEMENT_RENDER_TARGET 40 #define OSD_ELEMENT_RENDER_TARGET 30
// Allow a margin by which a group render can exceed that of the sum of the elements before declaring insane // Allow a margin by which a group render can exceed that of the sum of the elements before declaring insane
// This will most likely be violated by a USB interrupt whilst using the CLI // This will most likely be violated by a USB interrupt whilst using the CLI
#define OSD_ELEMENT_RENDER_GROUP_MARGIN 5 #if defined(STM32F411xE)
// Safe margin when rendering elements #define OSD_ELEMENT_RENDER_GROUP_MARGIN 7
#define OSD_ELEMENT_RENDER_MARGIN 5 #else
// Safe margin in other states #define OSD_ELEMENT_RENDER_GROUP_MARGIN 2
#define OSD_MARGIN 2 #endif
#define OSD_TASK_MARGIN 1
// Decay the estimated max task duration by 1/(1 << OSD_EXEC_TIME_SHIFT) on every invocation
#define OSD_EXEC_TIME_SHIFT 8
// Format a float to the specified number of decimal places with optional rounding. // Format a float to the specified number of decimal places with optional rounding.
// OSD symbols can optionally be placed before and after the formatted number (use SYM_NONE for no symbol). // OSD symbols can optionally be placed before and after the formatted number (use SYM_NONE for no symbol).
@ -1128,6 +1131,7 @@ typedef enum {
OSD_STATE_PROCESS_STATS3, OSD_STATE_PROCESS_STATS3,
OSD_STATE_UPDATE_ALARMS, OSD_STATE_UPDATE_ALARMS,
OSD_STATE_UPDATE_CANVAS, OSD_STATE_UPDATE_CANVAS,
OSD_STATE_GROUP_ELEMENTS,
OSD_STATE_UPDATE_ELEMENTS, OSD_STATE_UPDATE_ELEMENTS,
OSD_STATE_UPDATE_HEARTBEAT, OSD_STATE_UPDATE_HEARTBEAT,
OSD_STATE_COMMIT, OSD_STATE_COMMIT,
@ -1165,16 +1169,16 @@ bool osdUpdateCheck(timeUs_t currentTimeUs, timeDelta_t currentDeltaTimeUs)
// Called when there is OSD update work to be done // Called when there is OSD update work to be done
void osdUpdate(timeUs_t currentTimeUs) void osdUpdate(timeUs_t currentTimeUs)
{ {
static timeUs_t osdStateDurationUs[OSD_STATE_COUNT] = { 0 }; static uint16_t osdStateDurationFractionUs[OSD_STATE_COUNT] = { 0 };
static timeUs_t osdElementDurationUs[OSD_ITEM_COUNT] = { 0 }; static uint32_t osdElementDurationUs[OSD_ITEM_COUNT] = { 0 };
static timeUs_t osdElementGroupMembership[OSD_ITEM_COUNT]; static uint8_t osdElementGroupMemberships[OSD_ITEM_COUNT];
static timeUs_t osdElementGroupTargetUs[OSD_GROUP_COUNT] = { 0 }; static uint16_t osdElementGroupTargetFractionUs[OSD_GROUP_COUNT] = { 0 };
static timeUs_t osdElementGroupDurationUs[OSD_GROUP_COUNT] = { 0 }; static uint16_t osdElementGroupDurationFractionUs[OSD_GROUP_COUNT] = { 0 };
static uint8_t osdElementGroup; static uint8_t osdElementGroup;
static bool firstPass = true; static bool firstPass = true;
uint8_t osdCurElementGroup = 0; uint8_t osdCurrentElementGroup = 0;
timeUs_t executeTimeUs; timeUs_t executeTimeUs;
osdState_e osdCurState = osdState; osdState_e osdCurrentState = osdState;
if (osdState != OSD_STATE_UPDATE_CANVAS) { if (osdState != OSD_STATE_UPDATE_CANVAS) {
schedulerIgnoreTaskExecRate(); schedulerIgnoreTaskExecRate();
@ -1213,7 +1217,7 @@ void osdUpdate(timeUs_t currentTimeUs)
case OSD_STATE_UPDATE_HEARTBEAT: case OSD_STATE_UPDATE_HEARTBEAT:
if (displayHeartbeat(osdDisplayPort)) { if (displayHeartbeat(osdDisplayPort)) {
// Extraordinary action was taken, so return without allowing osdStateDurationUs table to be updated // Extraordinary action was taken, so return without allowing osdStateDurationFractionUs table to be updated
return; return;
} }
@ -1300,30 +1304,36 @@ void osdUpdate(timeUs_t currentTimeUs)
osdSyncBlink(); osdSyncBlink();
osdState = OSD_STATE_GROUP_ELEMENTS;
break;
case OSD_STATE_GROUP_ELEMENTS:
{
uint8_t elementGroup; uint8_t elementGroup;
uint8_t activeElements = osdGetActiveElementCount(); uint8_t activeElements = osdGetActiveElementCount();
// Reset groupings // Reset groupings
for (elementGroup = 0; elementGroup < OSD_GROUP_COUNT; elementGroup++) { for (elementGroup = 0; elementGroup < OSD_GROUP_COUNT; elementGroup++) {
if (osdElementGroupDurationUs[elementGroup] > (osdElementGroupTargetUs[elementGroup] + OSD_ELEMENT_RENDER_GROUP_MARGIN)) { if (osdElementGroupDurationFractionUs[elementGroup] > (OSD_ELEMENT_RENDER_TARGET << OSD_EXEC_TIME_SHIFT)) {
osdElementGroupDurationUs[elementGroup] = 0; osdElementGroupDurationFractionUs[elementGroup] = 0;
} }
osdElementGroupTargetUs[elementGroup] = 0; osdElementGroupTargetFractionUs[elementGroup] = 0;
} }
elementGroup = 0; elementGroup = 0;
// Based on the current element rendering, group to execute in approx 40us // Based on the current element rendering, group to execute in approx 40us
for (uint8_t curElement = 0; curElement < activeElements; curElement++) { for (uint8_t curElement = 0; curElement < activeElements; curElement++) {
if ((osdElementGroupTargetUs[elementGroup] == 0) || if ((osdElementGroupTargetFractionUs[elementGroup] == 0) ||
((osdElementGroupTargetUs[elementGroup] + osdElementDurationUs[curElement]) <= OSD_ELEMENT_RENDER_TARGET) || (osdElementGroupTargetFractionUs[elementGroup] + (osdElementDurationUs[curElement]) <= (OSD_ELEMENT_RENDER_TARGET << OSD_EXEC_TIME_SHIFT)) ||
(elementGroup == (OSD_GROUP_COUNT - 1))) { (elementGroup == (OSD_GROUP_COUNT - 1))) {
osdElementGroupTargetUs[elementGroup] += osdElementDurationUs[curElement]; osdElementGroupTargetFractionUs[elementGroup] += osdElementDurationUs[curElement];
// If group membership changes, reset the stats for the group // If group membership changes, reset the stats for the group
if (osdElementGroupMembership[curElement] != elementGroup) { if (osdElementGroupMemberships[curElement] != elementGroup) {
osdElementGroupDurationUs[elementGroup] = 0; osdElementGroupDurationFractionUs[elementGroup] = osdElementGroupTargetFractionUs[elementGroup] + (OSD_ELEMENT_RENDER_GROUP_MARGIN << OSD_EXEC_TIME_SHIFT);
} }
osdElementGroupMembership[curElement] = elementGroup; osdElementGroupMemberships[curElement] = elementGroup;
} else { } else {
elementGroup++; elementGroup++;
// Try again for this element // Try again for this element
@ -1339,19 +1349,20 @@ void osdUpdate(timeUs_t currentTimeUs)
} else { } else {
osdState = OSD_STATE_COMMIT; osdState = OSD_STATE_COMMIT;
} }
}
break; break;
case OSD_STATE_UPDATE_ELEMENTS: case OSD_STATE_UPDATE_ELEMENTS:
{ {
osdCurElementGroup = osdElementGroup; osdCurrentElementGroup = osdElementGroup;
bool moreElements = true; bool moreElements = true;
do { do {
timeUs_t startElementTime = micros(); timeUs_t startElementTime = micros();
uint8_t osdCurElement = osdGetActiveElement(); uint8_t osdCurrentElement = osdGetActiveElement();
// This element should be rendered in the next group // This element should be rendered in the next group
if (osdElementGroupMembership[osdCurElement] != osdElementGroup) { if (osdElementGroupMemberships[osdCurrentElement] != osdElementGroup) {
osdElementGroup++; osdElementGroup++;
break; break;
} }
@ -1360,8 +1371,11 @@ void osdUpdate(timeUs_t currentTimeUs)
executeTimeUs = micros() - startElementTime; executeTimeUs = micros() - startElementTime;
if (executeTimeUs > osdElementDurationUs[osdCurElement]) { if (executeTimeUs > (osdElementDurationUs[osdCurrentElement] >> OSD_EXEC_TIME_SHIFT)) {
osdElementDurationUs[osdCurElement] = executeTimeUs; osdElementDurationUs[osdCurrentElement] = executeTimeUs << OSD_EXEC_TIME_SHIFT;
} else if (osdElementDurationUs[osdCurrentElement] > 0) {
// Slowly decay the max time
osdElementDurationUs[osdCurrentElement]--;
} }
} while (moreElements); } while (moreElements);
@ -1399,6 +1413,7 @@ void osdUpdate(timeUs_t currentTimeUs)
firstPass = false; firstPass = false;
osdState = OSD_STATE_IDLE; osdState = OSD_STATE_IDLE;
break; break;
case OSD_STATE_IDLE: case OSD_STATE_IDLE:
@ -1414,27 +1429,32 @@ void osdUpdate(timeUs_t currentTimeUs)
// On the first pass no element groups will have been formed, so all elements will have been // On the first pass no element groups will have been formed, so all elements will have been
// rendered which is unrepresentative, so ignore // rendered which is unrepresentative, so ignore
if (!firstPass) { if (!firstPass) {
if (osdCurState == OSD_STATE_UPDATE_ELEMENTS) { if (osdCurrentState == OSD_STATE_UPDATE_ELEMENTS) {
if (executeTimeUs > osdElementGroupDurationUs[osdCurElementGroup]) { if (executeTimeUs > (osdElementGroupDurationFractionUs[osdCurrentElementGroup] >> OSD_EXEC_TIME_SHIFT)) {
osdElementGroupDurationUs[osdCurElementGroup] = executeTimeUs; osdElementGroupDurationFractionUs[osdCurrentElementGroup] = executeTimeUs << OSD_EXEC_TIME_SHIFT;
} else if (osdElementGroupDurationFractionUs[osdCurrentElementGroup] > 0) {
// Slowly decay the max time
osdElementGroupDurationFractionUs[osdCurrentElementGroup]--;
} }
} }
if (executeTimeUs > osdStateDurationUs[osdCurState]) { if (executeTimeUs > (osdStateDurationFractionUs[osdCurrentState] >> OSD_EXEC_TIME_SHIFT)) {
osdStateDurationUs[osdCurState] = executeTimeUs; osdStateDurationFractionUs[osdCurrentState] = executeTimeUs << OSD_EXEC_TIME_SHIFT;
} else if (osdStateDurationFractionUs[osdCurrentState] > 0) {
// Slowly decay the max time
osdStateDurationFractionUs[osdCurrentState]--;
} }
} }
} }
if (osdState == OSD_STATE_UPDATE_ELEMENTS) { if (osdState == OSD_STATE_UPDATE_ELEMENTS) {
schedulerSetNextStateTime(osdElementGroupDurationUs[osdElementGroup] + OSD_ELEMENT_RENDER_MARGIN); schedulerSetNextStateTime((osdElementGroupDurationFractionUs[osdElementGroup] >> OSD_EXEC_TIME_SHIFT) + OSD_ELEMENT_RENDER_GROUP_MARGIN);
} else { } else {
if (osdState == OSD_STATE_IDLE) { if (osdState == OSD_STATE_IDLE) {
schedulerSetNextStateTime(osdStateDurationUs[OSD_STATE_CHECK] + OSD_MARGIN); schedulerSetNextStateTime((osdStateDurationFractionUs[OSD_STATE_CHECK] >> OSD_EXEC_TIME_SHIFT) + OSD_TASK_MARGIN);
} else { } else {
schedulerSetNextStateTime(osdStateDurationUs[osdState] + OSD_MARGIN); schedulerSetNextStateTime((osdStateDurationFractionUs[osdState] >> OSD_EXEC_TIME_SHIFT) + OSD_TASK_MARGIN);
} }
schedulerIgnoreTaskExecTime();
} }
} }

View file

@ -709,7 +709,7 @@ TEST_F(OsdTest, TestAlarms)
// elements showing values in alarm range should flash // elements showing values in alarm range should flash
simulationTime += 1000000; simulationTime += 1000000;
simulationTime -= simulationTime % 1000000; simulationTime -= simulationTime % 1000000;
timeUs_t startTime = simulationTime + 0.25e6; timeUs_t startTime = simulationTime;
for (int i = 0; i < 15; i++) { for (int i = 0; i < 15; i++) {
// Blinking should happen at 2Hz // Blinking should happen at 2Hz
simulationTime = startTime + i*0.25e6; simulationTime = startTime + i*0.25e6;
@ -1083,6 +1083,7 @@ TEST_F(OsdTest, TestElementWarningsBattery)
// Delay as the warnings are flashing // Delay as the warnings are flashing
simulationTime += 1000000; simulationTime += 1000000;
simulationTime -= simulationTime % 1000000; simulationTime -= simulationTime % 1000000;
simulationTime += 0.25e6;
osdRefresh(); osdRefresh();
// then // then
@ -1098,6 +1099,7 @@ TEST_F(OsdTest, TestElementWarningsBattery)
// Delay as the warnings are flashing // Delay as the warnings are flashing
simulationTime += 1000000; simulationTime += 1000000;
simulationTime -= simulationTime % 1000000; simulationTime -= simulationTime % 1000000;
simulationTime += 0.25e6;
osdRefresh(); osdRefresh();
// then // then
@ -1204,7 +1206,7 @@ TEST_F(OsdTest, TestGpsElements)
// Sat indicator should blink and show "NC" // Sat indicator should blink and show "NC"
simulationTime += 1000000; simulationTime += 1000000;
simulationTime -= simulationTime % 1000000; simulationTime -= simulationTime % 1000000;
timeUs_t startTime = simulationTime + 0.25e6; timeUs_t startTime = simulationTime;
for (int i = 0; i < 15; i++) { for (int i = 0; i < 15; i++) {
// Blinking should happen at 2Hz // Blinking should happen at 2Hz
simulationTime = startTime + i*0.25e6; simulationTime = startTime + i*0.25e6;
@ -1228,7 +1230,7 @@ TEST_F(OsdTest, TestGpsElements)
// Sat indicator should blink and show "0" // Sat indicator should blink and show "0"
simulationTime += 1000000; simulationTime += 1000000;
simulationTime -= simulationTime % 1000000; simulationTime -= simulationTime % 1000000;
startTime = simulationTime + 0.25e6; startTime = simulationTime;
for (int i = 0; i < 15; i++) { for (int i = 0; i < 15; i++) {
// Blinking should happen at 2Hz // Blinking should happen at 2Hz
simulationTime = startTime + i*0.25e6; simulationTime = startTime + i*0.25e6;