diff --git a/src/main/drivers/bus_spi_ll.c b/src/main/drivers/bus_spi_ll.c index a37ee3fad5..aaa6e1a289 100644 --- a/src/main/drivers/bus_spi_ll.c +++ b/src/main/drivers/bus_spi_ll.c @@ -635,7 +635,7 @@ void spiSequenceStart(const extDevice_t *dev) } // 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 spiInternalInitStream(dev, false); diff --git a/src/main/drivers/bus_spi_stdperiph.c b/src/main/drivers/bus_spi_stdperiph.c index 56572fef05..603b8315e3 100644 --- a/src/main/drivers/bus_spi_stdperiph.c +++ b/src/main/drivers/bus_spi_stdperiph.c @@ -367,7 +367,7 @@ void spiSequenceStart(const extDevice_t *dev) xferLen += checkSegment->len; } // 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 spiInternalInitStream(dev, false); diff --git a/src/main/drivers/max7456.c b/src/main/drivers/max7456.c index fd20325b61..1788bf193c 100644 --- a/src/main/drivers/max7456.c +++ b/src/main/drivers/max7456.c @@ -206,7 +206,9 @@ static uint8_t shadowBuffer[VIDEO_BUFFER_CHARS_PAL]; //Max bytes to update in one call to max7456DrawScreen() #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]; @@ -622,11 +624,13 @@ bool max7456DrawScreen(void) uint8_t *buffer = getActiveLayerBuffer(); int spiBufIndex = 0; int maxSpiBufStartIndex; + timeDelta_t maxEncodeTime; bool setAddress = true; bool autoInc = false; int posLimit = pos + (maxScreenSize / 2); 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 if (spiIsBusy(dev)) { @@ -634,11 +638,13 @@ bool max7456DrawScreen(void) return true; } - // Allow for 8 bytes followed by an ESCAPE and reset of DMM at end of buffer - maxSpiBufStartIndex -= 12; + timeUs_t startTime = micros(); + + // 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 - while ((spiBufIndex < maxSpiBufStartIndex) && (pos < posLimit)) { + while ((spiBufIndex < maxSpiBufStartIndex) && (pos < posLimit) && (cmpTimeUs(micros(), startTime) < maxEncodeTime)) { if (buffer[pos] != shadowBuffer[pos]) { if (buffer[pos] == 0xff) { buffer[pos] = ' '; diff --git a/src/main/fc/tasks.c b/src/main/fc/tasks.c index ac0434883f..f3bcd62ce5 100644 --- a/src/main/fc/tasks.c +++ b/src/main/fc/tasks.c @@ -182,7 +182,7 @@ bool taskUpdateRxMainInProgress() static void taskUpdateRxMain(timeUs_t currentTimeUs) { - static timeDelta_t rxStateDurationFracUs[RX_STATE_COUNT]; + static timeDelta_t rxStateDurationFractionUs[RX_STATE_COUNT]; timeDelta_t executeTimeUs; rxState_e oldRxState = rxState; 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 anticipatedExecutionTime = schedulerGetNextStateTime(); - if (anticipatedExecutionTime != (rxStateDurationFracUs[oldRxState] >> RX_TASK_DECAY_SHIFT)) { - rxStateDurationFracUs[oldRxState] = anticipatedExecutionTime << RX_TASK_DECAY_SHIFT; + if (anticipatedExecutionTime != (rxStateDurationFractionUs[oldRxState] >> RX_TASK_DECAY_SHIFT)) { + rxStateDurationFractionUs[oldRxState] = anticipatedExecutionTime << RX_TASK_DECAY_SHIFT; } - if (executeTimeUs > (rxStateDurationFracUs[oldRxState] >> RX_TASK_DECAY_SHIFT)) { - rxStateDurationFracUs[oldRxState] = executeTimeUs << RX_TASK_DECAY_SHIFT; + if (executeTimeUs > (rxStateDurationFractionUs[oldRxState] >> RX_TASK_DECAY_SHIFT)) { + rxStateDurationFractionUs[oldRxState] = executeTimeUs << RX_TASK_DECAY_SHIFT; } else { // Slowly decay the max time - rxStateDurationFracUs[oldRxState]--; + rxStateDurationFractionUs[oldRxState]--; } } 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); } diff --git a/src/main/io/gps.c b/src/main/io/gps.c index f3756f9418..b5a9871c93 100644 --- a/src/main/io/gps.c +++ b/src/main/io/gps.c @@ -766,7 +766,7 @@ void gpsUpdate(timeUs_t currentTimeUs) { static gpsState_e gpsStateDurationUs[GPS_STATE_COUNT]; timeUs_t executeTimeUs; - gpsState_e gpsCurState = gpsData.state; + gpsState_e gpsCurrentState = gpsData.state; // read out available GPS bytes if (gpsPort) { @@ -861,8 +861,8 @@ void gpsUpdate(timeUs_t currentTimeUs) executeTimeUs = micros() - currentTimeUs; - if (executeTimeUs > gpsStateDurationUs[gpsCurState]) { - gpsStateDurationUs[gpsCurState] = executeTimeUs; + if (executeTimeUs > gpsStateDurationUs[gpsCurrentState]) { + gpsStateDurationUs[gpsCurrentState] = executeTimeUs; } schedulerSetNextStateTime(gpsStateDurationUs[gpsData.state]); diff --git a/src/main/osd/osd.c b/src/main/osd/osd.c index 78d1b255af..a385dc525e 100644 --- a/src/main/osd/osd.c +++ b/src/main/osd/osd.c @@ -188,16 +188,19 @@ const osd_stats_e osdStatsDisplayOrder[OSD_STAT_COUNT] = { }; // 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 -#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 // This will most likely be violated by a USB interrupt whilst using the CLI -#define OSD_ELEMENT_RENDER_GROUP_MARGIN 5 -// Safe margin when rendering elements -#define OSD_ELEMENT_RENDER_MARGIN 5 -// Safe margin in other states -#define OSD_MARGIN 2 +#if defined(STM32F411xE) +#define OSD_ELEMENT_RENDER_GROUP_MARGIN 7 +#else +#define OSD_ELEMENT_RENDER_GROUP_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. // 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_UPDATE_ALARMS, OSD_STATE_UPDATE_CANVAS, + OSD_STATE_GROUP_ELEMENTS, OSD_STATE_UPDATE_ELEMENTS, OSD_STATE_UPDATE_HEARTBEAT, 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 void osdUpdate(timeUs_t currentTimeUs) { - static timeUs_t osdStateDurationUs[OSD_STATE_COUNT] = { 0 }; - static timeUs_t osdElementDurationUs[OSD_ITEM_COUNT] = { 0 }; - static timeUs_t osdElementGroupMembership[OSD_ITEM_COUNT]; - static timeUs_t osdElementGroupTargetUs[OSD_GROUP_COUNT] = { 0 }; - static timeUs_t osdElementGroupDurationUs[OSD_GROUP_COUNT] = { 0 }; + static uint16_t osdStateDurationFractionUs[OSD_STATE_COUNT] = { 0 }; + static uint32_t osdElementDurationUs[OSD_ITEM_COUNT] = { 0 }; + static uint8_t osdElementGroupMemberships[OSD_ITEM_COUNT]; + static uint16_t osdElementGroupTargetFractionUs[OSD_GROUP_COUNT] = { 0 }; + static uint16_t osdElementGroupDurationFractionUs[OSD_GROUP_COUNT] = { 0 }; static uint8_t osdElementGroup; static bool firstPass = true; - uint8_t osdCurElementGroup = 0; + uint8_t osdCurrentElementGroup = 0; timeUs_t executeTimeUs; - osdState_e osdCurState = osdState; + osdState_e osdCurrentState = osdState; if (osdState != OSD_STATE_UPDATE_CANVAS) { schedulerIgnoreTaskExecRate(); @@ -1213,7 +1217,7 @@ void osdUpdate(timeUs_t currentTimeUs) case OSD_STATE_UPDATE_HEARTBEAT: 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; } @@ -1300,58 +1304,65 @@ void osdUpdate(timeUs_t currentTimeUs) osdSyncBlink(); - uint8_t elementGroup; - uint8_t activeElements = osdGetActiveElementCount(); + osdState = OSD_STATE_GROUP_ELEMENTS; - // Reset groupings - for (elementGroup = 0; elementGroup < OSD_GROUP_COUNT; elementGroup++) { - if (osdElementGroupDurationUs[elementGroup] > (osdElementGroupTargetUs[elementGroup] + OSD_ELEMENT_RENDER_GROUP_MARGIN)) { - osdElementGroupDurationUs[elementGroup] = 0; - } - osdElementGroupTargetUs[elementGroup] = 0; - } + break; - elementGroup = 0; + case OSD_STATE_GROUP_ELEMENTS: + { + uint8_t elementGroup; + uint8_t activeElements = osdGetActiveElementCount(); - // Based on the current element rendering, group to execute in approx 40us - for (uint8_t curElement = 0; curElement < activeElements; curElement++) { - if ((osdElementGroupTargetUs[elementGroup] == 0) || - ((osdElementGroupTargetUs[elementGroup] + osdElementDurationUs[curElement]) <= OSD_ELEMENT_RENDER_TARGET) || - (elementGroup == (OSD_GROUP_COUNT - 1))) { - osdElementGroupTargetUs[elementGroup] += osdElementDurationUs[curElement]; - // If group membership changes, reset the stats for the group - if (osdElementGroupMembership[curElement] != elementGroup) { - osdElementGroupDurationUs[elementGroup] = 0; + // Reset groupings + for (elementGroup = 0; elementGroup < OSD_GROUP_COUNT; elementGroup++) { + if (osdElementGroupDurationFractionUs[elementGroup] > (OSD_ELEMENT_RENDER_TARGET << OSD_EXEC_TIME_SHIFT)) { + osdElementGroupDurationFractionUs[elementGroup] = 0; } - osdElementGroupMembership[curElement] = elementGroup; - } else { - elementGroup++; - // Try again for this element - curElement--; + osdElementGroupTargetFractionUs[elementGroup] = 0; } - } - // Start with group 0 - osdElementGroup = 0; + elementGroup = 0; - if (activeElements > 0) { - osdState = OSD_STATE_UPDATE_ELEMENTS; - } else { - osdState = OSD_STATE_COMMIT; + // Based on the current element rendering, group to execute in approx 40us + for (uint8_t curElement = 0; curElement < activeElements; curElement++) { + if ((osdElementGroupTargetFractionUs[elementGroup] == 0) || + (osdElementGroupTargetFractionUs[elementGroup] + (osdElementDurationUs[curElement]) <= (OSD_ELEMENT_RENDER_TARGET << OSD_EXEC_TIME_SHIFT)) || + (elementGroup == (OSD_GROUP_COUNT - 1))) { + osdElementGroupTargetFractionUs[elementGroup] += osdElementDurationUs[curElement]; + // If group membership changes, reset the stats for the group + if (osdElementGroupMemberships[curElement] != elementGroup) { + osdElementGroupDurationFractionUs[elementGroup] = osdElementGroupTargetFractionUs[elementGroup] + (OSD_ELEMENT_RENDER_GROUP_MARGIN << OSD_EXEC_TIME_SHIFT); + } + osdElementGroupMemberships[curElement] = elementGroup; + } else { + elementGroup++; + // Try again for this element + curElement--; + } + } + + // Start with group 0 + osdElementGroup = 0; + + if (activeElements > 0) { + osdState = OSD_STATE_UPDATE_ELEMENTS; + } else { + osdState = OSD_STATE_COMMIT; + } } break; case OSD_STATE_UPDATE_ELEMENTS: { - osdCurElementGroup = osdElementGroup; + osdCurrentElementGroup = osdElementGroup; bool moreElements = true; do { timeUs_t startElementTime = micros(); - uint8_t osdCurElement = osdGetActiveElement(); + uint8_t osdCurrentElement = osdGetActiveElement(); // This element should be rendered in the next group - if (osdElementGroupMembership[osdCurElement] != osdElementGroup) { + if (osdElementGroupMemberships[osdCurrentElement] != osdElementGroup) { osdElementGroup++; break; } @@ -1360,8 +1371,11 @@ void osdUpdate(timeUs_t currentTimeUs) executeTimeUs = micros() - startElementTime; - if (executeTimeUs > osdElementDurationUs[osdCurElement]) { - osdElementDurationUs[osdCurElement] = executeTimeUs; + if (executeTimeUs > (osdElementDurationUs[osdCurrentElement] >> OSD_EXEC_TIME_SHIFT)) { + osdElementDurationUs[osdCurrentElement] = executeTimeUs << OSD_EXEC_TIME_SHIFT; + } else if (osdElementDurationUs[osdCurrentElement] > 0) { + // Slowly decay the max time + osdElementDurationUs[osdCurrentElement]--; } } while (moreElements); @@ -1399,6 +1413,7 @@ void osdUpdate(timeUs_t currentTimeUs) firstPass = false; osdState = OSD_STATE_IDLE; + break; 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 // rendered which is unrepresentative, so ignore if (!firstPass) { - if (osdCurState == OSD_STATE_UPDATE_ELEMENTS) { - if (executeTimeUs > osdElementGroupDurationUs[osdCurElementGroup]) { - osdElementGroupDurationUs[osdCurElementGroup] = executeTimeUs; + if (osdCurrentState == OSD_STATE_UPDATE_ELEMENTS) { + if (executeTimeUs > (osdElementGroupDurationFractionUs[osdCurrentElementGroup] >> OSD_EXEC_TIME_SHIFT)) { + osdElementGroupDurationFractionUs[osdCurrentElementGroup] = executeTimeUs << OSD_EXEC_TIME_SHIFT; + } else if (osdElementGroupDurationFractionUs[osdCurrentElementGroup] > 0) { + // Slowly decay the max time + osdElementGroupDurationFractionUs[osdCurrentElementGroup]--; } } - if (executeTimeUs > osdStateDurationUs[osdCurState]) { - osdStateDurationUs[osdCurState] = executeTimeUs; + if (executeTimeUs > (osdStateDurationFractionUs[osdCurrentState] >> OSD_EXEC_TIME_SHIFT)) { + 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) { - schedulerSetNextStateTime(osdElementGroupDurationUs[osdElementGroup] + OSD_ELEMENT_RENDER_MARGIN); + schedulerSetNextStateTime((osdElementGroupDurationFractionUs[osdElementGroup] >> OSD_EXEC_TIME_SHIFT) + OSD_ELEMENT_RENDER_GROUP_MARGIN); } else { 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 { - schedulerSetNextStateTime(osdStateDurationUs[osdState] + OSD_MARGIN); + schedulerSetNextStateTime((osdStateDurationFractionUs[osdState] >> OSD_EXEC_TIME_SHIFT) + OSD_TASK_MARGIN); } - schedulerIgnoreTaskExecTime(); } } diff --git a/src/test/unit/osd_unittest.cc b/src/test/unit/osd_unittest.cc index 69067a5004..d772ff65d9 100644 --- a/src/test/unit/osd_unittest.cc +++ b/src/test/unit/osd_unittest.cc @@ -709,7 +709,7 @@ TEST_F(OsdTest, TestAlarms) // elements showing values in alarm range should flash simulationTime += 1000000; simulationTime -= simulationTime % 1000000; - timeUs_t startTime = simulationTime + 0.25e6; + timeUs_t startTime = simulationTime; for (int i = 0; i < 15; i++) { // Blinking should happen at 2Hz simulationTime = startTime + i*0.25e6; @@ -1083,6 +1083,7 @@ TEST_F(OsdTest, TestElementWarningsBattery) // Delay as the warnings are flashing simulationTime += 1000000; simulationTime -= simulationTime % 1000000; + simulationTime += 0.25e6; osdRefresh(); // then @@ -1098,6 +1099,7 @@ TEST_F(OsdTest, TestElementWarningsBattery) // Delay as the warnings are flashing simulationTime += 1000000; simulationTime -= simulationTime % 1000000; + simulationTime += 0.25e6; osdRefresh(); // then @@ -1204,7 +1206,7 @@ TEST_F(OsdTest, TestGpsElements) // Sat indicator should blink and show "NC" simulationTime += 1000000; simulationTime -= simulationTime % 1000000; - timeUs_t startTime = simulationTime + 0.25e6; + timeUs_t startTime = simulationTime; for (int i = 0; i < 15; i++) { // Blinking should happen at 2Hz simulationTime = startTime + i*0.25e6; @@ -1228,7 +1230,7 @@ TEST_F(OsdTest, TestGpsElements) // Sat indicator should blink and show "0" simulationTime += 1000000; simulationTime -= simulationTime % 1000000; - startTime = simulationTime + 0.25e6; + startTime = simulationTime; for (int i = 0; i < 15; i++) { // Blinking should happen at 2Hz simulationTime = startTime + i*0.25e6;