move packet handling into its own thread

1.2-legacy
geeksville 2020-05-01 12:11:04 -07:00
rodzic 763276a2c8
commit 50213d8323
10 zmienionych plików z 178 dodań i 27 usunięć

Wyświetl plik

@ -62,6 +62,7 @@ Needed to be fully functional at least at the same level of the ESP32 boards. At
Nice ideas worth considering someday...
- turn on freertos stack size checking
- Currently we use Nordic's vendor ID, which is apparently okay: https://devzone.nordicsemi.com/f/nordic-q-a/44014/using-nordic-vid-and-pid-for-nrf52840 and I just picked a PID of 0x4403
- Use NRF logger module (includes flash logging etc...) instead of DEBUG_MSG
- Use "LED softblink" library on NRF52 to do nice pretty "breathing" LEDs. Don't whack LED from main thread anymore.

Wyświetl plik

@ -0,0 +1,44 @@
#include "WorkerThread.h"
#include <assert.h>
void Thread::start(const char *name, size_t stackSize, uint32_t priority)
{
auto r = xTaskCreate(callRun, name, stackSize, this, priority, &taskHandle);
assert(r == pdPASS);
}
void Thread::callRun(void *_this)
{
((Thread *)_this)->doRun();
}
void WorkerThread::doRun()
{
while (!wantExit) {
block();
loop();
}
}
/**
* Notify this thread so it can run
*/
void NotifiedWorkerThread::notify(uint32_t v, eNotifyAction action)
{
xTaskNotify(taskHandle, v, action);
}
/**
* Notify from an ISR
*/
void NotifiedWorkerThread::notifyFromISR(BaseType_t *highPriWoken, uint32_t v, eNotifyAction action)
{
xTaskNotifyFromISR(taskHandle, v, action, highPriWoken);
}
void NotifiedWorkerThread::block()
{
xTaskNotifyWait(0, // don't clear notification on entry
0, // do not reset notification value on read
&notification, portMAX_DELAY); // Wait forever
}

77
src/WorkerThread.h 100644
Wyświetl plik

@ -0,0 +1,77 @@
#include <Arduino.h>
class Thread
{
protected:
TaskHandle_t taskHandle = NULL;
/**
* set this to true to ask thread to cleanly exit asap
*/
volatile bool wantExit = false;
public:
void start(const char *name, size_t stackSize = 1024, uint32_t priority = tskIDLE_PRIORITY);
virtual ~Thread() { vTaskDelete(taskHandle); }
protected:
/**
* The method that will be called when start is called.
*/
virtual void doRun() = 0;
private:
static void callRun(void *_this);
};
/**
* This wraps threading (FreeRTOS for now) with a blocking API intended for efficiently converting onlyschool arduino loop() code.
*
* Use as a mixin base class for the classes you want to convert.
*
* https://www.freertos.org/RTOS_Task_Notification_As_Mailbox.html
*/
class WorkerThread : public Thread
{
protected:
/**
* A method that should block execution - either waiting ona queue/mutex or a "task notification"
*/
virtual void block() = 0;
virtual void loop() = 0;
/**
* The method that will be called when start is called.
*/
virtual void doRun();
};
/**
* A worker thread that waits on a freertos notification
*/
class NotifiedWorkerThread : public WorkerThread
{
public:
/**
* Notify this thread so it can run
*/
void notify(uint32_t v = 0, eNotifyAction action = eNoAction);
/**
* Notify from an ISR
*/
void notifyFromISR(BaseType_t *highPriWoken, uint32_t v = 0, eNotifyAction action = eNoAction);
protected:
/**
* The notification that was most recently used to wake the thread. Read from loop()
*/
uint32_t notification = 0;
/**
* A method that should block execution - either waiting ona queue/mutex or a "task notification"
*/
virtual void block();
};

Wyświetl plik

@ -14,6 +14,8 @@ RF95Interface::RF95Interface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOL
/// \return true if initialisation succeeded.
bool RF95Interface::init()
{
RadioLibInterface::init();
applyModemConfig();
if (power > 20) // This chip has lower power limits than some
power = 20;
@ -25,7 +27,7 @@ bool RF95Interface::init()
if (res == ERR_NONE)
res = lora->setCRC(SX126X_LORA_CRC_ON);
if (res == ERR_NONE)
if (res == ERR_NONE)
startReceive(); // start receiving
return res == ERR_NONE;

Wyświetl plik

@ -7,11 +7,20 @@
#include <pb_decode.h>
#include <pb_encode.h>
#define RADIO_STACK_SIZE 4096
RadioInterface::RadioInterface() : txQueue(MAX_TX_QUEUE)
{
assert(sizeof(PacketHeader) == 4); // make sure the compiler did what we expected
}
bool RadioInterface::init()
{
start("radio", RADIO_STACK_SIZE); // Start our worker thread
return true;
}
ErrorCode SimRadio::send(MeshPacket *p)
{
DEBUG_MSG("SimRadio.send\n");

Wyświetl plik

@ -3,6 +3,7 @@
#include "MemoryPool.h"
#include "MeshTypes.h"
#include "PointerQueue.h"
#include "WorkerThread.h"
#include "mesh.pb.h"
#define MAX_TX_QUEUE 16 // max number of packets which can be waiting for transmission
@ -29,7 +30,7 @@ typedef enum {
*
* This defines the SOLE API for talking to radios (because soon we will have alternate radio implementations)
*/
class RadioInterface
class RadioInterface : protected NotifiedWorkerThread
{
friend class MeshRadio; // for debugging we let that class touch pool
PointerQueue<MeshPacket> *rxDest = NULL;
@ -64,8 +65,6 @@ class RadioInterface
*/
void setReceiver(PointerQueue<MeshPacket> *_rxDest) { rxDest = _rxDest; }
virtual void loop() {} // Idle processing
/**
* Return true if we think the board can go to sleep (i.e. our tx queue is empty, we are not sending or receiving)
*
@ -88,7 +87,7 @@ class RadioInterface
/// Initialise the Driver transport hardware and software.
/// Make sure the Driver is properly configured before calling init().
/// \return true if initialisation succeeded.
virtual bool init() = 0;
virtual bool init();
/// Apply any radio provisioning changes
/// Make sure the Driver is properly configured before calling init().
@ -103,6 +102,8 @@ class RadioInterface
* Used as the first step of
*/
size_t beginSending(MeshPacket *p);
virtual void loop() {} // Idle processing
};
class SimRadio : public RadioInterface

Wyświetl plik

@ -17,16 +17,39 @@ RadioLibInterface::RadioLibInterface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq
instance = this;
}
#ifndef NO_ESP32
// ESP32 doesn't use that flag
#define YIELD_FROM_ISR(x) portYIELD_FROM_ISR()
#else
#define YIELD_FROM_ISR(x) portYIELD_FROM_ISR(x)
#endif
void INTERRUPT_ATTR RadioLibInterface::isrRxLevel0()
{
instance->pending = ISR_RX;
instance->disableInterrupt();
instance->pending = ISR_RX;
BaseType_t xHigherPriorityTaskWoken;
instance->notifyFromISR(&xHigherPriorityTaskWoken);
/* Force a context switch if xHigherPriorityTaskWoken is now set to pdTRUE.
The macro used to do this is dependent on the port and may be called
portEND_SWITCHING_ISR. */
YIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
void INTERRUPT_ATTR RadioLibInterface::isrTxLevel0()
{
instance->pending = ISR_TX;
instance->disableInterrupt();
instance->pending = ISR_TX;
BaseType_t xHigherPriorityTaskWoken;
instance->notifyFromISR(&xHigherPriorityTaskWoken);
/* Force a context switch if xHigherPriorityTaskWoken is now set to pdTRUE.
The macro used to do this is dependent on the port and may be called
portEND_SWITCHING_ISR. */
YIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
/** Our ISR code currently needs this to find our active instance
@ -117,19 +140,17 @@ bool RadioLibInterface::canSleep()
void RadioLibInterface::loop()
{
PendingISR wasPending;
while ((wasPending = pending) != 0) { // atomic read
pending = ISR_NONE; // If the flag was set, it is _guaranteed_ the ISR won't be running, because it masked itself
PendingISR wasPending = pending;
pending = ISR_NONE;
if (wasPending == ISR_TX)
handleTransmitInterrupt();
else if (wasPending == ISR_RX)
handleReceiveInterrupt();
else
assert(0);
if (wasPending == ISR_TX)
handleTransmitInterrupt();
else if (wasPending == ISR_RX)
handleReceiveInterrupt();
else
assert(0); // We expected to receive a valid notification from the ISR
startNextWork();
}
startNextWork();
}
void RadioLibInterface::startNextWork()

Wyświetl plik

@ -13,10 +13,9 @@
class RadioLibInterface : public RadioInterface
{
/// Used as our notification from the ISR
enum PendingISR { ISR_NONE = 0, ISR_RX, ISR_TX };
/**
* What sort of interrupt do we expect our helper thread to now handle */
volatile PendingISR pending = ISR_NONE;
/** Our ISR code currently needs this to find our active instance
@ -73,10 +72,6 @@ class RadioLibInterface : public RadioInterface
virtual ErrorCode send(MeshPacket *p);
// methods from radiohead
virtual void loop(); // Idle processing
/**
* Return true if we think the board can go to sleep (i.e. our tx queue is empty, we are not sending or receiving)
*
@ -124,4 +119,6 @@ class RadioLibInterface : public RadioInterface
* Add SNR data to received messages
*/
virtual void addReceiveMetadata(MeshPacket *mp) = 0;
virtual void loop(); // Idle processing
};

Wyświetl plik

@ -35,9 +35,6 @@ Router::Router() : fromRadioQueue(MAX_RX_FROMRADIO) {}
*/
void Router::loop()
{
if (iface)
iface->loop();
MeshPacket *mp;
while ((mp = fromRadioQueue.dequeuePtr(0)) != NULL) {
handleReceived(mp);

Wyświetl plik

@ -12,6 +12,8 @@ SX1262Interface::SX1262Interface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RA
/// \return true if initialisation succeeded.
bool SX1262Interface::init()
{
RadioLibInterface::init();
float tcxoVoltage = 0; // None - we use an XTAL
bool useRegulatorLDO = false; // Seems to depend on the connection to pin 9/DCC_SW - if an inductor DCDC?