1
0
Fork 0
mirror of https://github.com/betaflight/betaflight.git synced 2025-07-26 01:35:41 +03:00

Add OSD_STATE_GROUP_ELEMENTS state to osdUpdate() and optimise DMA vs polled MAX7456 SPI access

This commit is contained in:
Steve Evans 2022-01-26 20:24:12 +00:00
parent 28e596a500
commit 2e8d026b4e
5 changed files with 68 additions and 45 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

@ -198,6 +198,7 @@ const osd_stats_e osdStatsDisplayOrder[OSD_STAT_COUNT] = {
#else #else
#define OSD_ELEMENT_RENDER_GROUP_MARGIN 2 #define OSD_ELEMENT_RENDER_GROUP_MARGIN 2
#endif #endif
#define OSD_TASK_MARGIN 1
// Decay the estimated max task duration by 1/(1 << OSD_EXEC_TIME_SHIFT) on every invocation // Decay the estimated max task duration by 1/(1 << OSD_EXEC_TIME_SHIFT) on every invocation
#define OSD_EXEC_TIME_SHIFT 8 #define OSD_EXEC_TIME_SHIFT 8
@ -1130,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,
@ -1164,14 +1166,13 @@ bool osdUpdateCheck(timeUs_t currentTimeUs, timeDelta_t currentDeltaTimeUs)
return (osdState != OSD_STATE_IDLE); return (osdState != OSD_STATE_IDLE);
} }
// 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)
{ {
// Times are known to be short so use uint16_t rather than timeUs_t // Times are known to be short so use uint16_t rather than timeUs_t
static uint16_t osdStateDurationFracUs[OSD_STATE_COUNT] = { 0 }; static uint16_t osdStateDurationFracUs[OSD_STATE_COUNT] = { 0 };
static uint16_t osdElementDurationUs[OSD_ITEM_COUNT] = { 0 }; static uint32_t osdElementDurationUs[OSD_ITEM_COUNT] = { 0 };
static uint16_t osdElementGroupMembership[OSD_ITEM_COUNT]; static uint8_t osdElementGroupMembership[OSD_ITEM_COUNT];
static uint16_t osdElementGroupTargetFracUs[OSD_GROUP_COUNT] = { 0 }; static uint16_t osdElementGroupTargetFracUs[OSD_GROUP_COUNT] = { 0 };
static uint16_t osdElementGroupDurationFracUs[OSD_GROUP_COUNT] = { 0 }; static uint16_t osdElementGroupDurationFracUs[OSD_GROUP_COUNT] = { 0 };
static uint8_t osdElementGroup; static uint8_t osdElementGroup;
@ -1304,44 +1305,51 @@ void osdUpdate(timeUs_t currentTimeUs)
osdSyncBlink(); osdSyncBlink();
uint8_t elementGroup; osdState = OSD_STATE_GROUP_ELEMENTS;
uint8_t activeElements = osdGetActiveElementCount();
// Reset groupings break;
for (elementGroup = 0; elementGroup < OSD_GROUP_COUNT; elementGroup++) {
if (osdElementGroupDurationFracUs[elementGroup] > (OSD_ELEMENT_RENDER_TARGET << OSD_EXEC_TIME_SHIFT)) {
osdElementGroupDurationFracUs[elementGroup] = 0;
}
osdElementGroupTargetFracUs[elementGroup] = 0;
}
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 // Reset groupings
for (uint8_t curElement = 0; curElement < activeElements; curElement++) { for (elementGroup = 0; elementGroup < OSD_GROUP_COUNT; elementGroup++) {
if ((osdElementGroupTargetFracUs[elementGroup] == 0) || if (osdElementGroupDurationFracUs[elementGroup] > (OSD_ELEMENT_RENDER_TARGET << OSD_EXEC_TIME_SHIFT)) {
(osdElementGroupTargetFracUs[elementGroup] + (osdElementDurationUs[curElement]) <= (OSD_ELEMENT_RENDER_TARGET << OSD_EXEC_TIME_SHIFT)) || osdElementGroupDurationFracUs[elementGroup] = 0;
(elementGroup == (OSD_GROUP_COUNT - 1))) {
osdElementGroupTargetFracUs[elementGroup] += osdElementDurationUs[curElement];
// If group membership changes, reset the stats for the group
if (osdElementGroupMembership[curElement] != elementGroup) {
osdElementGroupDurationFracUs[elementGroup] = osdElementGroupTargetFracUs[elementGroup];
} }
osdElementGroupMembership[curElement] = elementGroup; osdElementGroupTargetFracUs[elementGroup] = 0;
} else {
elementGroup++;
// Try again for this element
curElement--;
} }
}
// Start with group 0 elementGroup = 0;
osdElementGroup = 0;
if (activeElements > 0) { // Based on the current element rendering, group to execute in approx 40us
osdState = OSD_STATE_UPDATE_ELEMENTS; for (uint8_t curElement = 0; curElement < activeElements; curElement++) {
} else { if ((osdElementGroupTargetFracUs[elementGroup] == 0) ||
osdState = OSD_STATE_COMMIT; (osdElementGroupTargetFracUs[elementGroup] + (osdElementDurationUs[curElement]) <= (OSD_ELEMENT_RENDER_TARGET << OSD_EXEC_TIME_SHIFT)) ||
(elementGroup == (OSD_GROUP_COUNT - 1))) {
osdElementGroupTargetFracUs[elementGroup] += osdElementDurationUs[curElement];
// If group membership changes, reset the stats for the group
if (osdElementGroupMembership[curElement] != elementGroup) {
osdElementGroupDurationFracUs[elementGroup] = osdElementGroupTargetFracUs[elementGroup] + (OSD_ELEMENT_RENDER_GROUP_MARGIN << OSD_EXEC_TIME_SHIFT);
}
osdElementGroupMembership[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; break;
@ -1406,6 +1414,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:
@ -1424,11 +1433,17 @@ void osdUpdate(timeUs_t currentTimeUs)
if (osdCurState == OSD_STATE_UPDATE_ELEMENTS) { if (osdCurState == OSD_STATE_UPDATE_ELEMENTS) {
if (executeTimeUs > (osdElementGroupDurationFracUs[osdCurElementGroup] >> OSD_EXEC_TIME_SHIFT)) { if (executeTimeUs > (osdElementGroupDurationFracUs[osdCurElementGroup] >> OSD_EXEC_TIME_SHIFT)) {
osdElementGroupDurationFracUs[osdCurElementGroup] = executeTimeUs << OSD_EXEC_TIME_SHIFT; osdElementGroupDurationFracUs[osdCurElementGroup] = executeTimeUs << OSD_EXEC_TIME_SHIFT;
} else if (osdElementGroupDurationFracUs[osdCurElementGroup] > 0) {
// Slowly decay the max time
osdElementGroupDurationFracUs[osdCurElementGroup]--;
} }
} }
if (executeTimeUs > (osdStateDurationFracUs[osdCurState] >> OSD_EXEC_TIME_SHIFT)) { if (executeTimeUs > (osdStateDurationFracUs[osdCurState] >> OSD_EXEC_TIME_SHIFT)) {
osdStateDurationFracUs[osdCurState] = executeTimeUs << OSD_EXEC_TIME_SHIFT; osdStateDurationFracUs[osdCurState] = executeTimeUs << OSD_EXEC_TIME_SHIFT;
} else if (osdStateDurationFracUs[osdCurState] > 0) {
// Slowly decay the max time
osdStateDurationFracUs[osdCurState]--;
} }
} }
} }
@ -1437,9 +1452,9 @@ void osdUpdate(timeUs_t currentTimeUs)
schedulerSetNextStateTime((osdElementGroupDurationFracUs[osdElementGroup] >> OSD_EXEC_TIME_SHIFT) + OSD_ELEMENT_RENDER_GROUP_MARGIN); schedulerSetNextStateTime((osdElementGroupDurationFracUs[osdElementGroup] >> OSD_EXEC_TIME_SHIFT) + OSD_ELEMENT_RENDER_GROUP_MARGIN);
} else { } else {
if (osdState == OSD_STATE_IDLE) { if (osdState == OSD_STATE_IDLE) {
schedulerSetNextStateTime(osdStateDurationFracUs[OSD_STATE_CHECK] >> OSD_EXEC_TIME_SHIFT); schedulerSetNextStateTime((osdStateDurationFracUs[OSD_STATE_CHECK] >> OSD_EXEC_TIME_SHIFT) + OSD_TASK_MARGIN);
} else { } else {
schedulerSetNextStateTime(osdStateDurationFracUs[osdState] >> OSD_EXEC_TIME_SHIFT); schedulerSetNextStateTime((osdStateDurationFracUs[osdState] >> OSD_EXEC_TIME_SHIFT) + OSD_TASK_MARGIN);
} }
} }
} }

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;