1
0
Fork 0
mirror of https://github.com/betaflight/betaflight.git synced 2025-07-13 03:20:00 +03:00

Interrupt/DMA driven SX1280 interaction for ELRS

This commit is contained in:
Steve Evans 2022-02-02 21:05:58 +00:00
parent 7b4415f062
commit fc8640154a
39 changed files with 1003 additions and 438 deletions

View file

@ -221,7 +221,7 @@ bool mpuAccReadSPI(accDev_t *acc)
busSegment_t segments[] = { busSegment_t segments[] = {
{.u.buffers = {NULL, NULL}, 7, true, NULL}, {.u.buffers = {NULL, NULL}, 7, true, NULL},
{.u.buffers = {NULL, NULL}, 0, true, NULL}, {.u.link = {NULL, NULL}, 0, true, NULL},
}; };
segments[0].u.buffers.txData = acc->gyro->dev.txBuf; segments[0].u.buffers.txData = acc->gyro->dev.txBuf;
segments[0].u.buffers.rxData = &acc->gyro->dev.rxBuf[1]; segments[0].u.buffers.rxData = &acc->gyro->dev.rxBuf[1];
@ -298,7 +298,7 @@ bool mpuGyroReadSPI(gyroDev_t *gyro)
busSegment_t segments[] = { busSegment_t segments[] = {
{.u.buffers = {NULL, NULL}, 7, true, NULL}, {.u.buffers = {NULL, NULL}, 7, true, NULL},
{.u.buffers = {NULL, NULL}, 0, true, NULL}, {.u.link = {NULL, NULL}, 0, true, NULL},
}; };
segments[0].u.buffers.txData = gyro->dev.txBuf; segments[0].u.buffers.txData = gyro->dev.txBuf;
segments[0].u.buffers.rxData = &gyro->dev.rxBuf[1]; segments[0].u.buffers.rxData = &gyro->dev.rxBuf[1];

View file

@ -320,7 +320,7 @@ static bool bmi270AccRead(accDev_t *acc)
busSegment_t segments[] = { busSegment_t segments[] = {
{.u.buffers = {NULL, NULL}, 8, true, NULL}, {.u.buffers = {NULL, NULL}, 8, true, NULL},
{.u.buffers = {NULL, NULL}, 0, true, NULL}, {.u.link = {NULL, NULL}, 0, true, NULL},
}; };
segments[0].u.buffers.txData = acc->gyro->dev.txBuf; segments[0].u.buffers.txData = acc->gyro->dev.txBuf;
segments[0].u.buffers.rxData = acc->gyro->dev.rxBuf; segments[0].u.buffers.rxData = acc->gyro->dev.rxBuf;
@ -398,7 +398,7 @@ static bool bmi270GyroReadRegister(gyroDev_t *gyro)
busSegment_t segments[] = { busSegment_t segments[] = {
{.u.buffers = {NULL, NULL}, 8, true, NULL}, {.u.buffers = {NULL, NULL}, 8, true, NULL},
{.u.buffers = {NULL, NULL}, 0, true, NULL}, {.u.link = {NULL, NULL}, 0, true, NULL},
}; };
segments[0].u.buffers.txData = gyro->dev.txBuf; segments[0].u.buffers.txData = gyro->dev.txBuf;
segments[0].u.buffers.rxData = gyro->dev.rxBuf; segments[0].u.buffers.rxData = gyro->dev.rxBuf;

View file

@ -73,7 +73,7 @@ typedef struct busDevice_s {
DMA_InitTypeDef *initRx; DMA_InitTypeDef *initRx;
#endif #endif
#endif // UNIT_TEST #endif // UNIT_TEST
struct busSegment_s* volatile curSegment; volatile struct busSegment_s* volatile curSegment;
bool initSegment; bool initSegment;
} busDevice_t; } busDevice_t;
@ -112,7 +112,10 @@ typedef struct extDevice_s {
} extDevice_t; } extDevice_t;
/* Each SPI access may comprise multiple parts, for example, wait/write enable/write/data each of which /* Each SPI access may comprise multiple parts, for example, wait/write enable/write/data each of which
* is defined by a segment, with optional callback after each is completed * is defined by a segment, with optional callback after each is completed.
*
* If there are more than one segments, or a single segment with negateCS negated then DMA will be used irrespective of length
*
*/ */
typedef struct busSegment_s { typedef struct busSegment_s {
union { union {
@ -126,7 +129,7 @@ typedef struct busSegment_s {
// Link to the device associated with the next transfer // Link to the device associated with the next transfer
const extDevice_t *dev; const extDevice_t *dev;
// Segments to process in the next transfer. // Segments to process in the next transfer.
struct busSegment_s *segments; volatile struct busSegment_s *segments;
} link; } link;
} u; } u;
int len; int len;

View file

@ -172,7 +172,7 @@ void spiReadWriteBuf(const extDevice_t *dev, uint8_t *txData, uint8_t *rxData, i
// This routine blocks so no need to use static data // This routine blocks so no need to use static data
busSegment_t segments[] = { busSegment_t segments[] = {
{.u.buffers = {txData, rxData}, len, true, NULL}, {.u.buffers = {txData, rxData}, len, true, NULL},
{.u.buffers = {NULL, NULL}, 0, true, NULL}, {.u.link = {NULL, NULL}, 0, true, NULL},
}; };
spiSequence(dev, &segments[0]); spiSequence(dev, &segments[0]);
@ -201,7 +201,7 @@ uint8_t spiReadWrite(const extDevice_t *dev, uint8_t data)
// This routine blocks so no need to use static data // This routine blocks so no need to use static data
busSegment_t segments[] = { busSegment_t segments[] = {
{.u.buffers = {&data, &retval}, sizeof(data), true, NULL}, {.u.buffers = {&data, &retval}, sizeof(data), true, NULL},
{.u.buffers = {NULL, NULL}, 0, true, NULL}, {.u.link = {NULL, NULL}, 0, true, NULL},
}; };
spiSequence(dev, &segments[0]); spiSequence(dev, &segments[0]);
@ -220,7 +220,7 @@ uint8_t spiReadWriteReg(const extDevice_t *dev, uint8_t reg, uint8_t data)
busSegment_t segments[] = { busSegment_t segments[] = {
{.u.buffers = {&reg, NULL}, sizeof(reg), false, NULL}, {.u.buffers = {&reg, NULL}, sizeof(reg), false, NULL},
{.u.buffers = {&data, &retval}, sizeof(data), true, NULL}, {.u.buffers = {&data, &retval}, sizeof(data), true, NULL},
{.u.buffers = {NULL, NULL}, 0, true, NULL}, {.u.link = {NULL, NULL}, 0, true, NULL},
}; };
spiSequence(dev, &segments[0]); spiSequence(dev, &segments[0]);
@ -236,7 +236,7 @@ void spiWrite(const extDevice_t *dev, uint8_t data)
// This routine blocks so no need to use static data // This routine blocks so no need to use static data
busSegment_t segments[] = { busSegment_t segments[] = {
{.u.buffers = {&data, NULL}, sizeof(data), true, NULL}, {.u.buffers = {&data, NULL}, sizeof(data), true, NULL},
{.u.buffers = {NULL, NULL}, 0, true, NULL}, {.u.link = {NULL, NULL}, 0, true, NULL},
}; };
spiSequence(dev, &segments[0]); spiSequence(dev, &segments[0]);
@ -251,7 +251,7 @@ void spiWriteReg(const extDevice_t *dev, uint8_t reg, uint8_t data)
busSegment_t segments[] = { busSegment_t segments[] = {
{.u.buffers = {&reg, NULL}, sizeof(reg), false, NULL}, {.u.buffers = {&reg, NULL}, sizeof(reg), false, NULL},
{.u.buffers = {&data, NULL}, sizeof(data), true, NULL}, {.u.buffers = {&data, NULL}, sizeof(data), true, NULL},
{.u.buffers = {NULL, NULL}, 0, true, NULL}, {.u.link = {NULL, NULL}, 0, true, NULL},
}; };
spiSequence(dev, &segments[0]); spiSequence(dev, &segments[0]);
@ -279,7 +279,7 @@ void spiReadRegBuf(const extDevice_t *dev, uint8_t reg, uint8_t *data, uint8_t l
busSegment_t segments[] = { busSegment_t segments[] = {
{.u.buffers = {&reg, NULL}, sizeof(reg), false, NULL}, {.u.buffers = {&reg, NULL}, sizeof(reg), false, NULL},
{.u.buffers = {NULL, data}, length, true, NULL}, {.u.buffers = {NULL, data}, length, true, NULL},
{.u.buffers = {NULL, NULL}, 0, true, NULL}, {.u.link = {NULL, NULL}, 0, true, NULL},
}; };
spiSequence(dev, &segments[0]); spiSequence(dev, &segments[0]);
@ -313,7 +313,7 @@ void spiWriteRegBuf(const extDevice_t *dev, uint8_t reg, uint8_t *data, uint32_t
busSegment_t segments[] = { busSegment_t segments[] = {
{.u.buffers = {&reg, NULL}, sizeof(reg), false, NULL}, {.u.buffers = {&reg, NULL}, sizeof(reg), false, NULL},
{.u.buffers = {data, NULL}, length, true, NULL}, {.u.buffers = {data, NULL}, length, true, NULL},
{.u.buffers = {NULL, NULL}, 0, true, NULL}, {.u.link = {NULL, NULL}, 0, true, NULL},
}; };
spiSequence(dev, &segments[0]); spiSequence(dev, &segments[0]);
@ -329,7 +329,7 @@ uint8_t spiReadReg(const extDevice_t *dev, uint8_t reg)
busSegment_t segments[] = { busSegment_t segments[] = {
{.u.buffers = {&reg, NULL}, sizeof(reg), false, NULL}, {.u.buffers = {&reg, NULL}, sizeof(reg), false, NULL},
{.u.buffers = {NULL, &data}, sizeof(data), true, NULL}, {.u.buffers = {NULL, &data}, sizeof(data), true, NULL},
{.u.buffers = {NULL, NULL}, 0, true, NULL}, {.u.link = {NULL, NULL}, 0, true, NULL},
}; };
spiSequence(dev, &segments[0]); spiSequence(dev, &segments[0]);
@ -404,13 +404,19 @@ static void spiIrqHandler(const extDevice_t *dev)
} }
// Advance through the segment list // Advance through the segment list
nextSegment = bus->curSegment + 1; // OK to discard the volatile qualifier here
nextSegment = (busSegment_t *)bus->curSegment + 1;
if (nextSegment->len == 0) { if (nextSegment->len == 0) {
if (!bus->curSegment->negateCS) {
// Negate Chip Select if not done so already
IOHi(dev->busType_u.spi.csnPin);
}
// If a following transaction has been linked, start it // If a following transaction has been linked, start it
if (nextSegment->u.link.dev) { if (nextSegment->u.link.dev) {
const extDevice_t *nextDev = nextSegment->u.link.dev; const extDevice_t *nextDev = nextSegment->u.link.dev;
busSegment_t *nextSegments = nextSegment->u.link.segments; busSegment_t *nextSegments = (busSegment_t *)nextSegment->u.link.segments;
// The end of the segment list has been reached // The end of the segment list has been reached
bus->curSegment = nextSegments; bus->curSegment = nextSegments;
nextSegment->u.link.dev = NULL; nextSegment->u.link.dev = NULL;
@ -420,6 +426,9 @@ static void spiIrqHandler(const extDevice_t *dev)
bus->curSegment = (busSegment_t *)BUS_SPI_FREE; bus->curSegment = (busSegment_t *)BUS_SPI_FREE;
} }
} else { } else {
// Do as much processing as possible before asserting CS to avoid violating minimum high time
bool negateCS = bus->curSegment->negateCS;
bus->curSegment = nextSegment; bus->curSegment = nextSegment;
// After the completion of the first segment setup the init structure for the subsequent segment // After the completion of the first segment setup the init structure for the subsequent segment
@ -428,6 +437,11 @@ static void spiIrqHandler(const extDevice_t *dev)
bus->initSegment = false; bus->initSegment = false;
} }
if (negateCS) {
// Assert Chip Select - it's costly so only do so if necessary
IOLo(dev->busType_u.spi.csnPin);
}
// Launch the next transfer // Launch the next transfer
spiInternalStartDMA(dev); spiInternalStartDMA(dev);
@ -713,10 +727,11 @@ void spiSequence(const extDevice_t *dev, busSegment_t *segments)
// Defer this transfer to be triggered upon completion of the current transfer // Defer this transfer to be triggered upon completion of the current transfer
// Find the last segment of the current transfer // Find the last segment of the new transfer
for (endSegment = segments; endSegment->len; endSegment++); for (endSegment = segments; endSegment->len; endSegment++);
busSegment_t *endCmpSegment = bus->curSegment; // Safe to discard the volatile qualifier as we're in an atomic block
busSegment_t *endCmpSegment = (busSegment_t *)bus->curSegment;
if (endCmpSegment) { if (endCmpSegment) {
while (true) { while (true) {
@ -736,16 +751,16 @@ void spiSequence(const extDevice_t *dev, busSegment_t *segments)
break; break;
} else { } else {
// Follow the link to the next queued segment list // Follow the link to the next queued segment list
endCmpSegment = endCmpSegment->u.link.segments; endCmpSegment = (busSegment_t *)endCmpSegment->u.link.segments;
} }
} }
// Record the dev and segments parameters in the terminating segment entry
endCmpSegment->u.link.dev = dev;
endCmpSegment->u.link.segments = segments;
return;
} }
// Record the dev and segments parameters in the terminating segment entry
endCmpSegment->u.link.dev = dev;
endCmpSegment->u.link.segments = segments;
return;
} else { } else {
// Claim the bus with this list of segments // Claim the bus with this list of segments
bus->curSegment = segments; bus->curSegment = segments;

View file

@ -290,7 +290,7 @@ void spiInternalInitStream(const extDevice_t *dev, bool preInit)
STATIC_DMA_DATA_AUTO uint8_t dummyRxByte; STATIC_DMA_DATA_AUTO uint8_t dummyRxByte;
busDevice_t *bus = dev->bus; busDevice_t *bus = dev->bus;
busSegment_t *segment = bus->curSegment; busSegment_t *segment = (busSegment_t *)bus->curSegment;
if (preInit) { if (preInit) {
// Prepare the init structure for the next segment to reduce inter-segment interval // Prepare the init structure for the next segment to reduce inter-segment interval
@ -368,9 +368,6 @@ void spiInternalStartDMA(const extDevice_t *dev)
{ {
busDevice_t *bus = dev->bus; busDevice_t *bus = dev->bus;
// Assert Chip Select
IOLo(dev->busType_u.spi.csnPin);
dmaChannelDescriptor_t *dmaTx = bus->dmaTx; dmaChannelDescriptor_t *dmaTx = bus->dmaTx;
dmaChannelDescriptor_t *dmaRx = bus->dmaRx; dmaChannelDescriptor_t *dmaRx = bus->dmaRx;
@ -584,7 +581,7 @@ void spiSequenceStart(const extDevice_t *dev)
*/ */
// Check that any reads are cache aligned and of multiple cache lines in length // Check that any reads are cache aligned and of multiple cache lines in length
for (busSegment_t *checkSegment = bus->curSegment; checkSegment->len; checkSegment++) { for (busSegment_t *checkSegment = (busSegment_t *)bus->curSegment; checkSegment->len; checkSegment++) {
// Check there is no receive data as only transmit DMA is available // Check there is no receive data as only transmit DMA is available
if ((checkSegment->u.buffers.rxData) && (bus->dmaRx == (dmaChannelDescriptor_t *)NULL)) { if ((checkSegment->u.buffers.rxData) && (bus->dmaRx == (dmaChannelDescriptor_t *)NULL)) {
dmaSafe = false; dmaSafe = false;
@ -635,17 +632,25 @@ void spiSequenceStart(const extDevice_t *dev)
} }
// Use DMA if possible // Use DMA if possible
if (bus->useDMA && dmaSafe && ((segmentCount > 1) || (xferLen >= 8))) { // If there are more than one segments, or a single segment with negateCS negated then force DMA irrespective of length
if (bus->useDMA && dmaSafe && ((segmentCount > 1) || (xferLen >= 8) || !bus->curSegment->negateCS)) {
// Intialise the init structures for the first transfer // Intialise the init structures for the first transfer
spiInternalInitStream(dev, false); spiInternalInitStream(dev, false);
// Assert Chip Select
IOLo(dev->busType_u.spi.csnPin);
// Start the transfers // Start the transfers
spiInternalStartDMA(dev); spiInternalStartDMA(dev);
} else { } else {
busSegment_t *lastSegment = NULL;
// Manually work through the segment list performing a transfer for each // Manually work through the segment list performing a transfer for each
while (bus->curSegment->len) { while (bus->curSegment->len) {
// Assert Chip Select if (!lastSegment || lastSegment->negateCS) {
IOLo(dev->busType_u.spi.csnPin); // Assert Chip Select if necessary - it's costly so only do so if necessary
IOLo(dev->busType_u.spi.csnPin);
}
spiInternalReadWriteBufPolled( spiInternalReadWriteBufPolled(
bus->busType_u.spi.instance, bus->busType_u.spi.instance,
@ -675,14 +680,20 @@ void spiSequenceStart(const extDevice_t *dev)
break; break;
} }
} }
lastSegment = (busSegment_t *)bus->curSegment;
bus->curSegment++; bus->curSegment++;
} }
if (lastSegment && !lastSegment->negateCS) {
// Negate Chip Select if not done so already
IOHi(dev->busType_u.spi.csnPin);
}
// If a following transaction has been linked, start it // If a following transaction has been linked, start it
if (bus->curSegment->u.link.dev) { if (bus->curSegment->u.link.dev) {
const extDevice_t *nextDev = bus->curSegment->u.link.dev; const extDevice_t *nextDev = bus->curSegment->u.link.dev;
busSegment_t *nextSegments = bus->curSegment->u.link.segments; busSegment_t *nextSegments = (busSegment_t *)bus->curSegment->u.link.segments;
busSegment_t *endSegment = bus->curSegment; busSegment_t *endSegment = (busSegment_t *)bus->curSegment;
bus->curSegment = nextSegments; bus->curSegment = nextSegments;
endSegment->u.link.dev = NULL; endSegment->u.link.dev = NULL;
spiSequenceStart(nextDev); spiSequenceStart(nextDev);

View file

@ -215,9 +215,6 @@ void spiInternalInitStream(const extDevice_t *dev, bool preInit)
void spiInternalStartDMA(const extDevice_t *dev) void spiInternalStartDMA(const extDevice_t *dev)
{ {
// Assert Chip Select
IOLo(dev->busType_u.spi.csnPin);
dmaChannelDescriptor_t *dmaTx = dev->bus->dmaTx; dmaChannelDescriptor_t *dmaTx = dev->bus->dmaTx;
dmaChannelDescriptor_t *dmaRx = dev->bus->dmaRx; dmaChannelDescriptor_t *dmaRx = dev->bus->dmaRx;
DMA_Stream_TypeDef *streamRegsTx = (DMA_Stream_TypeDef *)dmaTx->ref; DMA_Stream_TypeDef *streamRegsTx = (DMA_Stream_TypeDef *)dmaTx->ref;
@ -355,7 +352,7 @@ void spiSequenceStart(const extDevice_t *dev)
SPI_Cmd(instance, ENABLE); SPI_Cmd(instance, ENABLE);
// Check that any there are no attempts to DMA to/from CCD SRAM // Check that any there are no attempts to DMA to/from CCD SRAM
for (busSegment_t *checkSegment = bus->curSegment; checkSegment->len; checkSegment++) { for (busSegment_t *checkSegment = (busSegment_t *)bus->curSegment; checkSegment->len; checkSegment++) {
// Check there is no receive data as only transmit DMA is available // Check there is no receive data as only transmit DMA is available
if (((checkSegment->u.buffers.rxData) && (IS_CCM(checkSegment->u.buffers.rxData) || (bus->dmaRx == (dmaChannelDescriptor_t *)NULL))) || if (((checkSegment->u.buffers.rxData) && (IS_CCM(checkSegment->u.buffers.rxData) || (bus->dmaRx == (dmaChannelDescriptor_t *)NULL))) ||
((checkSegment->u.buffers.txData) && IS_CCM(checkSegment->u.buffers.txData))) { ((checkSegment->u.buffers.txData) && IS_CCM(checkSegment->u.buffers.txData))) {
@ -367,17 +364,25 @@ 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 there are more than one segments, or a single segment with negateCS negated then force DMA irrespective of length
if (bus->useDMA && dmaSafe && ((segmentCount > 1) || (xferLen >= 8) || !bus->curSegment->negateCS)) {
// Intialise the init structures for the first transfer // Intialise the init structures for the first transfer
spiInternalInitStream(dev, false); spiInternalInitStream(dev, false);
// Assert Chip Select
IOLo(dev->busType_u.spi.csnPin);
// Start the transfers // Start the transfers
spiInternalStartDMA(dev); spiInternalStartDMA(dev);
} else { } else {
busSegment_t *lastSegment = NULL;
// Manually work through the segment list performing a transfer for each // Manually work through the segment list performing a transfer for each
while (bus->curSegment->len) { while (bus->curSegment->len) {
// Assert Chip Select if (!lastSegment || lastSegment->negateCS) {
IOLo(dev->busType_u.spi.csnPin); // Assert Chip Select if necessary - it's costly so only do so if necessary
IOLo(dev->busType_u.spi.csnPin);
}
spiInternalReadWriteBufPolled( spiInternalReadWriteBufPolled(
bus->busType_u.spi.instance, bus->busType_u.spi.instance,
@ -407,14 +412,20 @@ void spiSequenceStart(const extDevice_t *dev)
break; break;
} }
} }
lastSegment = (busSegment_t *)bus->curSegment;
bus->curSegment++; bus->curSegment++;
} }
if (lastSegment && !lastSegment->negateCS) {
// Negate Chip Select if not done so already
IOHi(dev->busType_u.spi.csnPin);
}
// If a following transaction has been linked, start it // If a following transaction has been linked, start it
if (bus->curSegment->u.link.dev) { if (bus->curSegment->u.link.dev) {
const extDevice_t *nextDev = bus->curSegment->u.link.dev; const extDevice_t *nextDev = bus->curSegment->u.link.dev;
busSegment_t *nextSegments = bus->curSegment->u.link.segments; busSegment_t *nextSegments = (busSegment_t *)bus->curSegment->u.link.segments;
busSegment_t *endSegment = bus->curSegment; busSegment_t *endSegment = (busSegment_t *)bus->curSegment;
bus->curSegment = nextSegments; bus->curSegment = nextSegments;
endSegment->u.link.dev = NULL; endSegment->u.link.dev = NULL;
spiSequenceStart(nextDev); spiSequenceStart(nextDev);

View file

@ -284,7 +284,7 @@ static void m25p16_eraseSector(flashDevice_t *fdevice, uint32_t address)
{.u.buffers = {readStatus, readyStatus}, sizeof(readStatus), true, m25p16_callbackReady}, {.u.buffers = {readStatus, readyStatus}, sizeof(readStatus), true, m25p16_callbackReady},
{.u.buffers = {writeEnable, NULL}, sizeof(writeEnable), true, m25p16_callbackWriteEnable}, {.u.buffers = {writeEnable, NULL}, sizeof(writeEnable), true, m25p16_callbackWriteEnable},
{.u.buffers = {sectorErase, NULL}, fdevice->isLargeFlash ? 5 : 4, true, NULL}, {.u.buffers = {sectorErase, NULL}, fdevice->isLargeFlash ? 5 : 4, true, NULL},
{.u.buffers = {NULL, NULL}, 0, true, NULL}, {.u.link = {NULL, NULL}, 0, true, NULL},
}; };
// Ensure any prior DMA has completed before continuing // Ensure any prior DMA has completed before continuing
@ -309,7 +309,7 @@ static void m25p16_eraseCompletely(flashDevice_t *fdevice)
{.u.buffers = {readStatus, readyStatus}, sizeof(readStatus), true, m25p16_callbackReady}, {.u.buffers = {readStatus, readyStatus}, sizeof(readStatus), true, m25p16_callbackReady},
{.u.buffers = {writeEnable, NULL}, sizeof(writeEnable), true, m25p16_callbackWriteEnable}, {.u.buffers = {writeEnable, NULL}, sizeof(writeEnable), true, m25p16_callbackWriteEnable},
{.u.buffers = {bulkErase, NULL}, sizeof(bulkErase), true, NULL}, {.u.buffers = {bulkErase, NULL}, sizeof(bulkErase), true, NULL},
{.u.buffers = {NULL, NULL}, 0, true, NULL}, {.u.link = {NULL, NULL}, 0, true, NULL},
}; };
spiSequence(fdevice->io.handle.dev, segments); spiSequence(fdevice->io.handle.dev, segments);
@ -337,9 +337,9 @@ static uint32_t m25p16_pageProgramContinue(flashDevice_t *fdevice, uint8_t const
{.u.buffers = {readStatus, readyStatus}, sizeof(readStatus), true, m25p16_callbackReady}, {.u.buffers = {readStatus, readyStatus}, sizeof(readStatus), true, m25p16_callbackReady},
{.u.buffers = {writeEnable, NULL}, sizeof(writeEnable), true, m25p16_callbackWriteEnable}, {.u.buffers = {writeEnable, NULL}, sizeof(writeEnable), true, m25p16_callbackWriteEnable},
{.u.buffers = {pageProgram, NULL}, 0, false, NULL}, {.u.buffers = {pageProgram, NULL}, 0, false, NULL},
{.u.buffers = {NULL, NULL}, 0, true, NULL}, {.u.link = {NULL, NULL}, 0, true, NULL},
{.u.buffers = {NULL, NULL}, 0, true, NULL}, {.u.link = {NULL, NULL}, 0, true, NULL},
{.u.buffers = {NULL, NULL}, 0, true, NULL}, {.u.link = {NULL, NULL}, 0, true, NULL},
}; };
// Ensure any prior DMA has completed before continuing // Ensure any prior DMA has completed before continuing
@ -430,7 +430,7 @@ static int m25p16_readBytes(flashDevice_t *fdevice, uint32_t address, uint8_t *b
{.u.buffers = {readStatus, readyStatus}, sizeof(readStatus), true, m25p16_callbackReady}, {.u.buffers = {readStatus, readyStatus}, sizeof(readStatus), true, m25p16_callbackReady},
{.u.buffers = {readBytes, NULL}, fdevice->isLargeFlash ? 5 : 4, false, NULL}, {.u.buffers = {readBytes, NULL}, fdevice->isLargeFlash ? 5 : 4, false, NULL},
{.u.buffers = {NULL, buffer}, length, true, NULL}, {.u.buffers = {NULL, buffer}, length, true, NULL},
{.u.buffers = {NULL, NULL}, 0, true, NULL}, {.u.link = {NULL, NULL}, 0, true, NULL},
}; };
// Patch the readBytes command // Patch the readBytes command

View file

@ -74,7 +74,7 @@ static void w25m_dieSelect(const extDevice_t *dev, int die)
busSegment_t segments[] = { busSegment_t segments[] = {
{.u.buffers = {command, NULL}, sizeof(command), true, NULL}, {.u.buffers = {command, NULL}, sizeof(command), true, NULL},
{.u.buffers = {NULL, NULL}, 0, true, NULL}, {.u.link = {NULL, NULL}, 0, true, NULL},
}; };
// Ensure any prior DMA has completed before continuing // Ensure any prior DMA has completed before continuing

View file

@ -151,7 +151,7 @@ static void w25n01g_performOneByteCommand(flashDeviceIO_t *io, uint8_t command)
busSegment_t segments[] = { busSegment_t segments[] = {
{.u.buffers = {&command, NULL}, sizeof(command), true, NULL}, {.u.buffers = {&command, NULL}, sizeof(command), true, NULL},
{.u.buffers = {NULL, NULL}, 0, true, NULL}, {.u.link = {NULL, NULL}, 0, true, NULL},
}; };
spiSequence(dev, &segments[0]); spiSequence(dev, &segments[0]);
@ -176,7 +176,7 @@ static void w25n01g_performCommandWithPageAddress(flashDeviceIO_t *io, uint8_t c
busSegment_t segments[] = { busSegment_t segments[] = {
{.u.buffers = {cmd, NULL}, sizeof(cmd), true, NULL}, {.u.buffers = {cmd, NULL}, sizeof(cmd), true, NULL},
{.u.buffers = {NULL, NULL}, 0, true, NULL}, {.u.link = {NULL, NULL}, 0, true, NULL},
}; };
spiSequence(dev, &segments[0]); spiSequence(dev, &segments[0]);
@ -203,7 +203,7 @@ static uint8_t w25n01g_readRegister(flashDeviceIO_t *io, uint8_t reg)
busSegment_t segments[] = { busSegment_t segments[] = {
{.u.buffers = {cmd, in}, sizeof(cmd), true, NULL}, {.u.buffers = {cmd, in}, sizeof(cmd), true, NULL},
{.u.buffers = {NULL, NULL}, 0, true, NULL}, {.u.link = {NULL, NULL}, 0, true, NULL},
}; };
// Ensure any prior DMA has completed before continuing // Ensure any prior DMA has completed before continuing
@ -238,7 +238,7 @@ static void w25n01g_writeRegister(flashDeviceIO_t *io, uint8_t reg, uint8_t data
busSegment_t segments[] = { busSegment_t segments[] = {
{.u.buffers = {cmd, NULL}, sizeof(cmd), true, NULL}, {.u.buffers = {cmd, NULL}, sizeof(cmd), true, NULL},
{.u.buffers = {NULL, NULL}, 0, true, NULL}, {.u.link = {NULL, NULL}, 0, true, NULL},
}; };
// Ensure any prior DMA has completed before continuing // Ensure any prior DMA has completed before continuing
@ -410,7 +410,7 @@ static void w25n01g_programDataLoad(flashDevice_t *fdevice, uint16_t columnAddre
busSegment_t segments[] = { busSegment_t segments[] = {
{.u.buffers = {cmd, NULL}, sizeof(cmd), false, NULL}, {.u.buffers = {cmd, NULL}, sizeof(cmd), false, NULL},
{.u.buffers = {(uint8_t *)data, NULL}, length, true, NULL}, {.u.buffers = {(uint8_t *)data, NULL}, length, true, NULL},
{.u.buffers = {NULL, NULL}, 0, true, NULL}, {.u.link = {NULL, NULL}, 0, true, NULL},
}; };
spiSequence(dev, &segments[0]); spiSequence(dev, &segments[0]);
@ -441,7 +441,7 @@ static void w25n01g_randomProgramDataLoad(flashDevice_t *fdevice, uint16_t colum
busSegment_t segments[] = { busSegment_t segments[] = {
{.u.buffers = {cmd, NULL}, sizeof(cmd), false, NULL}, {.u.buffers = {cmd, NULL}, sizeof(cmd), false, NULL},
{.u.buffers = {(uint8_t *)data, NULL}, length, true, NULL}, {.u.buffers = {(uint8_t *)data, NULL}, length, true, NULL},
{.u.buffers = {NULL, NULL}, 0, true, NULL}, {.u.link = {NULL, NULL}, 0, true, NULL},
}; };
spiSequence(dev, &segments[0]); spiSequence(dev, &segments[0]);
@ -684,7 +684,7 @@ int w25n01g_readBytes(flashDevice_t *fdevice, uint32_t address, uint8_t *buffer,
busSegment_t segments[] = { busSegment_t segments[] = {
{.u.buffers = {cmd, NULL}, sizeof(cmd), false, NULL}, {.u.buffers = {cmd, NULL}, sizeof(cmd), false, NULL},
{.u.buffers = {NULL, buffer}, length, true, NULL}, {.u.buffers = {NULL, buffer}, length, true, NULL},
{.u.buffers = {NULL, NULL}, 0, true, NULL}, {.u.link = {NULL, NULL}, 0, true, NULL},
}; };
spiSequence(dev, &segments[0]); spiSequence(dev, &segments[0]);
@ -749,7 +749,7 @@ int w25n01g_readExtensionBytes(flashDevice_t *fdevice, uint32_t address, uint8_t
busSegment_t segments[] = { busSegment_t segments[] = {
{.u.buffers = {cmd, NULL}, sizeof(cmd), false, NULL}, {.u.buffers = {cmd, NULL}, sizeof(cmd), false, NULL},
{.u.buffers = {NULL, buffer}, length, true, NULL}, {.u.buffers = {NULL, buffer}, length, true, NULL},
{.u.buffers = {NULL, NULL}, 0, true, NULL}, {.u.link = {NULL, NULL}, 0, true, NULL},
}; };
// Ensure any prior DMA has completed before continuing // Ensure any prior DMA has completed before continuing
@ -849,7 +849,7 @@ void w25n01g_readBBLUT(flashDevice_t *fdevice, bblut_t *bblut, int lutsize)
busSegment_t segments[] = { busSegment_t segments[] = {
{.u.buffers = {cmd, NULL}, sizeof(cmd), false, NULL}, {.u.buffers = {cmd, NULL}, sizeof(cmd), false, NULL},
{.u.buffers = {NULL, in}, sizeof(in), true, w25n01g_readBBLUTCallback}, {.u.buffers = {NULL, in}, sizeof(in), true, w25n01g_readBBLUTCallback},
{.u.buffers = {NULL, NULL}, 0, true, NULL}, {.u.link = {NULL, NULL}, 0, true, NULL},
}; };
spiSequence(dev, &segments[0]); spiSequence(dev, &segments[0]);
@ -888,7 +888,7 @@ void w25n01g_writeBBLUT(flashDevice_t *fdevice, uint16_t lba, uint16_t pba)
busSegment_t segments[] = { busSegment_t segments[] = {
{.u.buffers = {cmd, NULL}, sizeof(cmd), true, NULL}, {.u.buffers = {cmd, NULL}, sizeof(cmd), true, NULL},
{.u.buffers = {NULL, NULL}, 0, true, NULL}, {.u.link = {NULL, NULL}, 0, true, NULL},
}; };
// Ensure any prior DMA has completed before continuing // Ensure any prior DMA has completed before continuing

View file

@ -616,8 +616,8 @@ bool max7456DrawScreen(void)
static uint16_t pos = 0; static uint16_t pos = 0;
// This routine doesn't block so need to use static data // This routine doesn't block so need to use static data
static busSegment_t segments[] = { static busSegment_t segments[] = {
{.u.buffers = {NULL, NULL}, 0, true, NULL}, {.u.link = {NULL, NULL}, 0, true, NULL},
{.u.buffers = {NULL, NULL}, 0, true, NULL}, {.u.link = {NULL, NULL}, 0, true, NULL},
}; };
if (!fontIsLoading) { if (!fontIsLoading) {

View file

@ -28,6 +28,8 @@
#define NVIC_PRIO_SONAR_EXTI NVIC_BUILD_PRIORITY(2, 0) // maybe increase slightly #define NVIC_PRIO_SONAR_EXTI NVIC_BUILD_PRIORITY(2, 0) // maybe increase slightly
#define NVIC_PRIO_DSHOT_DMA NVIC_BUILD_PRIORITY(2, 1) #define NVIC_PRIO_DSHOT_DMA NVIC_BUILD_PRIORITY(2, 1)
#define NVIC_PRIO_TRANSPONDER_DMA NVIC_BUILD_PRIORITY(3, 0) #define NVIC_PRIO_TRANSPONDER_DMA NVIC_BUILD_PRIORITY(3, 0)
#define NVIC_PRIO_RX_INT_EXTI NVIC_BUILD_PRIORITY(0x0f, 0x0f)
#define NVIC_PRIO_RX_BUSY_EXTI NVIC_BUILD_PRIORITY(3, 0)
#define NVIC_PRIO_MPU_INT_EXTI NVIC_BUILD_PRIORITY(0x0f, 0x0f) #define NVIC_PRIO_MPU_INT_EXTI NVIC_BUILD_PRIORITY(0x0f, 0x0f)
#define NVIC_PRIO_MAG_INT_EXTI NVIC_BUILD_PRIORITY(0x0f, 0x0f) #define NVIC_PRIO_MAG_INT_EXTI NVIC_BUILD_PRIORITY(0x0f, 0x0f)
#define NVIC_PRIO_RX_SPI_INT_EXTI NVIC_BUILD_PRIORITY(0x0f, 0x0f) #define NVIC_PRIO_RX_SPI_INT_EXTI NVIC_BUILD_PRIORITY(0x0f, 0x0f)
@ -74,7 +76,6 @@
#define NVIC_PRIO_MPU_DATA_READY NVIC_BUILD_PRIORITY(0, 1) #define NVIC_PRIO_MPU_DATA_READY NVIC_BUILD_PRIORITY(0, 1)
#define NVIC_PRIO_MAG_DATA_READY NVIC_BUILD_PRIORITY(0x0f, 0x0f) #define NVIC_PRIO_MAG_DATA_READY NVIC_BUILD_PRIORITY(0x0f, 0x0f)
#define NVIC_PRIO_CALLBACK NVIC_BUILD_PRIORITY(0x0f, 0x0f) #define NVIC_PRIO_CALLBACK NVIC_BUILD_PRIORITY(0x0f, 0x0f)
#define NVIC_PRIO_MAX7456_DMA NVIC_BUILD_PRIORITY(3, 0)
#define NVIC_PRIO_SPI_DMA NVIC_BUILD_PRIORITY(0, 0) #define NVIC_PRIO_SPI_DMA NVIC_BUILD_PRIORITY(0, 0)
#define NVIC_PRIO_SDIO_DMA NVIC_BUILD_PRIORITY(0, 0) #define NVIC_PRIO_SDIO_DMA NVIC_BUILD_PRIORITY(0, 0)

View file

@ -43,6 +43,11 @@
#include "rx_spi.h" #include "rx_spi.h"
#ifdef USE_RX_EXPRESSLRS
#include "rx/rx_spi.h"
#include "rx/expresslrs.h"
#endif
// 13.5 MHz max SPI frequency // 13.5 MHz max SPI frequency
#define RX_MAX_SPI_CLK_HZ 13500000 #define RX_MAX_SPI_CLK_HZ 13500000
// 6.5 MHz max SPI frequency during startup // 6.5 MHz max SPI frequency during startup
@ -52,14 +57,19 @@ static extDevice_t rxSpiDevice;
static extDevice_t *dev = &rxSpiDevice; static extDevice_t *dev = &rxSpiDevice;
static IO_t extiPin = IO_NONE; static IO_t extiPin = IO_NONE;
static extiCallbackRec_t rxSpiExtiCallbackRec;
static bool extiLevel = true; static bool extiLevel = true;
static extiCallbackRec_t rxSpiExtiCallbackRec;
static volatile bool extiHasOccurred = false; static volatile bool extiHasOccurred = false;
static volatile timeUs_t lastExtiTimeUs = 0; static volatile timeUs_t lastExtiTimeUs = 0;
static uint32_t spiNormalSpeedMhz = RX_MAX_SPI_CLK_HZ; static uint32_t spiNormalSpeedMhz = RX_MAX_SPI_CLK_HZ;
extDevice_t *rxSpiGetDevice(void)
{
return dev;
}
void rxSpiDevicePreInit(const rxSpiConfig_t *rxSpiConfig) void rxSpiDevicePreInit(const rxSpiConfig_t *rxSpiConfig)
{ {
spiPreinitRegister(rxSpiConfig->csnTag, IOCFG_IPU, 1); spiPreinitRegister(rxSpiConfig->csnTag, IOCFG_IPU, 1);
@ -69,12 +79,12 @@ void rxSpiExtiHandler(extiCallbackRec_t* callback)
{ {
UNUSED(callback); UNUSED(callback);
const timeUs_t extiTimeUs = microsISR(); lastExtiTimeUs = microsISR();
extiHasOccurred = true;
if (IORead(extiPin) == extiLevel) { #ifdef USE_RX_EXPRESSLRS
lastExtiTimeUs = extiTimeUs; expressLrsISR(true);
extiHasOccurred = true; #endif
}
} }
void rxSpiSetNormalSpeedMhz(uint32_t mhz) void rxSpiSetNormalSpeedMhz(uint32_t mhz)
@ -121,12 +131,19 @@ bool rxSpiDeviceInit(const rxSpiConfig_t *rxSpiConfig)
void rxSpiExtiInit(ioConfig_t rxSpiExtiPinConfig, extiTrigger_t rxSpiExtiPinTrigger) void rxSpiExtiInit(ioConfig_t rxSpiExtiPinConfig, extiTrigger_t rxSpiExtiPinTrigger)
{ {
if (extiPin) { if (extiPin) {
// Use interrupts on the EXTI pin
if (rxSpiExtiPinTrigger == BETAFLIGHT_EXTI_TRIGGER_FALLING) { if (rxSpiExtiPinTrigger == BETAFLIGHT_EXTI_TRIGGER_FALLING) {
extiLevel = false; extiLevel = false;
} }
EXTIHandlerInit(&rxSpiExtiCallbackRec, rxSpiExtiHandler); EXTIHandlerInit(&rxSpiExtiCallbackRec, rxSpiExtiHandler);
EXTIConfig(extiPin, &rxSpiExtiCallbackRec, NVIC_PRIO_RX_SPI_INT_EXTI, rxSpiExtiPinConfig, rxSpiExtiPinTrigger); EXTIConfig(extiPin, &rxSpiExtiCallbackRec, NVIC_PRIO_RX_INT_EXTI, rxSpiExtiPinConfig, rxSpiExtiPinTrigger);
EXTIEnable(extiPin, true); EXTIEnable(extiPin, true);
// Check that we've not missed the rising edge on the interrupt line
if (rxSpiGetExtiState()) {
rxSpiExtiHandler(NULL);
}
} }
} }

View file

@ -24,12 +24,14 @@
#include "common/time.h" #include "common/time.h"
#include "drivers/bus.h"
#include "drivers/exti.h" #include "drivers/exti.h"
#define RX_SPI_MAX_PAYLOAD_SIZE 35 #define RX_SPI_MAX_PAYLOAD_SIZE 35
struct rxSpiConfig_s; struct rxSpiConfig_s;
extDevice_t *rxSpiGetDevice(void);
void rxSpiDevicePreInit(const struct rxSpiConfig_s *rxSpiConfig); void rxSpiDevicePreInit(const struct rxSpiConfig_s *rxSpiConfig);
bool rxSpiDeviceInit(const struct rxSpiConfig_s *rxSpiConfig); bool rxSpiDeviceInit(const struct rxSpiConfig_s *rxSpiConfig);
void rxSpiSetNormalSpeedMhz(uint32_t mhz); void rxSpiSetNormalSpeedMhz(uint32_t mhz);
@ -43,6 +45,7 @@ void rxSpiWriteCommandMulti(uint8_t command, const uint8_t *data, uint8_t length
uint8_t rxSpiReadCommand(uint8_t command, uint8_t commandData); uint8_t rxSpiReadCommand(uint8_t command, uint8_t commandData);
void rxSpiReadCommandMulti(uint8_t command, uint8_t commandData, uint8_t *retData, uint8_t length); void rxSpiReadCommandMulti(uint8_t command, uint8_t commandData, uint8_t *retData, uint8_t length);
void rxSpiExtiInit(ioConfig_t rxSpiExtiPinConfig, extiTrigger_t rxSpiExtiPinTrigger); void rxSpiExtiInit(ioConfig_t rxSpiExtiPinConfig, extiTrigger_t rxSpiExtiPinTrigger);
void rxSpiEnableExti(void);
bool rxSpiExtiConfigured(void); bool rxSpiExtiConfigured(void);
bool rxSpiGetExtiState(void); bool rxSpiGetExtiState(void);
bool rxSpiPollExti(void); bool rxSpiPollExti(void);

View file

@ -33,6 +33,7 @@
#ifdef USE_RX_SX1280 #ifdef USE_RX_SX1280
#include "build/atomic.h" #include "build/atomic.h"
#include "build/debug.h"
#include "drivers/bus_spi.h" #include "drivers/bus_spi.h"
#include "drivers/io.h" #include "drivers/io.h"
@ -42,10 +43,35 @@
#include "drivers/rx/rx_spi.h" #include "drivers/rx/rx_spi.h"
#include "drivers/time.h" #include "drivers/time.h"
#define SX1280_MAX_SPI_MHZ 10000000 #include "rx/rx_spi.h"
#include "rx/expresslrs.h"
#include "rx/expresslrs_common.h"
#include "rx/expresslrs_impl.h"
#define SX1280_MAX_SPI_MHZ 18000000
// The following global variables are accessed from interrupt context to process the sequence of steps in packet processing
// As there is only ever one device, no need to add a device context; globals will do
static volatile dioReason_e irqReason; // Used to pass irq status from sx1280IrqStatusRead() to sx1280ProcessIrq()
static volatile uint8_t packetStats[2];
static volatile uint8_t FIFOaddr; // Used to pass data from sx1280GotFIFOAddr() to sx1280DoReadBuffer()
static IO_t busy; static IO_t busy;
typedef struct busyIntContext_s {
extiCallbackRec_t exti;
} busyIntContext_t;
static busyIntContext_t busyIntContext;
static volatile timeUs_t sx1280Processing;
static volatile bool pendingISR = false;
static volatile bool pendingDoFHSS = false;
#define SX1280_BUSY_TIMEOUT_US 1000
bool sx1280IsBusy(void) bool sx1280IsBusy(void)
{ {
return IORead(busy); return IORead(busy);
@ -55,7 +81,7 @@ static bool sx1280PollBusy(void)
{ {
uint32_t startTime = micros(); uint32_t startTime = micros();
while (IORead(busy)) { while (IORead(busy)) {
if ((micros() - startTime) > 1000) { if ((micros() - startTime) > SX1280_BUSY_TIMEOUT_US) {
return false; return false;
} else { } else {
__asm__("nop"); __asm__("nop");
@ -64,9 +90,108 @@ static bool sx1280PollBusy(void)
return true; return true;
} }
static bool sx1280MarkBusy(void)
{
// Check that there isn't already a sequence of accesses to the SX1280 in progress
ATOMIC_BLOCK(NVIC_PRIO_MAX) {
if (sx1280Processing) {
return false;
}
sx1280Processing = micros();
}
return true;
}
static void sx1280ClearBusyFn(void)
{
EXTIEnable(busy, false);
}
// Switch to waiting for busy interrupt
static bool sx1280EnableBusy(void)
{
if (!sx1280MarkBusy()) {
return false;
}
/* Ensure BUSY EXTI is enabled
*
* This is needed because the BETAFPV F4SX1280 target defines the following resources which cannot be
* simultaneously used with the EXTI15_10_IRQHandler. Fortunately we can enable RX_SPI_EXTI until an
* interrupt is received, then enable RX_SPI_EXPRESSLRS_BUSY with the call below until data transfers
* are complete and then switch back with a call to sx1280EnableExti().
*
* resource RX_SPI_EXTI 1 C13
* resource RX_SPI_EXPRESSLRS_BUSY 1 A13
*
*/
EXTIConfig(busy, &busyIntContext.exti, NVIC_PRIO_RX_BUSY_EXTI, IOCFG_IN_FLOATING, BETAFLIGHT_EXTI_TRIGGER_FALLING);
return true;
}
// waitingFn() must call sx1280ClearBusyFn() to prevent repeated calls
static void sx1280SetBusyFn(extiHandlerCallback *waitingFn)
{
bool sx1280Busy;
ATOMIC_BLOCK(NVIC_PRIO_RX_BUSY_EXTI) {
sx1280Busy = IORead(busy);
if (sx1280Busy) {
EXTIHandlerInit(&busyIntContext.exti, waitingFn);
EXTIEnable(busy, true);
} else {
EXTIEnable(busy, false);
}
}
if (!sx1280Busy) {
waitingFn(&busyIntContext.exti);
}
}
static void sx1280MarkFree(void)
{
// Mark that current sequence of accesses is concluded
sx1280Processing = (timeUs_t)0;
}
// Switch to waiting for EXTI interrupt
static void sx1280EnableExti(void)
{
sx1280MarkFree();
rxSpiEnableExti();
}
// Unlikely as it is for the code to lock up waiting on a busy SX1280, we can't afford the risk
// If this routine is called twice in succession whilst waiting on the same busy, force the code to advance
// Called from the Tick timer
bool sx1280HandleFromTick(void)
{
// Grab a copy to prevent a race condition
timeUs_t startTime = sx1280Processing;
if (startTime) {
// No operation should take SX1280_BUSY_TIMEOUT_US us
if (cmpTimeUs(micros(), startTime) > SX1280_BUSY_TIMEOUT_US) {
// Brute force abandon the current sequence of operations
sx1280ClearBusyFn();
// Renable EXTI
sx1280EnableExti();
return true;
}
}
return false;
}
bool sx1280Init(IO_t resetPin, IO_t busyPin) bool sx1280Init(IO_t resetPin, IO_t busyPin)
{ {
if (!rxSpiExtiConfigured()) { if (!rxSpiExtiConfigured()) {
return false; return false;
} }
@ -83,7 +208,6 @@ bool sx1280Init(IO_t resetPin, IO_t busyPin)
if (busyPin) { if (busyPin) {
IOInit(busyPin, OWNER_RX_SPI_EXPRESSLRS_BUSY, 0); IOInit(busyPin, OWNER_RX_SPI_EXPRESSLRS_BUSY, 0);
IOConfigGPIO(busyPin, IOCFG_IPU);
} else { } else {
busyPin = IO_NONE; busyPin = IO_NONE;
} }
@ -101,33 +225,13 @@ bool sx1280Init(IO_t resetPin, IO_t busyPin)
return false; return false;
} }
// Record the dev pointer for callbacks
extDevice_t *dev = rxSpiGetDevice();
dev->callbackArg = (uint32_t)dev;
return true; return true;
} }
uint8_t sx1280ISR(timeUs_t *timeStamp)
{
bool extiTriggered = false;
timeUs_t extiTimestamp;
ATOMIC_BLOCK(NVIC_PRIO_RX_SPI_INT_EXTI) {
// prevent a data-race that can occur if a new EXTI ISR occurs during this block.
extiTriggered = rxSpiPollExti();
extiTimestamp = rxSpiGetLastExtiTimeUs();
if (extiTriggered) {
rxSpiResetExti();
}
}
if (extiTriggered) {
uint8_t irqReason = sx1280GetIrqReason();
if (extiTimestamp) {
*timeStamp = extiTimestamp;
}
return irqReason;
}
return 0;
}
void sx1280WriteCommand(const uint8_t address, const uint8_t data) void sx1280WriteCommand(const uint8_t address, const uint8_t data)
{ {
sx1280PollBusy(); sx1280PollBusy();
@ -242,8 +346,8 @@ void sx1280ConfigLoraDefaults(void)
sx1280WriteCommand(SX1280_RADIO_SET_AUTOFS, 0x01); //enable auto FS sx1280WriteCommand(SX1280_RADIO_SET_AUTOFS, 0x01); //enable auto FS
sx1280WriteRegister(0x0891, (sx1280ReadRegister(0x0891) | 0xC0)); //default is low power mode, switch to high sensitivity instead sx1280WriteRegister(0x0891, (sx1280ReadRegister(0x0891) | 0xC0)); //default is low power mode, switch to high sensitivity instead
sx1280SetPacketParams(12, SX1280_LORA_PACKET_IMPLICIT, 8, SX1280_LORA_CRC_OFF, SX1280_LORA_IQ_NORMAL); //default params sx1280SetPacketParams(12, SX1280_LORA_PACKET_IMPLICIT, 8, SX1280_LORA_CRC_OFF, SX1280_LORA_IQ_NORMAL); //default params
sx1280SetFrequencyHZ(2400000000); //Step 3: Set Freq sx1280SetFrequencyReg(fhssGetInitialFreq(0)); //Step 3: Set Freq
sx1280SetFIFOaddr(0x00, 0x00); //Step 4: Config FIFO addr sx1280SetFifoAddr(0x00, 0x00); //Step 4: Config FIFO addr
sx1280SetDioIrqParams(SX1280_IRQ_RADIO_ALL, SX1280_IRQ_TX_DONE | SX1280_IRQ_RX_DONE, SX1280_IRQ_RADIO_NONE, SX1280_IRQ_RADIO_NONE); //set IRQ to both RXdone/TXdone on DIO1 sx1280SetDioIrqParams(SX1280_IRQ_RADIO_ALL, SX1280_IRQ_TX_DONE | SX1280_IRQ_RX_DONE, SX1280_IRQ_RADIO_NONE, SX1280_IRQ_RADIO_NONE); //set IRQ to both RXdone/TXdone on DIO1
} }
@ -352,25 +456,13 @@ void sx1280ConfigLoraModParams(const sx1280LoraBandwidths_e bw, const sx1280Lora
} }
} }
void sx1280SetFrequencyHZ(const uint32_t reqFreq) void sx1280SetFrequencyReg(const uint32_t freqReg)
{ {
uint8_t buf[3] = {0}; uint8_t buf[3] = {0};
uint32_t freq = (uint32_t)(reqFreq / SX1280_FREQ_STEP); buf[0] = (uint8_t)((freqReg >> 16) & 0xFF);
buf[0] = (uint8_t)((freq >> 16) & 0xFF); buf[1] = (uint8_t)((freqReg >> 8) & 0xFF);
buf[1] = (uint8_t)((freq >> 8) & 0xFF); buf[2] = (uint8_t)(freqReg & 0xFF);
buf[2] = (uint8_t)(freq & 0xFF);
sx1280WriteCommandBurst(SX1280_RADIO_SET_RFFREQUENCY, buf, 3);
}
void sx1280SetFrequencyReg(const uint32_t freq)
{
uint8_t buf[3] = {0};
buf[0] = (uint8_t)((freq >> 16) & 0xFF);
buf[1] = (uint8_t)((freq >> 8) & 0xFF);
buf[2] = (uint8_t)(freq & 0xFF);
sx1280WriteCommandBurst(SX1280_RADIO_SET_RFFREQUENCY, buf, 3); sx1280WriteCommandBurst(SX1280_RADIO_SET_RFFREQUENCY, buf, 3);
} }
@ -382,7 +474,7 @@ void sx1280AdjustFrequency(int32_t offset, const uint32_t freq)
UNUSED(freq); UNUSED(freq);
} }
void sx1280SetFIFOaddr(const uint8_t txBaseAddr, const uint8_t rxBaseAddr) void sx1280SetFifoAddr(const uint8_t txBaseAddr, const uint8_t rxBaseAddr)
{ {
uint8_t buf[2]; uint8_t buf[2];
@ -407,39 +499,6 @@ void sx1280SetDioIrqParams(const uint16_t irqMask, const uint16_t dio1Mask, cons
sx1280WriteCommandBurst(SX1280_RADIO_SET_DIOIRQPARAMS, buf, 8); sx1280WriteCommandBurst(SX1280_RADIO_SET_DIOIRQPARAMS, buf, 8);
} }
uint16_t sx1280GetIrqStatus(void)
{
uint8_t status[2];
sx1280ReadCommandBurst(SX1280_RADIO_GET_IRQSTATUS, status, 2);
return status[0] << 8 | status[1];
}
void sx1280ClearIrqStatus(const uint16_t irqMask)
{
uint8_t buf[2];
buf[0] = (uint8_t)(((uint16_t)irqMask >> 8) & 0x00FF);
buf[1] = (uint8_t)((uint16_t)irqMask & 0x00FF);
sx1280WriteCommandBurst(SX1280_RADIO_CLR_IRQSTATUS, buf, 2);
}
uint8_t sx1280GetIrqReason(void)
{
uint16_t irqStatus = sx1280GetIrqStatus();
sx1280ClearIrqStatus(SX1280_IRQ_RADIO_ALL);
if ((irqStatus & SX1280_IRQ_TX_DONE) && (irqStatus & SX1280_IRQ_RX_DONE)) {
return 3;
} else if ((irqStatus & SX1280_IRQ_TX_DONE)) {
return 2;
} else if ((irqStatus & SX1280_IRQ_RX_DONE)) {
return 1;
}
return 0;
}
void sx1280TransmitData(const uint8_t *data, const uint8_t length) void sx1280TransmitData(const uint8_t *data, const uint8_t length)
{ {
sx1280WriteBuffer(0x00, data, length); sx1280WriteBuffer(0x00, data, length);
@ -461,18 +520,414 @@ void sx1280ReceiveData(uint8_t *data, const uint8_t length)
void sx1280StartReceiving(void) void sx1280StartReceiving(void)
{ {
sx1280SetMode(SX1280_MODE_RX); if (sx1280MarkBusy()) {
sx1280SetMode(SX1280_MODE_RX);
sx1280MarkFree();
}
} }
void sx1280GetLastPacketStats(int8_t *rssi, int8_t *snr) void sx1280GetLastPacketStats(int8_t *rssi, int8_t *snr)
{ {
uint8_t status[2]; *rssi = -(int8_t)(packetStats[0] / 2);
*snr = ((int8_t) packetStats[1]) / 4;
sx1280ReadCommandBurst(SX1280_RADIO_GET_PACKETSTATUS, status, 2);
*rssi = -(int8_t)(status[0] / 2);
*snr = ((int8_t) status[1]) / 4;
int8_t negOffset = (*snr < 0) ? *snr : 0; int8_t negOffset = (*snr < 0) ? *snr : 0;
*rssi += negOffset; *rssi += negOffset;
} }
void sx1280DoFHSS(void)
{
return;
}
void sx1280ClearIrqStatus(const uint16_t irqMask)
{
uint8_t buf[2];
buf[0] = (uint8_t)(((uint16_t)irqMask >> 8) & 0x00FF);
buf[1] = (uint8_t)((uint16_t)irqMask & 0x00FF);
sx1280WriteCommandBurst(SX1280_RADIO_CLR_IRQSTATUS, buf, 2);
}
// Forward Definitions for DMA Chain //
static void sx1280IrqGetStatus(extiCallbackRec_t *cb);
static busStatus_e sx1280IrqStatusRead(uint32_t arg);
static void sx1280IrqClearStatus(extiCallbackRec_t *cb);
static busStatus_e sx1280IrqCmdComplete(uint32_t arg);
static void sx1280ProcessIrq(extiCallbackRec_t *cb);
static busStatus_e sx1280GotFIFOAddr(uint32_t arg);
static void sx1280DoReadBuffer(extiCallbackRec_t *cb);
static busStatus_e sx1280ReadBufferComplete(uint32_t arg);
static void sx1280GetPacketStats(extiCallbackRec_t *cb);
static busStatus_e sx1280GetStatsCmdComplete(uint32_t arg);
static busStatus_e sx1280IsFhssReq(uint32_t arg);
static void sx1280SetFrequency(extiCallbackRec_t *cb);
static busStatus_e sx1280SetFreqComplete(uint32_t arg);
static void sx1280StartReceivingDMA(extiCallbackRec_t *cb);
static busStatus_e sx1280EnableIRQs(uint32_t arg);
static void sx1280SendTelemetryBuffer(extiCallbackRec_t *cb);
static busStatus_e sx1280TelemetryComplete(uint32_t arg);
static void sx1280StartTransmittingDMA(extiCallbackRec_t *cb);
void sx1280ISR(void)
{
// Only attempt to access the SX1280 if it is currently idle to avoid any race condition
ATOMIC_BLOCK(NVIC_PRIO_MAX) {
if (sx1280EnableBusy()) {
pendingISR = false;
sx1280SetBusyFn(sx1280IrqGetStatus);
} else {
pendingISR = true;
}
}
}
// Next, the reason for the IRQ must be read
static void sx1280IrqGetStatus(extiCallbackRec_t *cb)
{
extDevice_t *dev = rxSpiGetDevice();
UNUSED(cb);
sx1280ClearBusyFn();
STATIC_DMA_DATA_AUTO uint8_t irqStatusCmd[] = {SX1280_RADIO_GET_IRQSTATUS, 0, 0, 0};
STATIC_DMA_DATA_AUTO uint8_t irqStatus[sizeof(irqStatusCmd)];
static busSegment_t segments[] = {
{.u.buffers = {irqStatusCmd, irqStatus}, sizeof(irqStatusCmd), false, sx1280IrqStatusRead},
{.u.link = {NULL, NULL}, 0, true, NULL},
};
spiSequence(dev, segments);
}
// Read the IRQ status, and save it to irqStatus variable
static busStatus_e sx1280IrqStatusRead(uint32_t arg)
{
extDevice_t *dev = (extDevice_t *)arg;
uint16_t irqStatus = (dev->bus->curSegment->u.buffers.rxData[2] << 8) | dev->bus->curSegment->u.buffers.rxData[3];
if (irqStatus & SX1280_IRQ_TX_DONE) {
irqReason = ELRS_DIO_TX_DONE;
} else if (irqStatus & SX1280_IRQ_RX_DONE) {
irqReason = ELRS_DIO_RX_DONE;
} else {
irqReason = ELRS_DIO_UNKNOWN;
}
sx1280SetBusyFn(sx1280IrqClearStatus);
return BUS_READY;
}
// Clear the IRQ bit in the Radio registers
static void sx1280IrqClearStatus(extiCallbackRec_t *cb)
{
extDevice_t *dev = rxSpiGetDevice();
UNUSED(cb);
sx1280ClearBusyFn();
STATIC_DMA_DATA_AUTO uint8_t irqCmd[] = {SX1280_RADIO_CLR_IRQSTATUS, 0, 0};
irqCmd[1] = (uint8_t)(((uint16_t)SX1280_IRQ_RADIO_ALL >> 8) & 0x00FF);
irqCmd[2] = (uint8_t)((uint16_t)SX1280_IRQ_RADIO_ALL & 0x00FF);
static busSegment_t segments[] = {
{.u.buffers = {irqCmd, NULL}, sizeof(irqCmd), false, sx1280IrqCmdComplete},
{.u.link = {NULL, NULL}, 0, true, NULL},
};
spiSequence(dev, segments);
}
// Callback follow clear of IRQ status
static busStatus_e sx1280IrqCmdComplete(uint32_t arg)
{
UNUSED(arg);
sx1280SetBusyFn(sx1280ProcessIrq);
return BUS_READY;
}
// Process IRQ status
static void sx1280ProcessIrq(extiCallbackRec_t *cb)
{
extDevice_t *dev = rxSpiGetDevice();
UNUSED(cb);
sx1280ClearBusyFn();
if (irqReason == ELRS_DIO_RX_DONE || irqReason == ELRS_DIO_UNKNOWN) {
// Fire off the chain to read and decode the packet from the radio
// Get the buffer status to determine the FIFO address
STATIC_DMA_DATA_AUTO uint8_t cmdBufStatusCmd[] = {SX1280_RADIO_GET_RXBUFFERSTATUS, 0, 0, 0};
STATIC_DMA_DATA_AUTO uint8_t bufStatus[sizeof(cmdBufStatusCmd)];
static busSegment_t segments[] = {
{.u.buffers = {cmdBufStatusCmd, bufStatus}, sizeof(cmdBufStatusCmd), false, sx1280GotFIFOAddr},
{.u.link = {NULL, NULL}, 0, true, NULL},
};
spiSequence(dev, segments);
} else {
// return to RX mode immediately, the next packet will be an RX and we won't need to FHSS
STATIC_DMA_DATA_AUTO uint8_t irqSetRxCmd[] = {SX1280_RADIO_SET_RX, 0, 0xff, 0xff};
static busSegment_t segments[] = {
{.u.buffers = {irqSetRxCmd, NULL}, sizeof(irqSetRxCmd), false, sx1280EnableIRQs},
{.u.link = {NULL, NULL}, 0, true, NULL},
};
spiSequence(dev, segments);
}
}
// First we read from the FIFO address register to determine the FIFO address
static busStatus_e sx1280GotFIFOAddr(uint32_t arg)
{
extDevice_t *dev = (extDevice_t *)arg;
FIFOaddr = dev->bus->curSegment->u.buffers.rxData[3];
// Wait until no longer busy and read the buffer
sx1280SetBusyFn(sx1280DoReadBuffer);
return BUS_READY;
}
// Using the addr val stored to the global varable FIFOaddr, read the buffer
static void sx1280DoReadBuffer(extiCallbackRec_t *cb)
{
extDevice_t *dev = rxSpiGetDevice();
UNUSED(cb);
sx1280ClearBusyFn();
STATIC_DMA_DATA_AUTO uint8_t cmdReadBuf[] = {SX1280_RADIO_READ_BUFFER, 0, 0};
cmdReadBuf[1] = FIFOaddr;
static busSegment_t segments[] = {
{.u.buffers = {cmdReadBuf, NULL}, sizeof(cmdReadBuf), false, NULL},
{.u.buffers = {NULL, NULL}, ELRS_RX_TX_BUFF_SIZE, true, sx1280ReadBufferComplete},
{.u.link = {NULL, NULL}, 0, true, NULL},
};
segments[1].u.buffers.rxData = (uint8_t *)expressLrsGetRxBuffer();
spiSequence(dev, segments);
}
// Get the Packet Status and RSSI
static busStatus_e sx1280ReadBufferComplete(uint32_t arg)
{
UNUSED(arg);
sx1280SetBusyFn(sx1280GetPacketStats);
return BUS_READY;
}
// Save the Packet Stats to the global variables
static void sx1280GetPacketStats(extiCallbackRec_t *cb)
{
UNUSED(cb);
extDevice_t *dev = rxSpiGetDevice();
sx1280ClearBusyFn();
STATIC_DMA_DATA_AUTO uint8_t getStatsCmd[] = {SX1280_RADIO_GET_PACKETSTATUS, 0, 0, 0};
STATIC_DMA_DATA_AUTO uint8_t stats[sizeof(getStatsCmd)];
static busSegment_t segments[] = {
{.u.buffers = {getStatsCmd, stats}, sizeof(getStatsCmd), false, sx1280GetStatsCmdComplete},
{.u.link = {NULL, NULL}, 0, true, NULL},
};
spiSequence(dev, segments);
}
// Process and decode the RF packet
static busStatus_e sx1280GetStatsCmdComplete(uint32_t arg)
{
extDevice_t *dev = (extDevice_t *)arg;
volatile uint8_t *payload = expressLrsGetPayloadBuffer();
packetStats[0] = dev->bus->curSegment->u.buffers.rxData[2];
packetStats[1] = dev->bus->curSegment->u.buffers.rxData[3];
expressLrsSetRfPacketStatus(processRFPacket(payload, rxSpiGetLastExtiTimeUs()));
return sx1280IsFhssReq(arg);
}
void sx1280HandleFromTock(void)
{
ATOMIC_BLOCK(NVIC_PRIO_MAX) {
if (expressLrsIsFhssReq()) {
if (sx1280EnableBusy()) {
pendingDoFHSS = false;
sx1280SetBusyFn(sx1280SetFrequency);
} else {
pendingDoFHSS = true;
}
}
}
}
// Next we need to check if we need to FHSS and then do so if needed
static busStatus_e sx1280IsFhssReq(uint32_t arg)
{
UNUSED(arg);
if (expressLrsIsFhssReq()) {
sx1280SetBusyFn(sx1280SetFrequency);
} else {
sx1280SetFreqComplete(arg);
}
return BUS_READY;
}
// Set the frequency
static void sx1280SetFrequency(extiCallbackRec_t *cb)
{
UNUSED(cb);
extDevice_t *dev = rxSpiGetDevice();
uint32_t currentFreq = expressLrsGetCurrentFreq();
sx1280ClearBusyFn();
STATIC_DMA_DATA_AUTO uint8_t setFreqCmd[] = {SX1280_RADIO_SET_RFFREQUENCY, 0, 0, 0};
setFreqCmd[1] = (uint8_t)((currentFreq >> 16) & 0xFF);
setFreqCmd[2] = (uint8_t)((currentFreq >> 8) & 0xFF);
setFreqCmd[3] = (uint8_t)(currentFreq & 0xFF);
static busSegment_t segments[] = {
{.u.buffers = {setFreqCmd, NULL}, sizeof(setFreqCmd), false, sx1280SetFreqComplete},
{.u.link = {NULL, NULL}, 0, true, NULL},
};
spiSequence(dev, segments);
}
// Determine if we need to go back to RX or if we need to send TLM data
static busStatus_e sx1280SetFreqComplete(uint32_t arg)
{
UNUSED(arg);
if (expressLrsTelemRespReq()) {
expressLrsDoTelem();
// if it's time to do TLM and we have enough to do so
sx1280SetBusyFn(sx1280SendTelemetryBuffer);
} else {
// we don't need to send TLM and we've already FHSS so just hop back into RX mode
sx1280SetBusyFn(sx1280StartReceivingDMA);
}
return BUS_READY;
}
// Go back into RX mode
static void sx1280StartReceivingDMA(extiCallbackRec_t *cb)
{
UNUSED(cb);
extDevice_t *dev = rxSpiGetDevice();
sx1280ClearBusyFn();
// Issue command to start receiving
// periodBase = 1ms, page 71 datasheet, set to FF for cont RX
STATIC_DMA_DATA_AUTO uint8_t irqSetRxCmd[] = {SX1280_RADIO_SET_RX, 0, 0xff, 0xff};
static busSegment_t segments[] = {
{.u.buffers = {irqSetRxCmd, NULL}, sizeof(irqSetRxCmd), false, sx1280EnableIRQs},
{.u.link = {NULL, NULL}, 0, true, NULL},
};
spiSequence(dev, segments);
}
static busStatus_e sx1280EnableIRQs(uint32_t arg)
{
UNUSED(arg);
// Handle any queued interrupt processing
if (pendingISR) {
pendingISR = false;
sx1280SetBusyFn(sx1280IrqGetStatus);
} else if (pendingDoFHSS) {
pendingDoFHSS = false;
sx1280SetBusyFn(sx1280SetFrequency);
} else {
// Switch back to waiting for EXTI interrupt
sx1280EnableExti();
}
return BUS_READY;
}
// Send telemetry response
static void sx1280SendTelemetryBuffer(extiCallbackRec_t *cb)
{
UNUSED(cb);
extDevice_t *dev = rxSpiGetDevice();
sx1280ClearBusyFn();
STATIC_DMA_DATA_AUTO uint8_t writeBufferCmd[] = {SX1280_RADIO_WRITE_BUFFER, 0};
static busSegment_t segments[] = {
{.u.buffers = {writeBufferCmd, NULL}, sizeof(writeBufferCmd), false, NULL},
{.u.buffers = {NULL, NULL}, ELRS_RX_TX_BUFF_SIZE, true, sx1280TelemetryComplete},
{.u.link = {NULL, NULL}, 0, true, NULL},
};
segments[1].u.buffers.txData = (uint8_t *)expressLrsGetTelemetryBuffer();
spiSequence(dev, segments);
}
static busStatus_e sx1280TelemetryComplete(uint32_t arg)
{
UNUSED(arg);
sx1280SetBusyFn(sx1280StartTransmittingDMA);
return BUS_READY;
}
static void sx1280StartTransmittingDMA(extiCallbackRec_t *cb)
{
UNUSED(cb);
extDevice_t *dev = rxSpiGetDevice();
sx1280ClearBusyFn();
//uses timeout Time-out duration = periodBase * periodBaseCount
// periodBase = 1ms, page 71 datasheet
// no timeout set for now
// TODO dynamic timeout based on expected onairtime
STATIC_DMA_DATA_AUTO uint8_t irqSetRxCmd[] = {SX1280_RADIO_SET_TX, 0, 0xff, 0xff};
static busSegment_t segments[] = {
{.u.buffers = {irqSetRxCmd, NULL}, sizeof(irqSetRxCmd), false, sx1280EnableIRQs},
{.u.link = {NULL, NULL}, 0, true, NULL},
};
spiSequence(dev, segments);
}
#endif /* USE_RX_SX1280 */ #endif /* USE_RX_SX1280 */

View file

@ -25,6 +25,8 @@
#pragma once #pragma once
#include "common/time.h"
#define REG_LR_FIRMWARE_VERSION_MSB 0x0153 //The address of the register holding the firmware version MSB #define REG_LR_FIRMWARE_VERSION_MSB 0x0153 //The address of the register holding the firmware version MSB
#define SX1280_REG_LR_ESTIMATED_FREQUENCY_ERROR_MSB 0x0954 #define SX1280_REG_LR_ESTIMATED_FREQUENCY_ERROR_MSB 0x0954
#define SX1280_REG_LR_ESTIMATED_FREQUENCY_ERROR_MASK 0x0FFFFF #define SX1280_REG_LR_ESTIMATED_FREQUENCY_ERROR_MASK 0x0FFFFF
@ -247,7 +249,8 @@ typedef enum {
} sx1280TickSizes_e; } sx1280TickSizes_e;
bool sx1280Init(IO_t resetPin, IO_t busyPin); bool sx1280Init(IO_t resetPin, IO_t busyPin);
uint8_t sx1280ISR(uint32_t *timeStamp); void sx1280ISR(void);
bool sx1280HandleFromTick(void);
bool sx1280IsBusy(void); bool sx1280IsBusy(void);
void sx1280WriteCommand(const uint8_t address, const uint8_t data); void sx1280WriteCommand(const uint8_t address, const uint8_t data);
void sx1280WriteCommandBurst(const uint8_t address, const uint8_t *data, const uint8_t length); void sx1280WriteCommandBurst(const uint8_t address, const uint8_t *data, const uint8_t length);
@ -266,14 +269,14 @@ void sx1280SetOutputPower(const int8_t power);
void sx1280SetPacketParams(const uint8_t preambleLength, const sx1280LoraPacketLengthsModes_e headerType, const uint8_t payloadLength, const sx1280LoraCrcModes_e crc, const sx1280LoraIqModes_e invertIQ); void sx1280SetPacketParams(const uint8_t preambleLength, const sx1280LoraPacketLengthsModes_e headerType, const uint8_t payloadLength, const sx1280LoraCrcModes_e crc, const sx1280LoraIqModes_e invertIQ);
void sx1280SetMode(const sx1280OperatingModes_e opMode); void sx1280SetMode(const sx1280OperatingModes_e opMode);
void sx1280ConfigLoraModParams(const sx1280LoraBandwidths_e bw, const sx1280LoraSpreadingFactors_e sf, const sx1280LoraCodingRates_e cr); void sx1280ConfigLoraModParams(const sx1280LoraBandwidths_e bw, const sx1280LoraSpreadingFactors_e sf, const sx1280LoraCodingRates_e cr);
void sx1280SetFrequencyHZ(const uint32_t reqFreq); void sx1280SetFrequencyReg(const uint32_t freqReg);
void sx1280SetFrequencyReg(const uint32_t freq);
void sx1280AdjustFrequency(int32_t offset, const uint32_t freq); void sx1280AdjustFrequency(int32_t offset, const uint32_t freq);
void sx1280SetFIFOaddr(const uint8_t txBaseAddr, const uint8_t rxBaseAddr); void sx1280SetFifoAddr(const uint8_t txBaseAddr, const uint8_t rxBaseAddr);
void sx1280SetDioIrqParams(const uint16_t irqMask, const uint16_t dio1Mask, const uint16_t dio2Mask, const uint16_t dio3Mask); void sx1280SetDioIrqParams(const uint16_t irqMask, const uint16_t dio1Mask, const uint16_t dio2Mask, const uint16_t dio3Mask);
uint16_t sx1280GetIrqStatus(void);
void sx1280ClearIrqStatus(const uint16_t irqMask); void sx1280ClearIrqStatus(const uint16_t irqMask);
uint8_t sx1280GetIrqReason(void); void sx1280GetIrqReason(void);
void sx1280HandleFromTock();
void sx1280TransmitData(const uint8_t *data, const uint8_t length); void sx1280TransmitData(const uint8_t *data, const uint8_t length);
void sx1280ReceiveData(uint8_t *data, const uint8_t length); void sx1280ReceiveData(uint8_t *data, const uint8_t length);

View file

@ -767,7 +767,6 @@ bool processRx(timeUs_t currentTimeUs)
if (currentTimeUs > FAILSAFE_POWER_ON_DELAY_US && !failsafeIsMonitoring()) { if (currentTimeUs > FAILSAFE_POWER_ON_DELAY_US && !failsafeIsMonitoring()) {
failsafeStartMonitoring(); failsafeStartMonitoring();
} }
failsafeUpdateState();
const throttleStatus_e throttleStatus = calculateThrottleStatus(); const throttleStatus_e throttleStatus = calculateThrottleStatus();
const uint8_t throttlePercent = calculateThrottlePercentAbs(); const uint8_t throttlePercent = calculateThrottlePercentAbs();
@ -1269,6 +1268,14 @@ FAST_CODE bool pidLoopReady(void)
return false; return false;
} }
FAST_CODE bool rxFrameReady(void)
{
if ((activePidLoopDenom == 1) || (pidUpdateCounter % activePidLoopDenom == 0)) {
return true;
}
return false;
}
FAST_CODE void taskFiltering(timeUs_t currentTimeUs) FAST_CODE void taskFiltering(timeUs_t currentTimeUs)
{ {
gyroFiltering(currentTimeUs); gyroFiltering(currentTimeUs);

View file

@ -82,6 +82,7 @@ void updateArmingStatus(void);
void taskGyroSample(timeUs_t currentTimeUs); void taskGyroSample(timeUs_t currentTimeUs);
bool gyroFilterReady(void); bool gyroFilterReady(void);
bool pidLoopReady(void); bool pidLoopReady(void);
bool rxFrameReady(void);
void taskFiltering(timeUs_t currentTimeUs); void taskFiltering(timeUs_t currentTimeUs);
void taskMainPidLoop(timeUs_t currentTimeUs); void taskMainPidLoop(timeUs_t currentTimeUs);

View file

@ -144,6 +144,7 @@ void processRcStickPositions()
// an extra guard for disarming through switch to prevent that one frame can disarm it // an extra guard for disarming through switch to prevent that one frame can disarm it
static uint8_t rcDisarmTicks; static uint8_t rcDisarmTicks;
static bool doNotRepeat; static bool doNotRepeat;
static bool pendingApplyRollAndPitchTrimDeltaSave = false;
// checking sticks positions // checking sticks positions
uint8_t stTmp = 0; uint8_t stTmp = 0;
@ -306,6 +307,12 @@ void processRcStickPositions()
rollAndPitchTrims_t accelerometerTrimsDelta; rollAndPitchTrims_t accelerometerTrimsDelta;
memset(&accelerometerTrimsDelta, 0, sizeof(accelerometerTrimsDelta)); memset(&accelerometerTrimsDelta, 0, sizeof(accelerometerTrimsDelta));
if (pendingApplyRollAndPitchTrimDeltaSave && ((rcSticks & THR_MASK) != THR_HI)) {
saveConfigAndNotify();
pendingApplyRollAndPitchTrimDeltaSave = false;
return;
}
bool shouldApplyRollAndPitchTrimDelta = false; bool shouldApplyRollAndPitchTrimDelta = false;
switch (rcSticks) { switch (rcSticks) {
case THR_HI + YAW_CE + PIT_HI + ROL_CE: case THR_HI + YAW_CE + PIT_HI + ROL_CE:
@ -329,7 +336,9 @@ void processRcStickPositions()
#if defined(USE_ACC) #if defined(USE_ACC)
applyAccelerometerTrimsDelta(&accelerometerTrimsDelta); applyAccelerometerTrimsDelta(&accelerometerTrimsDelta);
#endif #endif
saveConfigAndNotify(); pendingApplyRollAndPitchTrimDeltaSave = true;
beeperConfirmationBeeps(1);
repeatAfter(STICK_AUTOREPEAT_MS); repeatAfter(STICK_AUTOREPEAT_MS);

View file

@ -70,6 +70,7 @@ typedef enum {
#define THR_LO (1 << (2 * THROTTLE)) #define THR_LO (1 << (2 * THROTTLE))
#define THR_CE (3 << (2 * THROTTLE)) #define THR_CE (3 << (2 * THROTTLE))
#define THR_HI (2 << (2 * THROTTLE)) #define THR_HI (2 << (2 * THROTTLE))
#define THR_MASK (3 << (2 * THROTTLE))
#define CONTROL_RATE_CONFIG_RC_EXPO_MAX 100 #define CONTROL_RATE_CONFIG_RC_EXPO_MAX 100

View file

@ -167,7 +167,6 @@ static void taskUpdateAccelerometer(timeUs_t currentTimeUs)
typedef enum { typedef enum {
RX_STATE_CHECK, RX_STATE_CHECK,
RX_STATE_PROCESS,
RX_STATE_MODES, RX_STATE_MODES,
RX_STATE_UPDATE, RX_STATE_UPDATE,
RX_STATE_COUNT RX_STATE_COUNT
@ -195,10 +194,6 @@ static void taskUpdateRxMain(timeUs_t currentTimeUs)
switch (rxState) { switch (rxState) {
default: default:
case RX_STATE_CHECK: case RX_STATE_CHECK:
rxState = RX_STATE_PROCESS;
break;
case RX_STATE_PROCESS:
if (!processRx(currentTimeUs)) { if (!processRx(currentTimeUs)) {
rxState = RX_STATE_CHECK; rxState = RX_STATE_CHECK;
break; break;

View file

@ -192,7 +192,15 @@ void failsafeOnValidDataFailed(void)
} }
} }
void failsafeUpdateState(void) void failsafeCheckDataFailurePeriod(void)
{
if (cmp32(millis(), failsafeState.validRxDataReceivedAt) > (int32_t)failsafeState.rxDataFailurePeriod) {
setArmingDisabled(ARMING_DISABLED_RX_FAILSAFE); // To prevent arming with no RX link
failsafeState.rxLinkState = FAILSAFE_RXLINK_DOWN;
}
}
FAST_CODE_NOINLINE void failsafeUpdateState(void)
{ {
if (!failsafeIsMonitoring()) { if (!failsafeIsMonitoring()) {
return; return;

View file

@ -103,6 +103,7 @@ failsafePhase_e failsafePhase(void);
bool failsafeIsMonitoring(void); bool failsafeIsMonitoring(void);
bool failsafeIsActive(void); bool failsafeIsActive(void);
bool failsafeIsReceivingRxData(void); bool failsafeIsReceivingRxData(void);
void failsafeCheckDataFailurePeriod(void);
void failsafeOnRxSuspend(uint32_t suspendPeriod); void failsafeOnRxSuspend(uint32_t suspendPeriod);
void failsafeOnRxResume(void); void failsafeOnRxResume(void);

View file

@ -69,7 +69,7 @@
#include "rx/expresslrs_impl.h" #include "rx/expresslrs_impl.h"
#include "rx/expresslrs_telemetry.h" #include "rx/expresslrs_telemetry.h"
STATIC_UNIT_TESTED elrsReceiver_t receiver; UNIT_TESTED elrsReceiver_t receiver;
static const uint8_t BindingUID[6] = {0,1,2,3,4,5}; // Special binding UID values static const uint8_t BindingUID[6] = {0,1,2,3,4,5}; // Special binding UID values
static uint16_t crcInitializer = 0; static uint16_t crcInitializer = 0;
static uint8_t bindingRateIndex = 0; static uint8_t bindingRateIndex = 0;
@ -79,9 +79,14 @@ static uint8_t wideSwitchIndex = 0;
static simpleLowpassFilter_t rssiFilter; static simpleLowpassFilter_t rssiFilter;
static volatile DMA_DATA uint8_t dmaBuffer[ELRS_RX_TX_BUFF_SIZE];
static volatile DMA_DATA uint8_t telemetryPacket[ELRS_RX_TX_BUFF_SIZE];
static volatile rx_spi_received_e rfPacketStatus = RX_SPI_RECEIVED_NONE;
static volatile uint8_t *payload;
static void rssiFilterReset(void) static void rssiFilterReset(void)
{ {
simpleLPFilterInit(&rssiFilter, 3, 5); simpleLPFilterInit(&rssiFilter, 2, 5);
} }
#define PACKET_HANDLING_TO_TOCK_ISR_DELAY_US 250 #define PACKET_HANDLING_TO_TOCK_ISR_DELAY_US 250
@ -107,28 +112,28 @@ eprState_t eprState = {
.eventRecorded = {0}, .eventRecorded = {0},
}; };
static void expressLrsEPRRecordEvent(eprEvent_e event, uint32_t currentTimeUs) static void phaseLockEprEvent(eprEvent_e event, uint32_t currentTimeUs)
{ {
eprState.eventAtUs[event] = currentTimeUs; eprState.eventAtUs[event] = currentTimeUs;
eprState.eventRecorded[event] = true; eprState.eventRecorded[event] = true;
} }
static bool expressLrsEPRHaveBothEvents(void) static bool phaseLockEprHaveBothEvents(void)
{ {
bool bothEventsRecorded = eprState.eventRecorded[EPR_SECOND] && eprState.eventRecorded[EPR_FIRST]; bool bothEventsRecorded = eprState.eventRecorded[EPR_SECOND] && eprState.eventRecorded[EPR_FIRST];
return bothEventsRecorded; return bothEventsRecorded;
} }
static int32_t expressLrsEPRGetResult(void) static int32_t phaseLockEprResult(void)
{ {
if (!expressLrsEPRHaveBothEvents()) { if (!phaseLockEprHaveBothEvents()) {
return 0; return 0;
} }
return (int32_t)(eprState.eventAtUs[EPR_SECOND] - eprState.eventAtUs[EPR_FIRST]); return (int32_t)(eprState.eventAtUs[EPR_SECOND] - eprState.eventAtUs[EPR_FIRST]);
} }
static void expressLrsEPRReset(void) static void phaseLockEprReset(void)
{ {
memset(&eprState, 0, sizeof(eprState_t)); memset(&eprState, 0, sizeof(eprState_t));
} }
@ -161,7 +166,7 @@ static void expressLrsPhaseLockReset(void)
simpleLPFilterInit(&pl.offsetFilter, 2, 5); simpleLPFilterInit(&pl.offsetFilter, 2, 5);
simpleLPFilterInit(&pl.offsetDxFilter, 4, 5); simpleLPFilterInit(&pl.offsetDxFilter, 4, 5);
expressLrsEPRReset(); phaseLockEprReset();
} }
static uint8_t nextTelemetryType = ELRS_TELEMETRY_TYPE_LINK; static uint8_t nextTelemetryType = ELRS_TELEMETRY_TYPE_LINK;
@ -290,12 +295,6 @@ static void unpackChannelDataHybridWide(uint16_t *rcData, const uint8_t *payload
setRssiChannelData(rcData); setRssiChannelData(rcData);
} }
static void startReceiving(void)
{
dbgPinLo(1);
receiver.startReceiving();
}
static uint8_t minLqForChaos(void) static uint8_t minLqForChaos(void)
{ {
// Determine the most number of CRC-passing packets we could receive on // Determine the most number of CRC-passing packets we could receive on
@ -307,12 +306,12 @@ static uint8_t minLqForChaos(void)
// FHSShopInterval * ceil(100 / FHSShopInterval * numfhss) or // FHSShopInterval * ceil(100 / FHSShopInterval * numfhss) or
// FHSShopInterval * trunc((100 + (FHSShopInterval * numfhss) - 1) / (FHSShopInterval * numfhss)) // FHSShopInterval * trunc((100 + (FHSShopInterval * numfhss) - 1) / (FHSShopInterval * numfhss))
// With a interval of 4 this works out to: 2.4=4, FCC915=4, AU915=8, EU868=8, EU/AU433=36 // With a interval of 4 this works out to: 2.4=4, FCC915=4, AU915=8, EU868=8, EU/AU433=36
const uint32_t numfhss = getFHSSNumEntries(); const uint32_t numfhss = fhssGetNumEntries();
const uint8_t interval = receiver.modParams->fhssHopInterval; const uint8_t interval = receiver.modParams->fhssHopInterval;
return interval * ((interval * numfhss + 99) / (interval * numfhss)); return interval * ((interval * numfhss + 99) / (interval * numfhss));
} }
static void setRFLinkRate(const uint8_t index) static void setRfLinkRate(const uint8_t index)
{ {
#if defined(USE_RX_SX1280) && defined(USE_RX_SX127X) #if defined(USE_RX_SX1280) && defined(USE_RX_SX127X)
receiver.modParams = (rxExpressLrsSpiConfig()->domain == ISM2400) ? &airRateConfig[1][index] : &airRateConfig[0][index]; receiver.modParams = (rxExpressLrsSpiConfig()->domain == ISM2400) ? &airRateConfig[1][index] : &airRateConfig[0][index];
@ -321,9 +320,9 @@ static void setRFLinkRate(const uint8_t index)
receiver.modParams = &airRateConfig[0][index]; receiver.modParams = &airRateConfig[0][index];
receiver.rfPerfParams = &rfPerfConfig[0][index]; receiver.rfPerfParams = &rfPerfConfig[0][index];
#endif #endif
receiver.currentFreq = getInitialFreq(receiver.freqOffset); receiver.currentFreq = fhssGetInitialFreq(receiver.freqOffset);
// Wait for (11/10) 110% of time it takes to cycle through all freqs in FHSS table (in ms) // Wait for (11/10) 110% of time it takes to cycle through all freqs in FHSS table (in ms)
receiver.cycleIntervalMs = ((uint32_t)11U * getFHSSNumEntries() * receiver.modParams->fhssHopInterval * receiver.modParams->interval) / (10U * 1000U); receiver.cycleIntervalMs = ((uint32_t)11U * fhssGetNumEntries() * receiver.modParams->fhssHopInterval * receiver.modParams->interval) / (10U * 1000U);
receiver.config(receiver.modParams->bw, receiver.modParams->sf, receiver.modParams->cr, receiver.currentFreq, receiver.modParams->preambleLen, receiver.UID[5] & 0x01); receiver.config(receiver.modParams->bw, receiver.modParams->sf, receiver.modParams->cr, receiver.currentFreq, receiver.modParams->preambleLen, receiver.UID[5] & 0x01);
@ -338,57 +337,73 @@ static void setRFLinkRate(const uint8_t index)
#endif #endif
} }
static bool handleFHSS(void) uint32_t expressLrsGetCurrentFreq(void)
{
return receiver.currentFreq;
}
void expressLrsSetRfPacketStatus(rx_spi_received_e status)
{
rfPacketStatus = status;
}
volatile uint8_t *expressLrsGetRxBuffer(void) {
return dmaBuffer;
}
volatile uint8_t *expressLrsGetTelemetryBuffer(void)
{
return telemetryPacket;
}
volatile uint8_t *expressLrsGetPayloadBuffer(void)
{
return payload;
}
bool expressLrsIsFhssReq(void)
{ {
uint8_t modresultFHSS = (receiver.nonceRX + 1) % receiver.modParams->fhssHopInterval; uint8_t modresultFHSS = (receiver.nonceRX + 1) % receiver.modParams->fhssHopInterval;
if ((receiver.modParams->fhssHopInterval == 0) || receiver.alreadyFHSS == true || receiver.inBindingMode || (modresultFHSS != 0) || (receiver.connectionState == ELRS_DISCONNECTED)) { if ((receiver.modParams->fhssHopInterval == 0) || receiver.alreadyFhss == true || receiver.inBindingMode || (modresultFHSS != 0) || (receiver.connectionState == ELRS_DISCONNECTED)) {
return false; return false;
} }
receiver.alreadyFHSS = true; receiver.alreadyFhss = true;
receiver.currentFreq = FHSSgetNextFreq(receiver.freqOffset); receiver.currentFreq = fhssGetNextFreq(receiver.freqOffset);
receiver.setFrequency(receiver.currentFreq);
uint8_t modresultTLM = (receiver.nonceRX + 1) % (tlmRatioEnumToValue(receiver.modParams->tlmInterval));
if (modresultTLM != 0 || receiver.modParams->tlmInterval == TLM_RATIO_NO_TLM) { // if we are about to send a tlm response don't bother going back to rx
startReceiving();
}
return true; return true;
} }
static bool shouldSendTelemetryResponse(void) bool expressLrsTelemRespReq(void)
{ {
uint8_t modresult = (receiver.nonceRX + 1) % tlmRatioEnumToValue(receiver.modParams->tlmInterval); uint8_t modresult = (receiver.nonceRX + 1) % tlmRatioEnumToValue(receiver.modParams->tlmInterval);
if ((receiver.connectionState == ELRS_DISCONNECTED) || (receiver.modParams->tlmInterval == TLM_RATIO_NO_TLM) || (receiver.alreadyTLMresp == true) || (modresult != 0)) { if (receiver.inBindingMode || (receiver.connectionState == ELRS_DISCONNECTED) || (receiver.modParams->tlmInterval == TLM_RATIO_NO_TLM) || (modresult != 0)) {
return false; // don't bother sending tlm if disconnected or TLM is off return false; // don't bother sending tlm if disconnected or TLM is off
} else { } else {
return true; return true;
} }
} }
static void handleSendTelemetryResponse(void) static void expressLrsSendTelemResp(void)
{ {
uint8_t packet[8];
uint8_t *data; uint8_t *data;
uint8_t maxLength; uint8_t maxLength;
uint8_t packageIndex; uint8_t packageIndex;
receiver.alreadyTLMresp = true; receiver.alreadyTelemResp = true;
packet[0] = ELRS_TLM_PACKET; telemetryPacket[0] = ELRS_TLM_PACKET;
if (nextTelemetryType == ELRS_TELEMETRY_TYPE_LINK || !isTelemetrySenderActive()) { if (nextTelemetryType == ELRS_TELEMETRY_TYPE_LINK || !isTelemetrySenderActive()) {
packet[1] = ELRS_TELEMETRY_TYPE_LINK; telemetryPacket[1] = ELRS_TELEMETRY_TYPE_LINK;
packet[2] = receiver.rssiFiltered > 0 ? 0 : -receiver.rssiFiltered; //diversity not supported telemetryPacket[2] = receiver.rssiFiltered > 0 ? 0 : -receiver.rssiFiltered; //diversity not supported
packet[3] = connectionHasModelMatch << 7; telemetryPacket[3] = connectionHasModelMatch << 7;
packet[4] = receiver.snr; telemetryPacket[4] = receiver.snr;
packet[5] = receiver.uplinkLQ; telemetryPacket[5] = receiver.uplinkLQ;
#ifdef USE_MSP_OVER_TELEMETRY #ifdef USE_MSP_OVER_TELEMETRY
packet[6] = getCurrentMspConfirm() ? 1 : 0; telemetryPacket[6] = getCurrentMspConfirm() ? 1 : 0;
#else #else
packet[6] = 0; telemetryPacket[6] = 0;
#endif #endif
nextTelemetryType = ELRS_TELEMETRY_TYPE_DATA; nextTelemetryType = ELRS_TELEMETRY_TYPE_DATA;
// Start the count at 1 because the next will be DATA and doing +1 before checking // Start the count at 1 because the next will be DATA and doing +1 before checking
@ -402,27 +417,24 @@ static void handleSendTelemetryResponse(void)
} }
getCurrentTelemetryPayload(&packageIndex, &maxLength, &data); getCurrentTelemetryPayload(&packageIndex, &maxLength, &data);
packet[1] = (packageIndex << ELRS_TELEMETRY_SHIFT) + ELRS_TELEMETRY_TYPE_DATA; telemetryPacket[1] = (packageIndex << ELRS_TELEMETRY_SHIFT) + ELRS_TELEMETRY_TYPE_DATA;
packet[2] = maxLength > 0 ? *data : 0; telemetryPacket[2] = maxLength > 0 ? *data : 0;
packet[3] = maxLength >= 1 ? *(data + 1) : 0; telemetryPacket[3] = maxLength >= 1 ? *(data + 1) : 0;
packet[4] = maxLength >= 2 ? *(data + 2) : 0; telemetryPacket[4] = maxLength >= 2 ? *(data + 2) : 0;
packet[5] = maxLength >= 3 ? *(data + 3) : 0; telemetryPacket[5] = maxLength >= 3 ? *(data + 3) : 0;
packet[6] = maxLength >= 4 ? *(data + 4) : 0; telemetryPacket[6] = maxLength >= 4 ? *(data + 4) : 0;
} }
uint16_t crc = calcCrc14(packet, 7, crcInitializer); uint16_t crc = calcCrc14((uint8_t *)telemetryPacket, 7, crcInitializer);
packet[0] |= (crc >> 6) & 0xFC; telemetryPacket[0] |= (crc >> 6) & 0xFC;
packet[7] = crc & 0xFF; telemetryPacket[7] = crc & 0xFF;
dbgPinHi(1);
receiver.transmitData(packet, ELRS_RX_TX_BUFF_SIZE);
} }
static void updatePhaseLock(void) static void updatePhaseLock(void)
{ {
if (receiver.connectionState != ELRS_DISCONNECTED && expressLrsEPRHaveBothEvents()) { if (receiver.connectionState != ELRS_DISCONNECTED && phaseLockEprHaveBothEvents()) {
int32_t maxOffset = receiver.modParams->interval / 4; int32_t maxOffset = receiver.modParams->interval / 4;
pl.rawOffsetUs = constrain(expressLrsEPRGetResult(), -maxOffset, maxOffset); pl.rawOffsetUs = constrain(phaseLockEprResult(), -maxOffset, maxOffset);
pl.offsetUs = simpleLPFilterUpdate(&pl.offsetFilter, pl.rawOffsetUs); pl.offsetUs = simpleLPFilterUpdate(&pl.offsetFilter, pl.rawOffsetUs);
pl.offsetDeltaUs = simpleLPFilterUpdate(&pl.offsetDxFilter, pl.rawOffsetUs - pl.previousRawOffsetUs); pl.offsetDeltaUs = simpleLPFilterUpdate(&pl.offsetDxFilter, pl.rawOffsetUs - pl.previousRawOffsetUs);
@ -452,7 +464,7 @@ static void updatePhaseLock(void)
DEBUG_SET(DEBUG_RX_EXPRESSLRS_PHASELOCK, 1, pl.offsetUs); DEBUG_SET(DEBUG_RX_EXPRESSLRS_PHASELOCK, 1, pl.offsetUs);
} }
expressLrsEPRReset(); phaseLockEprReset();
} }
//hwTimerCallbackTick //hwTimerCallbackTick
@ -464,12 +476,14 @@ void expressLrsOnTimerTickISR(void) // this is 180 out of phase with the other c
// Save the LQ value before the inc() reduces it by 1 // Save the LQ value before the inc() reduces it by 1
receiver.uplinkLQ = lqGet(); receiver.uplinkLQ = lqGet();
// Only advance the LQI period counter if we didn't send Telemetry this period // Only advance the LQI period counter if we didn't send Telemetry this period
if (!receiver.alreadyTLMresp) { if (!receiver.alreadyTelemResp) {
lqNewPeriod(); lqNewPeriod();
} }
receiver.alreadyTLMresp = false; receiver.alreadyTelemResp = false;
receiver.alreadyFHSS = false; receiver.alreadyFhss = false;
receiver.rxHandleFromTick();
} }
//hwTimerCallbackTock //hwTimerCallbackTock
@ -477,9 +491,9 @@ void expressLrsOnTimerTockISR(void)
{ {
uint32_t currentTimeUs = micros(); uint32_t currentTimeUs = micros();
expressLrsEPRRecordEvent(EPR_INTERNAL, currentTimeUs); phaseLockEprEvent(EPR_INTERNAL, currentTimeUs);
receiver.fhssRequired = true; //Rest of the code is moved to expressLrsDataReceived to avoid race condition receiver.rxHandleFromTock();
} }
static uint16_t lostConnectionCounter = 0; static uint16_t lostConnectionCounter = 0;
@ -501,14 +515,13 @@ void lostConnection(void)
receiver.uplinkLQ = 0; receiver.uplinkLQ = 0;
lqReset(); lqReset();
expressLrsPhaseLockReset(); expressLrsPhaseLockReset();
receiver.alreadyTLMresp = false; receiver.alreadyTelemResp = false;
receiver.alreadyFHSS = false; receiver.alreadyFhss = false;
if (!receiver.inBindingMode) { if (!receiver.inBindingMode) {
//while (micros() - expressLrsEPRGetResult() > 250); // time it just after the tock() TODO this currently breaks and is blocking, not a fan of this.
expressLrsTimerStop(); expressLrsTimerStop();
setRFLinkRate(receiver.nextRateIndex); // also sets to initialFreq setRfLinkRate(receiver.nextRateIndex); // also sets to initialFreq
startReceiving(); receiver.startReceiving();
} }
} }
@ -548,7 +561,7 @@ static void gotConnection(const uint32_t timeStampMs)
//setup radio //setup radio
static void initializeReceiver(void) static void initializeReceiver(void)
{ {
FHSSrandomiseFHSSsequence(receiver.UID, rxExpressLrsSpiConfig()->domain); fhssGenSequence(receiver.UID, rxExpressLrsSpiConfig()->domain);
lqReset(); lqReset();
receiver.nonceRX = 0; receiver.nonceRX = 0;
receiver.freqOffset = 0; receiver.freqOffset = 0;
@ -558,11 +571,11 @@ static void initializeReceiver(void)
receiver.snr = 0; receiver.snr = 0;
receiver.uplinkLQ = 0; receiver.uplinkLQ = 0;
receiver.rateIndex = receiver.inBindingMode ? bindingRateIndex : rxExpressLrsSpiConfig()->rateIndex; receiver.rateIndex = receiver.inBindingMode ? bindingRateIndex : rxExpressLrsSpiConfig()->rateIndex;
setRFLinkRate(receiver.rateIndex); setRfLinkRate(receiver.rateIndex);
receiver.started = false; receiver.started = false;
receiver.alreadyFHSS = false; receiver.alreadyFhss = false;
receiver.alreadyTLMresp = false; receiver.alreadyTelemResp = false;
receiver.lockRFmode = false; receiver.lockRFmode = false;
receiver.timerState = ELRS_TIM_DISCONNECTED; receiver.timerState = ELRS_TIM_DISCONNECTED;
receiver.connectionState = ELRS_DISCONNECTED; receiver.connectionState = ELRS_DISCONNECTED;
@ -579,7 +592,7 @@ static void initializeReceiver(void)
receiver.rfModeCycleMultiplier = 1; receiver.rfModeCycleMultiplier = 1;
} }
static void unpackBindPacket(uint8_t *packet) static void unpackBindPacket(volatile uint8_t *packet)
{ {
rxExpressLrsSpiConfigMutable()->UID[2] = packet[3]; rxExpressLrsSpiConfigMutable()->UID[2] = packet[3];
rxExpressLrsSpiConfigMutable()->UID[3] = packet[4]; rxExpressLrsSpiConfigMutable()->UID[3] = packet[4];
@ -589,16 +602,13 @@ static void unpackBindPacket(uint8_t *packet)
receiver.UID = rxExpressLrsSpiConfigMutable()->UID; receiver.UID = rxExpressLrsSpiConfigMutable()->UID;
crcInitializer = (receiver.UID[4] << 8) | receiver.UID[5]; crcInitializer = (receiver.UID[4] << 8) | receiver.UID[5];
receiver.inBindingMode = false; receiver.inBindingMode = false;
initializeReceiver();
receiver.configChanged = true; //after initialize as it sets it to false receiver.configChanged = true; //after initialize as it sets it to false
} }
/** /**
* Process the assembled MSP packet in mspBuffer[] * Process the assembled MSP packet in mspBuffer[]
**/ **/
static void processRFMspPacket(uint8_t *packet) static void processRFMspPacket(volatile uint8_t *packet)
{ {
// Always examine MSP packets for bind information if in bind mode // Always examine MSP packets for bind information if in bind mode
// [1] is the package index, first packet of the MSP // [1] is the package index, first packet of the MSP
@ -635,7 +645,7 @@ static void processRFMspPacket(uint8_t *packet)
#endif #endif
} }
static bool processRFSyncPacket(uint8_t *packet, const uint32_t timeStampMs) static bool processRFSyncPacket(volatile uint8_t *packet, const uint32_t timeStampMs)
{ {
// Verify the first two of three bytes of the binding ID, which should always match // Verify the first two of three bytes of the binding ID, which should always match
if (packet[4] != receiver.UID[3] || packet[5] != receiver.UID[4]) { if (packet[4] != receiver.UID[3] || packet[5] != receiver.UID[4]) {
@ -672,8 +682,8 @@ static bool processRFSyncPacket(uint8_t *packet, const uint32_t timeStampMs)
uint8_t modelXor = (~rxExpressLrsSpiConfig()->modelId) & ELRS_MODELMATCH_MASK; uint8_t modelXor = (~rxExpressLrsSpiConfig()->modelId) & ELRS_MODELMATCH_MASK;
bool modelMatched = packet[6] == (receiver.UID[5] ^ modelXor); bool modelMatched = packet[6] == (receiver.UID[5] ^ modelXor);
if (receiver.connectionState == ELRS_DISCONNECTED || receiver.nonceRX != packet[2] || FHSSgetCurrIndex() != packet[1] || connectionHasModelMatch != modelMatched) { if (receiver.connectionState == ELRS_DISCONNECTED || receiver.nonceRX != packet[2] || fhssGetCurrIndex() != packet[1] || connectionHasModelMatch != modelMatched) {
FHSSsetCurrIndex(packet[1]); fhssSetCurrIndex(packet[1]);
receiver.nonceRX = packet[2]; receiver.nonceRX = packet[2];
tentativeConnection(timeStampMs); tentativeConnection(timeStampMs);
@ -687,30 +697,26 @@ static bool processRFSyncPacket(uint8_t *packet, const uint32_t timeStampMs)
return false; return false;
} }
static rx_spi_received_e processRFPacket(uint8_t *payload, uint32_t timeStampUs) rx_spi_received_e processRFPacket(volatile uint8_t *payload, uint32_t timeStampUs)
{ {
uint8_t packet[ELRS_RX_TX_BUFF_SIZE]; elrsPacketType_e type = dmaBuffer[0] & 0x03;
uint16_t inCRC = (((uint16_t)(dmaBuffer[0] & 0xFC)) << 6 ) | dmaBuffer[7];
receiver.receiveData(packet, ELRS_RX_TX_BUFF_SIZE);
elrsPacketType_e type = packet[0] & 0x03;
uint16_t inCRC = (((uint16_t)(packet[0] & 0xFC)) << 6 ) | packet[7];
// For SM_HYBRID the CRC only has the packet type in byte 0 // For SM_HYBRID the CRC only has the packet type in byte 0
// For SM_HYBRID_WIDE the FHSS slot is added to the CRC in byte 0 on RC_DATA_PACKETs // For SM_HYBRID_WIDE the FHSS slot is added to the CRC in byte 0 on RC_DATA_PACKETs
if (type != ELRS_RC_DATA_PACKET || rxExpressLrsSpiConfig()->switchMode != SM_HYBRID_WIDE) { if (type != ELRS_RC_DATA_PACKET || rxExpressLrsSpiConfig()->switchMode != SM_HYBRID_WIDE) {
packet[0] = type; dmaBuffer[0] = type;
} else { } else {
uint8_t nonceFHSSresult = receiver.nonceRX % receiver.modParams->fhssHopInterval; uint8_t nonceFHSSresult = receiver.nonceRX % receiver.modParams->fhssHopInterval;
packet[0] = type | (nonceFHSSresult << 2); dmaBuffer[0] = type | (nonceFHSSresult << 2);
} }
uint16_t calculatedCRC = calcCrc14(packet, 7, crcInitializer); uint16_t calculatedCRC = calcCrc14((uint8_t *)dmaBuffer, 7, crcInitializer);
if (inCRC != calculatedCRC) { if (inCRC != calculatedCRC) {
return RX_SPI_RECEIVED_NONE; return RX_SPI_RECEIVED_NONE;
} }
expressLrsEPRRecordEvent(EPR_EXTERNAL, timeStampUs + PACKET_HANDLING_TO_TOCK_ISR_DELAY_US); phaseLockEprEvent(EPR_EXTERNAL, timeStampUs + PACKET_HANDLING_TO_TOCK_ISR_DELAY_US);
bool shouldStartTimer = false; bool shouldStartTimer = false;
uint32_t timeStampMs = millis(); uint32_t timeStampMs = millis();
@ -725,31 +731,32 @@ static rx_spi_received_e processRFPacket(uint8_t *payload, uint32_t timeStampUs)
if (rxExpressLrsSpiConfig()->switchMode == SM_HYBRID_WIDE) { if (rxExpressLrsSpiConfig()->switchMode == SM_HYBRID_WIDE) {
wideSwitchIndex = hybridWideNonceToSwitchIndex(receiver.nonceRX); wideSwitchIndex = hybridWideNonceToSwitchIndex(receiver.nonceRX);
if ((tlmRatioEnumToValue(receiver.modParams->tlmInterval) < 8) || wideSwitchIndex == 7) { if ((tlmRatioEnumToValue(receiver.modParams->tlmInterval) < 8) || wideSwitchIndex == 7) {
confirmCurrentTelemetryPayload((packet[6] & 0x40) >> 6); confirmCurrentTelemetryPayload((dmaBuffer[6] & 0x40) >> 6);
} }
} else { } else {
confirmCurrentTelemetryPayload(packet[6] & (1 << 7)); confirmCurrentTelemetryPayload(dmaBuffer[6] & (1 << 7));
} }
memcpy(payload, &packet[1], 6); // stick data handling is done in expressLrsSetRcDataFromPayload memcpy((uint8_t *)payload, (uint8_t *)&dmaBuffer[1], 6); // stick data handling is done in expressLrsSetRcDataFromPayload
} }
break; break;
case ELRS_MSP_DATA_PACKET: case ELRS_MSP_DATA_PACKET:
processRFMspPacket(packet); processRFMspPacket(dmaBuffer);
break; break;
case ELRS_TLM_PACKET: case ELRS_TLM_PACKET:
//not implemented //not implemented
break; break;
case ELRS_SYNC_PACKET: case ELRS_SYNC_PACKET:
shouldStartTimer = processRFSyncPacket(packet, timeStampMs) && !receiver.inBindingMode; shouldStartTimer = processRFSyncPacket(dmaBuffer, timeStampMs) && !receiver.inBindingMode;
break; break;
default: default:
return RX_SPI_RECEIVED_NONE; return RX_SPI_RECEIVED_NONE;
} }
// Store the LQ/RSSI/Antenna // Store the LQ/RSSI/Antenna
receiver.getRFlinkInfo(&receiver.rssi, &receiver.snr); receiver.getRfLinkInfo(&receiver.rssi, &receiver.snr);
// Received a packet, that's the definition of LQ // Received a packet, that's the definition of LQ
lqIncrease(); lqIncrease();
// Extend sync duration since we've received a packet at this rate // Extend sync duration since we've received a packet at this rate
// but do not extend it indefinitely // but do not extend it indefinitely
receiver.rfModeCycleMultiplier = ELRS_MODE_CYCLE_MULTIPLIER_SLOW; //RFModeCycleMultiplierSlow receiver.rfModeCycleMultiplier = ELRS_MODE_CYCLE_MULTIPLIER_SLOW; //RFModeCycleMultiplierSlow
@ -758,8 +765,6 @@ static rx_spi_received_e processRFPacket(uint8_t *payload, uint32_t timeStampUs)
expressLrsTimerResume(); expressLrsTimerResume();
} }
receiver.fhssRequired = true;
return RX_SPI_RECEIVED_DATA; return RX_SPI_RECEIVED_DATA;
} }
@ -798,10 +803,10 @@ static void cycleRfMode(const uint32_t timeStampMs)
receiver.rfModeCycledAtMs = timeStampMs; receiver.rfModeCycledAtMs = timeStampMs;
receiver.lastSyncPacketMs = timeStampMs; // reset this variable receiver.lastSyncPacketMs = timeStampMs; // reset this variable
receiver.rateIndex = (receiver.rateIndex + 1) % ELRS_RATE_MAX; receiver.rateIndex = (receiver.rateIndex + 1) % ELRS_RATE_MAX;
setRFLinkRate(receiver.rateIndex); // switch between rates setRfLinkRate(receiver.rateIndex); // switch between rates
receiver.statsUpdatedAtMs = timeStampMs; receiver.statsUpdatedAtMs = timeStampMs;
lqReset(); lqReset();
startReceiving(); receiver.startReceiving();
// Switch to FAST_SYNC if not already in it (won't be if was just connected) // Switch to FAST_SYNC if not already in it (won't be if was just connected)
receiver.rfModeCycleMultiplier = 1; receiver.rfModeCycleMultiplier = 1;
@ -815,10 +820,9 @@ static inline void configureReceiverForSX1280(void)
receiver.config = (elrsRxConfigFnPtr) sx1280Config; receiver.config = (elrsRxConfigFnPtr) sx1280Config;
receiver.startReceiving = (elrsRxStartReceivingFnPtr) sx1280StartReceiving; receiver.startReceiving = (elrsRxStartReceivingFnPtr) sx1280StartReceiving;
receiver.rxISR = (elrsRxISRFnPtr) sx1280ISR; receiver.rxISR = (elrsRxISRFnPtr) sx1280ISR;
receiver.transmitData = (elrsRxTransmitDataFnPtr) sx1280TransmitData; receiver.rxHandleFromTock = (elrsRxHandleFromTockFnPtr) sx1280HandleFromTock;
receiver.receiveData = (elrsRxReceiveDataFnPtr) sx1280ReceiveData; receiver.rxHandleFromTick = (elrsRxBusyTimeoutFnPtr) sx1280HandleFromTick;
receiver.getRFlinkInfo = (elrsRxGetRFlinkInfoFnPtr) sx1280GetLastPacketStats; receiver.getRfLinkInfo = (elrsRxgetRfLinkInfoFnPtr) sx1280GetLastPacketStats;
receiver.setFrequency = (elrsRxSetFrequencyFnPtr) sx1280SetFrequencyReg;
receiver.handleFreqCorrection = (elrsRxHandleFreqCorrectionFnPtr) sx1280AdjustFrequency; receiver.handleFreqCorrection = (elrsRxHandleFreqCorrectionFnPtr) sx1280AdjustFrequency;
} }
#endif #endif
@ -830,10 +834,7 @@ static inline void configureReceiverForSX127x(void)
receiver.config = (elrsRxConfigFnPtr) sx127xConfig; receiver.config = (elrsRxConfigFnPtr) sx127xConfig;
receiver.startReceiving = (elrsRxStartReceivingFnPtr) sx127xStartReceiving; receiver.startReceiving = (elrsRxStartReceivingFnPtr) sx127xStartReceiving;
receiver.rxISR = (elrsRxISRFnPtr) sx127xISR; receiver.rxISR = (elrsRxISRFnPtr) sx127xISR;
receiver.transmitData = (elrsRxTransmitDataFnPtr) sx127xTransmitData; receiver.getRfLinkInfo = (elrsRxgetRfLinkInfoFnPtr) sx127xGetLastPacketStats;
receiver.receiveData = (elrsRxReceiveDataFnPtr) sx127xReceiveData;
receiver.getRFlinkInfo = (elrsRxGetRFlinkInfoFnPtr) sx127xGetLastPacketStats;
receiver.setFrequency = (elrsRxSetFrequencyFnPtr) sx127xSetFrequencyReg;
receiver.handleFreqCorrection = (elrsRxHandleFreqCorrectionFnPtr) sx127xAdjustFrequency; receiver.handleFreqCorrection = (elrsRxHandleFreqCorrectionFnPtr) sx127xAdjustFrequency;
} }
#endif #endif
@ -938,7 +939,7 @@ static void handleConnectionStateUpdate(const uint32_t timeStampMs)
{ {
if ((receiver.connectionState != ELRS_DISCONNECTED) && (receiver.modParams->index != receiver.nextRateIndex)) { // forced change if ((receiver.connectionState != ELRS_DISCONNECTED) && (receiver.modParams->index != receiver.nextRateIndex)) { // forced change
lostConnection(); lostConnection();
receiver.lastSyncPacketMs = timeStampMs; // reset this variable to stop rf mode switching and add extra time receiver.lastSyncPacketMs = timeStampMs; // reset this variable to stop rf mode switching and add extra time
receiver.rfModeCycledAtMs = timeStampMs; // reset this variable to stop rf mode switching and add extra time receiver.rfModeCycledAtMs = timeStampMs; // reset this variable to stop rf mode switching and add extra time
setRssiDirect(0, RSSI_SOURCE_RX_PROTOCOL); setRssiDirect(0, RSSI_SOURCE_RX_PROTOCOL);
#ifdef USE_RX_RSSI_DBM #ifdef USE_RX_RSSI_DBM
@ -981,7 +982,8 @@ static void handleConfigUpdate(const uint32_t timeStampMs)
if ((timeStampMs - receiver.configCheckedAtMs) > ELRS_CONFIG_CHECK_MS) { if ((timeStampMs - receiver.configCheckedAtMs) > ELRS_CONFIG_CHECK_MS) {
receiver.configCheckedAtMs = timeStampMs; receiver.configCheckedAtMs = timeStampMs;
if (receiver.configChanged) { if (receiver.configChanged) {
writeEEPROM(); saveConfigAndNotify();
receiver.initializeReceiverPending = true;
receiver.configChanged = false; receiver.configChanged = false;
} }
} }
@ -1018,7 +1020,7 @@ static void handleLinkStatsUpdate(const uint32_t timeStampMs)
} }
} }
static void handleTelemetryUpdate(void) void expressLrsHandleTelemetryUpdate(void)
{ {
if (receiver.connectionState != ELRS_CONNECTED || (receiver.modParams->tlmInterval == TLM_RATIO_NO_TLM)) { if (receiver.connectionState != ELRS_CONNECTED || (receiver.modParams->tlmInterval == TLM_RATIO_NO_TLM)) {
return; return;
@ -1041,75 +1043,48 @@ void expressLrsSetRcDataFromPayload(uint16_t *rcData, const uint8_t *payload)
static void enterBindingMode(void) static void enterBindingMode(void)
{ {
if ((receiver.connectionState == ELRS_CONNECTED) || receiver.inBindingMode) {
// Don't enter binding if:
// - we're already connected
// - we're already binding
return;
}
// Set UID to special binding values // Set UID to special binding values
receiver.UID = BindingUID; receiver.UID = BindingUID;
crcInitializer = 0; crcInitializer = 0;
receiver.inBindingMode = true; receiver.inBindingMode = true;
setRFLinkRate(bindingRateIndex); setRfLinkRate(bindingRateIndex);
startReceiving(); receiver.startReceiving();
} }
static uint32_t isrTimeStampUs; void expressLrsDoTelem(void)
rx_spi_received_e expressLrsDataReceived(uint8_t *payload)
{ {
rx_spi_received_e result = RX_SPI_RECEIVED_NONE; expressLrsHandleTelemetryUpdate();
expressLrsSendTelemResp();
if (rxExpressLrsSpiConfig()->domain != ISM2400 && !receiver.didFhss && !expressLrsTelemRespReq() && lqPeriodIsSet()) {
// TODO No need to handle this on SX1280, but will on SX127x
// TODO this needs to be DMA aswell, SX127x unlikely to work right now
receiver.handleFreqCorrection(receiver.freqOffset, receiver.currentFreq); //corrects for RX freq offset
}
}
rx_spi_received_e expressLrsDataReceived(uint8_t *payloadBuffer)
{
payload = payloadBuffer;
rx_spi_received_e rfPacketReturnStatus = RX_SPI_RECEIVED_NONE;
if (!receiver.started && (systemState & SYSTEM_STATE_READY)) { if (!receiver.started && (systemState & SYSTEM_STATE_READY)) {
receiver.started = true; receiver.started = true;
startReceiving(); // delay receiving after initialization to ensure a clean connect receiver.startReceiving(); // delay receiving after initialization to ensure a clean connect
}
if (receiver.initializeReceiverPending) {
initializeReceiver();
receiver.initializeReceiverPending = false;
} }
if (rxSpiCheckBindRequested(true)) { if (rxSpiCheckBindRequested(true)) {
enterBindingMode(); enterBindingMode();
} }
uint8_t irqReason = receiver.rxISR(&isrTimeStampUs);
if (irqReason == ELRS_DIO_RX_AND_TX_DONE) {
startReceiving();
} else if (irqReason == ELRS_DIO_TX_DONE) {
startReceiving();
} else if (irqReason == ELRS_DIO_RX_DONE) {
result = processRFPacket(payload, isrTimeStampUs);
}
if (receiver.fhssRequired) {
receiver.fhssRequired = false;
bool didFHSS = false;
bool tlmReq = false;
ATOMIC_BLOCK(NVIC_PRIO_TIMER) { // prevent from updating nonce in TICK
didFHSS = handleFHSS();
tlmReq = shouldSendTelemetryResponse();
}
if (tlmReq) {
// in case we miss a packet before TLM we still need to estimate processing time using %
uint32_t processingTime = (micros() - isrTimeStampUs) % receiver.modParams->interval;
if (processingTime < PACKET_HANDLING_TO_TOCK_ISR_DELAY_US && receiver.timerState == ELRS_TIM_LOCKED) {
handleSendTelemetryResponse();
} else {
receiver.alreadyTLMresp = true;
startReceiving();
}
}
if (rxExpressLrsSpiConfig()->domain != ISM2400 && !didFHSS && !tlmReq && lqPeriodIsSet()) {
receiver.handleFreqCorrection(receiver.freqOffset, receiver.currentFreq); //corrects for RX freq offset
}
}
handleTelemetryUpdate();
const uint32_t timeStampMs = millis(); const uint32_t timeStampMs = millis();
handleConnectionStateUpdate(timeStampMs); handleConnectionStateUpdate(timeStampMs);
handleConfigUpdate(timeStampMs); handleConfigUpdate(timeStampMs);
handleLinkStatsUpdate(timeStampMs); handleLinkStatsUpdate(timeStampMs);
@ -1119,9 +1094,14 @@ rx_spi_received_e expressLrsDataReceived(uint8_t *payload)
DEBUG_SET(DEBUG_RX_EXPRESSLRS_SPI, 2, receiver.snr); DEBUG_SET(DEBUG_RX_EXPRESSLRS_SPI, 2, receiver.snr);
DEBUG_SET(DEBUG_RX_EXPRESSLRS_SPI, 3, receiver.uplinkLQ); DEBUG_SET(DEBUG_RX_EXPRESSLRS_SPI, 3, receiver.uplinkLQ);
receiver.inBindingMode ? rxSpiLedBlinkBind() : rxSpiLedBlinkRxLoss(result); receiver.inBindingMode ? rxSpiLedBlinkBind() : rxSpiLedBlinkRxLoss(rfPacketStatus);
return result; if (rfPacketStatus != RX_SPI_RECEIVED_NONE) {
// A packet has been received since last time
rfPacketReturnStatus = rfPacketStatus;
rfPacketStatus = RX_SPI_RECEIVED_NONE;
}
return rfPacketReturnStatus;
} }
void expressLrsStop(void) void expressLrsStop(void)
@ -1131,4 +1111,10 @@ void expressLrsStop(void)
} }
} }
void expressLrsISR(bool runAlways)
{
if (runAlways || !expressLrsTimerIsRunning()) {
receiver.rxISR();
}
}
#endif /* USE_RX_EXPRESSLRS */ #endif /* USE_RX_EXPRESSLRS */

View file

@ -34,4 +34,15 @@
bool expressLrsSpiInit(const struct rxSpiConfig_s *rxConfig, struct rxRuntimeState_s *rxRuntimeState, rxSpiExtiConfig_t *extiConfig); bool expressLrsSpiInit(const struct rxSpiConfig_s *rxConfig, struct rxRuntimeState_s *rxRuntimeState, rxSpiExtiConfig_t *extiConfig);
void expressLrsSetRcDataFromPayload(uint16_t *rcData, const uint8_t *payload); void expressLrsSetRcDataFromPayload(uint16_t *rcData, const uint8_t *payload);
rx_spi_received_e expressLrsDataReceived(uint8_t *payload); rx_spi_received_e expressLrsDataReceived(uint8_t *payload);
rx_spi_received_e processRFPacket(volatile uint8_t *payload, uint32_t timeStampUs);
bool expressLrsIsFhssReq(void);
void expressLrsDoTelem(void);
bool expressLrsTelemRespReq(void);
void expressLrsSetRfPacketStatus(rx_spi_received_e status);
uint32_t expressLrsGetCurrentFreq(void);
volatile uint8_t *expressLrsGetRxBuffer(void);
volatile uint8_t *expressLrsGetTelemetryBuffer(void);
volatile uint8_t *expressLrsGetPayloadBuffer(void);
void expressLrsHandleTelemetryUpdate(void);
void expressLrsStop(void); void expressLrsStop(void);
void expressLrsISR(bool runAlways);

View file

@ -38,9 +38,9 @@
STATIC_UNIT_TESTED uint16_t crc14tab[ELRS_CRC_LEN] = {0}; STATIC_UNIT_TESTED uint16_t crc14tab[ELRS_CRC_LEN] = {0};
static uint8_t volatile FHSSptr = 0; static uint8_t volatile fhssIndex = 0;
STATIC_UNIT_TESTED uint8_t FHSSsequence[ELRS_NR_SEQUENCE_ENTRIES] = {0}; STATIC_UNIT_TESTED uint8_t fhssSequence[ELRS_NR_SEQUENCE_ENTRIES] = {0};
static const uint32_t *FHSSfreqs; static const uint32_t *fhssFreqs;
static uint8_t numFreqs = 0; // The number of FHSS frequencies in the table static uint8_t numFreqs = 0; // The number of FHSS frequencies in the table
static uint8_t seqCount = 0; static uint8_t seqCount = 0;
static uint8_t syncChannel = 0; static uint8_t syncChannel = 0;
@ -96,12 +96,12 @@ elrsRfPerfParams_t rfPerfConfig[][ELRS_RATE_MAX] = {
}; };
#ifdef USE_RX_SX127X #ifdef USE_RX_SX127X
const uint32_t FHSSfreqsAU433[] = { const uint32_t fhssFreqsAU433[] = {
FREQ_HZ_TO_REG_VAL_900(433420000), FREQ_HZ_TO_REG_VAL_900(433420000),
FREQ_HZ_TO_REG_VAL_900(433920000), FREQ_HZ_TO_REG_VAL_900(433920000),
FREQ_HZ_TO_REG_VAL_900(434420000)}; FREQ_HZ_TO_REG_VAL_900(434420000)};
const uint32_t FHSSfreqsAU915[] = { const uint32_t fhssFreqsAU915[] = {
FREQ_HZ_TO_REG_VAL_900(915500000), FREQ_HZ_TO_REG_VAL_900(915500000),
FREQ_HZ_TO_REG_VAL_900(916100000), FREQ_HZ_TO_REG_VAL_900(916100000),
FREQ_HZ_TO_REG_VAL_900(916700000), FREQ_HZ_TO_REG_VAL_900(916700000),
@ -135,7 +135,7 @@ const uint32_t FHSSfreqsAU915[] = {
* Therefore we simply maximize the usage of available spectrum so laboratory testing of the software won't disturb existing * Therefore we simply maximize the usage of available spectrum so laboratory testing of the software won't disturb existing
* 868MHz ISM band traffic too much. * 868MHz ISM band traffic too much.
*/ */
const uint32_t FHSSfreqsEU868[] = { const uint32_t fhssFreqsEU868[] = {
FREQ_HZ_TO_REG_VAL_900(863275000), // band H1, 863 - 865MHz, 0.1% duty cycle or CSMA techniques, 25mW EIRP FREQ_HZ_TO_REG_VAL_900(863275000), // band H1, 863 - 865MHz, 0.1% duty cycle or CSMA techniques, 25mW EIRP
FREQ_HZ_TO_REG_VAL_900(863800000), FREQ_HZ_TO_REG_VAL_900(863800000),
FREQ_HZ_TO_REG_VAL_900(864325000), FREQ_HZ_TO_REG_VAL_900(864325000),
@ -157,7 +157,7 @@ const uint32_t FHSSfreqsEU868[] = {
* There is currently no mention of Direct-sequence spread spectrum, * There is currently no mention of Direct-sequence spread spectrum,
* So these frequencies are a subset of Regulatory_Domain_EU_868 frequencies. * So these frequencies are a subset of Regulatory_Domain_EU_868 frequencies.
*/ */
const uint32_t FHSSfreqsIN866[] = { const uint32_t fhssFreqsIN866[] = {
FREQ_HZ_TO_REG_VAL_900(865375000), FREQ_HZ_TO_REG_VAL_900(865375000),
FREQ_HZ_TO_REG_VAL_900(865900000), FREQ_HZ_TO_REG_VAL_900(865900000),
FREQ_HZ_TO_REG_VAL_900(866425000), FREQ_HZ_TO_REG_VAL_900(866425000),
@ -167,14 +167,14 @@ const uint32_t FHSSfreqsIN866[] = {
* Note: As is the case with the 868Mhz band, these frequencies only comply to the license free portion * Note: As is the case with the 868Mhz band, these frequencies only comply to the license free portion
* of the spectrum, nothing else. As such, these are likely illegal to use. * of the spectrum, nothing else. As such, these are likely illegal to use.
*/ */
const uint32_t FHSSfreqsEU433[] = { const uint32_t fhssFreqsEU433[] = {
FREQ_HZ_TO_REG_VAL_900(433100000), FREQ_HZ_TO_REG_VAL_900(433100000),
FREQ_HZ_TO_REG_VAL_900(433925000), FREQ_HZ_TO_REG_VAL_900(433925000),
FREQ_HZ_TO_REG_VAL_900(434450000)}; FREQ_HZ_TO_REG_VAL_900(434450000)};
/* Very definitely not fully checked. An initial pass at increasing the hops /* Very definitely not fully checked. An initial pass at increasing the hops
*/ */
const uint32_t FHSSfreqsFCC915[] = { const uint32_t fhssFreqsFCC915[] = {
FREQ_HZ_TO_REG_VAL_900(903500000), FREQ_HZ_TO_REG_VAL_900(903500000),
FREQ_HZ_TO_REG_VAL_900(904100000), FREQ_HZ_TO_REG_VAL_900(904100000),
FREQ_HZ_TO_REG_VAL_900(904700000), FREQ_HZ_TO_REG_VAL_900(904700000),
@ -226,7 +226,7 @@ const uint32_t FHSSfreqsFCC915[] = {
FREQ_HZ_TO_REG_VAL_900(926900000)}; FREQ_HZ_TO_REG_VAL_900(926900000)};
#endif #endif
#ifdef USE_RX_SX1280 #ifdef USE_RX_SX1280
const uint32_t FHSSfreqsISM2400[] = { const uint32_t fhssFreqsISM2400[] = {
FREQ_HZ_TO_REG_VAL_24(2400400000), FREQ_HZ_TO_REG_VAL_24(2400400000),
FREQ_HZ_TO_REG_VAL_24(2401400000), FREQ_HZ_TO_REG_VAL_24(2401400000),
FREQ_HZ_TO_REG_VAL_24(2402400000), FREQ_HZ_TO_REG_VAL_24(2402400000),
@ -344,70 +344,70 @@ uint16_t calcCrc14(uint8_t *data, uint8_t len, uint16_t crc)
return crc & 0x3FFF; return crc & 0x3FFF;
} }
static void initializeFHSSFrequencies(const elrsFreqDomain_e dom) { static void initializeFhssFrequencies(const elrsFreqDomain_e dom) {
switch (dom) { switch (dom) {
#ifdef USE_RX_SX127X #ifdef USE_RX_SX127X
case AU433: case AU433:
FHSSfreqs = FHSSfreqsAU433; fhssFreqs = fhssFreqsAU433;
numFreqs = sizeof(FHSSfreqsAU433) / sizeof(uint32_t); numFreqs = sizeof(fhssFreqsAU433) / sizeof(uint32_t);
break; break;
case AU915: case AU915:
FHSSfreqs = FHSSfreqsAU915; fhssFreqs = fhssFreqsAU915;
numFreqs = sizeof(FHSSfreqsAU915) / sizeof(uint32_t); numFreqs = sizeof(fhssFreqsAU915) / sizeof(uint32_t);
break; break;
case EU433: case EU433:
FHSSfreqs = FHSSfreqsEU433; fhssFreqs = fhssFreqsEU433;
numFreqs = sizeof(FHSSfreqsEU433) / sizeof(uint32_t); numFreqs = sizeof(fhssFreqsEU433) / sizeof(uint32_t);
break; break;
case EU868: case EU868:
FHSSfreqs = FHSSfreqsEU868; fhssFreqs = fhssFreqsEU868;
numFreqs = sizeof(FHSSfreqsEU868) / sizeof(uint32_t); numFreqs = sizeof(fhssFreqsEU868) / sizeof(uint32_t);
break; break;
case IN866: case IN866:
FHSSfreqs = FHSSfreqsIN866; fhssFreqs = fhssFreqsIN866;
numFreqs = sizeof(FHSSfreqsIN866) / sizeof(uint32_t); numFreqs = sizeof(fhssFreqsIN866) / sizeof(uint32_t);
break; break;
case FCC915: case FCC915:
FHSSfreqs = FHSSfreqsFCC915; fhssFreqs = fhssFreqsFCC915;
numFreqs = sizeof(FHSSfreqsFCC915) / sizeof(uint32_t); numFreqs = sizeof(fhssFreqsFCC915) / sizeof(uint32_t);
break; break;
#endif #endif
#ifdef USE_RX_SX1280 #ifdef USE_RX_SX1280
case ISM2400: case ISM2400:
FHSSfreqs = FHSSfreqsISM2400; fhssFreqs = fhssFreqsISM2400;
numFreqs = sizeof(FHSSfreqsISM2400) / sizeof(uint32_t); numFreqs = sizeof(fhssFreqsISM2400) / sizeof(uint32_t);
break; break;
#endif #endif
default: default:
FHSSfreqs = NULL; fhssFreqs = NULL;
numFreqs = 0; numFreqs = 0;
} }
} }
uint32_t getInitialFreq(const int32_t freqCorrection) uint32_t fhssGetInitialFreq(const int32_t freqCorrection)
{ {
return FHSSfreqs[syncChannel] - freqCorrection; return fhssFreqs[syncChannel] - freqCorrection;
} }
uint8_t getFHSSNumEntries(void) uint8_t fhssGetNumEntries(void)
{ {
return numFreqs; return numFreqs;
} }
uint8_t FHSSgetCurrIndex(void) uint8_t fhssGetCurrIndex(void)
{ {
return FHSSptr; return fhssIndex;
} }
void FHSSsetCurrIndex(const uint8_t value) void fhssSetCurrIndex(const uint8_t value)
{ {
FHSSptr = value % seqCount; fhssIndex = value % seqCount;
} }
uint32_t FHSSgetNextFreq(const int32_t freqCorrection) uint32_t fhssGetNextFreq(const int32_t freqCorrection)
{ {
FHSSptr = (FHSSptr + 1) % seqCount; fhssIndex = (fhssIndex + 1) % seqCount;
return FHSSfreqs[FHSSsequence[FHSSptr]] - freqCorrection; return fhssFreqs[fhssSequence[fhssIndex]] - freqCorrection;
} }
static uint32_t seed = 0; static uint32_t seed = 0;
@ -435,11 +435,11 @@ Approach:
another random entry, excluding the sync channel. another random entry, excluding the sync channel.
*/ */
void FHSSrandomiseFHSSsequence(const uint8_t UID[], const elrsFreqDomain_e dom) void fhssGenSequence(const uint8_t UID[], const elrsFreqDomain_e dom)
{ {
seed = ((long)UID[2] << 24) + ((long)UID[3] << 16) + ((long)UID[4] << 8) + UID[5]; seed = ((long)UID[2] << 24) + ((long)UID[3] << 16) + ((long)UID[4] << 8) + UID[5];
initializeFHSSFrequencies(dom); initializeFhssFrequencies(dom);
seqCount = (256 / MAX(numFreqs, 1)) * numFreqs; seqCount = (256 / MAX(numFreqs, 1)) * numFreqs;
@ -448,11 +448,11 @@ void FHSSrandomiseFHSSsequence(const uint8_t UID[], const elrsFreqDomain_e dom)
// initialize the sequence array // initialize the sequence array
for (uint8_t i = 0; i < seqCount; i++) { for (uint8_t i = 0; i < seqCount; i++) {
if (i % numFreqs == 0) { if (i % numFreqs == 0) {
FHSSsequence[i] = syncChannel; fhssSequence[i] = syncChannel;
} else if (i % numFreqs == syncChannel) { } else if (i % numFreqs == syncChannel) {
FHSSsequence[i] = 0; fhssSequence[i] = 0;
} else { } else {
FHSSsequence[i] = i % numFreqs; fhssSequence[i] = i % numFreqs;
} }
} }
@ -463,9 +463,9 @@ void FHSSrandomiseFHSSsequence(const uint8_t UID[], const elrsFreqDomain_e dom)
uint8_t rand = rngN(numFreqs - 1) + 1; // random number between 1 and numFreqs uint8_t rand = rngN(numFreqs - 1) + 1; // random number between 1 and numFreqs
// switch this entry and another random entry in the same block // switch this entry and another random entry in the same block
uint8_t temp = FHSSsequence[i]; uint8_t temp = fhssSequence[i];
FHSSsequence[i] = FHSSsequence[offset + rand]; fhssSequence[i] = fhssSequence[offset + rand];
FHSSsequence[offset + rand] = temp; fhssSequence[offset + rand] = temp;
} }
} }
} }

View file

@ -133,10 +133,12 @@ typedef struct elrsRfPerfParams_s {
typedef bool (*elrsRxInitFnPtr)(IO_t resetPin, IO_t busyPin); typedef bool (*elrsRxInitFnPtr)(IO_t resetPin, IO_t busyPin);
typedef void (*elrsRxConfigFnPtr)(const uint8_t bw, const uint8_t sf, const uint8_t cr, const uint32_t freq, const uint8_t preambleLen, const bool iqInverted); typedef void (*elrsRxConfigFnPtr)(const uint8_t bw, const uint8_t sf, const uint8_t cr, const uint32_t freq, const uint8_t preambleLen, const bool iqInverted);
typedef void (*elrsRxStartReceivingFnPtr)(void); typedef void (*elrsRxStartReceivingFnPtr)(void);
typedef uint8_t (*elrsRxISRFnPtr)(timeUs_t *timeStamp); typedef void (*elrsRxISRFnPtr)(void);
typedef void (*elrsRxHandleFromTockFnPtr)(void);
typedef bool (*elrsRxBusyTimeoutFnPtr)(void);
typedef void (*elrsRxTransmitDataFnPtr)(const uint8_t *data, const uint8_t length); typedef void (*elrsRxTransmitDataFnPtr)(const uint8_t *data, const uint8_t length);
typedef void (*elrsRxReceiveDataFnPtr)(uint8_t *data, const uint8_t length); typedef void (*elrsRxReceiveDataFnPtr)(uint8_t *data, const uint8_t length);
typedef void (*elrsRxGetRFlinkInfoFnPtr)(int8_t *rssi, int8_t *snr); typedef void (*elrsRxgetRfLinkInfoFnPtr)(int8_t *rssi, int8_t *snr);
typedef void (*elrsRxSetFrequencyFnPtr)(const uint32_t freq); typedef void (*elrsRxSetFrequencyFnPtr)(const uint32_t freq);
typedef void (*elrsRxHandleFreqCorrectionFnPtr)(int32_t offset, const uint32_t freq); typedef void (*elrsRxHandleFreqCorrectionFnPtr)(int32_t offset, const uint32_t freq);
@ -146,12 +148,12 @@ extern elrsRfPerfParams_t rfPerfConfig[][ELRS_RATE_MAX];
void generateCrc14Table(void); void generateCrc14Table(void);
uint16_t calcCrc14(uint8_t *data, uint8_t len, uint16_t crc); uint16_t calcCrc14(uint8_t *data, uint8_t len, uint16_t crc);
uint32_t getInitialFreq(const int32_t freqCorrection); uint32_t fhssGetInitialFreq(const int32_t freqCorrection);
uint8_t getFHSSNumEntries(void); uint8_t fhssGetNumEntries(void);
uint8_t FHSSgetCurrIndex(void); uint8_t fhssGetCurrIndex(void);
void FHSSsetCurrIndex(const uint8_t value); void fhssSetCurrIndex(const uint8_t value);
uint32_t FHSSgetNextFreq(const int32_t freqCorrection); uint32_t fhssGetNextFreq(const int32_t freqCorrection);
void FHSSrandomiseFHSSsequence(const uint8_t UID[], const elrsFreqDomain_e dom); void fhssGenSequence(const uint8_t UID[], const elrsFreqDomain_e dom);
uint8_t tlmRatioEnumToValue(const elrsTlmRatio_e enumval); uint8_t tlmRatioEnumToValue(const elrsTlmRatio_e enumval);
uint16_t rateEnumToHz(const elrsRfRate_e eRate); uint16_t rateEnumToHz(const elrsRfRate_e eRate);
uint16_t txPowerIndexToValue(const uint8_t index); uint16_t txPowerIndexToValue(const uint8_t index);

View file

@ -71,8 +71,8 @@ typedef struct elrsReceiver_s {
uint8_t uplinkLQ; uint8_t uplinkLQ;
bool alreadyFHSS; bool alreadyFhss;
bool alreadyTLMresp; bool alreadyTelemResp;
bool lockRFmode; bool lockRFmode;
bool started; bool started;
@ -93,7 +93,9 @@ typedef struct elrsReceiver_s {
bool configChanged; bool configChanged;
bool inBindingMode; bool inBindingMode;
volatile bool initializeReceiverPending;
volatile bool fhssRequired; volatile bool fhssRequired;
volatile bool didFhss;
uint32_t statsUpdatedAtMs; uint32_t statsUpdatedAtMs;
@ -101,9 +103,9 @@ typedef struct elrsReceiver_s {
elrsRxConfigFnPtr config; elrsRxConfigFnPtr config;
elrsRxStartReceivingFnPtr startReceiving; elrsRxStartReceivingFnPtr startReceiving;
elrsRxISRFnPtr rxISR; elrsRxISRFnPtr rxISR;
elrsRxTransmitDataFnPtr transmitData; elrsRxHandleFromTockFnPtr rxHandleFromTock;
elrsRxReceiveDataFnPtr receiveData; elrsRxBusyTimeoutFnPtr rxHandleFromTick;
elrsRxGetRFlinkInfoFnPtr getRFlinkInfo; elrsRxgetRfLinkInfoFnPtr getRfLinkInfo;
elrsRxSetFrequencyFnPtr setFrequency; elrsRxSetFrequencyFnPtr setFrequency;
elrsRxHandleFreqCorrectionFnPtr handleFreqCorrection; elrsRxHandleFreqCorrectionFnPtr handleFreqCorrection;

View file

@ -469,13 +469,21 @@ void rxSetUplinkTxPwrMw(uint16_t uplinkTxPwrMwValue)
#endif #endif
bool rxUpdateCheck(timeUs_t currentTimeUs, timeDelta_t currentDeltaTimeUs) bool rxUpdateCheck(timeUs_t currentTimeUs, timeDelta_t currentDeltaTimeUs)
{
UNUSED(currentTimeUs);
UNUSED(currentDeltaTimeUs);
return taskUpdateRxMainInProgress() || rxDataProcessingRequired || auxiliaryProcessingRequired || !failsafeIsReceivingRxData();
}
FAST_CODE_NOINLINE void rxFrameCheck(timeUs_t currentTimeUs, timeDelta_t currentDeltaTimeUs)
{ {
bool signalReceived = false; bool signalReceived = false;
bool useDataDrivenProcessing = true; bool useDataDrivenProcessing = true;
if (taskUpdateRxMainInProgress()) { if (taskUpdateRxMainInProgress()) {
// There are more states to process // No need to check for new data as a packet is being processed already
return true; return;
} }
switch (rxRuntimeState.rxProvider) { switch (rxRuntimeState.rxProvider) {
@ -535,8 +543,6 @@ bool rxUpdateCheck(timeUs_t currentTimeUs, timeDelta_t currentDeltaTimeUs)
if ((signalReceived && useDataDrivenProcessing) || cmpTimeUs(currentTimeUs, rxNextUpdateAtUs) > 0) { if ((signalReceived && useDataDrivenProcessing) || cmpTimeUs(currentTimeUs, rxNextUpdateAtUs) > 0) {
rxDataProcessingRequired = true; rxDataProcessingRequired = true;
} }
return rxDataProcessingRequired || auxiliaryProcessingRequired; // data driven or 50Hz
} }
#if defined(USE_PWM) || defined(USE_PPM) #if defined(USE_PWM) || defined(USE_PPM)

View file

@ -177,6 +177,7 @@ extern rxRuntimeState_t rxRuntimeState; //!!TODO remove this extern, only needed
void rxInit(void); void rxInit(void);
void rxProcessPending(bool state); void rxProcessPending(bool state);
bool rxUpdateCheck(timeUs_t currentTimeUs, timeDelta_t currentDeltaTimeUs); bool rxUpdateCheck(timeUs_t currentTimeUs, timeDelta_t currentDeltaTimeUs);
void rxFrameCheck(timeUs_t currentTimeUs, timeDelta_t currentDeltaTimeUs);
bool rxIsReceivingSignal(void); bool rxIsReceivingSignal(void);
bool rxAreFlightChannelsValid(void); bool rxAreFlightChannelsValid(void);
bool calculateRxChannelsAndUpdateFailsafe(timeUs_t currentTimeUs); bool calculateRxChannelsAndUpdateFailsafe(timeUs_t currentTimeUs);

View file

@ -71,6 +71,11 @@ static protocolProcessFrameFnPtr protocolProcessFrame;
static protocolSetRcDataFromPayloadFnPtr protocolSetRcDataFromPayload; static protocolSetRcDataFromPayloadFnPtr protocolSetRcDataFromPayload;
static protocolStopFnPtr protocolStop = nullProtocolStop; static protocolStopFnPtr protocolStop = nullProtocolStop;
static rxSpiExtiConfig_t extiConfig = {
.ioConfig = IOCFG_IN_FLOATING,
.trigger = BETAFLIGHT_EXTI_TRIGGER_RISING,
};
STATIC_UNIT_TESTED float rxSpiReadRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t channel) STATIC_UNIT_TESTED float rxSpiReadRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t channel)
{ {
STATIC_ASSERT(NRF24L01_MAX_PAYLOAD_SIZE <= RX_SPI_MAX_PAYLOAD_SIZE, NRF24L01_MAX_PAYLOAD_SIZE_larger_than_RX_SPI_MAX_PAYLOAD_SIZE); STATIC_ASSERT(NRF24L01_MAX_PAYLOAD_SIZE <= RX_SPI_MAX_PAYLOAD_SIZE, NRF24L01_MAX_PAYLOAD_SIZE_larger_than_RX_SPI_MAX_PAYLOAD_SIZE);
@ -194,10 +199,8 @@ STATIC_UNIT_TESTED bool rxSpiSetProtocol(rx_spi_protocol_e protocol)
return true; return true;
} }
/* /* Called by scheduler immediately after real-time tasks
* Returns true if the RX has received new data. * Returns true if the RX has received new data.
* Called from updateRx in rx.c, updateRx called from taskUpdateRxCheck.
* If taskUpdateRxCheck returns true, then taskUpdateRxMain will shortly be called.
*/ */
static uint8_t rxSpiFrameStatus(rxRuntimeState_t *rxRuntimeState) static uint8_t rxSpiFrameStatus(rxRuntimeState_t *rxRuntimeState)
{ {
@ -218,7 +221,10 @@ static uint8_t rxSpiFrameStatus(rxRuntimeState_t *rxRuntimeState)
return status; return status;
} }
/* Called from updateRx in rx.c, updateRx called from taskUpdateRxCheck.
* If taskUpdateRxCheck returns true, then taskUpdateRxMain will shortly be called.
*
*/
static bool rxSpiProcessFrame(const rxRuntimeState_t *rxRuntimeState) static bool rxSpiProcessFrame(const rxRuntimeState_t *rxRuntimeState)
{ {
UNUSED(rxRuntimeState); UNUSED(rxRuntimeState);
@ -253,11 +259,6 @@ bool rxSpiInit(const rxSpiConfig_t *rxSpiConfig, rxRuntimeState_t *rxRuntimeStat
return false; return false;
} }
rxSpiExtiConfig_t extiConfig = {
.ioConfig = IOCFG_IN_FLOATING,
.trigger = BETAFLIGHT_EXTI_TRIGGER_RISING,
};
ret = protocolInit(rxSpiConfig, rxRuntimeState, &extiConfig); ret = protocolInit(rxSpiConfig, rxRuntimeState, &extiConfig);
if (rxSpiExtiConfigured()) { if (rxSpiExtiConfigured()) {
@ -276,6 +277,11 @@ bool rxSpiInit(const rxSpiConfig_t *rxSpiConfig, rxRuntimeState_t *rxRuntimeStat
return ret; return ret;
} }
void rxSpiEnableExti(void)
{
rxSpiExtiInit(extiConfig.ioConfig, extiConfig.trigger);
}
void rxSpiStop(void) void rxSpiStop(void)
{ {
protocolStop(); protocolStop();

View file

@ -42,15 +42,17 @@
#include "fc/core.h" #include "fc/core.h"
#include "fc/tasks.h" #include "fc/tasks.h"
#include "rx/rx.h"
#include "flight/failsafe.h"
#include "scheduler.h" #include "scheduler.h"
#include "sensors/gyro_init.h" #include "sensors/gyro_init.h"
// DEBUG_SCHEDULER, timings for: // DEBUG_SCHEDULER, timings for:
// 0 - gyroUpdate() // 0 - Average time spent executing check function
// 1 - pidController() // 1 - Time spent priortising
// 2 - time spent in scheduler // 2 - time spent in scheduler
// 3 - time spent executing check function
// DEBUG_SCHEDULER_DETERMINISM, requires USE_LATE_TASK_STATISTICS to be defined // DEBUG_SCHEDULER_DETERMINISM, requires USE_LATE_TASK_STATISTICS to be defined
// 0 - Gyro task start cycle time in 10th of a us // 0 - Gyro task start cycle time in 10th of a us
@ -104,6 +106,8 @@ static int16_t taskCount = 0;
static uint32_t nextTimingCycles; static uint32_t nextTimingCycles;
#endif #endif
static timeMs_t lastFailsafeCheckMs = 0;
// No need for a linked list for the queue, since items are only inserted at startup // No need for a linked list for the queue, since items are only inserted at startup
STATIC_UNIT_TESTED FAST_DATA_ZERO_INIT task_t* taskQueueArray[TASK_COUNT + 1]; // extra item for NULL pointer at end of queue STATIC_UNIT_TESTED FAST_DATA_ZERO_INIT task_t* taskQueueArray[TASK_COUNT + 1]; // extra item for NULL pointer at end of queue
@ -498,6 +502,20 @@ FAST_CODE void scheduler(void)
if (pidLoopReady()) { if (pidLoopReady()) {
taskExecutionTimeUs += schedulerExecuteTask(getTask(TASK_PID), currentTimeUs); taskExecutionTimeUs += schedulerExecuteTask(getTask(TASK_PID), currentTimeUs);
} }
if (rxFrameReady()) {
// Check to for incoming RX data. Don't do this in the checker as that is called repeatedly within
// a given gyro loop, and ELRS takes a long time to process this and so can only be safely processed
// before the checkers
rxFrameCheck(currentTimeUs, cmpTimeUs(currentTimeUs, getTask(TASK_RX)->lastExecutedAtUs));
}
// Check for failsafe conditions without reliance on the RX task being well behaved
if (cmp32(millis(), lastFailsafeCheckMs) > PERIOD_RXDATA_FAILURE) {
// This is very low cost taking less that 4us every 200ms
failsafeCheckDataFailurePeriod();
failsafeUpdateState();
lastFailsafeCheckMs = millis();
}
#if defined(USE_LATE_TASK_STATISTICS) #if defined(USE_LATE_TASK_STATISTICS)
// % CPU busy // % CPU busy
@ -587,9 +605,6 @@ FAST_CODE void scheduler(void)
task->dynamicPriority = 1 + task->attribute->staticPriority * task->taskAgePeriods; task->dynamicPriority = 1 + task->attribute->staticPriority * task->taskAgePeriods;
} else if (task->attribute->checkFunc(currentTimeUs, cmpTimeUs(currentTimeUs, task->lastExecutedAtUs))) { } else if (task->attribute->checkFunc(currentTimeUs, cmpTimeUs(currentTimeUs, task->lastExecutedAtUs))) {
const uint32_t checkFuncExecutionTimeUs = cmpTimeUs(micros(), currentTimeUs); const uint32_t checkFuncExecutionTimeUs = cmpTimeUs(micros(), currentTimeUs);
#if !defined(UNIT_TEST)
DEBUG_SET(DEBUG_SCHEDULER, 3, checkFuncExecutionTimeUs);
#endif
checkFuncMovingSumExecutionTimeUs += checkFuncExecutionTimeUs - checkFuncMovingSumExecutionTimeUs / TASK_STATS_MOVING_SUM_COUNT; checkFuncMovingSumExecutionTimeUs += checkFuncExecutionTimeUs - checkFuncMovingSumExecutionTimeUs / TASK_STATS_MOVING_SUM_COUNT;
checkFuncMovingSumDeltaTimeUs += task->taskLatestDeltaTimeUs - checkFuncMovingSumDeltaTimeUs / TASK_STATS_MOVING_SUM_COUNT; checkFuncMovingSumDeltaTimeUs += task->taskLatestDeltaTimeUs - checkFuncMovingSumDeltaTimeUs / TASK_STATS_MOVING_SUM_COUNT;
checkFuncTotalExecutionTimeUs += checkFuncExecutionTimeUs; // time consumed by scheduler + task checkFuncTotalExecutionTimeUs += checkFuncExecutionTimeUs; // time consumed by scheduler + task

0
src/main/startup/startup_stm32g474xx.s Executable file → Normal file
View file

View file

@ -125,7 +125,7 @@
#define USE_LED_STRIP #define USE_LED_STRIP
#define ENABLE_DSHOT_DMAR DSHOT_DMAR_AUTO #define ENABLE_DSHOT_DMAR DSHOT_DMAR_AUTO
#define DSHOT_BITBANG_DEFAULT DSHOT_BITBANG_ON #define DSHOT_BITBANG_DEFAULT DSHOT_BITBANG_OFF
#define USE_PINIO #define USE_PINIO
//#define PINIO1_PIN PB5 // VTX switcher //#define PINIO1_PIN PB5 // VTX switcher

View file

@ -494,6 +494,7 @@ extern "C" {
bool areMotorsRunning(void){ return true; } bool areMotorsRunning(void){ return true; }
bool pidOsdAntiGravityActive(void) { return false; } bool pidOsdAntiGravityActive(void) { return false; }
bool failsafeIsActive(void) { return false; } bool failsafeIsActive(void) { return false; }
bool failsafeIsReceivingRxData(void) { return true; }
bool gpsIsHealthy(void) { return true; } bool gpsIsHealthy(void) { return true; }
bool gpsRescueIsConfigured(void) { return false; } bool gpsRescueIsConfigured(void) { return false; }
int8_t calculateThrottlePercent(void) { return 0; } int8_t calculateThrottlePercent(void) { return 0; }

View file

@ -107,6 +107,7 @@ extern "C" {
void failsafeOnRxSuspend(uint32_t ) {} void failsafeOnRxSuspend(uint32_t ) {}
void failsafeOnRxResume(void) {} void failsafeOnRxResume(void) {}
bool failsafeIsReceivingRxData(void) { return true; }
bool taskUpdateRxMainInProgress() { return true; } bool taskUpdateRxMainInProgress() { return true; }
uint32_t micros(void) { return 0; } uint32_t micros(void) { return 0; }

View file

@ -210,6 +210,7 @@ extern "C" {
void failsafeOnRxSuspend(uint32_t ) {} void failsafeOnRxSuspend(uint32_t ) {}
void failsafeOnRxResume(void) {} void failsafeOnRxResume(void) {}
bool failsafeIsReceivingRxData(void) { return true; }
uint32_t micros(void) { return 0; } uint32_t micros(void) { return 0; }
uint32_t millis(void) { return 0; } uint32_t millis(void) { return 0; }

View file

@ -45,7 +45,7 @@ extern "C" {
#include "drivers/rx/rx_sx127x.h" #include "drivers/rx/rx_sx127x.h"
#include "drivers/rx/rx_sx1280.h" #include "drivers/rx/rx_sx1280.h"
extern uint8_t FHSSsequence[ELRS_NR_SEQUENCE_ENTRIES]; extern uint8_t fhssSequence[ELRS_NR_SEQUENCE_ENTRIES];
extern uint16_t crc14tab[ELRS_CRC_LEN]; extern uint16_t crc14tab[ELRS_CRC_LEN];
extern elrsReceiver_t receiver; extern elrsReceiver_t receiver;
@ -164,14 +164,14 @@ TEST(RxSpiExpressLrsUnitTest, TestFHSSTable)
} }
}; };
FHSSrandomiseFHSSsequence(UID, ISM2400); fhssGenSequence(UID, ISM2400);
for (int i = 0; i < ELRS_NR_SEQUENCE_ENTRIES; i++) { for (int i = 0; i < ELRS_NR_SEQUENCE_ENTRIES; i++) {
EXPECT_EQ(expectedSequence[0][i], FHSSsequence[i]); EXPECT_EQ(expectedSequence[0][i], fhssSequence[i]);
} }
FHSSrandomiseFHSSsequence(UID, FCC915); fhssGenSequence(UID, FCC915);
for (int i = 0; i < ELRS_NR_SEQUENCE_ENTRIES; i++) { for (int i = 0; i < ELRS_NR_SEQUENCE_ENTRIES; i++) {
EXPECT_EQ(expectedSequence[1][i], FHSSsequence[i]); EXPECT_EQ(expectedSequence[1][i], fhssSequence[i]);
} }
} }
@ -390,7 +390,8 @@ extern "C" {
IO_t IOGetByTag(ioTag_t ) { return (IO_t)1; } IO_t IOGetByTag(ioTag_t ) { return (IO_t)1; }
void IOHi(IO_t ) {} void IOHi(IO_t ) {}
void IOLo(IO_t ) {} void IOLo(IO_t ) {}
void writeEEPROM(void) {}
void saveConfigAndNotify(void) {}
void rxSpiCommonIOInit(const rxSpiConfig_t *) {} void rxSpiCommonIOInit(const rxSpiConfig_t *) {}
void rxSpiLedBlinkRxLoss(rx_spi_received_e ) {} void rxSpiLedBlinkRxLoss(rx_spi_received_e ) {}
@ -404,11 +405,10 @@ extern "C" {
bool sx1280IsBusy(void) { return false; } bool sx1280IsBusy(void) { return false; }
void sx1280Config(const sx1280LoraBandwidths_e , const sx1280LoraSpreadingFactors_e , const sx1280LoraCodingRates_e , const uint32_t , const uint8_t , const bool ) {} void sx1280Config(const sx1280LoraBandwidths_e , const sx1280LoraSpreadingFactors_e , const sx1280LoraCodingRates_e , const uint32_t , const uint8_t , const bool ) {}
void sx1280StartReceiving(void) {} void sx1280StartReceiving(void) {}
uint8_t sx1280ISR(uint32_t *timestamp) void sx1280ISR(void) {}
{ bool rxSpiGetExtiState(void) { return false; }
*timestamp = 0; void sx1280HandleFromTock(void) {}
return 0; bool sx1280HandleFromTick(void) { return false; }
}
void sx1280TransmitData(const uint8_t *, const uint8_t ) {} void sx1280TransmitData(const uint8_t *, const uint8_t ) {}
void sx1280ReceiveData(uint8_t *, const uint8_t ) {} void sx1280ReceiveData(uint8_t *, const uint8_t ) {}
void sx1280SetFrequencyReg(const uint32_t ) {} void sx1280SetFrequencyReg(const uint32_t ) {}
@ -469,5 +469,4 @@ extern "C" {
void getCurrentTelemetryPayload(uint8_t *, uint8_t *, uint8_t **) {} void getCurrentTelemetryPayload(uint8_t *, uint8_t *, uint8_t **) {}
void confirmCurrentTelemetryPayload(const bool ) {} void confirmCurrentTelemetryPayload(const bool ) {}
void updateTelemetryRate(const uint16_t , const uint8_t , const uint8_t ) {} void updateTelemetryRate(const uint16_t , const uint8_t , const uint8_t ) {}
} }

View file

@ -67,9 +67,13 @@ extern "C" {
int16_t debug[1]; int16_t debug[1];
uint8_t debugMode = 0; uint8_t debugMode = 0;
bool rxFrameReady(void) { return 0; }
void rxFrameCheck(timeUs_t, timeDelta_t) {}
// set up micros() to simulate time // set up micros() to simulate time
uint32_t simulatedTime = 0; uint32_t simulatedTime = 0;
uint32_t micros(void) { return simulatedTime; } uint32_t micros(void) { return simulatedTime; }
uint32_t millis(void) { return simulatedTime/1000; } // Note simplistic mapping suitable only for short unit tests
uint32_t clockCyclesToMicros(uint32_t x) { return x/10;} uint32_t clockCyclesToMicros(uint32_t x) { return x/10;}
int32_t clockCyclesTo10thMicros(int32_t x) { return x;} int32_t clockCyclesTo10thMicros(int32_t x) { return x;}
uint32_t clockMicrosToCycles(uint32_t x) { return x*10;} uint32_t clockMicrosToCycles(uint32_t x) { return x*10;}
@ -78,6 +82,8 @@ extern "C" {
// set up tasks to take a simulated representative time to execute // set up tasks to take a simulated representative time to execute
bool gyroFilterReady(void) { return taskFilterReady; } bool gyroFilterReady(void) { return taskFilterReady; }
bool pidLoopReady(void) { return taskPidReady; } bool pidLoopReady(void) { return taskPidReady; }
void failsafeCheckDataFailurePeriod(void) {}
void failsafeUpdateState(void) {}
void taskGyroSample(timeUs_t) { simulatedTime += TEST_GYRO_SAMPLE_TIME; taskGyroRan = true; } void taskGyroSample(timeUs_t) { simulatedTime += TEST_GYRO_SAMPLE_TIME; taskGyroRan = true; }
void taskFiltering(timeUs_t) { simulatedTime += TEST_FILTERING_TIME; taskFilterRan = true; } void taskFiltering(timeUs_t) { simulatedTime += TEST_FILTERING_TIME; taskFilterRan = true; }
void taskMainPidLoop(timeUs_t) { simulatedTime += TEST_PID_LOOP_TIME; taskPidRan = true; } void taskMainPidLoop(timeUs_t) { simulatedTime += TEST_PID_LOOP_TIME; taskPidRan = true; }

View file

@ -1,13 +0,0 @@
board_name CRAZYBEEF4SX1280
manufacturer_id HAMO
resource RX_SPI_CS 1 A15
resource RX_SPI_EXTI 1 C14
resource RX_SPI_BIND 1 B02
resource RX_SPI_LED 1 B09
resource RX_SPI_EXPRESSLRS_RESET 1 A08
resource RX_SPI_EXPRESSLRS_BUSY 1 A13
set expresslrs_domain = ISM2400
set expresslrs_rate_index = 0
set expresslrs_switch_mode = HYBRID