mirror of
				https://github.com/manuelbl/ttn-esp32.git
				synced 2025-10-31 18:50:33 +01:00 
			
		
		
		
	Convert hal_esp32 to C
This commit is contained in:
		
							
								
								
									
										6
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							| @ -6,6 +6,10 @@ | ||||
|         "oslmic.h": "c", | ||||
|         "hal_esp32.h": "c", | ||||
|         "esp_log.h": "c", | ||||
|         "nvs_flash.h": "c" | ||||
|         "nvs_flash.h": "c", | ||||
|         "__config": "c", | ||||
|         "__nullptr": "c", | ||||
|         "stdint.h": "c", | ||||
|         "*.ipp": "c" | ||||
|     } | ||||
| } | ||||
| @ -82,7 +82,7 @@ TheThingsNetwork::TheThingsNetwork() | ||||
|  | ||||
|     ASSERT(ttnInstance == nullptr); | ||||
|     ttnInstance = this; | ||||
|     ttn_hal.initCriticalSection(); | ||||
|     hal_esp32_init_critical_section(); | ||||
| } | ||||
|  | ||||
| TheThingsNetwork::~TheThingsNetwork() | ||||
| @ -92,7 +92,7 @@ TheThingsNetwork::~TheThingsNetwork() | ||||
|  | ||||
| void TheThingsNetwork::configurePins(spi_host_device_t spi_host, uint8_t nss, uint8_t rxtx, uint8_t rst, uint8_t dio0, uint8_t dio1) | ||||
| { | ||||
|     ttn_hal.configurePins(spi_host, nss, rxtx, rst, dio0, dio1); | ||||
|     hal_esp32_configure_pins(spi_host, nss, rxtx, rst, dio0, dio1); | ||||
|  | ||||
| #if LMIC_ENABLE_event_logging | ||||
|     logging = TTNLogging::initInstance(); | ||||
| @ -106,33 +106,33 @@ void TheThingsNetwork::configurePins(spi_host_device_t spi_host, uint8_t nss, ui | ||||
|  | ||||
|     lmicEventQueue = xQueueCreate(4, sizeof(TTNLmicEvent)); | ||||
|     ASSERT(lmicEventQueue != nullptr); | ||||
|     ttn_hal.startLMICTask(); | ||||
|     hal_esp32_start_lmic_task(); | ||||
| } | ||||
|  | ||||
| void TheThingsNetwork::reset() | ||||
| { | ||||
|     ttn_hal.enterCriticalSection(); | ||||
|     hal_esp32_enter_critical_section(); | ||||
|     LMIC_reset(); | ||||
|     LMIC_setClockError(MAX_CLOCK_ERROR * 4 / 100); | ||||
|     waitingReason = eWaitingNone; | ||||
|     ttn_hal.leaveCriticalSection(); | ||||
|     hal_esp32_leave_critical_section(); | ||||
| } | ||||
|  | ||||
| void TheThingsNetwork::shutdown() | ||||
| { | ||||
|     ttn_hal.enterCriticalSection(); | ||||
|     hal_esp32_enter_critical_section(); | ||||
|     LMIC_shutdown(); | ||||
|     ttn_hal.stopLMICTask(); | ||||
|     hal_esp32_stop_lmic_task(); | ||||
|     waitingReason = eWaitingNone; | ||||
|     ttn_hal.leaveCriticalSection(); | ||||
|     hal_esp32_leave_critical_section(); | ||||
| } | ||||
|  | ||||
| void TheThingsNetwork::startup() | ||||
| { | ||||
|     ttn_hal.enterCriticalSection(); | ||||
|     hal_esp32_enter_critical_section(); | ||||
|     LMIC_reset(); | ||||
|     ttn_hal.startLMICTask(); | ||||
|     ttn_hal.leaveCriticalSection(); | ||||
|     hal_esp32_start_lmic_task(); | ||||
|     hal_esp32_leave_critical_section(); | ||||
| } | ||||
|  | ||||
| bool TheThingsNetwork::provision(const char *devEui, const char *appEui, const char *appKey) | ||||
| @ -210,12 +210,12 @@ bool TheThingsNetwork::joinCore() | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     ttn_hal.enterCriticalSection(); | ||||
|     hal_esp32_enter_critical_section(); | ||||
|     xQueueReset(lmicEventQueue); | ||||
|     waitingReason = eWaitingForJoin; | ||||
|     LMIC_startJoining(); | ||||
|     ttn_hal.wakeUp(); | ||||
|     ttn_hal.leaveCriticalSection(); | ||||
|     hal_esp32_wake_up(); | ||||
|     hal_esp32_leave_critical_section(); | ||||
|  | ||||
|     TTNLmicEvent event; | ||||
|     xQueueReceive(lmicEventQueue, &event, portMAX_DELAY); | ||||
| @ -224,10 +224,10 @@ bool TheThingsNetwork::joinCore() | ||||
|  | ||||
| TTNResponseCode TheThingsNetwork::transmitMessage(const uint8_t *payload, size_t length, port_t port, bool confirm) | ||||
| { | ||||
|     ttn_hal.enterCriticalSection(); | ||||
|     hal_esp32_enter_critical_section(); | ||||
|     if (waitingReason != eWaitingNone || (LMIC.opmode & OP_TXRXPEND) != 0) | ||||
|     { | ||||
|         ttn_hal.leaveCriticalSection(); | ||||
|         hal_esp32_leave_critical_section(); | ||||
|         return kTTNErrorTransmissionFailed; | ||||
|     } | ||||
|  | ||||
| @ -235,8 +235,8 @@ TTNResponseCode TheThingsNetwork::transmitMessage(const uint8_t *payload, size_t | ||||
|     LMIC.client.txMessageCb = messageTransmittedCallback; | ||||
|     LMIC.client.txMessageUserData = nullptr; | ||||
|     LMIC_setTxData2(port, (xref2u1_t)payload, length, confirm); | ||||
|     ttn_hal.wakeUp(); | ||||
|     ttn_hal.leaveCriticalSection(); | ||||
|     hal_esp32_wake_up(); | ||||
|     hal_esp32_leave_critical_section(); | ||||
|  | ||||
|     while (true) | ||||
|     { | ||||
| @ -280,7 +280,7 @@ bool TheThingsNetwork::isProvisioned() | ||||
|  | ||||
| void TheThingsNetwork::setRSSICal(int8_t rssiCal) | ||||
| { | ||||
|     ttn_hal.rssiCal = rssiCal; | ||||
|     hal_esp32_set_rssi_cal(rssiCal); | ||||
| } | ||||
|  | ||||
| bool TheThingsNetwork::adrEnabled() | ||||
|  | ||||
							
								
								
									
										538
									
								
								src/hal/hal_esp32.c
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										538
									
								
								src/hal/hal_esp32.c
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,538 @@ | ||||
| /******************************************************************************* | ||||
|  *  | ||||
|  * ttn-esp32 - The Things Network device library for ESP-IDF / SX127x | ||||
|  *  | ||||
|  * Copyright (c) 2018-2019 Manuel Bleichenbacher | ||||
|  *  | ||||
|  * Licensed under MIT License | ||||
|  * https://opensource.org/licenses/MIT | ||||
|  * | ||||
|  * Hardware abstraction layer to run LMIC on a ESP32 using ESP-IDF. | ||||
|  *******************************************************************************/ | ||||
|  | ||||
| #include "hal_esp32.h" | ||||
| #include "../lmic/lmic.h" | ||||
|  | ||||
| #include "freertos/FreeRTOS.h" | ||||
| #include "freertos/queue.h" | ||||
| #include "freertos/task.h" | ||||
| #include "freertos/semphr.h" | ||||
| #include "driver/gpio.h" | ||||
| #include "driver/spi_master.h" | ||||
| #include "driver/timer.h" | ||||
| #include "esp_timer.h" | ||||
| #include "esp_log.h" | ||||
|  | ||||
| #define LMIC_UNUSED_PIN 0xff | ||||
|  | ||||
| #define NOTIFY_BIT_DIO 1 | ||||
| #define NOTIFY_BIT_TIMER 2 | ||||
| #define NOTIFY_BIT_WAKEUP 4 | ||||
| #define NOTIFY_BIT_STOP 8 | ||||
|  | ||||
|  | ||||
| #define TAG "ttn_hal" | ||||
|  | ||||
|  | ||||
| typedef enum { | ||||
|     WAIT_KIND_CHECK_IO, | ||||
|     WAIT_KIND_WAIT_FOR_ANY_EVENT, | ||||
|     WAIT_KIND_WAIT_FOR_TIMER | ||||
| } wait_kind_e; | ||||
|  | ||||
|  | ||||
| static void lmic_background_task(void* pvParameter); | ||||
| static void qio_irq_handler(void* arg); | ||||
| static void timer_callback(void *arg); | ||||
| static int64_t os_time_to_esp_time(int64_t esp_now, uint32_t os_time); | ||||
|  | ||||
| static void init_io(void); | ||||
| static void init_spi(void); | ||||
| static void init_timer(void); | ||||
|  | ||||
| static void set_next_alarm(int64_t time); | ||||
| static void arm_timer(int64_t esp_now); | ||||
| static void disarm_timer(void); | ||||
| static bool wait(wait_kind_e wait_kind); | ||||
|  | ||||
| static spi_host_device_t spi_host; | ||||
| static gpio_num_t pin_nss; | ||||
| static gpio_num_t pin_rx_tx; | ||||
| static gpio_num_t pin_rst; | ||||
| static gpio_num_t pin_dio0; | ||||
| static gpio_num_t pin_dio1; | ||||
| static int8_t rssi_cal = 10; | ||||
|  | ||||
|  | ||||
| static TaskHandle_t lmic_task; | ||||
| static uint32_t dio_interrupt_time; | ||||
| static uint8_t dio_num; | ||||
|  | ||||
| static spi_device_handle_t spi_handle; | ||||
| static spi_transaction_t spi_transaction; | ||||
| static SemaphoreHandle_t mutex; | ||||
| static esp_timer_handle_t timer; | ||||
| static int64_t next_alarm; | ||||
| static volatile bool run_background_task; | ||||
|  | ||||
|  | ||||
| // ----------------------------------------------------------------------------- | ||||
| // I/O | ||||
|  | ||||
| void hal_esp32_configure_pins(spi_host_device_t host, uint8_t nss, uint8_t rxtx, uint8_t rst, uint8_t dio0, uint8_t dio1) | ||||
| { | ||||
|     spi_host = host; | ||||
|     pin_nss = (gpio_num_t)nss; | ||||
|     pin_rx_tx = (gpio_num_t)rxtx; | ||||
|     pin_rst = (gpio_num_t)rst; | ||||
|     pin_dio0 = (gpio_num_t)dio0; | ||||
|     pin_dio1 = (gpio_num_t)dio1; | ||||
|  | ||||
|     // Until the background process has been started, use the current task | ||||
|     // for supporting calls like `hal_waitUntil()`. | ||||
|     lmic_task = xTaskGetCurrentTaskHandle(); | ||||
| } | ||||
|  | ||||
|  | ||||
| void IRAM_ATTR qio_irq_handler(void *arg) | ||||
| { | ||||
|     dio_interrupt_time = hal_ticks(); | ||||
|     dio_num = (u1_t)(long)arg; | ||||
|     BaseType_t higher_prio_task_woken = pdFALSE; | ||||
|     xTaskNotifyFromISR(lmic_task, NOTIFY_BIT_DIO, eSetBits, &higher_prio_task_woken); | ||||
|     if (higher_prio_task_woken) | ||||
|         portYIELD_FROM_ISR(); | ||||
| } | ||||
|  | ||||
| void init_io(void) | ||||
| { | ||||
|     // pin_nss and pin_dio0 and pin_dio1 are required | ||||
|     ASSERT(pin_nss != LMIC_UNUSED_PIN); | ||||
|     ASSERT(pin_dio0 != LMIC_UNUSED_PIN); | ||||
|     ASSERT(pin_dio1 != LMIC_UNUSED_PIN); | ||||
|  | ||||
|     gpio_pad_select_gpio(pin_nss); | ||||
|     gpio_set_level(pin_nss, 0); | ||||
|     gpio_set_direction(pin_nss, GPIO_MODE_OUTPUT); | ||||
|  | ||||
|     if (pin_rx_tx != LMIC_UNUSED_PIN) | ||||
|     { | ||||
|         gpio_pad_select_gpio(pin_rx_tx); | ||||
|         gpio_set_level(pin_rx_tx, 0); | ||||
|         gpio_set_direction(pin_rx_tx, GPIO_MODE_OUTPUT); | ||||
|     } | ||||
|  | ||||
|     if (pin_rst != LMIC_UNUSED_PIN) | ||||
|     { | ||||
|         gpio_pad_select_gpio(pin_rst); | ||||
|         gpio_set_level(pin_rst, 0); | ||||
|         gpio_set_direction(pin_rst, GPIO_MODE_OUTPUT); | ||||
|     } | ||||
|  | ||||
|     // DIO pins with interrupt handlers | ||||
|     gpio_pad_select_gpio(pin_dio0); | ||||
|     gpio_set_direction(pin_dio0, GPIO_MODE_INPUT); | ||||
|     gpio_set_intr_type(pin_dio0, GPIO_INTR_POSEDGE); | ||||
|  | ||||
|     gpio_pad_select_gpio(pin_dio1); | ||||
|     gpio_set_direction(pin_dio1, GPIO_MODE_INPUT); | ||||
|     gpio_set_intr_type(pin_dio1, GPIO_INTR_POSEDGE); | ||||
|  | ||||
|     ESP_LOGI(TAG, "IO initialized"); | ||||
| } | ||||
|  | ||||
| void hal_pin_rxtx(u1_t val) | ||||
| { | ||||
|     if (pin_rx_tx == LMIC_UNUSED_PIN) | ||||
|         return; | ||||
|      | ||||
|     gpio_set_level(pin_rx_tx, val); | ||||
| } | ||||
|  | ||||
| void hal_pin_rst(u1_t val) | ||||
| { | ||||
|     if (pin_rst == LMIC_UNUSED_PIN) | ||||
|         return; | ||||
|  | ||||
|     if (val == 0 || val == 1) | ||||
|     { | ||||
|         // drive pin | ||||
|         gpio_set_level(pin_rst, val); | ||||
|         gpio_set_direction(pin_rst, GPIO_MODE_OUTPUT); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
| #if defined(CONFIG_TTN_RESET_STATES_ASSERTED) | ||||
|         // drive up the pin because the hardware is nonstandard | ||||
|         gpio_set_level(pin_rst, 1); | ||||
|         gpio_set_direction(pin_rst, GPIO_MODE_OUTPUT); | ||||
| #else | ||||
|         // keep pin floating | ||||
|         gpio_set_level(pin_rst, val); | ||||
|         gpio_set_direction(pin_rst, GPIO_MODE_INPUT); | ||||
| #endif | ||||
|     } | ||||
| } | ||||
|  | ||||
| s1_t hal_getRssiCal(void) | ||||
| { | ||||
|     return rssi_cal; | ||||
| } | ||||
|  | ||||
| ostime_t hal_setModuleActive(bit_t val) | ||||
| { | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| bit_t hal_queryUsingTcxo(void) | ||||
| { | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| uint8_t hal_getTxPowerPolicy(u1_t inputPolicy, s1_t requestedPower, u4_t frequency) | ||||
| { | ||||
|     return LMICHAL_radio_tx_power_policy_paboost; | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| // ----------------------------------------------------------------------------- | ||||
| // SPI | ||||
|  | ||||
| void init_spi(void) | ||||
| { | ||||
|     // init device | ||||
|     spi_device_interface_config_t spi_config = { | ||||
|         .mode = 1, | ||||
|         .clock_speed_hz = CONFIG_TTN_SPI_FREQ, | ||||
|         .command_bits = 0, | ||||
|         .address_bits = 8, | ||||
|         .spics_io_num = pin_nss, | ||||
|         .queue_size = 1, | ||||
|         .cs_ena_posttrans = 2 | ||||
|     }; | ||||
|  | ||||
|     esp_err_t ret = spi_bus_add_device(spi_host, &spi_config, &spi_handle); | ||||
|     ESP_ERROR_CHECK(ret); | ||||
|  | ||||
|     ESP_LOGI(TAG, "SPI initialized"); | ||||
| } | ||||
|  | ||||
| void hal_spi_write(u1_t cmd, const u1_t *buf, size_t len) | ||||
| { | ||||
|     memset(&spi_transaction, 0, sizeof(spi_transaction)); | ||||
|     spi_transaction.addr = cmd; | ||||
|     spi_transaction.length = 8 * len; | ||||
|     spi_transaction.tx_buffer = buf; | ||||
|     esp_err_t err = spi_device_transmit(spi_handle, &spi_transaction); | ||||
|     ESP_ERROR_CHECK(err); | ||||
| } | ||||
|  | ||||
| void hal_spi_read(u1_t cmd, u1_t *buf, size_t len) | ||||
| { | ||||
|     memset(buf, 0, len); | ||||
|     memset(&spi_transaction, 0, sizeof(spi_transaction)); | ||||
|     spi_transaction.addr = cmd; | ||||
|     spi_transaction.length = 8 * len; | ||||
|     spi_transaction.rxlength = 8 * len; | ||||
|     spi_transaction.tx_buffer = buf; | ||||
|     spi_transaction.rx_buffer = buf; | ||||
|     esp_err_t err = spi_device_transmit(spi_handle, &spi_transaction); | ||||
|     ESP_ERROR_CHECK(err); | ||||
| } | ||||
|  | ||||
|  | ||||
| // ----------------------------------------------------------------------------- | ||||
| // TIME | ||||
|  | ||||
| /* | ||||
|  * LIMIC uses a 32 bit time system (ostime_t) counting ticks. In this | ||||
|  * implementation each tick is 16µs. It will wrap arounnd every 19 hours. | ||||
|  *  | ||||
|  * The ESP32 has a 64 bit timer counting microseconds. It will wrap around | ||||
|  * every 584,000 years. So we don't need to bother. | ||||
|  *  | ||||
|  * Based on this timer, future callbacks can be scheduled. This is used to | ||||
|  * schedule the next LMIC job. | ||||
|  */ | ||||
|  | ||||
| // Convert LMIC tick time (ostime_t) to ESP absolute time. | ||||
| // `os_time` is assumed to be somewhere between one hour in the past and | ||||
| // 18 hours into the future.  | ||||
| int64_t os_time_to_esp_time(int64_t esp_now, uint32_t os_time) | ||||
| { | ||||
|     int64_t esp_time; | ||||
|     uint32_t os_now = (uint32_t)(esp_now >> 4); | ||||
|  | ||||
|     // unsigned difference: | ||||
|     // 0x00000000 - 0xefffffff: future (0 to about 18 hours) | ||||
|     // 0xf0000000 - 0xffffffff: past (about 1 to 0 hours) | ||||
|     uint32_t os_diff = os_time - os_now; | ||||
|     if (os_diff < 0xf0000000) | ||||
|     { | ||||
|         esp_time = esp_now + (((int64_t)os_diff) << 4); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         // one's complement instead of two's complement: | ||||
|         // off by 1 µs and ignored | ||||
|         os_diff = ~os_diff; | ||||
|         esp_time = esp_now - (((int64_t)os_diff) << 4); | ||||
|     } | ||||
|  | ||||
|     return esp_time; | ||||
| } | ||||
|  | ||||
| void init_timer(void) | ||||
| { | ||||
|     esp_timer_create_args_t timer_config = { | ||||
|         .callback = &timer_callback, | ||||
|         .arg = NULL, | ||||
|         .dispatch_method = ESP_TIMER_TASK, | ||||
|         .name = "lmic_job" | ||||
|     }; | ||||
|     esp_err_t err = esp_timer_create(&timer_config, &timer); | ||||
|     ESP_ERROR_CHECK(err); | ||||
|  | ||||
|     ESP_LOGI(TAG, "Timer initialized"); | ||||
| } | ||||
|  | ||||
| void set_next_alarm(int64_t time) | ||||
| { | ||||
|     next_alarm = time; | ||||
| } | ||||
|  | ||||
| void arm_timer(int64_t esp_now) | ||||
| { | ||||
|     if (next_alarm == 0) | ||||
|         return; | ||||
|     int64_t timeout = next_alarm - esp_timer_get_time(); | ||||
|     if (timeout < 0) | ||||
|         timeout = 10; | ||||
|     esp_timer_start_once(timer, timeout); | ||||
| } | ||||
|  | ||||
| void disarm_timer(void) | ||||
| { | ||||
|     esp_timer_stop(timer); | ||||
| } | ||||
|  | ||||
| void timer_callback(void *arg) | ||||
| { | ||||
|     xTaskNotify(lmic_task, NOTIFY_BIT_TIMER, eSetBits); | ||||
| } | ||||
|  | ||||
| // Wait for the next external event. Either: | ||||
| // - scheduled timer due to scheduled job or waiting for a given time | ||||
| // - wake up event from the client code | ||||
| // - I/O interrupt (DIO0 or DIO1 pin) | ||||
| bool wait(wait_kind_e wait_kind) | ||||
| { | ||||
|     TickType_t ticks_to_wait = wait_kind == WAIT_KIND_CHECK_IO ? 0 : portMAX_DELAY; | ||||
|     while (true) | ||||
|     { | ||||
|         uint32_t bits = ulTaskNotifyTake(pdTRUE, ticks_to_wait); | ||||
|         if (bits == 0) | ||||
|             return false; | ||||
|  | ||||
|         if ((bits & NOTIFY_BIT_STOP) != 0) | ||||
|             return false; | ||||
|  | ||||
|         if ((bits & NOTIFY_BIT_WAKEUP) != 0) | ||||
|         { | ||||
|             if (wait_kind != WAIT_KIND_WAIT_FOR_TIMER) | ||||
|             { | ||||
|                 disarm_timer(); | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|         else if ((bits & NOTIFY_BIT_TIMER) != 0) | ||||
|         { | ||||
|             disarm_timer(); | ||||
|             set_next_alarm(0); | ||||
|             if (wait_kind != WAIT_KIND_CHECK_IO) | ||||
|                 return true; | ||||
|         } | ||||
|         else // IO interrupt | ||||
|         { | ||||
|             if (wait_kind != WAIT_KIND_WAIT_FOR_TIMER) | ||||
|                 disarm_timer(); | ||||
|             hal_esp32_enter_critical_section(); | ||||
|             radio_irq_handler_v2(dio_num, dio_interrupt_time); | ||||
|             hal_esp32_leave_critical_section(); | ||||
|             if (wait_kind != WAIT_KIND_WAIT_FOR_TIMER) | ||||
|                 return true; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| // Gets current time in LMIC ticks | ||||
| u4_t IRAM_ATTR hal_ticks(void) | ||||
| { | ||||
|     // LMIC tick unit: 16µs | ||||
|     // esp_timer unit: 1µs | ||||
|     return (u4_t)(esp_timer_get_time() >> 4); | ||||
| } | ||||
|  | ||||
| // Wait until the specified time. | ||||
| // Called if the LMIC code needs to wait for a precise time. | ||||
| // All other events are ignored and will be served later. | ||||
| u4_t hal_waitUntil(u4_t time) | ||||
| { | ||||
|     int64_t esp_now = esp_timer_get_time(); | ||||
|     int64_t esp_time = os_time_to_esp_time(esp_now, time); | ||||
|     set_next_alarm(esp_time); | ||||
|     arm_timer(esp_now); | ||||
|     wait(WAIT_KIND_WAIT_FOR_TIMER); | ||||
|  | ||||
|     u4_t os_now = hal_ticks(); | ||||
|     u4_t diff = os_now - time; | ||||
|     return diff < 0x80000000U ? diff : 0; | ||||
| } | ||||
|  | ||||
| // Called by client code to wake up LMIC to do something, | ||||
| // e.g. send a submitted messages. | ||||
| void hal_esp32_wake_up(void) | ||||
| { | ||||
|     xTaskNotify(lmic_task, NOTIFY_BIT_WAKEUP, eSetBits); | ||||
| } | ||||
|  | ||||
| // Check if the specified time has been reached or almost reached. | ||||
| // Otherwise, save it as alarm time. | ||||
| // LMIC calls this function with the scheduled time of the next job | ||||
| // in the queue. If the job is not due yet, LMIC will go to sleep. | ||||
| u1_t hal_checkTimer(uint32_t time) | ||||
| { | ||||
|     int64_t esp_now = esp_timer_get_time(); | ||||
|     int64_t esp_time = os_time_to_esp_time(esp_now, time); | ||||
|     int64_t diff = esp_time - esp_now; | ||||
|     if (diff < 100) | ||||
|         return 1; // timer has expired or will expire very soon | ||||
|  | ||||
|     set_next_alarm(esp_time); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| // Go to sleep until next event. | ||||
| // Called when LMIC is not busy and not job is due to be executed. | ||||
| void hal_sleep(void) | ||||
| { | ||||
|     if (wait(WAIT_KIND_CHECK_IO)) | ||||
|         return; | ||||
|  | ||||
|     arm_timer(esp_timer_get_time()); | ||||
|     wait(WAIT_KIND_WAIT_FOR_ANY_EVENT); | ||||
| } | ||||
|  | ||||
|  | ||||
| // ----------------------------------------------------------------------------- | ||||
| // IRQ | ||||
|  | ||||
| void hal_disableIRQs(void) | ||||
| { | ||||
|     // nothing to do as interrupt handlers post message to queue | ||||
|     // and don't access any shared data structures | ||||
| } | ||||
|  | ||||
| void hal_enableIRQs(void) | ||||
| { | ||||
|     // nothing to do as interrupt handlers post message to queue | ||||
|     // and don't access any shared data structures | ||||
| } | ||||
|  | ||||
| void hal_processPendingIRQs(void) | ||||
| { | ||||
|     // nothing to do as interrupt handlers post message to queue | ||||
|     // and don't access any shared data structures | ||||
| } | ||||
|  | ||||
|  | ||||
| // ----------------------------------------------------------------------------- | ||||
| // Synchronization between application code and background task | ||||
|  | ||||
| void hal_esp32_init_critical_section(void) | ||||
| { | ||||
|     mutex = xSemaphoreCreateRecursiveMutex(); | ||||
| } | ||||
|  | ||||
| void hal_esp32_enter_critical_section(void) | ||||
| { | ||||
|     xSemaphoreTakeRecursive(mutex, portMAX_DELAY); | ||||
| } | ||||
|  | ||||
| void hal_esp32_leave_critical_section(void) | ||||
| { | ||||
|     xSemaphoreGiveRecursive(mutex); | ||||
| } | ||||
|  | ||||
| // ----------------------------------------------------------------------------- | ||||
|  | ||||
| void lmic_background_task(void* pvParameter) | ||||
| { | ||||
|     while (run_background_task) | ||||
|         os_runloop_once(); | ||||
|     vTaskDelete(NULL); | ||||
| } | ||||
|  | ||||
| void hal_init_ex(const void *pContext) | ||||
| { | ||||
|     // configure radio I/O and interrupt handler | ||||
|     init_io(); | ||||
|     // configure radio SPI | ||||
|     init_spi(); | ||||
|     // configure timer and alarm callback | ||||
|     init_timer(); | ||||
| } | ||||
|  | ||||
| void hal_esp32_start_lmic_task(void) | ||||
| { | ||||
|     run_background_task = true; | ||||
|     xTaskCreate(lmic_background_task, "ttn_lmic", 1024 * 4, NULL, CONFIG_TTN_BG_TASK_PRIO, &lmic_task); | ||||
|  | ||||
|     // enable interrupts | ||||
|     gpio_isr_handler_add(pin_dio0, qio_irq_handler, (void *)0); | ||||
|     gpio_isr_handler_add(pin_dio1, qio_irq_handler, (void *)1); | ||||
| } | ||||
|  | ||||
| void hal_esp32_stop_lmic_task(void) | ||||
| { | ||||
|     run_background_task = false; | ||||
|     gpio_isr_handler_remove(pin_dio0); | ||||
|     gpio_isr_handler_remove(pin_dio1); | ||||
|     disarm_timer(); | ||||
|     xTaskNotify(lmic_task, NOTIFY_BIT_STOP, eSetBits); | ||||
| } | ||||
|  | ||||
|  | ||||
| // ----------------------------------------------------------------------------- | ||||
| // Fatal failure | ||||
|  | ||||
| static hal_failure_handler_t* custom_hal_failure_handler = NULL; | ||||
|  | ||||
| void hal_set_failure_handler(const hal_failure_handler_t* const handler) | ||||
| { | ||||
|     custom_hal_failure_handler = handler; | ||||
| } | ||||
|  | ||||
| void hal_failed(const char *file, u2_t line) | ||||
| { | ||||
|     if (custom_hal_failure_handler != NULL) | ||||
|         (*custom_hal_failure_handler)(file, line); | ||||
|  | ||||
|     ESP_LOGE(TAG, "LMIC failed and stopped: %s:%d", file, line); | ||||
|  | ||||
|     // go to sleep forever | ||||
|     while (true) | ||||
|     { | ||||
|         vTaskDelay(portMAX_DELAY); | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| // ----------------------------------------------------------------------------- | ||||
| // RSSI | ||||
|  | ||||
| void hal_esp32_set_rssi_cal(int8_t cal) | ||||
| { | ||||
|     rssi_cal = cal; | ||||
| } | ||||
| @ -1,529 +0,0 @@ | ||||
| /******************************************************************************* | ||||
|  *  | ||||
|  * ttn-esp32 - The Things Network device library for ESP-IDF / SX127x | ||||
|  *  | ||||
|  * Copyright (c) 2018-2019 Manuel Bleichenbacher | ||||
|  *  | ||||
|  * Licensed under MIT License | ||||
|  * https://opensource.org/licenses/MIT | ||||
|  * | ||||
|  * Hardware abstraction layer to run LMIC on a ESP32 using ESP-IDF. | ||||
|  *******************************************************************************/ | ||||
|  | ||||
| #include "../lmic/lmic.h" | ||||
| #include "../hal/hal_esp32.h" | ||||
|  | ||||
| #include "freertos/FreeRTOS.h" | ||||
| #include "freertos/task.h" | ||||
| #include "driver/gpio.h" | ||||
| #include "driver/spi_master.h" | ||||
| #include "driver/timer.h" | ||||
| #include "esp_log.h" | ||||
|  | ||||
| #define LMIC_UNUSED_PIN 0xff | ||||
|  | ||||
| #define NOTIFY_BIT_DIO 1 | ||||
| #define NOTIFY_BIT_TIMER 2 | ||||
| #define NOTIFY_BIT_WAKEUP 4 | ||||
| #define NOTIFY_BIT_STOP 8 | ||||
|  | ||||
|  | ||||
| static const char* const TAG = "ttn_hal"; | ||||
|  | ||||
| HAL_ESP32 ttn_hal; | ||||
|  | ||||
| TaskHandle_t HAL_ESP32::lmicTask = nullptr; | ||||
| uint32_t HAL_ESP32::dioInterruptTime = 0; | ||||
| uint8_t HAL_ESP32::dioNum = 0; | ||||
|  | ||||
|  | ||||
| // ----------------------------------------------------------------------------- | ||||
| // Constructor | ||||
|  | ||||
| HAL_ESP32::HAL_ESP32() | ||||
|     : rssiCal(10), nextAlarm(0) | ||||
| {     | ||||
| } | ||||
|  | ||||
| // ----------------------------------------------------------------------------- | ||||
| // I/O | ||||
|  | ||||
| void HAL_ESP32::configurePins(spi_host_device_t spi_host, uint8_t nss, uint8_t rxtx, uint8_t rst, uint8_t dio0, uint8_t dio1) | ||||
| { | ||||
|     spiHost = spi_host; | ||||
|     pinNSS = (gpio_num_t)nss; | ||||
|     pinRxTx = (gpio_num_t)rxtx; | ||||
|     pinRst = (gpio_num_t)rst; | ||||
|     pinDIO0 = (gpio_num_t)dio0; | ||||
|     pinDIO1 = (gpio_num_t)dio1; | ||||
|  | ||||
|     // Until the background process has been started, use the current task | ||||
|     // for supporting calls like `hal_waitUntil()`. | ||||
|     lmicTask = xTaskGetCurrentTaskHandle(); | ||||
| } | ||||
|  | ||||
|  | ||||
| void IRAM_ATTR HAL_ESP32::dioIrqHandler(void *arg) | ||||
| { | ||||
|     dioInterruptTime = hal_ticks(); | ||||
|     dioNum = (u1_t)(long)arg; | ||||
|     BaseType_t higherPrioTaskWoken = pdFALSE; | ||||
|     xTaskNotifyFromISR(lmicTask, NOTIFY_BIT_DIO, eSetBits, &higherPrioTaskWoken); | ||||
|     if (higherPrioTaskWoken) | ||||
|         portYIELD_FROM_ISR(); | ||||
| } | ||||
|  | ||||
| void HAL_ESP32::ioInit() | ||||
| { | ||||
|     // pinNSS and pinDIO0 and pinDIO1 are required | ||||
|     ASSERT(pinNSS != LMIC_UNUSED_PIN); | ||||
|     ASSERT(pinDIO0 != LMIC_UNUSED_PIN); | ||||
|     ASSERT(pinDIO1 != LMIC_UNUSED_PIN); | ||||
|  | ||||
|     gpio_pad_select_gpio(pinNSS); | ||||
|     gpio_set_level(pinNSS, 0); | ||||
|     gpio_set_direction(pinNSS, GPIO_MODE_OUTPUT); | ||||
|  | ||||
|     if (pinRxTx != LMIC_UNUSED_PIN) | ||||
|     { | ||||
|         gpio_pad_select_gpio(pinRxTx); | ||||
|         gpio_set_level(pinRxTx, 0); | ||||
|         gpio_set_direction(pinRxTx, GPIO_MODE_OUTPUT); | ||||
|     } | ||||
|  | ||||
|     if (pinRst != LMIC_UNUSED_PIN) | ||||
|     { | ||||
|         gpio_pad_select_gpio(pinRst); | ||||
|         gpio_set_level(pinRst, 0); | ||||
|         gpio_set_direction(pinRst, GPIO_MODE_OUTPUT); | ||||
|     } | ||||
|  | ||||
|     // DIO pins with interrupt handlers | ||||
|     gpio_pad_select_gpio(pinDIO0); | ||||
|     gpio_set_direction(pinDIO0, GPIO_MODE_INPUT); | ||||
|     gpio_set_intr_type(pinDIO0, GPIO_INTR_POSEDGE); | ||||
|  | ||||
|     gpio_pad_select_gpio(pinDIO1); | ||||
|     gpio_set_direction(pinDIO1, GPIO_MODE_INPUT); | ||||
|     gpio_set_intr_type(pinDIO1, GPIO_INTR_POSEDGE); | ||||
|  | ||||
|     ESP_LOGI(TAG, "IO initialized"); | ||||
| } | ||||
|  | ||||
| void hal_pin_rxtx(u1_t val) | ||||
| { | ||||
|     if (ttn_hal.pinRxTx == LMIC_UNUSED_PIN) | ||||
|         return; | ||||
|      | ||||
|     gpio_set_level(ttn_hal.pinRxTx, val); | ||||
| } | ||||
|  | ||||
| void hal_pin_rst(u1_t val) | ||||
| { | ||||
|     if (ttn_hal.pinRst == LMIC_UNUSED_PIN) | ||||
|         return; | ||||
|  | ||||
|     if (val == 0 || val == 1) | ||||
|     { | ||||
|         // drive pin | ||||
|         gpio_set_level(ttn_hal.pinRst, val); | ||||
|         gpio_set_direction(ttn_hal.pinRst, GPIO_MODE_OUTPUT); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
| #if defined(CONFIG_TTN_RESET_STATES_ASSERTED) | ||||
|         // drive up the pin because the hardware is nonstandard | ||||
|         gpio_set_level(ttn_hal.pinRst, 1); | ||||
|         gpio_set_direction(ttn_hal.pinRst, GPIO_MODE_OUTPUT); | ||||
| #else | ||||
|         // keep pin floating | ||||
|         gpio_set_level(ttn_hal.pinRst, val); | ||||
|         gpio_set_direction(ttn_hal.pinRst, GPIO_MODE_INPUT); | ||||
| #endif | ||||
|     } | ||||
| } | ||||
|  | ||||
| s1_t hal_getRssiCal (void) | ||||
| { | ||||
|     return ttn_hal.rssiCal; | ||||
| } | ||||
|  | ||||
| ostime_t hal_setModuleActive (bit_t val) | ||||
| { | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| bit_t hal_queryUsingTcxo(void) | ||||
| { | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| uint8_t hal_getTxPowerPolicy(u1_t inputPolicy, s1_t requestedPower, u4_t frequency) | ||||
| { | ||||
|     return LMICHAL_radio_tx_power_policy_paboost; | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| // ----------------------------------------------------------------------------- | ||||
| // SPI | ||||
|  | ||||
| void HAL_ESP32::spiInit() | ||||
| { | ||||
|     // init device | ||||
|     spi_device_interface_config_t spiConfig; | ||||
|     memset(&spiConfig, 0, sizeof(spiConfig)); | ||||
|     spiConfig.mode = 1; | ||||
|     spiConfig.clock_speed_hz = CONFIG_TTN_SPI_FREQ; | ||||
|     spiConfig.command_bits = 0; | ||||
|     spiConfig.address_bits = 8; | ||||
|     spiConfig.spics_io_num = pinNSS; | ||||
|     spiConfig.queue_size = 1; | ||||
|     spiConfig.cs_ena_posttrans = 2; | ||||
|  | ||||
|     esp_err_t ret = spi_bus_add_device(spiHost, &spiConfig, &spiHandle); | ||||
|     ESP_ERROR_CHECK(ret); | ||||
|  | ||||
|     ESP_LOGI(TAG, "SPI initialized"); | ||||
| } | ||||
|  | ||||
| void hal_spi_write(u1_t cmd, const u1_t *buf, size_t len) | ||||
| { | ||||
|     ttn_hal.spiWrite(cmd, buf, len); | ||||
| } | ||||
|  | ||||
| void HAL_ESP32::spiWrite(uint8_t cmd, const uint8_t *buf, size_t len) | ||||
| { | ||||
|     memset(&spiTransaction, 0, sizeof(spiTransaction)); | ||||
|     spiTransaction.addr = cmd; | ||||
|     spiTransaction.length = 8 * len; | ||||
|     spiTransaction.tx_buffer = buf; | ||||
|     esp_err_t err = spi_device_transmit(spiHandle, &spiTransaction); | ||||
|     ESP_ERROR_CHECK(err); | ||||
| } | ||||
|  | ||||
| void hal_spi_read(u1_t cmd, u1_t *buf, size_t len) | ||||
| { | ||||
|     ttn_hal.spiRead(cmd, buf, len); | ||||
| } | ||||
|  | ||||
| void HAL_ESP32::spiRead(uint8_t cmd, uint8_t *buf, size_t len) | ||||
| { | ||||
|     memset(buf, 0, len); | ||||
|     memset(&spiTransaction, 0, sizeof(spiTransaction)); | ||||
|     spiTransaction.addr = cmd; | ||||
|     spiTransaction.length = 8 * len; | ||||
|     spiTransaction.rxlength = 8 * len; | ||||
|     spiTransaction.tx_buffer = buf; | ||||
|     spiTransaction.rx_buffer = buf; | ||||
|     esp_err_t err = spi_device_transmit(spiHandle, &spiTransaction); | ||||
|     ESP_ERROR_CHECK(err); | ||||
| } | ||||
|  | ||||
|  | ||||
| // ----------------------------------------------------------------------------- | ||||
| // TIME | ||||
|  | ||||
| /* | ||||
|  * LIMIC uses a 32 bit time system (ostime_t) counting ticks. In this | ||||
|  * implementation each tick is 16µs. It will wrap arounnd every 19 hours. | ||||
|  *  | ||||
|  * The ESP32 has a 64 bit timer counting microseconds. It will wrap around | ||||
|  * every 584,000 years. So we don't need to bother. | ||||
|  *  | ||||
|  * Based on this timer, future callbacks can be scheduled. This is used to | ||||
|  * schedule the next LMIC job. | ||||
|  */ | ||||
|  | ||||
| // 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() | ||||
| { | ||||
|     esp_timer_create_args_t timerConfig = { | ||||
|         .callback = &timerCallback, | ||||
|         .arg = nullptr, | ||||
|         .dispatch_method = ESP_TIMER_TASK, | ||||
|         .name = "lmic_job" | ||||
|     }; | ||||
|     esp_err_t err = esp_timer_create(&timerConfig, &timer); | ||||
|     ESP_ERROR_CHECK(err); | ||||
|  | ||||
|     ESP_LOGI(TAG, "Timer initialized"); | ||||
| } | ||||
|  | ||||
| void HAL_ESP32::setNextAlarm(int64_t time) | ||||
| { | ||||
|     nextAlarm = time; | ||||
| } | ||||
|  | ||||
| void HAL_ESP32::armTimer(int64_t espNow) | ||||
| { | ||||
|     if (nextAlarm == 0) | ||||
|         return; | ||||
|     int64_t timeout = nextAlarm - esp_timer_get_time(); | ||||
|     if (timeout < 0) | ||||
|         timeout = 10; | ||||
|     esp_timer_start_once(timer, timeout); | ||||
| } | ||||
|  | ||||
| void HAL_ESP32::disarmTimer() | ||||
| { | ||||
|     esp_timer_stop(timer); | ||||
| } | ||||
|  | ||||
| void HAL_ESP32::timerCallback(void *arg) | ||||
| { | ||||
|     xTaskNotify(lmicTask, NOTIFY_BIT_TIMER, eSetBits); | ||||
| } | ||||
|  | ||||
| // Wait for the next external event. Either: | ||||
| // - scheduled timer due to scheduled job or waiting for a given time | ||||
| // - wake up event from the client code | ||||
| // - I/O interrupt (DIO0 or DIO1 pin) | ||||
| bool HAL_ESP32::wait(WaitKind waitKind) | ||||
| { | ||||
|     TickType_t ticksToWait = waitKind == CHECK_IO ? 0 : portMAX_DELAY; | ||||
|     while (true) | ||||
|     { | ||||
|         uint32_t bits = ulTaskNotifyTake(pdTRUE, ticksToWait); | ||||
|         if (bits == 0) | ||||
|             return false; | ||||
|  | ||||
|         if ((bits & NOTIFY_BIT_STOP) != 0) | ||||
|             return false; | ||||
|  | ||||
|         if ((bits & NOTIFY_BIT_WAKEUP) != 0) | ||||
|         { | ||||
|             if (waitKind != WAIT_FOR_TIMER) | ||||
|             { | ||||
|                 disarmTimer(); | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|         else if ((bits & NOTIFY_BIT_TIMER) != 0) | ||||
|         { | ||||
|             disarmTimer(); | ||||
|             setNextAlarm(0); | ||||
|             if (waitKind != CHECK_IO) | ||||
|                 return true; | ||||
|         } | ||||
|         else // IO interrupt | ||||
|         { | ||||
|             if (waitKind != WAIT_FOR_TIMER) | ||||
|                 disarmTimer(); | ||||
|             enterCriticalSection(); | ||||
|             radio_irq_handler_v2(dioNum, dioInterruptTime); | ||||
|             leaveCriticalSection(); | ||||
|             if (waitKind != WAIT_FOR_TIMER) | ||||
|                 return true; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| // Gets current time in LMIC ticks | ||||
| u4_t IRAM_ATTR hal_ticks() | ||||
| { | ||||
|     // LMIC tick unit: 16µs | ||||
|     // esp_timer unit: 1µs | ||||
|     return (u4_t)(esp_timer_get_time() >> 4); | ||||
| } | ||||
|  | ||||
| // Wait until the specified time. | ||||
| // Called if the LMIC code needs to wait for a precise time. | ||||
| // All other events are ignored and will be served later. | ||||
| u4_t hal_waitUntil(u4_t time) | ||||
| { | ||||
|     return ttn_hal.waitUntil(time); | ||||
| } | ||||
|  | ||||
| uint32_t HAL_ESP32::waitUntil(uint32_t osTime) | ||||
| { | ||||
|     int64_t espNow = esp_timer_get_time(); | ||||
|     int64_t espTime = osTimeToEspTime(espNow, osTime); | ||||
|     setNextAlarm(espTime); | ||||
|     armTimer(espNow); | ||||
|     wait(WAIT_FOR_TIMER); | ||||
|  | ||||
|     u4_t osNow = hal_ticks(); | ||||
|     u4_t diff = osNow - osTime; | ||||
|     return diff < 0x80000000U ? diff : 0; | ||||
| } | ||||
|  | ||||
| // Called by client code to wake up LMIC to do something, | ||||
| // e.g. send a submitted messages. | ||||
| void HAL_ESP32::wakeUp() | ||||
| { | ||||
|     xTaskNotify(lmicTask, NOTIFY_BIT_WAKEUP, eSetBits); | ||||
| } | ||||
|  | ||||
| // Check if the specified time has been reached or almost reached. | ||||
| // Otherwise, save it as alarm time. | ||||
| // LMIC calls this function with the scheduled time of the next job | ||||
| // in the queue. If the job is not due yet, LMIC will go to sleep. | ||||
| u1_t hal_checkTimer(uint32_t time) | ||||
| { | ||||
|     return ttn_hal.checkTimer(time); | ||||
| } | ||||
|  | ||||
| uint8_t HAL_ESP32::checkTimer(u4_t osTime) | ||||
| { | ||||
|     int64_t espNow = esp_timer_get_time(); | ||||
|     int64_t espTime = osTimeToEspTime(espNow, osTime); | ||||
|     int64_t diff = espTime - espNow; | ||||
|     if (diff < 100) | ||||
|         return 1; // timer has expired or will expire very soon | ||||
|  | ||||
|     setNextAlarm(espTime); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| // Go to sleep until next event. | ||||
| // Called when LMIC is not busy and not job is due to be executed. | ||||
| void hal_sleep() | ||||
| { | ||||
|     ttn_hal.sleep(); | ||||
| } | ||||
|  | ||||
| void HAL_ESP32::sleep() | ||||
| { | ||||
|     if (wait(CHECK_IO)) | ||||
|         return; | ||||
|  | ||||
|     armTimer(esp_timer_get_time()); | ||||
|     wait(WAIT_FOR_ANY_EVENT); | ||||
| } | ||||
|  | ||||
|  | ||||
| // ----------------------------------------------------------------------------- | ||||
| // IRQ | ||||
|  | ||||
| void hal_disableIRQs() | ||||
| { | ||||
|     // nothing to do as interrupt handlers post message to queue | ||||
|     // and don't access any shared data structures | ||||
| } | ||||
|  | ||||
| void hal_enableIRQs() | ||||
| { | ||||
|     // nothing to do as interrupt handlers post message to queue | ||||
|     // and don't access any shared data structures | ||||
| } | ||||
|  | ||||
| void hal_processPendingIRQs() | ||||
| { | ||||
|     // nothing to do as interrupt handlers post message to queue | ||||
|     // and don't access any shared data structures | ||||
| } | ||||
|  | ||||
|  | ||||
| // ----------------------------------------------------------------------------- | ||||
| // Synchronization between application code and background task | ||||
|  | ||||
| void HAL_ESP32::initCriticalSection() | ||||
| { | ||||
|     mutex = xSemaphoreCreateRecursiveMutex(); | ||||
| } | ||||
|  | ||||
| void HAL_ESP32::enterCriticalSection() | ||||
| { | ||||
|     xSemaphoreTakeRecursive(mutex, portMAX_DELAY); | ||||
| } | ||||
|  | ||||
| void HAL_ESP32::leaveCriticalSection() | ||||
| { | ||||
|     xSemaphoreGiveRecursive(mutex); | ||||
| } | ||||
|  | ||||
| // ----------------------------------------------------------------------------- | ||||
|  | ||||
| void HAL_ESP32::lmicBackgroundTask(void* pvParameter) | ||||
| { | ||||
|     HAL_ESP32* instance = (HAL_ESP32*)pvParameter; | ||||
|     while (instance->runBackgroundTask) | ||||
|         os_runloop_once(); | ||||
|     vTaskDelete(nullptr); | ||||
| } | ||||
|  | ||||
| void hal_init_ex(const void *pContext) | ||||
| { | ||||
|     ttn_hal.init(); | ||||
| } | ||||
|  | ||||
| void HAL_ESP32::init() | ||||
| { | ||||
|     // configure radio I/O and interrupt handler | ||||
|     ioInit(); | ||||
|     // configure radio SPI | ||||
|     spiInit(); | ||||
|     // configure timer and alarm callback | ||||
|     timerInit(); | ||||
| } | ||||
|  | ||||
| void HAL_ESP32::startLMICTask() | ||||
| { | ||||
|     runBackgroundTask = true; | ||||
|     xTaskCreate(lmicBackgroundTask, "ttn_lmic", 1024 * 4, this, CONFIG_TTN_BG_TASK_PRIO, &lmicTask); | ||||
|  | ||||
|     // enable interrupts | ||||
|     gpio_isr_handler_add(pinDIO0, dioIrqHandler, (void *)0); | ||||
|     gpio_isr_handler_add(pinDIO1, dioIrqHandler, (void *)1); | ||||
| } | ||||
|  | ||||
| void HAL_ESP32::stopLMICTask() | ||||
| { | ||||
|     runBackgroundTask = false; | ||||
|     gpio_isr_handler_remove(pinDIO0); | ||||
|     gpio_isr_handler_remove(pinDIO1); | ||||
|     disarmTimer(); | ||||
|     xTaskNotify(lmicTask, NOTIFY_BIT_STOP, eSetBits); | ||||
| } | ||||
|  | ||||
|  | ||||
| // ----------------------------------------------------------------------------- | ||||
| // Fatal failure | ||||
|  | ||||
| static hal_failure_handler_t* custom_hal_failure_handler = nullptr; | ||||
|  | ||||
| void hal_set_failure_handler(const hal_failure_handler_t* const handler) | ||||
| { | ||||
|     custom_hal_failure_handler = handler; | ||||
| } | ||||
|  | ||||
| void hal_failed(const char *file, u2_t line) | ||||
| { | ||||
|     if (custom_hal_failure_handler != nullptr) | ||||
|         (*custom_hal_failure_handler)(file, line); | ||||
|  | ||||
|     ESP_LOGE(TAG, "LMIC failed and stopped: %s:%d", file, line); | ||||
|  | ||||
|     // go to sleep forever | ||||
|     while (true) | ||||
|     { | ||||
|         vTaskDelay(portMAX_DELAY); | ||||
|     } | ||||
| } | ||||
| @ -2,7 +2,7 @@ | ||||
|  *  | ||||
|  * ttn-esp32 - The Things Network device library for ESP-IDF / SX127x | ||||
|  *  | ||||
|  * Copyright (c) 2018-2019 Manuel Bleichenbacher | ||||
|  * Copyright (c) 2018-2021 Manuel Bleichenbacher | ||||
|  *  | ||||
|  * Licensed under MIT License | ||||
|  * https://opensource.org/licenses/MIT | ||||
| @ -10,85 +10,33 @@ | ||||
|  * Hardware abstraction layer to run LMIC on a ESP32 using ESP-IDF. | ||||
|  *******************************************************************************/ | ||||
|  | ||||
| #ifndef _hal_esp32_h_ | ||||
| #define _hal_esp32_h_ | ||||
| #ifndef HAL_ESP32_H | ||||
| #define HAL_ESP32_H | ||||
|  | ||||
| #include <stdint.h> | ||||
| #include <freertos/FreeRTOS.h> | ||||
| #include <freertos/queue.h> | ||||
| #include <freertos/task.h> | ||||
| #include <freertos/semphr.h> | ||||
| #include <driver/gpio.h> | ||||
| #include <driver/spi_master.h> | ||||
| #include <esp_timer.h> | ||||
| #include "freertos/FreeRTOS.h" | ||||
| #include "driver/spi_master.h" | ||||
|  | ||||
|  | ||||
| enum WaitKind { | ||||
|     CHECK_IO, | ||||
|     WAIT_FOR_ANY_EVENT, | ||||
|     WAIT_FOR_TIMER | ||||
| }; | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
|  | ||||
|  | ||||
| void hal_esp32_configure_pins(spi_host_device_t spi_host, uint8_t nss, uint8_t rxtx, uint8_t rst, uint8_t dio0, uint8_t dio1); | ||||
| void hal_esp32_start_lmic_task(void); | ||||
| void hal_esp32_stop_lmic_task(void); | ||||
|  | ||||
| class HAL_ESP32 | ||||
| { | ||||
| public: | ||||
|     HAL_ESP32(); | ||||
| void hal_esp32_wake_up(void); | ||||
| void hal_esp32_init_critical_section(void); | ||||
| void hal_esp32_enter_critical_section(void); | ||||
| void hal_esp32_leave_critical_section(void); | ||||
|  | ||||
|     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 startLMICTask(); | ||||
|     void stopLMICTask(); | ||||
|      | ||||
|     void wakeUp(); | ||||
|     void initCriticalSection(); | ||||
|     void enterCriticalSection(); | ||||
|     void leaveCriticalSection(); | ||||
|  | ||||
|     void spiWrite(uint8_t cmd, const uint8_t *buf, size_t len); | ||||
|     void spiRead(uint8_t cmd, uint8_t *buf, size_t len); | ||||
|     uint8_t checkTimer(uint32_t osTime); | ||||
|     void sleep(); | ||||
|      | ||||
|     uint32_t waitUntil(uint32_t osTime); | ||||
|  | ||||
|     spi_host_device_t spiHost; | ||||
|     gpio_num_t pinNSS; | ||||
|     gpio_num_t pinRxTx; | ||||
|     gpio_num_t pinRst; | ||||
|     gpio_num_t pinDIO0; | ||||
|     gpio_num_t pinDIO1; | ||||
|     int8_t rssiCal; | ||||
|  | ||||
| private: | ||||
|     static void lmicBackgroundTask(void* pvParameter); | ||||
|     static void dioIrqHandler(void* arg); | ||||
|     static void timerCallback(void *arg); | ||||
|     static int64_t osTimeToEspTime(int64_t espNow, uint32_t osTime); | ||||
|  | ||||
|     void ioInit(); | ||||
|     void spiInit(); | ||||
|     void timerInit(); | ||||
|  | ||||
|     void setNextAlarm(int64_t time); | ||||
|     void armTimer(int64_t espNow); | ||||
|     void disarmTimer(); | ||||
|     bool wait(WaitKind waitKind); | ||||
|  | ||||
|     static TaskHandle_t lmicTask; | ||||
|     static uint32_t dioInterruptTime; | ||||
|     static uint8_t dioNum; | ||||
|  | ||||
|     spi_device_handle_t spiHandle; | ||||
|     spi_transaction_t spiTransaction; | ||||
|     SemaphoreHandle_t mutex; | ||||
|     esp_timer_handle_t timer; | ||||
|     int64_t nextAlarm; | ||||
|     volatile bool runBackgroundTask; | ||||
| }; | ||||
|  | ||||
| extern HAL_ESP32 ttn_hal; | ||||
| void hal_esp32_set_rssi_cal(int8_t rssi_cal); | ||||
|  | ||||
|  | ||||
| #endif // _hal_esp32_h_ | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
|  | ||||
|  | ||||
| #endif // HAL_ESP32_H | ||||
|  | ||||
| @ -18,7 +18,7 @@ | ||||
| #include "esp_system.h" | ||||
| #include "nvs_flash.h" | ||||
| #include "lmic/lmic.h" | ||||
| //#include "hal/hal_esp32.h" | ||||
| #include "hal/hal_esp32.h" | ||||
|  | ||||
| #if defined(TTN_HAS_AT_COMMANDS) | ||||
| #define UART_NUM CONFIG_TTN_PROVISION_UART_NUM | ||||
| @ -305,10 +305,9 @@ void process_line(void) | ||||
|  | ||||
|     if (reset_needed) | ||||
|     { | ||||
|         // TODO | ||||
|         // ttn_hal.enterCriticalSection(); | ||||
|         hal_esp32_enter_critical_section(); | ||||
|         LMIC_reset(); | ||||
|         // ttn_hal.leaveCriticalSection(); | ||||
|         hal_esp32_leave_critical_section(); | ||||
|         LMIC.client.eventCb(LMIC.client.eventUserData, EV_RESET); | ||||
|     } | ||||
|  | ||||
|  | ||||
		Reference in New Issue
	
	Block a user