Browse Source

Refactor sx127x_driver into a component

ugv_io
Alex Mikhalev 6 years ago
parent
commit
85f816850b
  1. 8
      components/sx127x_driver/CMakeLists.txt
  2. 175
      components/sx127x_driver/sx127x_driver.c
  3. 12
      components/sx127x_driver/sx127x_driver.h
  4. 112
      components/sx127x_driver/sx127x_internal.h
  5. 184
      components/sx127x_driver/sx127x_registers.c
  6. 20
      components/sx127x_driver/sx127x_registers.h
  7. 8
      main/CMakeLists.txt
  8. 468
      main/sx127x_driver/sx127x_driver.c

8
components/sx127x_driver/CMakeLists.txt

@ -0,0 +1,8 @@
set(COMPONENT_SRCS
"sx127x_driver.c"
"sx127x_registers.c")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()
component_compile_options("-Werror=incompatible-pointer-types")

175
components/sx127x_driver/sx127x_driver.c

@ -0,0 +1,175 @@
#include "sx127x_driver.h"
#include "sx127x_internal.h"
#include "sx127x_registers.h"
#include <esp_log.h>
#include <string.h>
const char *SX127X_TAG = "sx127x";
esp_err_t sx127x_init(sx127x_config_t *config, sx127x_t **handle_ptr) {
esp_err_t ret;
sx127x_t *handle = malloc(sizeof(sx127x_t));
SX127X_CHECK(handle != NULL, "malloc error", ESP_ERR_NO_MEM);
handle->task_handle = NULL;
memcpy(&handle->config, config, sizeof(sx127x_config_t));
ret = gpio_set_direction(config->rst_io_num, GPIO_MODE_OUTPUT);
SX127X_ERROR_CHECK2(ret, gpio_set_direction)
// perform reset
gpio_set_level(config->rst_io_num, 0);
vTaskDelay(RESET_DELAY);
gpio_set_level(config->rst_io_num, 1);
vTaskDelay(RESET_DELAY);
spi_bus_config_t bus_config = {.mosi_io_num = config->mosi_io_num,
.miso_io_num = config->miso_io_num,
.sclk_io_num = config->sck_io_num,
.quadhd_io_num = -1,
.quadwp_io_num = -1,
.max_transfer_sz = SX127X_MAX_TRANSFER};
ret = spi_bus_initialize(config->spi_host, &bus_config, 1);
SX127X_ERROR_CHECK2(ret, spi_bus_initialize)
spi_device_interface_config_t device_config = {
.command_bits = 0,
.address_bits = 8,
.dummy_bits = 0,
.mode = 0,
.duty_cycle_pos = 0,
.cs_ena_pretrans = 0,
.cs_ena_posttrans = 0,
.clock_speed_hz = SPI_CLOCK_HZ, // 8mhz
.input_delay_ns = 0,
.spics_io_num = config->cs_io_num,
.flags = 0,
.queue_size = 8,
.pre_cb = NULL,
.post_cb = NULL,
};
ret = spi_bus_add_device(config->spi_host, &device_config, &handle->device_handle);
SX127X_ERROR_CHECK2(ret, spi_bus_add_device)
// read version and check that it is compatible
uint8_t version;
ret = sx127x_read_register(handle, REG_VERSION, &version);
SX127X_ERROR_CHECK2(ret, sx127x_read_register);
SX127X_CHECK(version == 0x12, "unsupported version %#x", ESP_ERR_INVALID_VERSION, version);
ret = sx127x_sleep(handle);
SX127X_ERROR_CHECK(ret);
ret = sx127x_set_frequency(handle, config->frequency);
SX127X_ERROR_CHECK(ret);
ret = sx127x_write_register(handle, REG_FIFO_TX_BASE_ADDR, 0);
SX127X_ERROR_CHECK(ret);
sx127x_write_register(handle, REG_FIFO_RX_BASE_ADDR, 0);
SX127X_ERROR_CHECK(ret);
uint8_t reg_lna;
ret = sx127x_read_register(handle, REG_LNA, &reg_lna);
SX127X_ERROR_CHECK(ret);
reg_lna |= 0x03; // set LNA boost
ret = sx127x_write_register(handle, REG_LNA, reg_lna);
SX127X_ERROR_CHECK(ret);
// set auto AGC
ret = sx127x_write_register(handle, REG_MODEM_CONFIG_3, CONFIG3_AUTO_AGC);
SX127X_ERROR_CHECK(ret);
ret = sx127x_set_tx_power(handle, config->tx_power, true);
SX127X_ERROR_CHECK(ret);
ret = sx127x_set_spreading_factor(handle, config->spreading_factor);
SX127X_ERROR_CHECK(ret);
ret = sx127x_set_sync_word(handle, config->sync_word);
SX127X_ERROR_CHECK(ret);
ret = sx127x_set_crc(handle, config->crc);
SX127X_ERROR_CHECK(ret);
ret = sx127x_standby(handle);
ESP_ERROR_CHECK(ret);
*handle_ptr = handle;
return ESP_OK;
}
esp_err_t sx127x_free(sx127x_t *handle) {
esp_err_t ret;
if (handle->task_handle) {
ret = sx127x_stop(handle);
SX127X_ERROR_CHECK(ret);
}
ret = spi_bus_remove_device(handle->device_handle);
SX127X_ERROR_CHECK2(ret, spi_bus_remove_device)
ret = spi_bus_free(handle->config.spi_host);
SX127X_ERROR_CHECK2(ret, spi_bus_free)
free(handle);
return ESP_OK;
}
void sx127x_isr(void *arg) {
sx127x_t *handle = (sx127x_t *)arg;
ESP_LOGI(SX127X_TAG, "sx127x_isr");
}
void sx127x_task(void *arg) {
sx127x_t *handle = (sx127x_t *)arg;
while (handle->task_running) {
vTaskDelay(pdMS_TO_TICKS(10));
}
}
esp_err_t sx127x_start(sx127x_t *handle) {
esp_err_t ret;
SX127X_CHECK(handle->task_handle == NULL, "task already running", ESP_ERR_INVALID_STATE);
ret = sx127x_write_register(handle, REG_DIO_MAPPING_1, 0x00);
SX127X_ERROR_CHECK(ret);
gpio_config_t irq_io_config;
irq_io_config.intr_type = GPIO_INTR_POSEDGE;
irq_io_config.mode = GPIO_MODE_INPUT;
irq_io_config.pin_bit_mask = (1ULL << handle->config.irq_io_num);
irq_io_config.pull_down_en = 0;
irq_io_config.pull_up_en = 0;
ret = gpio_config(&irq_io_config);
SX127X_ERROR_CHECK2(ret, gpio_config)
ret = gpio_install_isr_service(ESP_INTR_FLAG_LEVEL1);
SX127X_ERROR_CHECK2(ret, gpio_install_isr_service);
ret = gpio_isr_handler_add(handle->config.irq_io_num, sx127x_isr, (void *)handle);
SX127X_ERROR_CHECK2(ret, gpio_isr_handler_add);
handle->task_running = true;
BaseType_t pdRet =
xTaskCreate(sx127x_task, "sx127x_task", TASK_STACK_SIZE, (void *)handle, TASK_PRIORITY, &handle->task_handle);
SX127X_CHECK(pdRet == pdPASS, "failed to create task", ESP_FAIL);
return ESP_OK;
}
esp_err_t sx127x_stop(sx127x_t *handle) {
esp_err_t ret;
SX127X_CHECK(handle->task_handle != NULL, "task has not been started", ESP_ERR_INVALID_STATE);
handle->task_running = false;
xTaskNotify(handle->task_handle, 0, eNoAction);
ret = gpio_isr_handler_remove(handle->config.irq_io_num);
SX127X_ERROR_CHECK2(ret, gpio_isr_handler_remove);
gpio_uninstall_isr_service();
return ESP_OK;
}

12
main/sx127x_driver/sx127x_driver.h → components/sx127x_driver/sx127x_driver.h

@ -76,18 +76,6 @@ esp_err_t sx127x_init(sx127x_config_t *config, sx127x_t **handle_ptr);
esp_err_t sx127x_free(sx127x_t *handle); esp_err_t sx127x_free(sx127x_t *handle);
esp_err_t sx127x_set_frequency(sx127x_t *handle, uint64_t frequency);
esp_err_t sx127x_set_tx_power(sx127x_t *handle, uint8_t tx_power, sx127x_pa_boost_t pa_boost);
esp_err_t sx127x_set_spreading_factor(sx127x_t *handle, uint8_t spreading_factor);
esp_err_t sx127x_set_signal_bandwidth(sx127x_t *handle, uint64_t signal_bandwidth);
esp_err_t sx127x_set_sync_word(sx127x_t *handle, uint8_t sync_word);
esp_err_t sx127x_set_crc(sx127x_t *handle, sx127x_crc_t crc);
esp_err_t sx127x_sleep(sx127x_t *handle); esp_err_t sx127x_sleep(sx127x_t *handle);
esp_err_t sx127x_standby(sx127x_t *handle); esp_err_t sx127x_standby(sx127x_t *handle);

112
components/sx127x_driver/sx127x_internal.h

@ -0,0 +1,112 @@
#include <esp_log.h>
#include <freertos/task.h>
#define MAX_PKT_LENGTH 255
#define RESET_DELAY (pdMS_TO_TICKS(15))
// 8mhz
#define SPI_CLOCK_HZ (8 * 1000 * 1000)
#define TASK_STACK_SIZE (2 * 1024)
#define TASK_PRIORITY 3
#define REG_FIFO 0x00
#define REG_OP_MODE 0x01
#define REG_FRF_MSB 0x06
#define REG_FRF_MID 0x07
#define REG_FRF_LSB 0x08
#define REG_PA_CONFIG 0x09
#define REG_OCP 0x0b
#define REG_LNA 0x0c
#define REG_FIFO_ADDR_PTR 0x0d
#define REG_FIFO_TX_BASE_ADDR 0x0e
#define REG_FIFO_RX_BASE_ADDR 0x0f
#define REG_FIFO_RX_CURRENT_ADDR 0x10
#define REG_IRQ_FLAGS 0x12
#define REG_RX_NB_BYTES 0x13
#define REG_PKT_SNR_VALUE 0x19
#define REG_PKT_RSSI_VALUE 0x1a
#define REG_MODEM_CONFIG_1 0x1d
#define REG_MODEM_CONFIG_2 0x1e
#define REG_PREAMBLE_MSB 0x20
#define REG_PREAMBLE_LSB 0x21
#define REG_PAYLOAD_LENGTH 0x22
#define REG_MODEM_CONFIG_3 0x26
#define REG_FREQ_ERROR_MSB 0x28
#define REG_FREQ_ERROR_MID 0x29
#define REG_FREQ_ERROR_LSB 0x2a
#define REG_RSSI_WIDEBAND 0x2c
#define REG_DETECTION_OPTIMIZE 0x31
#define REG_INVERTIQ 0x33
#define REG_DETECTION_THRESHOLD 0x37
#define REG_SYNC_WORD 0x39
#define REG_INVERTIQ2 0x3b
#define REG_DIO_MAPPING_1 0x40
#define REG_VERSION 0x42
#define REG_PA_DAC 0x4d
// modes
#define MODE_LONG_RANGE_MODE 0x80
#define MODE_SLEEP 0x00
#define MODE_STDBY 0x01
#define MODE_TX 0x03
#define MODE_RX_CONTINUOUS 0x05
#define MODE_RX_SINGLE 0x06
#define CONFIG2_CRC 0x04
#define CONFIG3_AUTO_AGC 0x04
// PA config
#define PA_BOOST 0x80
// IRQ masks
#define IRQ_TX_DONE_MASK 0x08
#define IRQ_PAYLOAD_CRC_ERROR_MASK 0x20
#define IRQ_RX_DONE_MASK 0x40
#ifdef NODEBUG
#define SX127X_CHECK(check, str, ret_val, ...) \
if (!(check)) { \
ESP_LOGE(SX127X_TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
return (ret_val); \
}
#define SX127X_ERROR_CHECK(ret) \
{ \
esp_err_t _error_code = (ret); \
if (_error_code != ESP_OK) { \
return _error_code; \
} \
}
#define SX127X_ERROR_CHECK2(ret, fun) SX127X_CHECK(ret)
#else
#define SX127X_CHECK(check, str, ret_val, ...) \
if (!(check)) { \
ESP_LOGE(SX127X_TAG, "%s(%d): " #check ": " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
return (ret_val); \
}
#define SX127X_ERROR_CHECK(ret) \
{ \
esp_err_t _error_code = (ret); \
if (_error_code != ESP_OK) { \
const char *_error_name = esp_err_to_name(_error_code); \
ESP_LOGE(SX127X_TAG, "%s(%d): %s (%d)", __FUNCTION__, __LINE__, _error_name, _error_code); \
return _error_code; \
} \
}
#define SX127X_ERROR_CHECK2(ret, fun) \
{ \
esp_err_t _error_code = (ret); \
if (_error_code != ESP_OK) { \
const char *_error_name = esp_err_to_name(_error_code); \
ESP_LOGE(SX127X_TAG, "%s(%d): " #fun ": %s (%d)", __FUNCTION__, __LINE__, _error_name, _error_code); \
return _error_code; \
} \
}
#endif
struct sx127x {
sx127x_config_t config;
spi_device_handle_t device_handle;
TaskHandle_t task_handle;
bool task_running;
};
const char *SX127X_TAG;

184
components/sx127x_driver/sx127x_registers.c

@ -0,0 +1,184 @@
#include "sx127x_registers.h"
#include "sx127x_internal.h"
#include <string.h>
esp_err_t sx127x_read_register(sx127x_t *handle, uint8_t reg, uint8_t *value) {
return sx127x_single_transfer(handle, reg & 0x7f, 0x00, value);
}
esp_err_t sx127x_write_register(sx127x_t *handle, uint8_t reg, uint8_t value) {
return sx127x_single_transfer(handle, reg | 0x80, value, NULL);
}
esp_err_t sx127x_single_transfer(sx127x_t *handle, uint8_t addr, uint8_t to_slave, uint8_t *from_slave) {
spi_transaction_t trans;
memset(&trans, 0, sizeof(trans));
trans.flags = SPI_TRANS_USE_RXDATA | SPI_TRANS_USE_TXDATA;
trans.addr = addr;
trans.length = 8;
trans.rxlength = 8;
trans.tx_data[0] = to_slave;
esp_err_t ret = spi_device_transmit(handle->device_handle, &trans);
SX127X_ERROR_CHECK2(ret, spi_device_transmit);
if (from_slave) {
*from_slave = trans.rx_data[0];
}
ESP_LOGV(SX127X_TAG, "sx127x_single_transfer(%#x, %#x, %#x)", addr, trans.tx_data[0], trans.rx_data[0]);
return ESP_OK;
}
esp_err_t sx127x_sleep(sx127x_t *handle) {
return sx127x_write_register(handle, REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_SLEEP);
}
esp_err_t sx127x_standby(sx127x_t *handle) {
return sx127x_write_register(handle, REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_STDBY);
}
esp_err_t sx127x_set_frequency(sx127x_t *handle, uint64_t frequency) {
uint64_t frf = ((uint64_t)frequency << 19) / 32000000;
esp_err_t ret;
ret = sx127x_write_register(handle, REG_FRF_MSB, (uint8_t)(frf));
SX127X_ERROR_CHECK(ret);
frf >>= 8;
ret = sx127x_write_register(handle, REG_FRF_MID, (uint8_t)(frf));
SX127X_ERROR_CHECK(ret);
frf >>= 8;
ret = sx127x_write_register(handle, REG_FRF_LSB, (uint8_t)(frf));
SX127X_ERROR_CHECK(ret);
handle->config.frequency = frequency;
return ESP_OK;
}
esp_err_t sx127x_set_tx_power(sx127x_t *handle, uint8_t tx_power, sx127x_pa_boost_t pa_boost) {
esp_err_t ret;
if (pa_boost == SX127X_PA_BOOST_ENABLED) {
// PA BOOST
SX127X_CHECK(tx_power >= 2 && tx_power <= 20, "invalid tx_power: %d", ESP_ERR_INVALID_ARG, tx_power);
ret = sx127x_write_register(handle, REG_PA_CONFIG, PA_BOOST | (tx_power - 2));
} else {
// RFO
SX127X_CHECK(tx_power <= 14, "invalid tx_power: %d", ESP_ERR_INVALID_ARG, tx_power);
ret = sx127x_write_register(handle, REG_PA_CONFIG, 0x70 | tx_power);
}
SX127X_ERROR_CHECK(ret);
handle->config.tx_power = tx_power;
return ESP_OK;
}
esp_err_t sx127x_set_spreading_factor(sx127x_t *handle, uint8_t spreading_factor) {
SX127X_CHECK(spreading_factor >= 6 && spreading_factor <= 12, "invalid spreading_factor", ESP_ERR_INVALID_ARG);
// section 4.1.1.2 in SX1276 datasheet
uint8_t detection_optimize, detection_threshold;
if (spreading_factor == 6) {
detection_optimize = 0xc5;
detection_threshold = 0x0c;
} else {
detection_optimize = 0xc3;
detection_threshold = 0x0a;
}
SX127X_ERROR_CHECK(sx127x_write_register(handle, REG_DETECTION_OPTIMIZE, detection_optimize));
SX127X_ERROR_CHECK(sx127x_write_register(handle, REG_DETECTION_THRESHOLD, detection_threshold));
uint8_t modem_config_3;
SX127X_ERROR_CHECK(sx127x_read_register(handle, REG_MODEM_CONFIG_3, &modem_config_3));
modem_config_3 = (modem_config_3 & 0x03) | ((spreading_factor << 4) & 0xf0);
SX127X_ERROR_CHECK(sx127x_write_register(handle, REG_MODEM_CONFIG_3, modem_config_3));
handle->config.spreading_factor = spreading_factor;
return ESP_OK;
}
esp_err_t sx127x_set_signal_bandwidth(sx127x_t *handle, uint64_t signal_bandwidth) {
uint8_t bw_reg = sx127x_bw_to_reg(signal_bandwidth);
uint8_t modem_config_1;
SX127X_ERROR_CHECK(sx127x_read_register(handle, REG_MODEM_CONFIG_1, &modem_config_1));
modem_config_1 = (modem_config_1 & 0x0f) | (bw_reg << 4);
SX127X_ERROR_CHECK(sx127x_write_register(handle, REG_MODEM_CONFIG_1, modem_config_1));
handle->config.signal_bandwidth = signal_bandwidth;
// set low data rate optimization flag
uint64_t bw = sx127x_reg_to_bw(bw_reg);
uint8_t sf = handle->config.spreading_factor;
// section 4.1.1.5
uint64_t symbol_duration_ms = 1000 / (bw / (1L << sf));
// section 4.1.1.6
bool must_have_ldo = (symbol_duration_ms > 16);
bool ldo = must_have_ldo || (handle->config.ldo == SX127X_LDO_ENABLED);
uint8_t modem_config_3;
SX127X_ERROR_CHECK(sx127x_read_register(handle, REG_MODEM_CONFIG_3, &modem_config_3));
if (ldo) {
modem_config_3 |= (1 << 3);
} else {
modem_config_3 &= ~(1 << 3);
}
SX127X_ERROR_CHECK(sx127x_write_register(handle, REG_MODEM_CONFIG_3, modem_config_3));
return ESP_OK;
}
uint8_t sx127x_bw_to_reg(uint64_t bandwidth) {
if (bandwidth <= 7.8E3) {
return 0;
} else if (bandwidth <= 10.4E3) {
return 1;
} else if (bandwidth <= 15.6E3) {
return 2;
} else if (bandwidth <= 20.8E3) {
return 3;
} else if (bandwidth <= 31.25E3) {
return 4;
} else if (bandwidth <= 41.7E3) {
return 5;
} else if (bandwidth <= 62.5E3) {
return 6;
} else if (bandwidth <= 125E3) {
return 7;
} else if (bandwidth <= 250E3) {
return 8;
} else /* if (bandwidth <= 500E3) */ {
return 9;
}
}
uint64_t sx127x_reg_to_bw(uint8_t bandwidth_reg) {
switch (bandwidth_reg) {
case 0: return 7.8E3;
case 1: return 10.4E3;
case 2: return 15.6E3;
case 3: return 20.8E3;
case 4: return 31.25E3;
case 5: return 41.7E3;
case 6: return 62.5E3;
case 7: return 125E3;
case 8: return 250E3;
default:
case 9: return 500E3;
}
}
esp_err_t sx127x_set_sync_word(sx127x_t *handle, uint8_t sync_word) {
SX127X_ERROR_CHECK(sx127x_write_register(handle, REG_SYNC_WORD, sync_word));
handle->config.sync_word = sync_word;
return ESP_OK;
}
esp_err_t sx127x_set_crc(sx127x_t *handle, sx127x_crc_t crc) {
uint8_t modem_config_2;
SX127X_ERROR_CHECK(sx127x_read_register(handle, REG_MODEM_CONFIG_2, &modem_config_2));
if (crc == SX127X_CRC_ENABLED) {
modem_config_2 |= CONFIG2_CRC;
} else {
modem_config_2 &= ~CONFIG2_CRC;
}
SX127X_ERROR_CHECK(sx127x_write_register(handle, REG_MODEM_CONFIG_2, modem_config_2));
handle->config.crc = crc;
return ESP_OK;
}

20
components/sx127x_driver/sx127x_registers.h

@ -0,0 +1,20 @@
#include "sx127x_driver.h"
esp_err_t sx127x_read_register(sx127x_t *handle, uint8_t reg, uint8_t *value);
esp_err_t sx127x_write_register(sx127x_t *handle, uint8_t reg, uint8_t value);
esp_err_t sx127x_single_transfer(sx127x_t *handle, uint8_t addr, uint8_t to_slave, uint8_t *from_slave);
uint8_t sx127x_bw_to_reg(uint64_t bandwidth);
uint64_t sx127x_reg_to_bw(uint8_t bandwidth_reg);
esp_err_t sx127x_set_frequency(sx127x_t *handle, uint64_t frequency);
esp_err_t sx127x_set_tx_power(sx127x_t *handle, uint8_t tx_power, sx127x_pa_boost_t pa_boost);
esp_err_t sx127x_set_spreading_factor(sx127x_t *handle, uint8_t spreading_factor);
esp_err_t sx127x_set_signal_bandwidth(sx127x_t *handle, uint64_t signal_bandwidth);
esp_err_t sx127x_set_sync_word(sx127x_t *handle, uint8_t sync_word);
esp_err_t sx127x_set_crc(sx127x_t *handle, sx127x_crc_t crc);

8
main/CMakeLists.txt

@ -1,9 +1,7 @@
set(COMPONENT_SRCS "ugv_main.c" set(COMPONENT_SRCS "ugv_main.c"
"u8g2_esp32_hal.c" "u8g2_esp32_hal.c")
"sx127x_driver/sx127x_driver.c") set(COMPONENT_ADD_INCLUDEDIRS ".")
set(COMPONENT_ADD_INCLUDEDIRS "." set(COMPONENT_REQUIRES "u8g2" "sx127x_driver")
"./sx127x_driver")
set(COMPONENT_REQUIRES "u8g2")
register_component() register_component()

468
main/sx127x_driver/sx127x_driver.c

@ -1,468 +0,0 @@
#include "sx127x_driver.h"
#include <esp_log.h>
#include <freertos/task.h>
#include <string.h>
static const char *TAG = "sx127x";
#define MAX_PKT_LENGTH 255
#define RESET_DELAY (pdMS_TO_TICKS(15))
// 8mhz
#define SPI_CLOCK_HZ (8 * 1000 * 1000)
#define TASK_STACK_SIZE (2 * 1024)
#define TASK_PRIORITY 3
#define REG_FIFO 0x00
#define REG_OP_MODE 0x01
#define REG_FRF_MSB 0x06
#define REG_FRF_MID 0x07
#define REG_FRF_LSB 0x08
#define REG_PA_CONFIG 0x09
#define REG_OCP 0x0b
#define REG_LNA 0x0c
#define REG_FIFO_ADDR_PTR 0x0d
#define REG_FIFO_TX_BASE_ADDR 0x0e
#define REG_FIFO_RX_BASE_ADDR 0x0f
#define REG_FIFO_RX_CURRENT_ADDR 0x10
#define REG_IRQ_FLAGS 0x12
#define REG_RX_NB_BYTES 0x13
#define REG_PKT_SNR_VALUE 0x19
#define REG_PKT_RSSI_VALUE 0x1a
#define REG_MODEM_CONFIG_1 0x1d
#define REG_MODEM_CONFIG_2 0x1e
#define REG_PREAMBLE_MSB 0x20
#define REG_PREAMBLE_LSB 0x21
#define REG_PAYLOAD_LENGTH 0x22
#define REG_MODEM_CONFIG_3 0x26
#define REG_FREQ_ERROR_MSB 0x28
#define REG_FREQ_ERROR_MID 0x29
#define REG_FREQ_ERROR_LSB 0x2a
#define REG_RSSI_WIDEBAND 0x2c
#define REG_DETECTION_OPTIMIZE 0x31
#define REG_INVERTIQ 0x33
#define REG_DETECTION_THRESHOLD 0x37
#define REG_SYNC_WORD 0x39
#define REG_INVERTIQ2 0x3b
#define REG_DIO_MAPPING_1 0x40
#define REG_VERSION 0x42
#define REG_PA_DAC 0x4d
// modes
#define MODE_LONG_RANGE_MODE 0x80
#define MODE_SLEEP 0x00
#define MODE_STDBY 0x01
#define MODE_TX 0x03
#define MODE_RX_CONTINUOUS 0x05
#define MODE_RX_SINGLE 0x06
#define CONFIG2_CRC 0x04
#define CONFIG3_AUTO_AGC 0x04
// PA config
#define PA_BOOST 0x80
// IRQ masks
#define IRQ_TX_DONE_MASK 0x08
#define IRQ_PAYLOAD_CRC_ERROR_MASK 0x20
#define IRQ_RX_DONE_MASK 0x40
#ifdef NODEBUG
#define SX127X_CHECK(check, str, ret_val, ...) \
if (!(check)) { \
ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
return (ret_val); \
}
#define SX127X_ERROR_CHECK(ret) \
{ \
esp_err_t _error_code = (ret); \
if (_error_code != ESP_OK) { \
return _error_code; \
} \
}
#define SX127X_ERROR_CHECK2(ret, fun) SX127X_CHECK(ret)
#else
#define SX127X_CHECK(check, str, ret_val, ...) \
if (!(check)) { \
ESP_LOGE(TAG, "%s(%d): " #check ": " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
return (ret_val); \
}
#define SX127X_ERROR_CHECK(ret) \
{ \
esp_err_t _error_code = (ret); \
if (_error_code != ESP_OK) { \
const char *_error_name = esp_err_to_name(_error_code); \
ESP_LOGE(TAG, "%s(%d): %s (%d)", __FUNCTION__, __LINE__, _error_name, _error_code); \
return _error_code; \
} \
}
#define SX127X_ERROR_CHECK2(ret, fun) \
{ \
esp_err_t _error_code = (ret); \
if (_error_code != ESP_OK) { \
const char *_error_name = esp_err_to_name(_error_code); \
ESP_LOGE(TAG, "%s(%d): " #fun ": %s (%d)", __FUNCTION__, __LINE__, _error_name, _error_code); \
return _error_code; \
} \
}
#endif
struct sx127x {
sx127x_config_t config;
spi_device_handle_t device_handle;
TaskHandle_t task_handle;
bool task_running;
};
static esp_err_t sx127x_read_register(sx127x_t *handle, uint8_t reg, uint8_t *value);
static esp_err_t sx127x_write_register(sx127x_t *handle, uint8_t reg, uint8_t value);
static esp_err_t sx127x_single_transfer(sx127x_t *handle, uint8_t addr, uint8_t to_slave, uint8_t *from_slave);
static uint8_t sx127x_bw_to_reg(uint64_t bandwidth);
static uint64_t sx127x_reg_to_bw(uint8_t bandwidth_reg);
esp_err_t sx127x_init(sx127x_config_t *config, sx127x_t **handle_ptr) {
esp_err_t ret;
sx127x_t *handle = malloc(sizeof(sx127x_t));
SX127X_CHECK(handle != NULL, "malloc error", ESP_ERR_NO_MEM);
handle->task_handle = NULL;
memcpy(&handle->config, config, sizeof(sx127x_config_t));
ret = gpio_set_direction(config->rst_io_num, GPIO_MODE_OUTPUT);
SX127X_ERROR_CHECK2(ret, gpio_set_direction)
// perform reset
gpio_set_level(config->rst_io_num, 0);
vTaskDelay(RESET_DELAY);
gpio_set_level(config->rst_io_num, 1);
vTaskDelay(RESET_DELAY);
spi_bus_config_t bus_config = {.mosi_io_num = config->mosi_io_num,
.miso_io_num = config->miso_io_num,
.sclk_io_num = config->sck_io_num,
.quadhd_io_num = -1,
.quadwp_io_num = -1,
.max_transfer_sz = SX127X_MAX_TRANSFER};
ret = spi_bus_initialize(config->spi_host, &bus_config, 1);
SX127X_ERROR_CHECK2(ret, spi_bus_initialize)
spi_device_interface_config_t device_config = {
.command_bits = 0,
.address_bits = 8,
.dummy_bits = 0,
.mode = 0,
.duty_cycle_pos = 0,
.cs_ena_pretrans = 0,
.cs_ena_posttrans = 0,
.clock_speed_hz = SPI_CLOCK_HZ, // 8mhz
.input_delay_ns = 0,
.spics_io_num = config->cs_io_num,
.flags = 0,
.queue_size = 8,
.pre_cb = NULL,
.post_cb = NULL,
};
ret = spi_bus_add_device(config->spi_host, &device_config, &handle->device_handle);
SX127X_ERROR_CHECK2(ret, spi_bus_add_device)
// read version and check that it is compatible
uint8_t version;
ret = sx127x_read_register(handle, REG_VERSION, &version);
SX127X_ERROR_CHECK2(ret, sx127x_read_register);
SX127X_CHECK(version == 0x12, "unsupported version %#x", ESP_ERR_INVALID_VERSION, version);
ret = sx127x_sleep(handle);
SX127X_ERROR_CHECK(ret);
ret = sx127x_set_frequency(handle, config->frequency);
SX127X_ERROR_CHECK(ret);
ret = sx127x_write_register(handle, REG_FIFO_TX_BASE_ADDR, 0);
SX127X_ERROR_CHECK(ret);
sx127x_write_register(handle, REG_FIFO_RX_BASE_ADDR, 0);
SX127X_ERROR_CHECK(ret);
uint8_t reg_lna;
ret = sx127x_read_register(handle, REG_LNA, &reg_lna);
SX127X_ERROR_CHECK(ret);
reg_lna |= 0x03; // set LNA boost
ret = sx127x_write_register(handle, REG_LNA, reg_lna);
SX127X_ERROR_CHECK(ret);
// set auto AGC
ret = sx127x_write_register(handle, REG_MODEM_CONFIG_3, CONFIG3_AUTO_AGC);
SX127X_ERROR_CHECK(ret);
ret = sx127x_set_tx_power(handle, config->tx_power, true);
SX127X_ERROR_CHECK(ret);
ret = sx127x_set_spreading_factor(handle, config->spreading_factor);
SX127X_ERROR_CHECK(ret);
ret = sx127x_set_sync_word(handle, config->sync_word);
SX127X_ERROR_CHECK(ret);
ret = sx127x_set_crc(handle, config->crc);
SX127X_ERROR_CHECK(ret);
ret = sx127x_standby(handle);
ESP_ERROR_CHECK(ret);
*handle_ptr = handle;
return ESP_OK;
}
esp_err_t sx127x_free(sx127x_t *handle) {
esp_err_t ret;
if (handle->task_handle) {
ret = sx127x_stop(handle);
SX127X_ERROR_CHECK(ret);
}
ret = spi_bus_remove_device(handle->device_handle);
SX127X_ERROR_CHECK2(ret, spi_bus_remove_device)
ret = spi_bus_free(handle->config.spi_host);
SX127X_ERROR_CHECK2(ret, spi_bus_free)
free(handle);
return ESP_OK;
}
static esp_err_t sx127x_read_register(sx127x_t *handle, uint8_t reg, uint8_t *value) {
return sx127x_single_transfer(handle, reg & 0x7f, 0x00, value);
}
static esp_err_t sx127x_write_register(sx127x_t *handle, uint8_t reg, uint8_t value) {
return sx127x_single_transfer(handle, reg | 0x80, value, NULL);
}
static esp_err_t sx127x_single_transfer(sx127x_t *handle, uint8_t addr, uint8_t to_slave, uint8_t *from_slave) {
spi_transaction_t trans;
memset(&trans, 0, sizeof(trans));
trans.flags = SPI_TRANS_USE_RXDATA | SPI_TRANS_USE_TXDATA;
trans.addr = addr;
trans.length = 8;
trans.rxlength = 8;
trans.tx_data[0] = to_slave;
esp_err_t ret = spi_device_transmit(handle->device_handle, &trans);
SX127X_ERROR_CHECK2(ret, spi_device_transmit);
if (from_slave) {
*from_slave = trans.rx_data[0];
}
ESP_LOGV(TAG, "sx127x_single_transfer(%#x, %#x, %#x)", addr, trans.tx_data[0], trans.rx_data[0]);
return ESP_OK;
}
esp_err_t sx127x_sleep(sx127x_t *handle) {
return sx127x_write_register(handle, REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_SLEEP);
}
esp_err_t sx127x_standby(sx127x_t *handle) {
return sx127x_write_register(handle, REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_STDBY);
}
esp_err_t sx127x_set_frequency(sx127x_t *handle, uint64_t frequency) {
uint64_t frf = ((uint64_t)frequency << 19) / 32000000;
esp_err_t ret;
ret = sx127x_write_register(handle, REG_FRF_MSB, (uint8_t)(frf));
SX127X_ERROR_CHECK(ret);
frf >>= 8;
ret = sx127x_write_register(handle, REG_FRF_MID, (uint8_t)(frf));
SX127X_ERROR_CHECK(ret);
frf >>= 8;
ret = sx127x_write_register(handle, REG_FRF_LSB, (uint8_t)(frf));
SX127X_ERROR_CHECK(ret);
handle->config.frequency = frequency;
return ESP_OK;
}
esp_err_t sx127x_set_tx_power(sx127x_t *handle, uint8_t tx_power, sx127x_pa_boost_t pa_boost) {
esp_err_t ret;
if (pa_boost == SX127X_PA_BOOST_ENABLED) {
// PA BOOST
SX127X_CHECK(tx_power >= 2 && tx_power <= 20, "invalid tx_power: %d", ESP_ERR_INVALID_ARG, tx_power);
ret = sx127x_write_register(handle, REG_PA_CONFIG, PA_BOOST | (tx_power - 2));
} else {
// RFO
SX127X_CHECK(tx_power <= 14, "invalid tx_power: %d", ESP_ERR_INVALID_ARG, tx_power);
ret = sx127x_write_register(handle, REG_PA_CONFIG, 0x70 | tx_power);
}
SX127X_ERROR_CHECK(ret);
handle->config.tx_power = tx_power;
return ESP_OK;
}
esp_err_t sx127x_set_spreading_factor(sx127x_t *handle, uint8_t spreading_factor) {
SX127X_CHECK(spreading_factor >= 6 && spreading_factor <= 12, "invalid spreading_factor", ESP_ERR_INVALID_ARG);
// section 4.1.1.2 in SX1276 datasheet
uint8_t detection_optimize, detection_threshold;
if (spreading_factor == 6) {
detection_optimize = 0xc5;
detection_threshold = 0x0c;
} else {
detection_optimize = 0xc3;
detection_threshold = 0x0a;
}
SX127X_ERROR_CHECK(sx127x_write_register(handle, REG_DETECTION_OPTIMIZE, detection_optimize));
SX127X_ERROR_CHECK(sx127x_write_register(handle, REG_DETECTION_THRESHOLD, detection_threshold));
uint8_t modem_config_3;
SX127X_ERROR_CHECK(sx127x_read_register(handle, REG_MODEM_CONFIG_3, &modem_config_3));
modem_config_3 = (modem_config_3 & 0x03) | ((spreading_factor << 4) & 0xf0);
SX127X_ERROR_CHECK(sx127x_write_register(handle, REG_MODEM_CONFIG_3, modem_config_3));
handle->config.spreading_factor = spreading_factor;
return ESP_OK;
}
esp_err_t sx127x_set_signal_bandwidth(sx127x_t *handle, uint64_t signal_bandwidth) {
uint8_t bw_reg = sx127x_bw_to_reg(signal_bandwidth);
uint8_t modem_config_1;
SX127X_ERROR_CHECK(sx127x_read_register(handle, REG_MODEM_CONFIG_1, &modem_config_1));
modem_config_1 = (modem_config_1 & 0x0f) | (bw_reg << 4);
SX127X_ERROR_CHECK(sx127x_write_register(handle, REG_MODEM_CONFIG_1, modem_config_1));
handle->config.signal_bandwidth = signal_bandwidth;
// set low data rate optimization flag
uint64_t bw = sx127x_reg_to_bw(bw_reg);
uint8_t sf = handle->config.spreading_factor;
// section 4.1.1.5
uint64_t symbol_duration_ms = 1000 / (bw / (1L << sf));
// section 4.1.1.6
bool must_have_ldo = (symbol_duration_ms > 16);
bool ldo = must_have_ldo || (handle->config.ldo == SX127X_LDO_ENABLED);
uint8_t modem_config_3;
SX127X_ERROR_CHECK(sx127x_read_register(handle, REG_MODEM_CONFIG_3, &modem_config_3));
if (ldo) {
modem_config_3 |= (1 << 3);
} else {
modem_config_3 &= ~(1 << 3);
}
SX127X_ERROR_CHECK(sx127x_write_register(handle, REG_MODEM_CONFIG_3, modem_config_3));
return ESP_OK;
}
static uint8_t sx127x_bw_to_reg(uint64_t bandwidth) {
if (bandwidth <= 7.8E3) {
return 0;
} else if (bandwidth <= 10.4E3) {
return 1;
} else if (bandwidth <= 15.6E3) {
return 2;
} else if (bandwidth <= 20.8E3) {
return 3;
} else if (bandwidth <= 31.25E3) {
return 4;
} else if (bandwidth <= 41.7E3) {
return 5;
} else if (bandwidth <= 62.5E3) {
return 6;
} else if (bandwidth <= 125E3) {
return 7;
} else if (bandwidth <= 250E3) {
return 8;
} else /* if (bandwidth <= 500E3) */ {
return 9;
}
}
static uint64_t sx127x_reg_to_bw(uint8_t bandwidth_reg) {
switch (bandwidth_reg) {
case 0: return 7.8E3;
case 1: return 10.4E3;
case 2: return 15.6E3;
case 3: return 20.8E3;
case 4: return 31.25E3;
case 5: return 41.7E3;
case 6: return 62.5E3;
case 7: return 125E3;
case 8: return 250E3;
default:
case 9: return 500E3;
}
}
esp_err_t sx127x_set_sync_word(sx127x_t *handle, uint8_t sync_word) {
SX127X_ERROR_CHECK(sx127x_write_register(handle, REG_SYNC_WORD, sync_word));
handle->config.sync_word = sync_word;
return ESP_OK;
}
esp_err_t sx127x_set_crc(sx127x_t *handle, sx127x_crc_t crc) {
uint8_t modem_config_2;
SX127X_ERROR_CHECK(sx127x_read_register(handle, REG_MODEM_CONFIG_2, &modem_config_2));
if (crc == SX127X_CRC_ENABLED) {
modem_config_2 |= CONFIG2_CRC;
} else {
modem_config_2 &= ~CONFIG2_CRC;
}
SX127X_ERROR_CHECK(sx127x_write_register(handle, REG_MODEM_CONFIG_2, modem_config_2));
handle->config.crc = crc;
return ESP_OK;
}
void sx127x_isr(void *arg) {
sx127x_t *handle = (sx127x_t *)arg;
ESP_LOGI(TAG, "sx127x_isr");
}
void sx127x_task(void *arg) {
sx127x_t *handle = (sx127x_t *)arg;
while (handle->task_running) {
vTaskDelay(pdMS_TO_TICKS(10));
}
}
esp_err_t sx127x_start(sx127x_t *handle) {
esp_err_t ret;
SX127X_CHECK(handle->task_handle == NULL, "task already running", ESP_ERR_INVALID_STATE);
ret = sx127x_write_register(handle, REG_DIO_MAPPING_1, 0x00);
SX127X_ERROR_CHECK(ret);
gpio_config_t irq_io_config;
irq_io_config.intr_type = GPIO_INTR_POSEDGE;
irq_io_config.mode = GPIO_MODE_INPUT;
irq_io_config.pin_bit_mask = (1ULL << handle->config.irq_io_num);
irq_io_config.pull_down_en = 0;
irq_io_config.pull_up_en = 0;
ret = gpio_config(&irq_io_config);
SX127X_ERROR_CHECK2(ret, gpio_config)
ret = gpio_install_isr_service(ESP_INTR_FLAG_LEVEL1);
SX127X_ERROR_CHECK2(ret, gpio_install_isr_service);
ret = gpio_isr_handler_add(handle->config.irq_io_num, sx127x_isr, (void *)handle);
SX127X_ERROR_CHECK2(ret, gpio_isr_handler_add);
handle->task_running = true;
BaseType_t pdRet =
xTaskCreate(sx127x_task, "sx127x_task", TASK_STACK_SIZE, (void *)handle, TASK_PRIORITY, &handle->task_handle);
SX127X_CHECK(pdRet == pdPASS, "failed to create task", ESP_FAIL);
return ESP_OK;
}
esp_err_t sx127x_stop(sx127x_t *handle) {
esp_err_t ret;
SX127X_CHECK(handle->task_handle != NULL, "task has not been started", ESP_ERR_INVALID_STATE);
handle->task_running = false;
xTaskNotify(handle->task_handle, 0, eNoAction);
ret = gpio_isr_handler_remove(handle->config.irq_io_num);
SX127X_ERROR_CHECK2(ret, gpio_isr_handler_remove);
gpio_uninstall_isr_service();
return ESP_OK;
}
Loading…
Cancel
Save