mirror of
https://github.com/manuelbl/ttn-esp32.git
synced 2025-06-15 12:24:27 +02:00
New job scheduling implementation
This commit is contained in:
parent
d7d4a40c4f
commit
a601c2b2bf
@ -27,20 +27,20 @@ static const char * const TAG = "ttn_hal";
|
|||||||
HAL_ESP32 ttn_hal;
|
HAL_ESP32 ttn_hal;
|
||||||
|
|
||||||
|
|
||||||
struct HALQueueItem {
|
struct HALQueueItem
|
||||||
ostime_t time;
|
{
|
||||||
|
uint32_t osTime;
|
||||||
HAL_Event ev;
|
HAL_Event ev;
|
||||||
|
|
||||||
HALQueueItem() : time(0), ev(DIO0) { }
|
HALQueueItem() : osTime(0), ev(DIO0) { }
|
||||||
HALQueueItem(HAL_Event e, ostime_t t = 0)
|
HALQueueItem(HAL_Event e, int64_t t = 0) : osTime(t), ev(e) { }
|
||||||
: time(t), ev(e) { }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Constructor
|
// Constructor
|
||||||
|
|
||||||
HAL_ESP32::HAL_ESP32()
|
HAL_ESP32::HAL_ESP32()
|
||||||
: rssiCal(10), nextTimerEvent(0x200000000)
|
: rssiCal(10), nextAlarm(0), isTimerArmed(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,10 +60,8 @@ void HAL_ESP32::configurePins(spi_host_device_t spi_host, uint8_t nss, uint8_t r
|
|||||||
|
|
||||||
void IRAM_ATTR HAL_ESP32::dioIrqHandler(void *arg)
|
void IRAM_ATTR HAL_ESP32::dioIrqHandler(void *arg)
|
||||||
{
|
{
|
||||||
uint64_t now;
|
|
||||||
timer_get_counter_value(TTN_TIMER_GROUP, TTN_TIMER, &now);
|
|
||||||
BaseType_t higherPrioTaskWoken = pdFALSE;
|
BaseType_t higherPrioTaskWoken = pdFALSE;
|
||||||
HALQueueItem item { (HAL_Event)(long)arg, (ostime_t)now };
|
HALQueueItem item { (HAL_Event)(long)arg, hal_ticks() };
|
||||||
xQueueSendFromISR(ttn_hal.dioQueue, &item, &higherPrioTaskWoken);
|
xQueueSendFromISR(ttn_hal.dioQueue, &item, &higherPrioTaskWoken);
|
||||||
if (higherPrioTaskWoken)
|
if (higherPrioTaskWoken)
|
||||||
portYIELD_FROM_ISR();
|
portYIELD_FROM_ISR();
|
||||||
@ -94,9 +92,12 @@ void HAL_ESP32::ioInit()
|
|||||||
gpio_set_direction(pinRst, GPIO_MODE_OUTPUT);
|
gpio_set_direction(pinRst, GPIO_MODE_OUTPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// queue to communicate from interrupts / timer callbacks
|
||||||
|
// to LMIC core
|
||||||
dioQueue = xQueueCreate(12, sizeof(HALQueueItem));
|
dioQueue = xQueueCreate(12, sizeof(HALQueueItem));
|
||||||
ASSERT(dioQueue != NULL);
|
ASSERT(dioQueue != NULL);
|
||||||
|
|
||||||
|
// DIO pins with interrupt handlers
|
||||||
gpio_pad_select_gpio(pinDIO0);
|
gpio_pad_select_gpio(pinDIO0);
|
||||||
gpio_set_direction(pinDIO0, GPIO_MODE_INPUT);
|
gpio_set_direction(pinDIO0, GPIO_MODE_INPUT);
|
||||||
gpio_set_intr_type(pinDIO0, GPIO_INTR_POSEDGE);
|
gpio_set_intr_type(pinDIO0, GPIO_INTR_POSEDGE);
|
||||||
@ -150,6 +151,11 @@ bit_t hal_queryUsingTcxo(void)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t hal_getTxPowerPolicy(u1_t inputPolicy, s1_t requestedPower, u4_t frequency)
|
||||||
|
{
|
||||||
|
return LMICHAL_radio_tx_power_policy_paboost;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// SPI
|
// SPI
|
||||||
@ -210,85 +216,87 @@ void HAL_ESP32::spiRead(uint8_t cmd, uint8_t *buf, size_t len)
|
|||||||
// TIME
|
// TIME
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* LIMIC uses a 32 bit time (ostime_t) counting ticks. In this implementation
|
* LIMIC uses a 32 bit time system (ostime_t) counting ticks. In this
|
||||||
* each tick is 16µs. So the timer will wrap around once every 19 hour.
|
* implementation each tick is 16µs. It will wrap arounnd every 19 hours.
|
||||||
* The timer alarm should trigger when a specific value has been reached.
|
|
||||||
* Due to the wrap around, an alarm time in the future can have a lower value
|
|
||||||
* than the current timer value.
|
|
||||||
*
|
*
|
||||||
* ESP32 has 64 bits counters with a pecularity: the alarm does not only
|
* The ESP32 has a 64 bit timer counting microseconds. It will wrap around
|
||||||
* trigger when the exact value has been reached but also when the clock is
|
* every 584,000 years. So we don't need to bother.
|
||||||
* higer than the alarm value. Therefore, the wrap around is more difficult to
|
|
||||||
* handle.
|
|
||||||
*
|
*
|
||||||
* The approach here is to always use a higher value than the current timer
|
* Based on this timer, future callbacks can be scheduled. This is used to
|
||||||
* value. If it would be lower than the timer value, 0x100000000 is added.
|
* schedule the next LMIC job.
|
||||||
* The lower 32 bits still represent the desired value. After the timer has
|
|
||||||
* triggered an alarm and is higher than 0x100000000, it's value is reduced
|
|
||||||
* by 0x100000000.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static const ostime_t OVERRUN_TRESHOLD = 0x10000; // approx 10 seconds
|
// Convert LMIC tick time (ostime_t) to ESP absolute time.
|
||||||
|
// `osTime` is assumed to be somewhere between one hour in the past and
|
||||||
|
// 18 hours into the future.
|
||||||
|
int64_t HAL_ESP32::osTimeToEspTime(int64_t espNow, uint32_t osTime)
|
||||||
|
{
|
||||||
|
int64_t espTime;
|
||||||
|
uint32_t osNow = (uint32_t)(espNow >> 4);
|
||||||
|
|
||||||
|
// unsigned difference:
|
||||||
|
// 0x00000000 - 0xefffffff: future (0 to about 18 hours)
|
||||||
|
// 0xf0000000 - 0xffffffff: past (about 1 to 0 hours)
|
||||||
|
uint32_t osDiff = osTime - osNow;
|
||||||
|
if (osDiff < 0xf0000000)
|
||||||
|
{
|
||||||
|
espTime = espNow + (((int64_t)osDiff) << 4);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// one's complement instead of two's complement:
|
||||||
|
// off by 1 µs and ignored
|
||||||
|
osDiff = ~osDiff;
|
||||||
|
espTime = espNow - (((int64_t)osDiff) << 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
return espTime;
|
||||||
|
}
|
||||||
|
|
||||||
void HAL_ESP32::timerInit()
|
void HAL_ESP32::timerInit()
|
||||||
{
|
{
|
||||||
timer_config_t config = {
|
esp_timer_create_args_t timerConfig = {
|
||||||
.alarm_en = false,
|
.callback = &timerCallback,
|
||||||
.counter_en = false,
|
.arg = NULL,
|
||||||
.intr_type = TIMER_INTR_LEVEL,
|
.dispatch_method = ESP_TIMER_TASK,
|
||||||
.counter_dir = TIMER_COUNT_UP,
|
.name = "lmic_job"
|
||||||
.auto_reload = false,
|
|
||||||
.divider = 1280 /* 80 MHz APB_CLK * 16µs */
|
|
||||||
};
|
};
|
||||||
timer_init(TTN_TIMER_GROUP, TTN_TIMER, &config);
|
esp_err_t err = esp_timer_create(&timerConfig, &timer);
|
||||||
timer_set_counter_value(TTN_TIMER_GROUP, TTN_TIMER, 0x0);
|
ESP_ERROR_CHECK(err);
|
||||||
timer_isr_register(TTN_TIMER_GROUP, TTN_TIMER, timerIrqHandler, NULL, ESP_INTR_FLAG_IRAM, NULL);
|
|
||||||
timer_start(TTN_TIMER_GROUP, TTN_TIMER);
|
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Timer initialized");
|
ESP_LOGI(TAG, "Timer initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
void HAL_ESP32::prepareNextAlarm(u4_t time)
|
void HAL_ESP32::setNextAlarm(int64_t time)
|
||||||
{
|
{
|
||||||
uint64_t now;
|
nextAlarm = time;
|
||||||
timer_get_counter_value(TTN_TIMER_GROUP, TTN_TIMER, &now);
|
|
||||||
u4_t now32 = (u4_t)now;
|
|
||||||
|
|
||||||
if (now != now32)
|
|
||||||
{
|
|
||||||
// decrease timer to 32 bit value
|
|
||||||
now = now32;
|
|
||||||
timer_pause(TTN_TIMER_GROUP, TTN_TIMER);
|
|
||||||
timer_set_counter_value(TTN_TIMER_GROUP, TTN_TIMER, now);
|
|
||||||
timer_start(TTN_TIMER_GROUP, TTN_TIMER);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nextTimerEvent = time;
|
void HAL_ESP32::armTimer(int64_t espNow)
|
||||||
if (now32 > time && now32 - time > OVERRUN_TRESHOLD)
|
|
||||||
nextTimerEvent += 0x100000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HAL_ESP32::armTimer()
|
|
||||||
{
|
{
|
||||||
timer_set_alarm(TTN_TIMER_GROUP, TTN_TIMER, TIMER_ALARM_DIS);
|
if (isTimerArmed)
|
||||||
timer_set_alarm_value(TTN_TIMER_GROUP, TTN_TIMER, nextTimerEvent);
|
esp_timer_stop(timer);
|
||||||
timer_set_alarm(TTN_TIMER_GROUP, TTN_TIMER, TIMER_ALARM_EN);
|
if (nextAlarm == 0)
|
||||||
|
return;
|
||||||
|
int64_t timeout = nextAlarm - esp_timer_get_time();
|
||||||
|
if (timeout < 0)
|
||||||
|
timeout = 10;
|
||||||
|
esp_timer_start_once(timer, timeout);
|
||||||
|
isTimerArmed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HAL_ESP32::disarmTimer()
|
void HAL_ESP32::disarmTimer()
|
||||||
{
|
{
|
||||||
timer_set_alarm(TTN_TIMER_GROUP, TTN_TIMER, TIMER_ALARM_DIS);
|
if (!isTimerArmed)
|
||||||
nextTimerEvent = 0x200000000; // wait indefinitely (almost)
|
return;
|
||||||
|
esp_timer_stop(timer);
|
||||||
|
isTimerArmed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRAM_ATTR HAL_ESP32::timerIrqHandler(void *arg)
|
void HAL_ESP32::timerCallback(void *arg)
|
||||||
{
|
{
|
||||||
TTN_CLEAR_TIMER_ALARM;
|
|
||||||
BaseType_t higherPrioTaskWoken = pdFALSE;
|
|
||||||
HALQueueItem item { TIMER };
|
HALQueueItem item { TIMER };
|
||||||
xQueueSendFromISR(ttn_hal.dioQueue, &item, &higherPrioTaskWoken);
|
xQueueSend(ttn_hal.dioQueue, &item, 0);
|
||||||
if (higherPrioTaskWoken)
|
|
||||||
portYIELD_FROM_ISR();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HAL_ESP32::wait(WaitKind waitKind)
|
bool HAL_ESP32::wait(WaitKind waitKind)
|
||||||
@ -311,6 +319,7 @@ bool HAL_ESP32::wait(WaitKind waitKind)
|
|||||||
else if (item.ev == TIMER)
|
else if (item.ev == TIMER)
|
||||||
{
|
{
|
||||||
disarmTimer();
|
disarmTimer();
|
||||||
|
setNextAlarm(0);
|
||||||
if (waitKind != CHECK_IO)
|
if (waitKind != CHECK_IO)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -319,7 +328,7 @@ bool HAL_ESP32::wait(WaitKind waitKind)
|
|||||||
if (waitKind != WAIT_FOR_TIMER)
|
if (waitKind != WAIT_FOR_TIMER)
|
||||||
disarmTimer();
|
disarmTimer();
|
||||||
enterCriticalSection();
|
enterCriticalSection();
|
||||||
radio_irq_handler_v2(item.ev, item.time);
|
radio_irq_handler_v2(item.ev, item.osTime);
|
||||||
leaveCriticalSection();
|
leaveCriticalSection();
|
||||||
if (waitKind != WAIT_FOR_TIMER)
|
if (waitKind != WAIT_FOR_TIMER)
|
||||||
return true;
|
return true;
|
||||||
@ -329,9 +338,9 @@ bool HAL_ESP32::wait(WaitKind waitKind)
|
|||||||
|
|
||||||
u4_t hal_ticks()
|
u4_t hal_ticks()
|
||||||
{
|
{
|
||||||
uint64_t val;
|
// LMIC tick unit: 16µs
|
||||||
timer_get_counter_value(TTN_TIMER_GROUP, TTN_TIMER, &val);
|
// esp_timer unit: 1µs
|
||||||
return (u4_t)val;
|
return (u4_t)(esp_timer_get_time() >> 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
void hal_waitUntil(u4_t time)
|
void hal_waitUntil(u4_t time)
|
||||||
@ -339,10 +348,12 @@ void hal_waitUntil(u4_t time)
|
|||||||
ttn_hal.waitUntil(time);
|
ttn_hal.waitUntil(time);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HAL_ESP32::waitUntil(uint32_t time)
|
void HAL_ESP32::waitUntil(uint32_t osTime)
|
||||||
{
|
{
|
||||||
prepareNextAlarm(time);
|
int64_t espNow = esp_timer_get_time();
|
||||||
armTimer();
|
int64_t espTime = osTimeToEspTime(espNow, osTime);
|
||||||
|
setNextAlarm(espTime);
|
||||||
|
armTimer(espNow);
|
||||||
wait(WAIT_FOR_TIMER);
|
wait(WAIT_FOR_TIMER);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -353,29 +364,20 @@ void HAL_ESP32::wakeUp()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check and rewind for target time
|
// check and rewind for target time
|
||||||
u1_t hal_checkTimer(u4_t time)
|
u1_t hal_checkTimer(uint32_t time)
|
||||||
{
|
{
|
||||||
return ttn_hal.checkTimer(time);
|
return ttn_hal.checkTimer(time);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t HAL_ESP32::checkTimer(uint32_t time)
|
uint8_t HAL_ESP32::checkTimer(u4_t osTime)
|
||||||
{
|
{
|
||||||
uint64_t now;
|
int64_t espNow = esp_timer_get_time();
|
||||||
timer_get_counter_value(TTN_TIMER_GROUP, TTN_TIMER, &now);
|
int64_t espTime = osTimeToEspTime(espNow, osTime);
|
||||||
u4_t now32 = (u4_t)now;
|
int64_t diff = espTime - espNow;
|
||||||
|
if (diff < 100)
|
||||||
|
return 1; // timer has expired or will expire very soon
|
||||||
|
|
||||||
if (time >= now32)
|
setNextAlarm(espTime);
|
||||||
{
|
|
||||||
if (time - now32 < 5)
|
|
||||||
return 1; // timer will expire very soon
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (now32 - time < OVERRUN_TRESHOLD)
|
|
||||||
return 1; // timer has expired recently
|
|
||||||
}
|
|
||||||
|
|
||||||
prepareNextAlarm(time);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -389,7 +391,7 @@ void HAL_ESP32::sleep()
|
|||||||
if (wait(CHECK_IO))
|
if (wait(CHECK_IO))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
armTimer();
|
armTimer(esp_timer_get_time());
|
||||||
wait(WAIT_FOR_ANY_EVENT);
|
wait(WAIT_FOR_ANY_EVENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -445,7 +447,7 @@ void HAL_ESP32::init()
|
|||||||
ioInit();
|
ioInit();
|
||||||
// configure radio SPI
|
// configure radio SPI
|
||||||
spiInit();
|
spiInit();
|
||||||
// configure timer and interrupt handler
|
// configure timer and alarm callback
|
||||||
timerInit();
|
timerInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -458,7 +460,3 @@ void hal_failed(const char *file, u2_t line)
|
|||||||
ESP_LOGE(TAG, "%s:%d", file, line);
|
ESP_LOGE(TAG, "%s:%d", file, line);
|
||||||
ASSERT(0);
|
ASSERT(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t hal_getTxPowerPolicy(u1_t inputPolicy, s1_t requestedPower, u4_t frequency) {
|
|
||||||
return LMICHAL_radio_tx_power_policy_paboost;
|
|
||||||
}
|
|
||||||
|
@ -16,7 +16,8 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <freertos/FreeRTOS.h>
|
#include <freertos/FreeRTOS.h>
|
||||||
#include <freertos/queue.h>
|
#include <freertos/queue.h>
|
||||||
#include "driver/spi_master.h"
|
#include <driver/spi_master.h>
|
||||||
|
#include <esp_timer.h>
|
||||||
|
|
||||||
|
|
||||||
enum HAL_Event {
|
enum HAL_Event {
|
||||||
@ -44,15 +45,18 @@ public:
|
|||||||
void configurePins(spi_host_device_t spi_host, uint8_t nss, uint8_t rxtx, uint8_t rst, uint8_t dio0, uint8_t dio1);
|
void configurePins(spi_host_device_t spi_host, uint8_t nss, uint8_t rxtx, uint8_t rst, uint8_t dio0, uint8_t dio1);
|
||||||
void init();
|
void init();
|
||||||
void startBackgroundTask();
|
void startBackgroundTask();
|
||||||
|
|
||||||
void wakeUp();
|
void wakeUp();
|
||||||
void initCriticalSection();
|
void initCriticalSection();
|
||||||
void enterCriticalSection();
|
void enterCriticalSection();
|
||||||
void leaveCriticalSection();
|
void leaveCriticalSection();
|
||||||
|
|
||||||
void spiWrite(uint8_t cmd, const uint8_t *buf, size_t len);
|
void spiWrite(uint8_t cmd, const uint8_t *buf, size_t len);
|
||||||
void spiRead(uint8_t cmd, uint8_t *buf, size_t len);
|
void spiRead(uint8_t cmd, uint8_t *buf, size_t len);
|
||||||
uint8_t checkTimer(uint32_t time);
|
uint8_t checkTimer(uint32_t osTime);
|
||||||
void sleep();
|
void sleep();
|
||||||
void waitUntil(uint32_t time);
|
|
||||||
|
void waitUntil(uint32_t osTime);
|
||||||
|
|
||||||
spi_host_device_t spiHost;
|
spi_host_device_t spiHost;
|
||||||
gpio_num_t pinNSS;
|
gpio_num_t pinNSS;
|
||||||
@ -65,20 +69,25 @@ public:
|
|||||||
private:
|
private:
|
||||||
static void backgroundTask(void* pvParameter);
|
static void backgroundTask(void* pvParameter);
|
||||||
static void dioIrqHandler(void* arg);
|
static void dioIrqHandler(void* arg);
|
||||||
|
static void timerCallback(void *arg);
|
||||||
|
static int64_t osTimeToEspTime(int64_t espNow, uint32_t osTime);
|
||||||
|
|
||||||
void ioInit();
|
void ioInit();
|
||||||
void spiInit();
|
void spiInit();
|
||||||
void timerInit();
|
void timerInit();
|
||||||
void prepareNextAlarm(uint32_t time);
|
|
||||||
void armTimer();
|
void setNextAlarm(int64_t time);
|
||||||
|
void armTimer(int64_t espNow);
|
||||||
void disarmTimer();
|
void disarmTimer();
|
||||||
static void IRAM_ATTR timerIrqHandler(void *arg);
|
|
||||||
bool wait(WaitKind waitKind);
|
bool wait(WaitKind waitKind);
|
||||||
|
|
||||||
QueueHandle_t dioQueue;
|
QueueHandle_t dioQueue;
|
||||||
spi_device_handle_t spiHandle;
|
spi_device_handle_t spiHandle;
|
||||||
spi_transaction_t spiTransaction;
|
spi_transaction_t spiTransaction;
|
||||||
uint64_t nextTimerEvent;
|
|
||||||
SemaphoreHandle_t mutex;
|
SemaphoreHandle_t mutex;
|
||||||
|
esp_timer_handle_t timer;
|
||||||
|
int64_t nextAlarm;
|
||||||
|
bool isTimerArmed;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern HAL_ESP32 ttn_hal;
|
extern HAL_ESP32 ttn_hal;
|
||||||
|
Loading…
Reference in New Issue
Block a user