Alex Mikhalev
6 years ago
7 changed files with 948 additions and 451 deletions
@ -0,0 +1,118 @@ |
|||||||
|
--- |
||||||
|
Language: Cpp |
||||||
|
# BasedOnStyle: LLVM |
||||||
|
AccessModifierOffset: -2 |
||||||
|
AlignAfterOpenBracket: Align |
||||||
|
AlignConsecutiveAssignments: true |
||||||
|
AlignConsecutiveDeclarations: true |
||||||
|
AlignEscapedNewlines: Right |
||||||
|
AlignOperands: true |
||||||
|
AlignTrailingComments: true |
||||||
|
AllowAllParametersOfDeclarationOnNextLine: true |
||||||
|
AllowShortBlocksOnASingleLine: false |
||||||
|
AllowShortCaseLabelsOnASingleLine: true |
||||||
|
AllowShortFunctionsOnASingleLine: All |
||||||
|
AllowShortIfStatementsOnASingleLine: true |
||||||
|
AllowShortLoopsOnASingleLine: false |
||||||
|
AlwaysBreakAfterDefinitionReturnType: None |
||||||
|
AlwaysBreakAfterReturnType: None |
||||||
|
AlwaysBreakBeforeMultilineStrings: false |
||||||
|
AlwaysBreakTemplateDeclarations: MultiLine |
||||||
|
BinPackArguments: false |
||||||
|
BinPackParameters: false |
||||||
|
BraceWrapping: |
||||||
|
AfterClass: false |
||||||
|
AfterControlStatement: false |
||||||
|
AfterEnum: false |
||||||
|
AfterFunction: false |
||||||
|
AfterNamespace: false |
||||||
|
AfterObjCDeclaration: false |
||||||
|
AfterStruct: false |
||||||
|
AfterUnion: false |
||||||
|
AfterExternBlock: false |
||||||
|
BeforeCatch: false |
||||||
|
BeforeElse: false |
||||||
|
IndentBraces: false |
||||||
|
SplitEmptyFunction: true |
||||||
|
SplitEmptyRecord: true |
||||||
|
SplitEmptyNamespace: true |
||||||
|
BreakBeforeBinaryOperators: None |
||||||
|
BreakBeforeBraces: Attach |
||||||
|
BreakBeforeInheritanceComma: false |
||||||
|
BreakInheritanceList: BeforeColon |
||||||
|
BreakBeforeTernaryOperators: true |
||||||
|
BreakConstructorInitializersBeforeComma: false |
||||||
|
BreakConstructorInitializers: BeforeColon |
||||||
|
BreakAfterJavaFieldAnnotations: false |
||||||
|
BreakStringLiterals: true |
||||||
|
ColumnLimit: 120 |
||||||
|
CommentPragmas: '^ IWYU pragma:' |
||||||
|
CompactNamespaces: false |
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: false |
||||||
|
ConstructorInitializerIndentWidth: 4 |
||||||
|
ContinuationIndentWidth: 4 |
||||||
|
Cpp11BracedListStyle: true |
||||||
|
DerivePointerAlignment: false |
||||||
|
DisableFormat: false |
||||||
|
ExperimentalAutoDetectBinPacking: false |
||||||
|
FixNamespaceComments: true |
||||||
|
ForEachMacros: |
||||||
|
- foreach |
||||||
|
- Q_FOREACH |
||||||
|
- BOOST_FOREACH |
||||||
|
IncludeBlocks: Preserve |
||||||
|
IncludeCategories: |
||||||
|
- Regex: '^"(llvm|llvm-c|clang|clang-c)/' |
||||||
|
Priority: 2 |
||||||
|
- Regex: '^(<|"(gtest|gmock|isl|json)/)' |
||||||
|
Priority: 3 |
||||||
|
- Regex: '.*' |
||||||
|
Priority: 1 |
||||||
|
IncludeIsMainRegex: '(Test)?$' |
||||||
|
IndentCaseLabels: false |
||||||
|
IndentPPDirectives: None |
||||||
|
IndentWidth: 2 |
||||||
|
IndentWrappedFunctionNames: false |
||||||
|
JavaScriptQuotes: Leave |
||||||
|
JavaScriptWrapImports: true |
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: true |
||||||
|
MacroBlockBegin: '' |
||||||
|
MacroBlockEnd: '' |
||||||
|
MaxEmptyLinesToKeep: 1 |
||||||
|
NamespaceIndentation: None |
||||||
|
ObjCBinPackProtocolList: Auto |
||||||
|
ObjCBlockIndentWidth: 2 |
||||||
|
ObjCSpaceAfterProperty: false |
||||||
|
ObjCSpaceBeforeProtocolList: true |
||||||
|
PenaltyBreakAssignment: 2 |
||||||
|
PenaltyBreakBeforeFirstCallParameter: 1 |
||||||
|
PenaltyBreakComment: 300 |
||||||
|
PenaltyBreakFirstLessLess: 120 |
||||||
|
PenaltyBreakString: 1000 |
||||||
|
PenaltyBreakTemplateDeclaration: 10 |
||||||
|
PenaltyExcessCharacter: 1000000 |
||||||
|
PenaltyReturnTypeOnItsOwnLine: 60 |
||||||
|
PointerAlignment: Right |
||||||
|
ReflowComments: true |
||||||
|
SortIncludes: true |
||||||
|
SortUsingDeclarations: true |
||||||
|
SpaceAfterCStyleCast: false |
||||||
|
SpaceAfterTemplateKeyword: true |
||||||
|
SpaceBeforeAssignmentOperators: true |
||||||
|
SpaceBeforeCpp11BracedList: false |
||||||
|
SpaceBeforeCtorInitializerColon: true |
||||||
|
SpaceBeforeInheritanceColon: true |
||||||
|
SpaceBeforeParens: ControlStatements |
||||||
|
SpaceBeforeRangeBasedForLoopColon: true |
||||||
|
SpaceInEmptyParentheses: false |
||||||
|
SpacesBeforeTrailingComments: 1 |
||||||
|
SpacesInAngles: false |
||||||
|
SpacesInContainerLiterals: true |
||||||
|
SpacesInCStyleCastParentheses: false |
||||||
|
SpacesInParentheses: false |
||||||
|
SpacesInSquareBrackets: false |
||||||
|
Standard: Cpp11 |
||||||
|
TabWidth: 8 |
||||||
|
UseTab: Never |
||||||
|
... |
||||||
|
|
@ -1,101 +1,468 @@ |
|||||||
#include "sx127x_driver.h" |
#include "sx127x_driver.h" |
||||||
|
|
||||||
#include <string.h> |
|
||||||
#include <freertos/task.h> |
|
||||||
#include <esp_log.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 |
||||||
|
|
||||||
const char *SX127X_TAG = "sx127x"; |
#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 |
||||||
|
|
||||||
#define SX127X_CHECK(check, str, ret_val, ...) \ |
// modes
|
||||||
if (!(check)) \ |
#define MODE_LONG_RANGE_MODE 0x80 |
||||||
{ \ |
#define MODE_SLEEP 0x00 |
||||||
ESP_LOGE(SX127X_TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ |
#define MODE_STDBY 0x01 |
||||||
return (ret_val); \ |
#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 |
#ifdef NODEBUG |
||||||
#define SX127X_ERROR_CHECK(ret, fun) \ |
#define SX127X_CHECK(check, str, ret_val, ...) \ |
||||||
{ \ |
if (!(check)) { \ |
||||||
esp_err_t _error_code = (ret); \ |
ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ |
||||||
if (_error_code != ESP_OK) \ |
return (ret_val); \ |
||||||
{ \ |
} |
||||||
return _error_code; \ |
#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 |
#else |
||||||
#define SX127X_ERROR_CHECK(ret, fun) \ |
#define SX127X_CHECK(check, str, ret_val, ...) \ |
||||||
{ \ |
if (!(check)) { \ |
||||||
esp_err_t _error_code = (ret); \ |
ESP_LOGE(TAG, "%s(%d): " #check ": " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ |
||||||
if (_error_code != ESP_OK) \ |
return (ret_val); \ |
||||||
{ \ |
} |
||||||
const char *_error_name = esp_err_to_name(_error_code); \ |
#define SX127X_ERROR_CHECK(ret) \ |
||||||
ESP_LOGE(SX127X_TAG, "%s(%d): " fun ": %s (%d)", \ |
{ \ |
||||||
__FUNCTION__, __LINE__, _error_name, _error_code); \ |
esp_err_t _error_code = (ret); \ |
||||||
return _error_code; \ |
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 |
#endif |
||||||
|
|
||||||
esp_err_t sx127x_init(sx127x_config_t *config, sx127x_t *handle) |
struct sx127x { |
||||||
{ |
sx127x_config_t config; |
||||||
esp_err_t ret; |
spi_device_handle_t device_handle; |
||||||
|
TaskHandle_t task_handle; |
||||||
|
bool task_running; |
||||||
|
}; |
||||||
|
|
||||||
memcpy(&handle->config, config, sizeof(sx127x_config_t)); |
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); |
||||||
|
|
||||||
ret = gpio_set_direction(config->rst_io_num, GPIO_MODE_OUTPUT); |
static uint8_t sx127x_bw_to_reg(uint64_t bandwidth); |
||||||
SX127X_ERROR_CHECK(ret, "gpio_set_direction") |
static uint64_t sx127x_reg_to_bw(uint8_t bandwidth_reg); |
||||||
ret = gpio_set_direction(config->irq_io_num, GPIO_MODE_OUTPUT); |
|
||||||
SX127X_ERROR_CHECK(ret, "gpio_set_direction") |
|
||||||
|
|
||||||
gpio_set_level(config->cs_io_num, 1); |
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); |
||||||
|
|
||||||
// perform reset
|
handle->task_handle = NULL; |
||||||
gpio_set_level(config->rst_io_num, 0); |
memcpy(&handle->config, config, sizeof(sx127x_config_t)); |
||||||
vTaskDelay(pdMS_TO_TICKS(10)); |
|
||||||
gpio_set_level(config->rst_io_num, 1); |
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, ®_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)); |
vTaskDelay(pdMS_TO_TICKS(10)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
spi_bus_config_t bus_config = { |
esp_err_t sx127x_start(sx127x_t *handle) { |
||||||
.mosi_io_num = config->mosi_io_num, |
esp_err_t ret; |
||||||
.miso_io_num = config->miso_io_num, |
|
||||||
.sclk_io_num = config->sck_io_num, |
SX127X_CHECK(handle->task_handle == NULL, "task already running", ESP_ERR_INVALID_STATE); |
||||||
.quadhd_io_num = -1, |
|
||||||
.quadwp_io_num = -1, |
ret = sx127x_write_register(handle, REG_DIO_MAPPING_1, 0x00); |
||||||
.max_transfer_sz = SX127X_MAX_TRANSFER}; |
SX127X_ERROR_CHECK(ret); |
||||||
ret = spi_bus_initialize(config->spi_host, &bus_config, 1); |
|
||||||
SX127X_ERROR_CHECK(ret, "spi_bus_initialize") |
gpio_config_t irq_io_config; |
||||||
|
irq_io_config.intr_type = GPIO_INTR_POSEDGE; |
||||||
spi_device_interface_config_t device_config = { |
irq_io_config.mode = GPIO_MODE_INPUT; |
||||||
.command_bits = 0, |
irq_io_config.pin_bit_mask = (1ULL << handle->config.irq_io_num); |
||||||
.address_bits = 8, |
irq_io_config.pull_down_en = 0; |
||||||
.dummy_bits = 0, |
irq_io_config.pull_up_en = 0; |
||||||
.mode = 0, |
ret = gpio_config(&irq_io_config); |
||||||
.duty_cycle_pos = 0, |
SX127X_ERROR_CHECK2(ret, gpio_config) |
||||||
.cs_ena_pretrans = 2, |
|
||||||
.cs_ena_posttrans = 2, |
ret = gpio_install_isr_service(ESP_INTR_FLAG_LEVEL1); |
||||||
.clock_speed_hz = 8, // 80mhz / 8 = 10mhz
|
SX127X_ERROR_CHECK2(ret, gpio_install_isr_service); |
||||||
.input_delay_ns = 0, |
ret = gpio_isr_handler_add(handle->config.irq_io_num, sx127x_isr, (void *)handle); |
||||||
.spics_io_num = config->cs_io_num, |
SX127X_ERROR_CHECK2(ret, gpio_isr_handler_add); |
||||||
.flags = 0, |
|
||||||
.queue_size = 8, |
handle->task_running = true; |
||||||
.pre_cb = NULL, |
|
||||||
.post_cb = NULL, |
BaseType_t pdRet = |
||||||
}; |
xTaskCreate(sx127x_task, "sx127x_task", TASK_STACK_SIZE, (void *)handle, TASK_PRIORITY, &handle->task_handle); |
||||||
ret = spi_bus_add_device(config->spi_host, &device_config, &handle->device_handle); |
SX127X_CHECK(pdRet == pdPASS, "failed to create task", ESP_FAIL); |
||||||
SX127X_ERROR_CHECK(ret, "spi_bus_add_device") |
return ESP_OK; |
||||||
|
|
||||||
return ESP_OK; |
|
||||||
} |
|
||||||
|
|
||||||
esp_err_t sx127x_free(sx127x_t *handle) |
|
||||||
{ |
|
||||||
esp_err_t ret; |
|
||||||
|
|
||||||
ret = spi_bus_remove_device(handle->device_handle); |
|
||||||
SX127X_ERROR_CHECK(ret, "spi_bus_remove_device") |
|
||||||
|
|
||||||
ret = spi_bus_free(handle->config.spi_host); |
|
||||||
SX127X_ERROR_CHECK(ret, "spi_bus_free") |
|
||||||
|
|
||||||
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; |
||||||
|
} |
@ -1,40 +1,97 @@ |
|||||||
#pragma once |
#pragma once |
||||||
|
|
||||||
#include "driver/spi_master.h" |
#include <driver/spi_master.h> |
||||||
|
#include <driver/gpio.h> |
||||||
const char *SX127X_TAG; |
#include <stdint.h> |
||||||
|
|
||||||
#define SX127X_MAX_TRANSFER (1024) |
#define SX127X_MAX_TRANSFER (256) |
||||||
|
|
||||||
#define SX127X_CONFIG_DEFAULT \ |
// carrier frequency type
|
||||||
sx127x_config_t \ |
typedef uint64_t sx127x_freq_t; |
||||||
{ \ |
|
||||||
.spi_host = VSPI_HOST, \ |
#define SX127X_FREQ_433 ((sx127x_freq_t)433E6) |
||||||
.mosi_io_num = 19, \ |
#define SX127X_FREQ_915 ((sx127x_freq_t)915E6) |
||||||
.miso_io_num = 27, \ |
|
||||||
.sck_io_num = 5, \ |
// signal bandwidth type
|
||||||
.cs_io_num = 18, \ |
typedef uint64_t sx127x_bw_t; |
||||||
.rst_io_num = 14, \ |
// spreading factor type
|
||||||
.irq_io_num = 26 \ |
typedef uint8_t sx127x_sf_t; |
||||||
} |
|
||||||
|
typedef enum sx127x_pa_boost { |
||||||
typedef struct sx127x_config |
SX127X_PA_BOOST_DISABLED = 0, |
||||||
{ |
SX127X_PA_BOOST_ENABLED = 1, |
||||||
spi_host_device_t spi_host; |
} sx127x_pa_boost_t; |
||||||
gpio_num_t mosi_io_num; |
|
||||||
gpio_num_t miso_io_num; |
typedef enum sx127x_crc { |
||||||
gpio_num_t sck_io_num; |
SX127X_CRC_DISABLED = 0, |
||||||
gpio_num_t cs_io_num; |
SX127X_CRC_ENABLED = 1, |
||||||
gpio_num_t rst_io_num; |
} sx127x_crc_t; |
||||||
gpio_num_t irq_io_num; |
|
||||||
|
// low data rate optimization
|
||||||
|
typedef enum sx127x_ldo { |
||||||
|
SX127X_LDO_DISABLED = 0, |
||||||
|
SX127X_LDO_ENABLED = 1, |
||||||
|
} sx127x_ldo_t; |
||||||
|
|
||||||
|
typedef struct sx127x_config { |
||||||
|
spi_host_device_t spi_host; |
||||||
|
gpio_num_t mosi_io_num; |
||||||
|
gpio_num_t miso_io_num; |
||||||
|
gpio_num_t sck_io_num; |
||||||
|
gpio_num_t cs_io_num; |
||||||
|
gpio_num_t rst_io_num; |
||||||
|
gpio_num_t irq_io_num; |
||||||
|
uint8_t tx_power; |
||||||
|
sx127x_freq_t frequency; |
||||||
|
sx127x_bw_t signal_bandwidth; |
||||||
|
sx127x_sf_t spreading_factor; |
||||||
|
uint8_t sync_word; |
||||||
|
sx127x_crc_t crc; |
||||||
|
sx127x_ldo_t ldo; |
||||||
} sx127x_config_t; |
} sx127x_config_t; |
||||||
|
|
||||||
typedef struct sx127x |
// clang-format off
|
||||||
{ |
#define SX127X_CONFIG_DEFAULT \ |
||||||
sx127x_config_t config; |
{ \ |
||||||
spi_device_handle_t device_handle; |
.spi_host = VSPI_HOST, \ |
||||||
} sx127x_t; |
.mosi_io_num = 27, \ |
||||||
|
.miso_io_num = 19, \ |
||||||
|
.sck_io_num = 5, \ |
||||||
|
.cs_io_num = 18, \ |
||||||
|
.rst_io_num = 14, \ |
||||||
|
.irq_io_num = 26, \ |
||||||
|
.tx_power = 17, \ |
||||||
|
.frequency = SX127X_FREQ_433, \ |
||||||
|
.signal_bandwidth = 125E3, \ |
||||||
|
.spreading_factor = 11, \ |
||||||
|
.sync_word = 0x34, \ |
||||||
|
.crc = SX127X_CRC_ENABLED, \ |
||||||
|
.ldo = SX127X_LDO_DISABLED \ |
||||||
|
} |
||||||
|
// clang-format on
|
||||||
|
|
||||||
esp_err_t sx127x_init(sx127x_config_t *config, sx127x_t *handle); |
typedef struct sx127x sx127x_t; |
||||||
|
|
||||||
|
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_standby(sx127x_t *handle); |
||||||
|
|
||||||
|
esp_err_t sx127x_start(sx127x_t *handle); |
||||||
|
|
||||||
|
esp_err_t sx127x_stop(sx127x_t *handle); |
||||||
|
Loading…
Reference in new issue