From 92e35b3a27554765a24703378be012d5b9023d6f Mon Sep 17 00:00:00 2001 From: Arto Kinnunen Date: Wed, 23 Jun 2021 10:24:25 +0300 Subject: [PATCH] Squashed 'connectivity/drivers/802.15.4_RF/stm-s2lp-rf-driver/' content from commit f491d77de8 git-subtree-dir: connectivity/drivers/802.15.4_RF/stm-s2lp-rf-driver git-subtree-split: f491d77de847cd5358d53b5b4d975cb0d68d11f3 --- README.md | 11 + mbed_lib.json | 73 ++ source/NanostackRfPhys2lp.cpp | 1480 +++++++++++++++++++++++ source/at24mac_s2lp.cpp | 73 ++ source/at24mac_s2lp.h | 72 ++ source/rf_configuration.c | 186 +++ source/rf_configuration.h | 42 + source/s2lpReg.h | 344 ++++++ stm-s2lp-rf-driver/NanostackRfPhys2lp.h | 164 +++ 9 files changed, 2445 insertions(+) create mode 100644 README.md create mode 100644 mbed_lib.json create mode 100644 source/NanostackRfPhys2lp.cpp create mode 100644 source/at24mac_s2lp.cpp create mode 100644 source/at24mac_s2lp.h create mode 100644 source/rf_configuration.c create mode 100644 source/rf_configuration.h create mode 100644 source/s2lpReg.h create mode 100644 stm-s2lp-rf-driver/NanostackRfPhys2lp.h diff --git a/README.md b/README.md new file mode 100644 index 0000000000..51ece87ac3 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +# Example RF driver for STM 802.15.4 transceivers # + +Support for: + * S2-LP + +This driver is used with 6LoWPAN and Wi-SUN stacks. + +Driver is tested with X-Nucleo-S2868A1 RF expansion board. https://www.st.com/en/ecosystems/x-nucleo-s2868a1.html + +NOTE: Default SPI SCLK pin configuration is D13. On X-Nucleo-S2868A1, resistor R6 must be soldered instead of R11. For more information, see user manual UM2405. https://www.st.com/resource/en/user_manual/dm00498153.pdf + diff --git a/mbed_lib.json b/mbed_lib.json new file mode 100644 index 0000000000..55e4bf1748 --- /dev/null +++ b/mbed_lib.json @@ -0,0 +1,73 @@ +{ + "name": "s2lp", + "config": { + "SPI_SDI": { + "help": "SPI_SDI pin for SPI connection. D11 assumed, needs to be set/overwritten otherwise", + "value": null + }, + "SPI_SDO": { + "help": "SPI_SDO pin for SPI connection. D12 assumed, needs to be set/overwritten otherwise", + "value": null + }, + "SPI_SCLK": { + "help": "SPI_SCLK pin for SPI connection, D13 assumed, needs to be set/overwritten otherwise", + "value": null + }, + "SPI_CS": { + "help": "SPI_CS pin for SPI connection, A1 assumed, needs to be set/overwritten otherwise", + "value": null + }, + "SPI_SDN": { + "help": "SPI_SDN pin for SPI connection, D7 assumed, needs to be set/overwritten otherwise", + "value": null + }, + "TEST_PIN_TX": { + "help": "TEST_PIN_TX pin for serial connection, D6 assumed, needs to be set/overwritten otherwise", + "value": null + }, + "TEST_PIN_RX": { + "help": "TEST_PIN_RX pin for serial connection, D5 assumed, needs to be set/overwritten otherwise", + "value": null + }, + "TEST_PIN_CSMA": { + "help": "TEST_PIN_CSMA pin for CSMA, D4 assumed, needs to be set/overwritten otherwise", + "value": null + }, + "TEST_PIN_SPARE_1": { + "help": "TEST_PIN_SPARE_1 pin for testing, D2 assumed, needs to be set/overwritten otherwise", + "value": null + }, + "TEST_PIN_SPARE_2": { + "help": "TEST_PIN_SPARE_2 pin for testing, D8 assumed, needs to be set/overwritten otherwise", + "value": null + }, + "SPI_GPIO0": { + "help": "SPI_GPIO0 pin for GPIO testing, A0 assumed, needs to be set/overwritten otherwise", + "value": null + }, + "SPI_GPIO1": { + "help": "SPI_GPIO1 pin for GPIO testing, A2 assumed, needs to be set/overwritten otherwise", + "value": null + }, + "SPI_GPIO2": { + "help": "SPI_GPIO2 pin for GPIO testing, A3 assumed, needs to be set/overwritten otherwise", + "value": null + }, + "SPI_GPIO3": { + "help": "SPI_GPIO3 pin for GPIO testing, A5 assumed, needs to be set/overwritten otherwise", + "value": null + }, + "I2C_SDA": { + "help": "I2C_SDA pin for I2C SDA, null assumed, needs to be set/overwritten otherwise", + "value": null + }, + "I2C_SCL": { + "help": "I2C_SCL pin for I2C SCL, null assumed, needs to be set/overwritten otherwise", + "value": null + }, + "provide-default": { + "help": "Provide default NanostackRfpy. [true/false]", + "value": false + } + } +} diff --git a/source/NanostackRfPhys2lp.cpp b/source/NanostackRfPhys2lp.cpp new file mode 100644 index 0000000000..63a5f90dde --- /dev/null +++ b/source/NanostackRfPhys2lp.cpp @@ -0,0 +1,1480 @@ +/* + * Copyright (c) 2018-2021, Pelion and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#if defined(MBED_CONF_NANOSTACK_CONFIGURATION) && DEVICE_SPI && DEVICE_INTERRUPTIN && defined(MBED_CONF_RTOS_PRESENT) + +#include "platform/arm_hal_interrupt.h" +#include "nanostack/platform/arm_hal_phy.h" +#include "ns_types.h" +#include "NanostackRfPhys2lp.h" +#include "s2lpReg.h" +#include "rf_configuration.h" +#include "randLIB.h" +#include "mbed_trace.h" +#include "mbed_toolchain.h" +#include "common_functions.h" +#include +#include "Timeout.h" +#include "Thread.h" +#include "mbed_wait_api.h" +#include "platform/mbed_error.h" +#include "platform/mbed_version.h" + +#if (MBED_VERSION > MBED_ENCODE_VERSION(6, 0, 0)) +/* Mbed OS 6.0 introduces support for chrono time management */ +using namespace std::chrono; +#define S2LP_USE_CHRONO +#define S2LP_TIME_50US 50us +#define S2LP_TIME_10MS 10ms +#else +#define S2LP_TIME_10MS 10 +#endif + +using namespace mbed; +using namespace rtos; + +#define TRACE_GROUP "s2lp" + +#define INTERRUPT_GPIO S2LP_GPIO3 + +#if INTERRUPT_GPIO == S2LP_GPIO0 +#define INT_IN_GPIO rf->RF_S2LP_GPIO0 +#elif INTERRUPT_GPIO == S2LP_GPIO1 +#define INT_IN_GPIO rf->RF_S2LP_GPIO1 +#elif INTERRUPT_GPIO == S2LP_GPIO2 +#define INT_IN_GPIO rf->RF_S2LP_GPIO2 +#else +#define INT_IN_GPIO rf->RF_S2LP_GPIO3 +#endif + +#ifdef TEST_GPIOS_ENABLED +#define TEST_TX_STARTED test_pins->TEST1 = 1; +#define TEST_TX_DONE test_pins->TEST1 = 0; +#define TEST_RX_STARTED test_pins->TEST2 = 1; +#define TEST_RX_DONE test_pins->TEST2 = 0; +#define TEST_CSMA_STARTED test_pins->TEST3 = 1; +#define TEST_CSMA_DONE test_pins->TEST3 = 0; +#define TEST_SPARE_1_ON test_pins->TEST4 = 1; +#define TEST_SPARE_1_OFF test_pins->TEST4 = 0; +#define TEST_SPARE_2_ON test_pins->TEST5 = 1; +#define TEST_SPARE_2_OFF test_pins->TEST5 = 0; +extern void (*fhss_uc_switch)(void); +extern void (*fhss_bc_switch)(void); +#else //TEST_GPIOS_ENABLED +#define TEST_TX_STARTED +#define TEST_TX_DONE +#define TEST_RX_STARTED +#define TEST_RX_DONE +#define TEST_CSMA_STARTED +#define TEST_CSMA_DONE +#define TEST_SPARE_1_ON +#define TEST_SPARE_1_OFF +#define TEST_SPARE_2_ON +#define TEST_SPARE_2_OFF +#endif //TEST_GPIOS_ENABLED + +#define MAC_FRAME_TYPE_MASK 0x07 +#define MAC_FRAME_BEACON (0) +#define MAC_TYPE_DATA (1) +#define MAC_TYPE_ACK (2) +#define MAC_TYPE_COMMAND (3) +#define MAC_DATA_PENDING 0x10 +#define FC_DST_MODE 0x0C +#define FC_SRC_MODE 0xC0 +#define FC_DST_ADDR_NONE 0x00 +#define FC_DST_16_BITS 0x08 +#define FC_DST_64_BITS 0x0C +#define FC_SRC_64_BITS 0xC0 +#define FC_SEQUENCE_COMPRESSION 0x01 +#define FC_AR 0x20 +#define FC_PAN_ID_COMPRESSION 0x40 +#define VERSION_FIELD_MASK 0x30 +#define SHIFT_SEQ_COMP_FIELD (0) +#define SHIFT_VERSION_FIELD (4) +#define SHIFT_PANID_COMP_FIELD (6) +#define OFFSET_DST_PAN_ID (3) +#define OFFSET_DST_ADDR (5) + +#define CS_SELECT() {rf->CS = 0;} +#define CS_RELEASE() {rf->CS = 1;} + +typedef enum { + RF_MODE_NORMAL = 0, + RF_MODE_SNIFFER = 1 +} rf_mode_e; + +class UnlockedSPI : public SPI { +public: + UnlockedSPI(PinName sdi, PinName sdo, PinName sclk) : + SPI(sdi, sdo, sclk) { } + virtual void lock() { } + virtual void unlock() { } +}; + +class RFPins { +public: + RFPins(PinName spi_sdi, PinName spi_sdo, + PinName spi_sclk, PinName spi_cs, PinName spi_sdn, + PinName spi_gpio0, PinName spi_gpio1, PinName spi_gpio2, + PinName spi_gpio3); + UnlockedSPI spi; + DigitalOut CS; + DigitalOut SDN; + InterruptIn RF_S2LP_GPIO0; + InterruptIn RF_S2LP_GPIO1; + InterruptIn RF_S2LP_GPIO2; + InterruptIn RF_S2LP_GPIO3; + Timeout cca_timer; + Timeout backup_timer; + Timer tx_timer; + Thread irq_thread; + Mutex mutex; + void rf_irq_task(); +}; + +RFPins::RFPins(PinName spi_sdi, PinName spi_sdo, + PinName spi_sclk, PinName spi_cs, PinName spi_sdn, + PinName spi_gpio0, PinName spi_gpio1, PinName spi_gpio2, + PinName spi_gpio3) + : spi(spi_sdi, spi_sdo, spi_sclk), + CS(spi_cs), + SDN(spi_sdn), + RF_S2LP_GPIO0(spi_gpio0), + RF_S2LP_GPIO1(spi_gpio1), + RF_S2LP_GPIO2(spi_gpio2), + RF_S2LP_GPIO3(spi_gpio3), + irq_thread(osPriorityRealtime, 1024) +{ + irq_thread.start(mbed::callback(this, &RFPins::rf_irq_task)); +} + +class TestPins_S2LP { +public: + TestPins_S2LP(PinName test_pin_1, PinName test_pin_2, PinName test_pin_3, PinName test_pin_4, PinName test_pin_5); + DigitalOut TEST1; + DigitalOut TEST2; + DigitalOut TEST3; + DigitalOut TEST4; + DigitalOut TEST5; +}; + +TestPins_S2LP::TestPins_S2LP(PinName test_pin_1, PinName test_pin_2, PinName test_pin_3, PinName test_pin_4, PinName test_pin_5) + : TEST1(test_pin_1), + TEST2(test_pin_2), + TEST3(test_pin_3), + TEST4(test_pin_4), + TEST5(test_pin_5) +{ +} + +static uint8_t rf_read_register(uint8_t addr); +static s2lp_states_e rf_read_state(void); +static void rf_write_register(uint8_t addr, uint8_t data); +static void rf_interrupt_handler(void); +static void rf_receive(uint8_t rx_channel); +static void rf_cca_timer_stop(void); +static void rf_backup_timer_start(uint32_t slots); +static void rf_backup_timer_stop(void); +static void rf_rx_ready_handler(void); +static uint32_t read_irq_status(void); +static bool rf_rx_filter(uint8_t *mac_header, uint8_t *mac_64bit_addr, uint8_t *mac_16bit_addr, uint8_t *pan_id); +static void rf_cca_timer_start(uint32_t slots); + +static RFPins *rf; +#ifdef TEST_GPIOS_ENABLED +static TestPins_S2LP *test_pins; +#endif +static phy_device_driver_s device_driver; +static int8_t rf_radio_driver_id = -1; +static uint8_t *tx_data_ptr; +static uint16_t tx_data_length; +static uint16_t rx_data_length; +static uint32_t enabled_interrupts; +static uint8_t mac_tx_handle; +static uint8_t rf_rx_channel; +static uint8_t rf_new_channel; +static uint8_t rx_buffer[RF_MTU]; +static rf_states_e rf_state = RF_IDLE; +// This will be used when given channel spacing cannot be supported by transceiver +static uint8_t rf_channel_multiplier = 1; +static uint16_t tx_sequence = 0xffff; +static uint32_t tx_time = 0; +static uint32_t rx_time = 0; +static uint32_t tx_finnish_time = 0; +static uint32_t rf_symbol_rate; +static bool cca_enabled = true; +static uint8_t s2lp_PAN_ID[2] = {0xff, 0xff}; +static uint8_t s2lp_short_address[2]; +static uint8_t s2lp_MAC[8]; +static rf_mode_e rf_mode = RF_MODE_NORMAL; +static bool rf_update_config = false; +static bool rf_update_cca_threshold = false; +static uint16_t cur_packet_len = 0xffff; +static uint32_t receiver_ready_timestamp; +static int16_t rssi_threshold = RSSI_THRESHOLD; +static uint32_t tx_start_time = 0; + +/* Channel configurations for sub-GHz */ +static phy_rf_channel_configuration_s phy_subghz = { + .channel_0_center_frequency = 868300000U, + .channel_spacing = 500000U, + .datarate = 250000U, + .number_of_channels = 11U, + .modulation = M_2FSK, + .modulation_index = MODULATION_INDEX_UNDEFINED +}; + + +static const phy_device_channel_page_s phy_channel_pages[] = { + { CHANNEL_PAGE_2, &phy_subghz}, + { CHANNEL_PAGE_0, NULL} +}; + + +#include "rtos.h" + +static void rf_irq_task_process_irq(); + +#define SIG_ALL (SIG_RADIO|SIG_TIMER_CCA|SIG_TIMER_BACKUP) +#define SIG_RADIO 1 +#define SIG_TIMER_CCA 2 +#define SIG_TIMER_BACKUP 4 + + +#define ACK_FRAME_LENGTH 3 +// Give some additional time for processing, PHY headers, CRC etc. +#define PACKET_SENDING_EXTRA_TIME 5000 +#define MAX_PACKET_SENDING_TIME (uint32_t)(8000000/phy_subghz.datarate)*FIFO_SIZE + PACKET_SENDING_EXTRA_TIME +#define ACK_SENDING_TIME (uint32_t)(8000000/phy_subghz.datarate)*ACK_FRAME_LENGTH + PACKET_SENDING_EXTRA_TIME + +#ifdef TEST_GPIOS_ENABLED +static void test1_toggle(void) +{ + if (test_pins->TEST4) { + test_pins->TEST4 = 0; + } else { + test_pins->TEST4 = 1; + } +} +static void test2_toggle(void) +{ + if (test_pins->TEST5) { + test_pins->TEST5 = 0; + } else { + test_pins->TEST5 = 1; + } +} +#endif //TEST_GPIOS_ENABLED + +static void rf_calculate_symbol_rate(uint32_t baudrate, phy_modulation_e modulation) +{ + (void) modulation; + uint8_t bits_in_symbols = 1; + rf_symbol_rate = baudrate / bits_in_symbols; +} + +static uint32_t rf_get_timestamp(void) +{ +#ifdef S2LP_USE_CHRONO + return (uint32_t)rf->tx_timer.elapsed_time().count(); +#else + return (uint32_t)rf->tx_timer.read_us(); +#endif +} + +static void rf_update_tx_active_time(void) +{ + if (device_driver.phy_rf_statistics) { + device_driver.phy_rf_statistics->tx_active_time += rf_get_timestamp() - tx_start_time; + } +} + +static void rf_update_rx_active_time(void) +{ + if (device_driver.phy_rf_statistics) { + device_driver.phy_rf_statistics->rx_active_time += rf_get_timestamp() - rx_time; + } +} + +static void rf_lock(void) +{ + platform_enter_critical(); +} + +static void rf_unlock(void) +{ + platform_exit_critical(); +} + +static void rf_cca_timer_signal(void) +{ + rf->irq_thread.flags_set(SIG_TIMER_CCA); +} + +static void rf_backup_timer_signal(void) +{ + rf->irq_thread.flags_set(SIG_TIMER_BACKUP); +} + +/* + * \brief Function writes/read data in SPI interface + */ +static void rf_spi_exchange(const void *tx, size_t tx_len, void *rx, size_t rx_len) +{ + rf->spi.write(static_cast(tx), tx_len, static_cast(rx), rx_len); +} + +static uint8_t rf_read_register(uint8_t addr) +{ + const uint8_t tx[2] = {SPI_RD_REG, addr}; + uint8_t rx[3]; + rf_lock(); + CS_SELECT(); + rf_spi_exchange(tx, sizeof(tx), rx, sizeof(rx)); + CS_RELEASE(); + rf_unlock(); + return rx[2]; +} + +static void rf_write_register(uint8_t addr, uint8_t data) +{ + const uint8_t tx[3] = {SPI_WR_REG, addr, data}; + rf_lock(); + CS_SELECT(); + rf_spi_exchange(tx, sizeof(tx), NULL, 0); + CS_RELEASE(); + rf_unlock(); +} + +static void rf_write_register_field(uint8_t addr, uint8_t field, uint8_t value) +{ + uint8_t reg_tmp = rf_read_register(addr); + reg_tmp &= ~field; + reg_tmp |= value; + rf_write_register(addr, reg_tmp); +} + +static s2lp_states_e rf_read_state(void) +{ + return (s2lp_states_e)(rf_read_register(MC_STATE0) >> 1); +} + +static void rf_poll_state_change(s2lp_states_e state) +{ + uint16_t break_counter = 0; + while (state != rf_read_state()) { + if (break_counter++ == 0xffff) { + tr_err("Failed to change state from %x to: %x", rf_read_state(), state); + break; + } + } +} + +static void rf_enable_gpio_signal(uint8_t gpio_pin, uint8_t interrupt_signal, uint8_t gpio_mode) +{ + rf_write_register(GPIO0_CONF + gpio_pin, (interrupt_signal << 3) | gpio_mode); +} + +static void rf_enable_interrupt(uint8_t event) +{ + if (enabled_interrupts & (1 << event)) { + return; + } + rf_write_register_field(IRQ_MASK0 - (event / 8), 1 << (event % 8), 1 << (event % 8)); + enabled_interrupts |= (1 << event); +} + +static void rf_disable_interrupt(uint8_t event) +{ + if (!(enabled_interrupts & (1 << event))) { + return; + } + rf_write_register_field(IRQ_MASK0 - (event / 8), 1 << (event % 8), 0 << (event % 8)); + enabled_interrupts &= ~(1 << event); +} + +static void rf_disable_all_interrupts(void) +{ + if ((uint8_t)enabled_interrupts & 0xff) { + rf_write_register(IRQ_MASK0, 0); + } + if ((uint8_t)(enabled_interrupts >> 8) & 0xff) { + rf_write_register(IRQ_MASK1, 0); + } + if ((uint8_t)(enabled_interrupts >> 16) & 0xff) { + rf_write_register(IRQ_MASK2, 0); + } + if ((uint8_t)(enabled_interrupts >> 24) & 0xff) { + rf_write_register(IRQ_MASK3, 0); + } + enabled_interrupts = 0; + read_irq_status(); +} + +static void rf_enable_gpio_interrupt(void) +{ + rf_enable_gpio_signal(INTERRUPT_GPIO, NIRQ, DIG_OUT_HIGH); + INT_IN_GPIO.mode(PullUp); + INT_IN_GPIO.fall(&rf_interrupt_handler); + INT_IN_GPIO.enable_irq(); +} + +static void rf_send_command(s2lp_commands_e command) +{ + const uint8_t tx[2] = {SPI_CMD, command}; + rf_lock(); + CS_SELECT(); + rf_spi_exchange(tx, sizeof(tx), NULL, 0); + CS_RELEASE(); + rf_unlock(); +} + +static void rf_state_change(s2lp_states_e state, bool lock_mode_tx) +{ + s2lp_commands_e command; + + if (S2LP_STATE_READY == state) { + s2lp_states_e cur_state = rf_read_state(); + if (S2LP_STATE_TX == cur_state || S2LP_STATE_RX == cur_state) { + command = S2LP_CMD_SABORT; + } else { + command = S2LP_CMD_READY; + } + } else if (S2LP_STATE_LOCK == state && lock_mode_tx) { + command = S2LP_CMD_LOCKTX; + } else if (S2LP_STATE_LOCK == state && !lock_mode_tx) { + command = S2LP_CMD_LOCKRX; + } else if (S2LP_STATE_RX == state) { + command = S2LP_CMD_RX; + } else if (S2LP_STATE_TX == state) { + command = S2LP_CMD_TX; + } else { + tr_err("Invalid state %x", state); + return; + } + rf_send_command(command); + rf_poll_state_change(state); +} + +static uint8_t rf_write_tx_fifo(uint8_t *ptr, uint16_t length) +{ + uint8_t free_bytes_in_fifo = FIFO_SIZE - rf_read_register(TX_FIFO_STATUS); + const uint8_t spi_header[SPI_HEADER_LENGTH] = {SPI_WR_REG, TX_FIFO}; + uint8_t written_length = length; + if (length > free_bytes_in_fifo) { + written_length = free_bytes_in_fifo; + } + CS_SELECT(); + rf_spi_exchange(spi_header, SPI_HEADER_LENGTH, NULL, 0); + rf_spi_exchange(ptr, written_length, NULL, 0); + CS_RELEASE(); + return written_length; +} + +static int rf_read_rx_fifo(uint16_t rx_index, uint16_t length) +{ + if ((rx_index + length) > RF_MTU) { + return -1; + } + uint8_t *ptr = &rx_buffer[rx_index]; + const uint8_t spi_header[SPI_HEADER_LENGTH] = {SPI_RD_REG, RX_FIFO}; + CS_SELECT(); + rf_spi_exchange(spi_header, SPI_HEADER_LENGTH, NULL, 0); + rf_spi_exchange(NULL, 0, ptr, length); + CS_RELEASE(); + return length; +} + +static void rf_flush_tx_fifo(void) +{ + if (rf_read_register(TX_FIFO_STATUS)) { + rf_send_command(S2LP_CMD_FLUSHTXFIFO); + } +} + +static void rf_flush_rx_fifo(void) +{ + if (rf_read_register(RX_FIFO_STATUS)) { + rf_send_command(S2LP_CMD_FLUSHRXFIFO); + } +} + +static void rf_write_packet_length(uint16_t packet_length) +{ + if ((uint8_t)(cur_packet_len >> 8) != (packet_length / 256)) { + rf_write_register(PCKTLEN1, packet_length / 256); + } + if ((uint8_t)cur_packet_len != (packet_length % 256)) { + rf_write_register(PCKTLEN0, packet_length % 256); + } + cur_packet_len = packet_length; +} + +static uint32_t read_irq_status(void) +{ + const uint8_t tx[2] = {SPI_RD_REG, IRQ_STATUS3}; + uint8_t rx[6]; + CS_SELECT(); + rf_spi_exchange(tx, sizeof(tx), rx, sizeof(rx)); + CS_RELEASE(); + return (((uint32_t)rx[2] << 24) | ((uint32_t)rx[3] << 16) | ((uint32_t)rx[4] << 8) | rx[5]); +} + +static void rf_set_channel_configuration_registers(void) +{ + // Set RSSI threshold + uint8_t rssi_th; + rf_conf_calculate_rssi_threshold_registers(rssi_threshold, &rssi_th); + rf_write_register(RSSI_TH, rssi_th); + // Set deviation + uint32_t deviation = rf_conf_calculate_deviation(phy_subghz.modulation_index, phy_subghz.datarate); + if (!deviation) { + deviation = DEFAULT_DEVIATION; + } + uint8_t fdev_m, fdev_e; + rf_conf_calculate_deviation_registers(deviation, &fdev_m, &fdev_e); + rf_write_register(MOD0, fdev_m); + rf_write_register_field(MOD1, FDEV_E_FIELD, fdev_e); + + // Set datarate + uint16_t datarate_m; + uint8_t datarate_e; + rf_conf_calculate_datarate_registers(phy_subghz.datarate, &datarate_m, &datarate_e); + rf_write_register_field(MOD2, DATARATE_E_FIELD, datarate_e); + rf_write_register(MOD3, (uint8_t)datarate_m); + rf_write_register(MOD4, datarate_m >> 8); + // Set RX filter bandwidth, using channel spacing as RX filter bandwidth + uint8_t chflt_m, chflt_e; + rf_conf_calculate_rx_filter_bandwidth_registers(phy_subghz.channel_spacing, &chflt_m, &chflt_e); + rf_write_register_field(CHFLT, CHFLT_M_FIELD, chflt_m << 4); + rf_write_register_field(CHFLT, CHFLT_E_FIELD, chflt_e); + // Set base frequency (Channel 0 center frequency) + uint8_t synt3, synt2, synt1, synt0; + rf_conf_calculate_base_frequency_registers(phy_subghz.channel_0_center_frequency, &synt3, &synt2, &synt1, &synt0); + rf_write_register_field(SYNT3, SYNT_FIELD, synt3); + rf_write_register(SYNT2, synt2); + rf_write_register(SYNT1, synt1); + rf_write_register(SYNT0, synt0); + // Set channel spacing + uint8_t ch_space; + uint8_t ch_space_divider = 1; + while (rf_conf_calculate_channel_spacing_registers(phy_subghz.channel_spacing / ch_space_divider, &ch_space)) { + ch_space_divider++; + rf_channel_multiplier++; + } + rf_write_register(CHSPACE, ch_space); + /* Preamble is set for S2-LP as repetitions of 01 or 10 pair + * + * For datarate < 150kbps, using phyFskPreambleLength = 8 repetitions of 01010101 + * For datarate >= 150kbps and datarate < 300kbps, using phyFskPreambleLength = 12 repetitions of 01010101 + * For datarate >= 300kbps, using phyFskPreambleLength = 24 repetitions of 01010101 + */ + uint8_t preamble_len = 24 * 4; + if (phy_subghz.datarate < 150000) { + preamble_len = 8 * 4; + } else if (phy_subghz.datarate < 300000) { + preamble_len = 12 * 4; + } + rf_write_register(PCKTCTRL5, preamble_len); +} + +static void rf_init_registers(void) +{ + rf_write_register_field(PCKTCTRL3, PCKT_FORMAT_FIELD, PCKT_FORMAT_802_15_4); + rf_write_register_field(MOD2, MOD_TYPE_FIELD, MOD_2FSK); + rf_write_register(PCKT_FLT_OPTIONS, 0); + rf_write_register_field(PCKTCTRL1, PCKT_CRCMODE_FIELD, PCKT_CRCMODE_0x04C11DB7); + rf_write_register_field(PCKTCTRL1, PCKT_TXSOURCE_FIELD, PCKT_TXSOURCE_NORMAL); + rf_write_register_field(PCKTCTRL1, PCKT_WHITENING_FIELD, PCKT_WHITENING_ENABLED); + rf_write_register_field(PCKTCTRL2, PCKT_FIXVARLEN_FIELD, PCKT_VARIABLE_LEN); + rf_write_register_field(PCKTCTRL2, PCKT_FCS_TYPE_FIELD, PCKT_FCS_TYPE_4_OCTET); + rf_write_register_field(PCKTCTRL3, PCKT_RXMODE_FIELD, PCKT_RXMODE_NORMAL); + rf_write_register_field(PCKTCTRL3, PCKT_BYTE_SWAP_FIELD, PCKT_BYTE_SWAP_LSB); + rf_write_register_field(PCKTCTRL6, PCKT_SYNCLEN_FIELD, PCKT_SYNCLEN); + rf_write_register_field(QI, PQI_TH_FIELD, PQI_TH); + rf_write_register_field(QI, SQI_EN_FIELD, SQI_EN); + rf_write_register(SYNC0, SFD0); + rf_write_register(SYNC1, SFD1); + rf_set_channel_configuration_registers(); +} + +static int8_t rf_address_write(phy_address_type_e address_type, uint8_t *address_ptr) +{ + int8_t ret_val = 0; + switch (address_type) { + /*Set 48-bit address*/ + case PHY_MAC_48BIT: + break; + /*Set 64-bit address*/ + case PHY_MAC_64BIT: + memcpy(s2lp_MAC, address_ptr, 8); + break; + /*Set 16-bit address*/ + case PHY_MAC_16BIT: + memcpy(s2lp_short_address, address_ptr, 2); + break; + /*Set PAN Id*/ + case PHY_MAC_PANID: + memcpy(s2lp_PAN_ID, address_ptr, 2); + break; + } + return ret_val; +} + +static int8_t rf_extension(phy_extension_type_e extension_type, uint8_t *data_ptr) +{ + int8_t retval = 0; + phy_csma_params_t *csma_params; + phy_rf_channel_configuration_s *channel_params; + uint32_t *timer_value; + switch (extension_type) { + case PHY_EXTENSION_SET_CHANNEL: + if (rf_state == RF_IDLE || rf_state == RF_CSMA_STARTED) { + rf_receive(*data_ptr); + } else { + // Store the new channel if couldn't change it yet. + rf_new_channel = *data_ptr; + retval = -1; + } + break; + case PHY_EXTENSION_CTRL_PENDING_BIT: + break; + /*Return frame pending status*/ + case PHY_EXTENSION_READ_LAST_ACK_PENDING_STATUS: + break; + case PHY_EXTENSION_ACCEPT_ANY_BEACON: + break; + case PHY_EXTENSION_SET_TX_TIME: + tx_time = common_read_32_bit(data_ptr); + break; + case PHY_EXTENSION_READ_RX_TIME: + common_write_32_bit(rx_time, data_ptr); + break; + case PHY_EXTENSION_DYNAMIC_RF_SUPPORTED: + *data_ptr = true; + break; + case PHY_EXTENSION_GET_TIMESTAMP: + timer_value = (uint32_t *)data_ptr; + *timer_value = rf_get_timestamp(); + break; + case PHY_EXTENSION_SET_CSMA_PARAMETERS: + csma_params = (phy_csma_params_t *)data_ptr; + if (csma_params->backoff_time == 0) { + rf_cca_timer_stop(); + if (rf_read_register(TX_FIFO_STATUS)) { + rf_send_command(S2LP_CMD_SABORT); + rf_poll_state_change(S2LP_STATE_READY); + rf_send_command(S2LP_CMD_FLUSHTXFIFO); + rf_poll_state_change(S2LP_STATE_READY); + } + if (rf_state == RF_TX_STARTED) { + rf_state = RF_IDLE; + rf_receive(rf_rx_channel); + } + tx_time = 0; + } else { + tx_time = csma_params->backoff_time; + cca_enabled = csma_params->cca_enabled; + } + break; + case PHY_EXTENSION_READ_TX_FINNISH_TIME: + timer_value = (uint32_t *)data_ptr; + *timer_value = tx_finnish_time; + break; + + case PHY_EXTENSION_GET_SYMBOLS_PER_SECOND: + timer_value = (uint32_t *)data_ptr; + *timer_value = rf_symbol_rate; + break; + case PHY_EXTENSION_SET_RF_CONFIGURATION: + channel_params = (phy_rf_channel_configuration_s *)data_ptr; + phy_subghz.datarate = channel_params->datarate; + phy_subghz.channel_spacing = channel_params->channel_spacing; + phy_subghz.channel_0_center_frequency = channel_params->channel_0_center_frequency; + phy_subghz.number_of_channels = channel_params->number_of_channels; + phy_subghz.modulation = channel_params->modulation; + phy_subghz.modulation_index = channel_params->modulation_index; + rf_calculate_symbol_rate(phy_subghz.datarate, phy_subghz.modulation); + rf_update_config = true; + if (rf_state == RF_IDLE) { + rf_receive(rf_rx_channel); + } + break; + case PHY_EXTENSION_SET_TX_POWER: + // TODO: Set TX output power + break; + case PHY_EXTENSION_SET_CCA_THRESHOLD: + rssi_threshold = rf_conf_cca_threshold_percent_to_rssi(*data_ptr); + rf_update_config = true; + if (rf_state == RF_IDLE) { + rf_receive(rf_rx_channel); + } + break; + case PHY_EXTENSION_SET_CHANNEL_CCA_THRESHOLD: + if ((rssi_threshold != (int8_t)*data_ptr) && (rf_state != RF_RX_STARTED)) { + rssi_threshold = (int8_t)*data_ptr; // *NOPAD* + rf_update_cca_threshold = true; + rf_receive(rf_rx_channel); + } + break; + default: + break; + } + + return retval; +} + + +static int8_t rf_interface_state_control(phy_interface_state_e new_state, uint8_t rf_channel) +{ + int8_t ret_val = 0; + switch (new_state) { + /*Reset PHY driver and set to idle*/ + case PHY_INTERFACE_RESET: + break; + /*Disable PHY Interface driver*/ + case PHY_INTERFACE_DOWN: + rf_lock(); + rf_send_command(S2LP_CMD_SABORT); + rf_disable_all_interrupts(); + rf_poll_state_change(S2LP_STATE_READY); + rf_flush_rx_fifo(); + rf_flush_tx_fifo(); + rf_state = RF_IDLE; + rf_unlock(); + break; + /*Enable PHY Interface driver*/ + case PHY_INTERFACE_UP: + rf_mode = RF_MODE_NORMAL; + rf_receive(rf_channel); + break; + /*Enable wireless interface ED scan mode*/ + case PHY_INTERFACE_RX_ENERGY_STATE: + break; + /*Enable Sniffer state*/ + case PHY_INTERFACE_SNIFFER_STATE: + rf_mode = RF_MODE_SNIFFER; + rf_receive(rf_channel); + break; + } + return ret_val; +} + +static void rf_tx_sent_handler(void) +{ + TEST_TX_DONE + rf_backup_timer_stop(); + rf_disable_interrupt(TX_DATA_SENT); + rf_update_tx_active_time(); + if (rf_state != RF_TX_ACK) { + tx_finnish_time = rf_get_timestamp(); + rf_state = RF_IDLE; + rf_receive(rf_rx_channel); + if (device_driver.phy_tx_done_cb) { + device_driver.phy_tx_done_cb(rf_radio_driver_id, mac_tx_handle, PHY_LINK_TX_SUCCESS, 0, 0); + } + } else { + rf_receive(rf_rx_channel); + } +} + +static void rf_tx_threshold_handler(void) +{ + rf_backup_timer_stop(); + rf_disable_interrupt(TX_FIFO_ALMOST_EMPTY); + // TODO check the FIFO threshold. By default, threshold is half of the FIFO size + uint8_t written_length = rf_write_tx_fifo(tx_data_ptr, tx_data_length); + if (written_length < tx_data_length) { + tx_data_ptr += written_length; + tx_data_length -= written_length; + rf_enable_interrupt(TX_FIFO_ALMOST_EMPTY); + } + rf_backup_timer_start(MAX_PACKET_SENDING_TIME); +} + +static void rf_start_tx(void) +{ + rf_send_command(S2LP_CMD_SABORT); + rf_disable_all_interrupts(); + rf_poll_state_change(S2LP_STATE_READY); + rf_state_change(S2LP_STATE_TX, false); + tx_start_time = rf_get_timestamp(); + // More TX data to be written in FIFO when TX threshold interrupt occurs + if (tx_data_ptr) { + rf_enable_interrupt(TX_FIFO_ALMOST_EMPTY); + } + rf_enable_interrupt(TX_DATA_SENT); + rf_enable_interrupt(TX_FIFO_UNF_OVF); + rf_backup_timer_start(MAX_PACKET_SENDING_TIME); +} + +static int rf_cca_check(void) +{ + uint32_t time_from_receiver_ready = rf_get_timestamp() - receiver_ready_timestamp; + if (time_from_receiver_ready < RSSI_SETTLING_TIME) { + wait_us(RSSI_SETTLING_TIME - time_from_receiver_ready); + } + return (rf_read_register(LINK_QUALIF1) & CARRIER_SENSE); +} + +static void rf_cca_timer_interrupt(void) +{ + TEST_CSMA_DONE + int8_t status = device_driver.phy_tx_done_cb(rf_radio_driver_id, mac_tx_handle, PHY_LINK_CCA_PREPARE, 0, 0); + if (status == PHY_TX_NOT_ALLOWED) { + rf_flush_tx_fifo(); + if (rf_state == RF_CSMA_STARTED) { + rf_state = RF_IDLE; + } + return; + } + if ((cca_enabled == true) && ((rf_state != RF_CSMA_STARTED && rf_state != RF_IDLE) || (read_irq_status() & (1 << SYNC_WORD)) || rf_cca_check())) { + if (rf_state == RF_CSMA_STARTED) { + rf_state = RF_IDLE; + } + rf_flush_tx_fifo(); + tx_finnish_time = rf_get_timestamp(); + if (device_driver.phy_tx_done_cb) { + if (rf_state == RF_RX_STARTED) { + device_driver.phy_tx_done_cb(rf_radio_driver_id, mac_tx_handle, PHY_LINK_CCA_FAIL_RX, 0, 0); + } else { + device_driver.phy_tx_done_cb(rf_radio_driver_id, mac_tx_handle, PHY_LINK_CCA_FAIL, 0, 0); + } + } + } else { + if (status == PHY_RESTART_CSMA) { + if (device_driver.phy_tx_done_cb) { + device_driver.phy_tx_done_cb(rf_radio_driver_id, mac_tx_handle, PHY_LINK_CCA_OK, 0, 0); + } + if (tx_time) { + uint32_t backoff_time = tx_time - rf_get_timestamp(); + // Max. time to TX can be 65ms, otherwise time has passed already -> send immediately + if (backoff_time > 65000) { + backoff_time = 1; + } + rf_cca_timer_start(backoff_time); + } + } else { + rf_start_tx(); + rf_state = RF_TX_STARTED; + TEST_TX_STARTED + if (device_driver.phy_rf_statistics) { + device_driver.phy_rf_statistics->tx_bytes += tx_data_length; + } + } + } +} + +static void rf_cca_timer_stop(void) +{ + TEST_CSMA_DONE + rf->cca_timer.detach(); +} + +static void rf_cca_timer_start(uint32_t slots) +{ +#ifdef S2LP_USE_CHRONO + rf->cca_timer.attach(rf_cca_timer_signal, microseconds(slots)); +#else + rf->cca_timer.attach_us(rf_cca_timer_signal, slots); +#endif + TEST_CSMA_STARTED +} + +static void rf_backup_timer_interrupt(void) +{ + tx_finnish_time = rf_get_timestamp(); + if (rf_state == RF_RX_STARTED) { + rf_update_rx_active_time(); + if (device_driver.phy_rf_statistics) { + device_driver.phy_rf_statistics->rx_timeouts++; + } + } else { + rf_update_tx_active_time(); + if (device_driver.phy_rf_statistics) { + device_driver.phy_rf_statistics->tx_timeouts++; + } + } + if (rf_state == RF_TX_STARTED) { + if (device_driver.phy_tx_done_cb) { + device_driver.phy_tx_done_cb(rf_radio_driver_id, mac_tx_handle, PHY_LINK_TX_SUCCESS, 0, 0); + } + } + rf_flush_tx_fifo(); + TEST_TX_DONE + TEST_RX_DONE + rf_state = RF_IDLE; + rf_receive(rf_rx_channel); +} + +static void rf_backup_timer_stop(void) +{ + rf->backup_timer.detach(); +} + +static void rf_backup_timer_start(uint32_t slots) +{ +#ifdef S2LP_USE_CHRONO + rf->backup_timer.attach(rf_backup_timer_signal, microseconds(slots)); +#else + rf->backup_timer.attach_us(rf_backup_timer_signal, slots); +#endif +} + +static int8_t rf_start_cca(uint8_t *data_ptr, uint16_t data_length, uint8_t tx_handle, data_protocol_e data_protocol) +{ + rf_lock(); + if (rf_state != RF_IDLE) { + rf_unlock(); + return -1; + } + rf_state = RF_CSMA_STARTED; + uint8_t written_length = rf_write_tx_fifo(data_ptr, data_length); + if (written_length < data_length) { + tx_data_ptr = data_ptr + written_length; + tx_data_length = data_length - written_length; + } else { + tx_data_ptr = NULL; + } + // If Ack is requested, store the MAC sequence. This will be compared with received Ack. + uint8_t version = ((*(data_ptr + 1) & VERSION_FIELD_MASK) >> SHIFT_VERSION_FIELD); + if ((version != MAC_FRAME_VERSION_2) && (*data_ptr & FC_AR)) { + tx_sequence = *(data_ptr + 2); + } + // TODO: Define this better + rf_write_packet_length(data_length + 4); + mac_tx_handle = tx_handle; + if (tx_time) { + uint32_t backoff_time = tx_time - rf_get_timestamp(); + // Max. time to TX can be 65ms, otherwise time has passed already -> send immediately + if (backoff_time <= 65000) { + rf_cca_timer_start(backoff_time); + rf_unlock(); + return 0; + } + } + // Short timeout to start CCA immediately. + rf_cca_timer_start(1); + rf_unlock(); + return 0; +} + +static void rf_send_ack(uint8_t seq) +{ + rf_state = RF_TX_ACK; + uint8_t ack_frame[3] = {MAC_TYPE_ACK, 0, seq}; + rf_write_tx_fifo(ack_frame, sizeof(ack_frame)); + rf_write_packet_length(sizeof(ack_frame) + 4); + tx_data_ptr = NULL; + rf_start_tx(); + TEST_TX_STARTED + rf_backup_timer_start(ACK_SENDING_TIME); + if (device_driver.phy_rf_statistics) { + device_driver.phy_rf_statistics->tx_bytes += sizeof(ack_frame); + } +} + +static void rf_handle_ack(uint8_t seq_number, uint8_t pending) +{ + phy_link_tx_status_e phy_status; + if (tx_sequence == (uint16_t)seq_number) { + tx_finnish_time = rf_get_timestamp(); + if (pending) { + phy_status = PHY_LINK_TX_DONE_PENDING; + } else { + phy_status = PHY_LINK_TX_DONE; + } + // No CCA attempts done, just waited Ack + device_driver.phy_tx_done_cb(rf_radio_driver_id, mac_tx_handle, phy_status, 0, 0); + // Clear TX sequence when Ack is received to avoid duplicate Acks + tx_sequence = 0xffff; + } +} + +static void rf_rx_ready_handler(void) +{ + rf_backup_timer_stop(); + TEST_RX_DONE + rf_flush_tx_fifo(); + int rx_read_length = rf_read_rx_fifo(rx_data_length, rf_read_register(RX_FIFO_STATUS)); + if (rx_read_length < 0) { + rf_receive(rf_rx_channel); + return; + } + rx_data_length += rx_read_length; + if (rf_mode != RF_MODE_SNIFFER) { + rf_state = RF_IDLE; + uint8_t version = ((rx_buffer[1] & VERSION_FIELD_MASK) >> SHIFT_VERSION_FIELD); + if (((rx_buffer[0] & MAC_FRAME_TYPE_MASK) == MAC_TYPE_ACK) && (version < MAC_FRAME_VERSION_2)) { + rf_handle_ack(rx_buffer[2], rx_buffer[0] & MAC_DATA_PENDING); + } else if (rf_rx_filter(rx_buffer, s2lp_MAC, s2lp_short_address, s2lp_PAN_ID)) { + int8_t rssi = (rf_read_register(RSSI_LEVEL) - RSSI_OFFSET); + if (device_driver.phy_rx_cb) { + device_driver.phy_rx_cb(rx_buffer, rx_data_length, 0xf0, rssi, rf_radio_driver_id); + } + // Check Ack request + if ((version != MAC_FRAME_VERSION_2) && (rx_buffer[0] & FC_AR)) { + rf_send_ack(rx_buffer[2]); + } + } + if (device_driver.phy_rf_statistics) { + device_driver.phy_rf_statistics->rx_bytes += rx_data_length; + } + } else { + rf_state = RF_IDLE; + int8_t rssi = (rf_read_register(RSSI_LEVEL) - RSSI_OFFSET); + if (device_driver.phy_rx_cb) { + device_driver.phy_rx_cb(rx_buffer, rx_data_length, 0xf0, rssi, rf_radio_driver_id); + } + } + if ((rf_state != RF_TX_ACK) && (rf_state != RF_CSMA_STARTED)) { + rf_receive(rf_rx_channel); + } +} + +static void rf_rx_threshold_handler(void) +{ + rf_backup_timer_stop(); + int rx_read_length = rf_read_rx_fifo(rx_data_length, rf_read_register(RX_FIFO_STATUS)); + if (rx_read_length < 0) { + rf_receive(rf_rx_channel); + return; + } + rx_data_length += rx_read_length; + rf_backup_timer_start(MAX_PACKET_SENDING_TIME); +} + +static void rf_sync_detected_handler(void) +{ + rx_time = rf_get_timestamp(); + rf_state = RF_RX_STARTED; + TEST_RX_STARTED + rf_disable_interrupt(SYNC_WORD); + rf_enable_interrupt(RX_FIFO_ALMOST_FULL); + rf_enable_interrupt(RX_DATA_READY); + rf_backup_timer_start(MAX_PACKET_SENDING_TIME); +} + +static void rf_receive(uint8_t rx_channel) +{ + if (rf_state == RF_TX_STARTED) { + return; + } + rf_lock(); + rf_send_command(S2LP_CMD_SABORT); + rf_disable_all_interrupts(); + rf_poll_state_change(S2LP_STATE_READY); + rf_flush_rx_fifo(); + if (rf_update_config == true) { + rf_channel_multiplier = 1; + rf_update_config = false; + rf_set_channel_configuration_registers(); + } + if (rf_update_cca_threshold == true) { + rf_update_cca_threshold = false; + uint8_t rssi_th; + rf_conf_calculate_rssi_threshold_registers(rssi_threshold, &rssi_th); + rf_write_register(RSSI_TH, rssi_th); + } + if (rx_channel != rf_rx_channel) { + rf_write_register(CHNUM, rx_channel * rf_channel_multiplier); + rf_rx_channel = rf_new_channel = rx_channel; + } + rf_send_command(S2LP_CMD_RX); + rf_enable_interrupt(SYNC_WORD); + rf_enable_interrupt(RX_FIFO_UNF_OVF); + rx_data_length = 0; + if (rf_state != RF_CSMA_STARTED) { + rf_state = RF_IDLE; + } + rf_poll_state_change(S2LP_STATE_RX); + receiver_ready_timestamp = rf_get_timestamp(); + rf_unlock(); +} + +static void rf_interrupt_handler(void) +{ + rf->irq_thread.flags_set(SIG_RADIO); +} + +void RFPins::rf_irq_task(void) +{ + for (;;) { + uint32_t flags = ThisThread::flags_wait_any(SIG_ALL); + rf_lock(); + if (flags & SIG_RADIO) { + rf_irq_task_process_irq(); + } + if (flags & SIG_TIMER_CCA) { + rf_cca_timer_interrupt(); + } + if (flags & SIG_TIMER_BACKUP) { + rf_backup_timer_interrupt(); + } + rf_unlock(); + } +} + +static void rf_irq_task_process_irq(void) +{ + rf_lock(); + uint32_t irq_status = read_irq_status(); + if (rf_state == RF_TX_STARTED || rf_state == RF_TX_ACK) { + if ((irq_status & (1 << TX_DATA_SENT)) && (enabled_interrupts & (1 << TX_DATA_SENT))) { + rf_tx_sent_handler(); + } + } + if (rf_state == RF_TX_STARTED) { + if ((irq_status & (1 << TX_FIFO_ALMOST_EMPTY)) && (enabled_interrupts & (1 << TX_FIFO_ALMOST_EMPTY))) { + rf_tx_threshold_handler(); + } + } + if ((irq_status & (1 << TX_FIFO_UNF_OVF)) && (enabled_interrupts & (1 << TX_FIFO_UNF_OVF))) { + rf_backup_timer_stop(); + tx_finnish_time = rf_get_timestamp(); + rf_update_tx_active_time(); + TEST_TX_DONE + device_driver.phy_tx_done_cb(rf_radio_driver_id, mac_tx_handle, PHY_LINK_CCA_FAIL_RX, 1, 0); + rf_send_command(S2LP_CMD_SABORT); + rf_poll_state_change(S2LP_STATE_READY); + rf_send_command(S2LP_CMD_FLUSHTXFIFO); + rf_poll_state_change(S2LP_STATE_READY); + rf_state = RF_IDLE; + rf_receive(rf_rx_channel); + } + if (rf_state == RF_IDLE || rf_state == RF_CSMA_STARTED || rf_state == RF_TX_STARTED) { + if ((irq_status & (1 << SYNC_WORD)) && (enabled_interrupts & (1 << SYNC_WORD))) { + rf_sync_detected_handler(); + } + } else if (rf_state == RF_RX_STARTED) { + if ((irq_status & (1 << RX_DATA_READY)) && (enabled_interrupts & (1 << RX_DATA_READY))) { + rf_update_rx_active_time(); + if (!(irq_status & (1 << CRC_ERROR))) { + rf_rx_ready_handler(); + } else { + TEST_RX_DONE + rf_backup_timer_stop(); + rf_flush_tx_fifo(); + rf_state = RF_IDLE; + // In case the channel change was called during reception, driver is responsible to change the channel if CRC failed. + rf_receive(rf_new_channel); + if (device_driver.phy_rf_statistics) { + device_driver.phy_rf_statistics->crc_fails++; + } + } + } + if ((irq_status & (1 << RX_FIFO_ALMOST_FULL)) && (enabled_interrupts & (1 << RX_FIFO_ALMOST_FULL))) { + rf_rx_threshold_handler(); + } + if ((irq_status & (1 << RX_DATA_DISCARDED)) && (enabled_interrupts & (1 << RX_DATA_DISCARDED))) { + + } + if ((irq_status & (1 << CRC_ERROR)) && (enabled_interrupts & (1 << CRC_ERROR))) { + + } + } + if ((irq_status & (1 << RX_FIFO_UNF_OVF)) && (enabled_interrupts & (1 << RX_FIFO_UNF_OVF))) { + rf_update_rx_active_time(); + TEST_RX_DONE + rf_backup_timer_stop(); + rf_send_command(S2LP_CMD_SABORT); + rf_poll_state_change(S2LP_STATE_READY); + rf_send_command(S2LP_CMD_FLUSHRXFIFO); + rf_poll_state_change(S2LP_STATE_READY); + rf_flush_tx_fifo(); + rf_state = RF_IDLE; + rf_receive(rf_rx_channel); + } + rf_unlock(); +} + +static void rf_reset(void) +{ + // Shutdown + rf->SDN = 1; + ThisThread::sleep_for(S2LP_TIME_10MS); + // Wake up + rf->SDN = 0; + ThisThread::sleep_for(S2LP_TIME_10MS); +} + +static void rf_init(void) +{ +#ifdef TEST_GPIOS_ENABLED + fhss_bc_switch = test1_toggle; + fhss_uc_switch = test2_toggle; +#endif //TEST_GPIOS_ENABLED + rf_reset(); + rf->spi.frequency(10000000); + CS_RELEASE(); + rf_init_registers(); + rf_enable_gpio_interrupt(); + rf_calculate_symbol_rate(phy_subghz.datarate, phy_subghz.modulation); + rf->tx_timer.start(); +} + +static int8_t rf_device_register(const uint8_t *mac_addr) +{ + rf_init(); + /*Set pointer to MAC address*/ + device_driver.PHY_MAC = (uint8_t *)mac_addr; + device_driver.driver_description = (char *)"S2LP_MAC"; + device_driver.link_type = PHY_LINK_15_4_SUBGHZ_TYPE; + device_driver.phy_channel_pages = phy_channel_pages; + device_driver.phy_MTU = RF_MTU; + /*No header in PHY*/ + device_driver.phy_header_length = 0; + /*No tail in PHY*/ + device_driver.phy_tail_length = 0; + /*Set address write function*/ + device_driver.address_write = &rf_address_write; + /*Set RF extension function*/ + device_driver.extension = &rf_extension; + /*Set RF state control function*/ + device_driver.state_control = &rf_interface_state_control; + /*Set transmit function*/ + device_driver.tx = &rf_start_cca; + /*NULLIFY rx and tx_done callbacks*/ + device_driver.phy_rx_cb = NULL; + device_driver.phy_tx_done_cb = NULL; + /*Register device driver*/ + rf_radio_driver_id = arm_net_phy_register(&device_driver); + return rf_radio_driver_id; +} + +static void rf_device_unregister() +{ + if (rf_radio_driver_id >= 0) { + arm_net_phy_unregister(rf_radio_driver_id); + rf_radio_driver_id = -1; + } +} + +void NanostackRfPhys2lp::get_mac_address(uint8_t *mac) +{ + rf_lock(); + + if (NULL == rf) { + error("NanostackRfPhys2lp Must be registered to read mac address"); + rf_unlock(); + return; + } + memcpy((void *)mac, (void *)_mac_addr, sizeof(_mac_addr)); + + rf_unlock(); +} + +void NanostackRfPhys2lp::set_mac_address(uint8_t *mac) +{ + rf_lock(); + + if (NULL != rf) { + error("NanostackRfPhys2lp cannot change mac address when running"); + rf_unlock(); + return; + } + memcpy((void *)_mac_addr, (void *)mac, sizeof(_mac_addr)); + _mac_set = true; + + rf_unlock(); +} + +int8_t NanostackRfPhys2lp::rf_register() +{ + if (NULL == _rf) { + return -1; + } + rf_lock(); + if (rf != NULL) { + rf_unlock(); + error("Multiple registrations of NanostackRfPhyAtmel not supported"); + return -1; + } + + if (!_mac_set) { +#ifdef AT24MAC + int ret = _mac.read_eui64((void *)s2lp_MAC); + if (ret < 0) { + rf = NULL; + rf_unlock(); + return -1; + } +#else + randLIB_seed_random(); + randLIB_get_n_bytes_random(s2lp_MAC, 8); + s2lp_MAC[0] |= 2; //Set Local Bit + s2lp_MAC[0] &= ~1; //Clear multicast bit +#endif + set_mac_address(s2lp_MAC); + } + + rf = _rf; +#ifdef TEST_GPIOS_ENABLED + test_pins = _test_pins; +#endif + + int8_t radio_id = rf_device_register(_mac_addr); + if (radio_id < 0) { + rf = NULL; + } + rf_unlock(); + return radio_id; +} + +void NanostackRfPhys2lp::rf_unregister() +{ + rf_lock(); + if (NULL == rf) { + rf_unlock(); + return; + } + rf_device_unregister(); + rf = NULL; + rf_unlock(); +} + +NanostackRfPhys2lp::NanostackRfPhys2lp(PinName spi_sdi, PinName spi_sdo, PinName spi_sclk, PinName spi_cs, PinName spi_sdn + , PinName spi_gpio0, PinName spi_gpio1, PinName spi_gpio2, PinName spi_gpio3 +#ifdef AT24MAC + , PinName i2c_sda, PinName i2c_scl +#endif //AT24MAC + ) + : +#ifdef AT24MAC + _mac(i2c_sda, i2c_scl), +#endif //AT24MAC + _mac_addr(), _rf(NULL), _test_pins(NULL), _mac_set(false), + _spi_sdi(spi_sdi), _spi_sdo(spi_sdo), _spi_sclk(spi_sclk), _spi_cs(spi_cs), _spi_sdn(spi_sdn), + _spi_gpio0(spi_gpio0), _spi_gpio1(spi_gpio1), _spi_gpio2(spi_gpio2), _spi_gpio3(spi_gpio3) +{ + _rf = new RFPins(_spi_sdi, _spi_sdo, _spi_sclk, _spi_cs, _spi_sdn, _spi_gpio0, _spi_gpio1, _spi_gpio2, _spi_gpio3); +#ifdef TEST_GPIOS_ENABLED + _test_pins = new TestPins_S2LP(TEST_PIN_TX, TEST_PIN_RX, TEST_PIN_CSMA, TEST_PIN_SPARE_1, TEST_PIN_SPARE_2); +#endif //TEST_GPIOS_ENABLED +} + +NanostackRfPhys2lp::~NanostackRfPhys2lp() +{ + delete _rf; +} + +static bool rf_panid_filter_common(uint8_t *panid_start, uint8_t *pan_id, uint8_t frame_type) +{ + // PHY driver shouldn't drop received Beacon frames as they might be used by load balancing + if (frame_type == MAC_FRAME_BEACON) { + return true; + } + bool retval = true; + uint8_t cmp_table[2] = {0xff, 0xff}; + if (!(pan_id[0] == 0xff && pan_id[1] == 0xff)) { + if (memcmp((uint8_t *)panid_start, (uint8_t *) cmp_table, 2)) { + retval = false; + } + if (!retval) { + for (uint8_t i = 0; i < 2; i++) { + cmp_table[1 - i] = panid_start[i]; + } + if (!memcmp(pan_id, cmp_table, 2)) { + retval = true; + } + } + } + return retval; +} + +static bool rf_panid_v1_v0_filter(uint8_t *ptr, uint8_t *pan_id, uint8_t frame_type) +{ + return rf_panid_filter_common(ptr, pan_id, frame_type); +} + +static bool rf_addr_filter_common(uint8_t *ptr, uint8_t addr_mode, uint8_t *mac_64bit_addr, uint8_t *mac_16bit_addr) +{ + uint8_t cmp_table[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + bool retval = true; + switch (addr_mode) { + case FC_DST_16_BITS: + if (memcmp((uint8_t *)ptr, (uint8_t *) cmp_table, 2)) { + retval = false; + } + if (!retval) { + for (uint8_t i = 0; i < 2; i++) { + cmp_table[1 - i] = ptr[i]; + } + + if (!memcmp((uint8_t *)mac_16bit_addr, (uint8_t *) cmp_table, 2)) { + retval = true; + } + } + break; + case FC_DST_64_BITS: + if (memcmp((uint8_t *)ptr, (uint8_t *) cmp_table, 8)) { + retval = false; + } + if (!retval) { + for (uint8_t i = 0; i < 8; i++) { + cmp_table[7 - i] = ptr[i]; + } + + if (!memcmp((uint8_t *)mac_64bit_addr, (uint8_t *) cmp_table, 8)) { + retval = true; + } + } + break; + case FC_DST_ADDR_NONE: + retval = true; + break; + default: + retval = false; + break; + } + return retval; +} + +static bool rf_addr_v1_v0_filter(uint8_t *ptr, uint8_t *mac_64bit_addr, uint8_t *mac_16bit_addr, uint8_t dst_mode) +{ + return rf_addr_filter_common(ptr, dst_mode, mac_64bit_addr, mac_16bit_addr); +} + +static bool rf_rx_filter(uint8_t *mac_header, uint8_t *mac_64bit_addr, uint8_t *mac_16bit_addr, uint8_t *pan_id) +{ + uint8_t dst_mode = (mac_header[1] & FC_DST_MODE); + uint8_t frame_type = mac_header[0] & MAC_FRAME_TYPE_MASK; + uint8_t version = ((mac_header[1] & VERSION_FIELD_MASK) >> SHIFT_VERSION_FIELD); + if (version != MAC_FRAME_VERSION_2) { + if (!rf_panid_v1_v0_filter(mac_header + OFFSET_DST_PAN_ID, pan_id, frame_type)) { + return false; + } + if (!rf_addr_v1_v0_filter(mac_header + OFFSET_DST_ADDR, mac_64bit_addr, mac_16bit_addr, dst_mode)) { + return false; + } + } + return true; +} + +#if MBED_CONF_S2LP_PROVIDE_DEFAULT +NanostackRfPhy &NanostackRfPhy::get_default_instance() +{ + static NanostackRfPhys2lp rf_phy(S2LP_SPI_SDI, S2LP_SPI_SDO, S2LP_SPI_SCLK, S2LP_SPI_CS, S2LP_SPI_SDN + , S2LP_SPI_GPIO0, S2LP_SPI_GPIO1, S2LP_SPI_GPIO2, S2LP_SPI_GPIO3 +#ifdef AT24MAC + , S2LP_I2C_SDA, S2LP_I2C_SCL +#endif //AT24MAC + ); + return rf_phy; +} +#endif // MBED_CONF_S2LP_PROVIDE_DEFAULT +#endif // MBED_CONF_NANOSTACK_CONFIGURATION && DEVICE_SPI diff --git a/source/at24mac_s2lp.cpp b/source/at24mac_s2lp.cpp new file mode 100644 index 0000000000..6a41ffd024 --- /dev/null +++ b/source/at24mac_s2lp.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2019, Pelion and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "NanostackRfPhys2lp.h" +#include "at24mac_s2lp.h" + + +#if DEVICE_I2C +#ifdef AT24MAC + +/* Device addressing */ +#define AT24MAC_EEPROM_ADDRESS (0x0A<<4) +#define AT24MAC_RW_PROTECT_ADDRESS (0x06<<4) +#define AT24MAC_SERIAL_ADDRESS (0x0B<<4) + +/* Known memory blocks */ +#define AT24MAC_SERIAL_OFFSET (0x80) +#define AT24MAC_EUI64_OFFSET (0x98) +#define AT24MAC_EUI48_OFFSET (0x9A) + +#define SERIAL_LEN 16 +#define EUI64_LEN 8 +#define EUI48_LEN 6 + +using namespace mbed; + +AT24Mac_s2lp::AT24Mac_s2lp(PinName sda, PinName scl) : _i2c(sda, scl) +{ + // Do nothing +} + +int AT24Mac_s2lp::read_serial(void *buf) +{ + char offset = AT24MAC_SERIAL_OFFSET; + if (_i2c.write(AT24MAC_SERIAL_ADDRESS, &offset, 1, true)) { + return -1; //No ACK + } + return _i2c.read(AT24MAC_SERIAL_ADDRESS, (char *)buf, SERIAL_LEN); +} + +int AT24Mac_s2lp::read_eui64(void *buf) +{ + char offset = AT24MAC_EUI64_OFFSET; + if (_i2c.write(AT24MAC_SERIAL_ADDRESS, &offset, 1, true)) { + return -1; //No ACK + } + return _i2c.read(AT24MAC_SERIAL_ADDRESS, (char *)buf, EUI64_LEN); +} + +int AT24Mac_s2lp::read_eui48(void *buf) +{ + char offset = AT24MAC_EUI48_OFFSET; + if (_i2c.write(AT24MAC_SERIAL_ADDRESS, &offset, 1, true)) { + return -1; //No ACK + } + return _i2c.read(AT24MAC_SERIAL_ADDRESS, (char *)buf, EUI48_LEN); +} + +#endif /* AT24MAC */ +#endif /* DEVICE_I2C */ diff --git a/source/at24mac_s2lp.h b/source/at24mac_s2lp.h new file mode 100644 index 0000000000..7f6ecaeb74 --- /dev/null +++ b/source/at24mac_s2lp.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2019, Pelion and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef AT24MAC_S2LP_H +#define AT24MAC_S2LP_H + +#include "PinNames.h" + +#if DEVICE_I2C +#ifdef AT24MAC + +#include "I2C.h" +#include "drivers/DigitalInOut.h" +#include "platform/mbed_wait_api.h" + +/* + * AT24MAC drivers. + * + * This is a EEPROM chip designed to contain factory programmed read-only EUI-64 or EUI-48, + * a 128bit serial number and some user programmable EEPROM. + * + * AT24MAC602 contains EUI-64, use read_eui64() + * AT24MAC402 contains EUI-64, use read_eui48() + * + * NOTE: You cannot use both EUI-64 and EUI-48. Chip contains only one of those. + */ + +class AT24Mac_s2lp { +public: + AT24Mac_s2lp(PinName sda, PinName scl); + + /** + * Read unique serial number from chip. + * \param buf pointer to write serial number to. Must have space for 16 bytes. + * \return zero on success, negative number on failure + */ + int read_serial(void *buf); + + /** + * Read EUI-64 from chip. + * \param buf pointer to write EUI-64 to. Must have space for 8 bytes. + * \return zero on success, negative number on failure + */ + int read_eui64(void *buf); + + /** + * Read EUI-48 from chip. + * \param buf pointer to write EUI-48 to. Must have space for 6 bytes. + * \return zero on success, negative number on failure + */ + int read_eui48(void *buf); + +private: + mbed::I2C _i2c; +}; + +#endif /* AT24MAC */ +#endif /* DEVICE_I2C */ +#endif /* AT24MAC_S2LP_H */ diff --git a/source/rf_configuration.c b/source/rf_configuration.c new file mode 100644 index 0000000000..7676790fbb --- /dev/null +++ b/source/rf_configuration.c @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2018-2020, Pelion and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined(MBED_CONF_NANOSTACK_CONFIGURATION) && DEVICE_SPI && DEVICE_INTERRUPTIN && defined(MBED_CONF_RTOS_PRESENT) + +#include "nanostack/platform/arm_hal_phy.h" +#include "rf_configuration.h" + + +// Note that F_XO and F_DIG depends on the used clock frequency +#define F_XO 50000000 +#define F_DIG 25000000 +// Note that reference divider depends on REFDIV field in XO_RCO_CONF0 register +#define REF_DIVIDER 1 +// Note that band selector depends on BS field in SYNT3 register +#define BAND_SELECTOR 4 +#define DEF_2EXP33 8589934592 +#define DEF_2EXP20 1048576 +#define DEF_2EXP19 524288 +#define DEF_2EXP16 65536 +#define DEF_2EXP15 32768 +// Use multiplier for better resolution +#define RESOLUTION_MULTIPLIER 1000000 + +// RSSI_TH is a 8-bit register which can be converted to dBm using formula RSSI_TH-146 +#define MIN_RSSI_THRESHOLD -146 +#define MAX_RSSI_THRESHOLD 109 + +void rf_conf_calculate_datarate_registers(uint32_t datarate, uint16_t *datarate_mantissa, uint8_t *datarate_exponent) +{ + uint64_t datarate_m = (uint64_t)datarate * DEF_2EXP33; + uint8_t datarate_e = 1; + while (datarate_m >= DEF_2EXP16) { + datarate_e++; + uint16_t var_2exp_datarate_e = (uint32_t)2 << (datarate_e - 1); + datarate_m = (uint64_t)datarate * DEF_2EXP33; + datarate_m = datarate_m / ((uint64_t)var_2exp_datarate_e * F_DIG); + datarate_m -= DEF_2EXP16; + } + *datarate_mantissa = datarate_m; + *datarate_exponent = datarate_e; +} + +void rf_conf_calculate_base_frequency_registers(uint32_t frequency, uint8_t *synt3, uint8_t *synt2, uint8_t *synt1, uint8_t *synt0) +{ + uint64_t freq_tmp = (uint64_t)frequency * RESOLUTION_MULTIPLIER; + freq_tmp = (freq_tmp / (F_XO / ((BAND_SELECTOR / 2) * REF_DIVIDER))); + freq_tmp *= DEF_2EXP20; + freq_tmp /= RESOLUTION_MULTIPLIER; + *synt3 = (uint8_t)(freq_tmp >> 24); + *synt2 = (uint8_t)(freq_tmp >> 16); + *synt1 = (uint8_t)(freq_tmp >> 8); + *synt0 = (uint8_t)freq_tmp; +} + +void rf_conf_calculate_deviation_registers(uint32_t deviation, uint8_t *fdev_m, uint8_t *fdev_e) +{ + uint64_t fdev_m_tmp = 0xffff; + uint8_t fdev_e_tmp = 1; + + while (fdev_m_tmp > 255) { + fdev_e_tmp++; + uint16_t var_2exp_datarate_e_minus_1 = (uint16_t)2 << ((fdev_e_tmp - 1) - 1); + fdev_m_tmp = (uint64_t)deviation * RESOLUTION_MULTIPLIER; + fdev_m_tmp = (((fdev_m_tmp / F_XO) * DEF_2EXP19 * BAND_SELECTOR * REF_DIVIDER * (8 / BAND_SELECTOR)) / var_2exp_datarate_e_minus_1); + fdev_m_tmp += RESOLUTION_MULTIPLIER / 2; + fdev_m_tmp /= RESOLUTION_MULTIPLIER; + fdev_m_tmp -= 256; + } + *fdev_m = (uint8_t)fdev_m_tmp; + *fdev_e = fdev_e_tmp; +} + +int rf_conf_calculate_channel_spacing_registers(uint32_t channel_spacing, uint8_t *ch_space) +{ + uint64_t ch_space_tmp = (uint64_t)channel_spacing * RESOLUTION_MULTIPLIER; + ch_space_tmp /= F_XO; + ch_space_tmp *= DEF_2EXP15; + ch_space_tmp += RESOLUTION_MULTIPLIER / 2; + ch_space_tmp /= RESOLUTION_MULTIPLIER; + // Check if channel spacing is too high + if (ch_space_tmp > 255) { + return -1; + } + *ch_space = (uint8_t)ch_space_tmp; + return 0; +} + +/* Note: This function doesn't necessarily give the optimal RX filter settings. + * When accurate chflt_m and chflt_e settings are needed they must be computed manually. + * Function uses undefined values (900000, 852000, ...) + * to find the chflt_m and chflt_e settings from the RX filter table (see. S2-LP datasheet). + * + * E=0 E=1 E=2 E=3 E=4 E=5 E=6 E=7 E=8 E=9 + * M=0 800.1 450.9 224.7 112.3 56.1 28.0 14.0 7.0 3.5 1.8 + * M=1 795.1 425.9 212.4 106.2 53.0 26.5 13.3 6.6 3.3 1.7 + * M=2 768.4 403.2 201.1 100.5 50.2 25.1 12.6 6.3 3.1 1.6 + * M=3 736.8 380.8 190.0 95.0 47.4 23.7 11.9 5.9 3.0 1.5 + * M=4 705.1 362.1 180.7 90.3 45.1 22.6 11.3 5.6 2.8 1.4 + * M=5 670.9 341.7 170.6 85.3 42.6 21.3 10.6 5.3 2.7 1.3 + * M=6 642.3 325.4 162.4 81.2 40.6 20.3 10.1 5.1 2.5 1.3 + * M=7 586.7 294.5 147.1 73.5 36.7 18.4 9.2 4.6 2.3 1.2 + * M=8 541.4 270.3 135.0 67.5 33.7 16.9 8.4 4.2 2.1 1.1 + */ +void rf_conf_calculate_rx_filter_bandwidth_registers(uint32_t rx_bandwidth, uint8_t *chflt_m, uint8_t *chflt_e) +{ + uint8_t chflt_e_tmp = 0; + uint8_t chflt_m_tmp = 0; + + while (rx_bandwidth < 900000u / (2 << chflt_e_tmp)) { + chflt_e_tmp++; + } + uint32_t rx_bandwidth_tmp = rx_bandwidth; + if (chflt_e_tmp > 0) { + rx_bandwidth_tmp = rx_bandwidth * (2 << (chflt_e_tmp - 1)); + } + if (852000 > rx_bandwidth_tmp) { + chflt_m_tmp++; + } + if (806000 > rx_bandwidth_tmp) { + chflt_m_tmp++; + } + if (760000 > rx_bandwidth_tmp) { + chflt_m_tmp++; + } + if (724000 > rx_bandwidth_tmp) { + chflt_m_tmp++; + } + if (682000 > rx_bandwidth_tmp) { + chflt_m_tmp++; + } + if (650000 > rx_bandwidth_tmp) { + chflt_m_tmp++; + } + if (588000 > rx_bandwidth_tmp) { + chflt_m_tmp++; + } + if (542000 > rx_bandwidth_tmp) { + chflt_m_tmp++; + } + *chflt_m = chflt_m_tmp; + *chflt_e = chflt_e_tmp; +} + +int16_t rf_conf_cca_threshold_percent_to_rssi(uint8_t percent) +{ + uint8_t step = (MAX_RSSI_THRESHOLD - MIN_RSSI_THRESHOLD); + return MIN_RSSI_THRESHOLD + (step * percent) / 100; +} + +void rf_conf_calculate_rssi_threshold_registers(int16_t rssi_threshold, uint8_t *rssi_th) +{ + *rssi_th = rssi_threshold + RSSI_OFFSET; +} + +/* + * Function calculates deviation from given parameters for 2FSK and 2GFSK modulations. + * Calculated using formula Deviation=(modulation_index*datarate)/2 + */ +uint32_t rf_conf_calculate_deviation(phy_modulation_index_e modulation_index, uint32_t datarate) +{ + uint32_t deviation = 0; + if (modulation_index == MODULATION_INDEX_0_5) { + deviation = datarate / 4; + } else if (modulation_index == MODULATION_INDEX_1_0) { + deviation = datarate / 2; + } + return deviation; +} + +#endif // MBED_CONF_NANOSTACK_CONFIGURATION && DEVICE_SPI && DEVICE_INTERRUPTIN && defined(MBED_CONF_RTOS_PRESENT) + diff --git a/source/rf_configuration.h b/source/rf_configuration.h new file mode 100644 index 0000000000..19b6aad373 --- /dev/null +++ b/source/rf_configuration.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2018-2020, Pelion and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RF_CONF_H_ +#define RF_CONF_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define RSSI_OFFSET 146 + +void rf_conf_calculate_datarate_registers(uint32_t datarate, uint16_t *datarate_mantissa, uint8_t *datarate_exponent); +void rf_conf_calculate_base_frequency_registers(uint32_t frequency, uint8_t *synt3, uint8_t *synt2, uint8_t *synt1, uint8_t *synt0); +void rf_conf_calculate_deviation_registers(uint32_t deviation, uint8_t *fdev_m, uint8_t *fdev_e); +int rf_conf_calculate_channel_spacing_registers(uint32_t channel_spacing, uint8_t *ch_space); +void rf_conf_calculate_rx_filter_bandwidth_registers(uint32_t rx_bandwidth, uint8_t *chflt_m, uint8_t *chflt_e); +void rf_conf_calculate_rssi_threshold_registers(int16_t rssi_threshold, uint8_t *rssi_th); +uint32_t rf_conf_calculate_deviation(phy_modulation_index_e modulation_index, uint32_t datarate); +int16_t rf_conf_cca_threshold_percent_to_rssi(uint8_t percent); + +#ifdef __cplusplus +} +#endif + +#endif /* RF_CONF_H_ */ diff --git a/source/s2lpReg.h b/source/s2lpReg.h new file mode 100644 index 0000000000..16c6f9aae7 --- /dev/null +++ b/source/s2lpReg.h @@ -0,0 +1,344 @@ +/* + * Copyright (c) 2018-2020, Pelion and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef S2LPREG_H_ +#define S2LPREG_H_ +#ifdef __cplusplus +extern "C" { +#endif + +#define RF_MTU 2047 +#define PARTNUM 0x03 +#define VERSION 0xC1 +#define FIFO_SIZE 128 +#define SPI_HEADER_LENGTH 2 +#define RSSI_SETTLING_TIME 250 + +#define S2LP_GPIO0 0 +#define S2LP_GPIO1 1 +#define S2LP_GPIO2 2 +#define S2LP_GPIO3 3 + +// GPIO modes +#define DIG_IN 1 +#define DIG_OUT_LOW 2 +#define DIG_OUT_HIGH 3 + +// Interrupt events +#define RX_DATA_READY 0 +#define RX_DATA_DISCARDED 1 +#define TX_DATA_SENT 2 +#define MAX_RE_TX 3 +#define CRC_ERROR 4 +#define TX_FIFO_UNF_OVF 5 +#define RX_FIFO_UNF_OVF 6 +#define TX_FIFO_ALMOST_FULL 7 +#define TX_FIFO_ALMOST_EMPTY 8 +#define RX_FIFO_ALMOST_FULL 9 +#define RX_FIFO_ALMOST_EMPTY 10 +#define MAX_CCA_BACKOFFS 11 +#define VALID_PREAMBLE 12 +#define SYNC_WORD 13 +#define RSSI_ABOVE_THR 14 +#define WAKE_UP_TIMEOUT 15 +#define READY 16 +#define STANDBY_SWITCHING 17 +#define LOW_BATTERY_LVL 18 +#define POWER_ON_RESET 19 +#define RX_TIMER_TIMEOUT 28 +#define SNIFF_TIMER_TIMEOUT 29 + +// GPIO signals +#define NIRQ 0 +#define POR 1 +#define WUT_EXPIRE 2 +#define LOW_BATTERY 3 +#define TX_DATA_OUTPUT 4 +#define TX_STATE 5 +#define TXRX_FIFO_ALMOST_EMPTY 6 +#define TXRX_FIFO_ALMOST_FULL 7 +#define RX_DATA_OUTPUT 8 +#define RX_CLOCK_OUTPUT 9 +#define RX_STATE 10 +#define STATE_OTHER_THAN_SLEEP_OR_STANDBY 11 +#define STANDBY_STATE 12 +#define ANTENNA_SWITCH 13 +#define VALID_PREAMBLE_DETECTED 14 +#define SYNC_WORD_DETECTED 15 +#define RSSI_ABOVE_THRESHOLD 16 +#define TXRX_MODE_INDICATOR 18 +#define VDD 19 +#define GND 20 +#define SMPS_ENABLE 21 +#define SLEEP_STATE 22 +#define READY_STATE 23 +#define LOCK_STATE 24 +#define WAIT_LOCK_DETECTOR 25 +#define TX_DATA_OOK 26 +#define WAIT_READY 27 +#define WAIT_TIMER_EXPIRATION 28 +#define END_OF_CALIBRATION 29 +#define ENABLE_SYNTH_BLOCK 30 + +// RF registers +#define GPIO0_CONF 0x00 +#define GPIO1_CONF 0x01 +#define GPIO2_CONF 0x02 +#define GPIO3_CONF 0x03 +#define SYNT3 0x05 +#define SYNT2 0x06 +#define SYNT1 0x07 +#define SYNT0 0x08 +#define IF_OFFSET_ANA 0x09 +#define IF_OFFSET_DIG 0x0A +#define CHSPACE 0x0C +#define CHNUM 0x0D +#define MOD4 0x0E +#define MOD3 0x0F +#define MOD2 0x10 +#define MOD1 0x11 +#define MOD0 0x12 +#define CHFLT 0x13 +#define AFC2 0x14 +#define AFC1 0x15 +#define AFC0 0x16 +#define RSSI_FLT 0x17 +#define RSSI_TH 0x18 +#define AGCCTRL4 0x1A +#define AGCCTRL3 0x1B +#define AGCCTRL2 0x1C +#define AGCCTRL1 0x1D +#define AGCCTRL0 0x1E +#define ANT_SELECT_CONF 0x1F +#define CLOCKREC2 0x20 +#define CLOCKREC1 0x21 +#define PCKTCTRL6 0x2B +#define PCKTCTRL5 0x2C +#define PCKTCTRL4 0x2D +#define PCKTCTRL3 0x2E +#define PCKTCTRL2 0x2F +#define PCKTCTRL1 0x30 +#define PCKTLEN1 0x31 +#define PCKTLEN0 0x32 +#define SYNC3 0x33 +#define SYNC2 0x34 +#define SYNC1 0x35 +#define SYNC0 0x36 +#define QI 0x37 +#define PCKT_PSTMBL 0x38 +#define PROTOCOL2 0x39 +#define PROTOCOL1 0x3A +#define PROTOCOL0 0x3B +#define FIFO_CONFIG3 0x3C +#define FIFO_CONFIG2 0x3D +#define FIFO_CONFIG1 0x3E +#define FIFO_CONFIG0 0x3F +#define PCKT_FLT_OPTIONS 0x40 +#define PCKT_FLT_GOALS4 0x41 +#define PCKT_FLT_GOALS3 0x42 +#define PCKT_FLT_GOALS2 0x43 +#define PCKT_FLT_GOALS1 0x44 +#define PCKT_FLT_GOALS0 0x45 +#define TIMERS5 0x46 +#define TIMERS4 0x47 +#define TIMERS3 0x48 +#define TIMERS2 0x49 +#define TIMERS1 0x4A +#define TIMERS0 0x4B +#define CSMA_CONF3 0x4C +#define CSMA_CONF2 0x4D +#define CSMA_CONF1 0x4E +#define CSMA_CONF0 0x4F +#define IRQ_MASK3 0x50 +#define IRQ_MASK2 0x51 +#define IRQ_MASK1 0x52 +#define IRQ_MASK0 0x53 +#define FAST_RX_TIMER 0x54 +#define PA_POWER8 0x5A +#define PA_POWER7 0x5B +#define PA_POWER6 0x5C +#define PA_POWER5 0x5D +#define PA_POWER4 0x5E +#define PA_POWER3 0x5F +#define PA_POWER2 0x60 +#define PA_POWER1 0x61 +#define PA_POWER0 0x62 +#define PA_CONFIG1 0x63 +#define PA_CONFIG0 0x64 +#define SYNTH_CONFIG2 0x65 +#define VCO_CONFIG 0x68 +#define VCO_CALIBR_IN2 0x69 +#define VCO_CALIBR_IN1 0x6A +#define VCO_CALIBR_IN0 0x6B +#define XO_RCO_CONF1 0x6C +#define XO_RCO_CONF0 0x6D +#define RCO_CALIBR_CONF3 0x6E +#define RCO_CALIBR_CONF2 0x6F +#define PM_CONF4 0x75 +#define PM_CONF3 0x76 +#define PM_CONF2 0x77 +#define PM_CONF1 0x78 +#define PM_CONF0 0x79 +#define MC_STATE1 0x8D +#define MC_STATE0 0x8E +#define TX_FIFO_STATUS 0x8F +#define RX_FIFO_STATUS 0x90 +#define RCO_CALIBR_OUT4 0x94 +#define RCO_CALIBR_OUT3 0x95 +#define VCO_CALIBR_OUT1 0x99 +#define VCO_CALIBR_OUT0 0x9A +#define TX_PCKT_INFO 0x9C +#define RX_PCKT_INFO 0x9D +#define AFC_CORR 0x9E +#define LINK_QUALIF2 0x9F +#define LINK_QUALIF1 0xA0 +#define RSSI_LEVEL 0xA2 +#define RX_PCKT_LEN1 0xA4 +#define RX_PCKT_LEN0 0xA5 +#define CRC_FIELD3 0xA6 +#define CRC_FIELD2 0xA7 +#define CRC_FIELD1 0xA8 +#define CRC_FIELD0 0xA9 +#define RX_ADDRE_FIELD1 0xAA +#define RX_ADDRE_FIELD0 0xAB +#define RSSI_LEVEL_RUN 0xEF +#define DEVICE_INFO1 0xF0 +#define DEVICE_INFO0 0xF1 +#define IRQ_STATUS3 0xFA +#define IRQ_STATUS2 0xFB +#define IRQ_STATUS1 0xFC +#define IRQ_STATUS0 0xFD +#define TX_FIFO 0xFF +#define RX_FIFO 0xFF + +#define SFD0 0x90 +#define SFD1 0x4e + +#define DEFAULT_DEVIATION 125000 +#define RX_FILTER_BANDWIDTH 540000 +#define RSSI_THRESHOLD -85 + +// PCKTCTRL6 +#define PCKT_SYNCLEN_FIELD 0xFC +#define PCKT_SYNCLEN (16 << 2) + +// PCKTCTRL5 +#define PCKT_PREAMBLE_LEN 32 + +// PCKTCTRL3 +#define PCKT_FORMAT_FIELD 0xC0 +#define PCKT_FORMAT_802_15_4 (1 << 6) +#define PCKT_RXMODE_FIELD 0x30 +#define PCKT_RXMODE_NORMAL (0 << 4) +#define PCKT_BYTE_SWAP_FIELD 0x04 +#define PCKT_BYTE_SWAP_LSB (1 << 2) + +// PCKTCTRL2 +#define PCKT_FIXVARLEN_FIELD 0x01 +#define PCKT_VARIABLE_LEN (1 << 0) +#define PCKT_FCS_TYPE_FIELD 0x20 +#define PCKT_FCS_TYPE_4_OCTET (0 << 5) +#define PCKT_FCS_TYPE_2_OCTET (1 << 5) + +// PCKTCTRL1 +#define PCKT_CRCMODE_FIELD 0xE0 +#define PCKT_CRCMODE_0X1021 (3 << 5) +#define PCKT_CRCMODE_0x04C11DB7 (5 << 5) +#define PCKT_TXSOURCE_FIELD 0x0C +#define PCKT_TXSOURCE_NORMAL (0 << 2) +#define PCKT_WHITENING_FIELD 0x10 +#define PCKT_WHITENING_ENABLED (1 << 4) + +// MOD4 +#define DATARATE_M_MSB 0x47 +// MOD3 +#define DATARATE_M_LSB 0xAE + +// MOD2 +#define MOD_TYPE_FIELD 0xF0 +#define MOD_2FSK (0 << 4) +#define MOD_2GFSK (10 << 4) +#define DATARATE_E_FIELD 0x0F +#define DATARATE_E (10 << 0) + +// MOD1 +#define FDEV_E_FIELD 0x0F + +// QI +#define PQI_TH_FIELD 0x1E +#define PQI_TH (8 << 1) +#define SQI_EN_FIELD 0x01 +#define SQI_EN (1 << 0) + +// SYNT3 +#define SYNT_FIELD 0x0F + +// CHFLT +#define CHFLT_M_FIELD 0xF0 +#define CHFLT_E_FIELD 0x0F + +// LINK_QUALIF1 +#define CARRIER_SENSE (1 << 7) + +#define SPI_WR_REG 0x00 +#define SPI_RD_REG 0x01 +#define SPI_CMD 0x80 + +typedef enum { + S2LP_STATE_STANDBY = 0x02, + S2LP_STATE_SLEEPA = 0x01, + S2LP_STATE_SLEEPB = 0x03, + S2LP_STATE_READY = 0x00, + S2LP_STATE_LOCK = 0x0C, + S2LP_STATE_RX = 0x30, + S2LP_STATE_TX = 0x5C, + S2LP_STATE_SYNTH_SETUP = 0x50 +} s2lp_states_e; + +#if defined __cplusplus && __cplusplus >= 201103 +typedef enum : uint8_t { +#else +typedef enum { +#endif + S2LP_CMD_TX = 0x60, + S2LP_CMD_RX, + S2LP_CMD_READY, + S2LP_CMD_STANDBY, + S2LP_CMD_SLEEP, + S2LP_CMD_LOCKRX, + S2LP_CMD_LOCKTX, + S2LP_CMD_SABORT, + S2LP_CMD_LDC_RELOAD, + S2LP_CMD_SRES = 0x70, + S2LP_CMD_FLUSHRXFIFO, + S2LP_CMD_FLUSHTXFIFO, + S2LP_CMD_SEQUPDATE +} s2lp_commands_e; + +typedef enum { + RF_IDLE, + RF_CSMA_STARTED, + RF_TX_STARTED, + RF_RX_STARTED, + RF_TX_ACK +} rf_states_e; + +#ifdef __cplusplus +} +#endif + +#endif /* S2LPREG_H_ */ diff --git a/stm-s2lp-rf-driver/NanostackRfPhys2lp.h b/stm-s2lp-rf-driver/NanostackRfPhys2lp.h new file mode 100644 index 0000000000..e1d6ff54bf --- /dev/null +++ b/stm-s2lp-rf-driver/NanostackRfPhys2lp.h @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2018-2020, Pelion and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NANOSTACK_PHY_S2LP_H_ +#define NANOSTACK_PHY_S2LP_H_ + +#if defined(MBED_CONF_NANOSTACK_CONFIGURATION) && DEVICE_SPI && defined(MBED_CONF_RTOS_PRESENT) +#include "inttypes.h" +#include "NanostackRfPhy.h" +#include "DigitalIn.h" +#include "DigitalOut.h" +#include "InterruptIn.h" +#include "SPI.h" + +#if defined(MBED_CONF_S2LP_SPI_SDI) +#define S2LP_SPI_SDI MBED_CONF_S2LP_SPI_SDI +#else +#define S2LP_SPI_SDI D11 +#endif + +#if defined(MBED_CONF_S2LP_SPI_SDO) +#define S2LP_SPI_SDO MBED_CONF_S2LP_SPI_SDO +#else +#define S2LP_SPI_SDO D12 +#endif + +#if defined(MBED_CONF_S2LP_SPI_SCLK) +#define S2LP_SPI_SCLK MBED_CONF_S2LP_SPI_SCLK +#else +#define S2LP_SPI_SCLK D13 +#endif + +#if defined(MBED_CONF_S2LP_SPI_CS) +#define S2LP_SPI_CS MBED_CONF_S2LP_SPI_CS +#else +#define S2LP_SPI_CS A1 +#endif + +#if defined(MBED_CONF_S2LP_SPI_SDN) +#define S2LP_SPI_SDN MBED_CONF_S2LP_SPI_SDN +#else +#define S2LP_SPI_SDN D7 +#endif + +#if defined(MBED_CONF_S2LP_TEST_PIN_TX) +#define S2LP_TEST_PIN_TX MBED_CONF_S2LP_TEST_PIN_TX +#else +#define S2LP_TEST_PIN_TX D6 +#endif + +#if defined(MBED_CONF_S2LP_TEST_PIN_RX) +#define S2LP_TEST_PIN_RX MBED_CONF_S2LP_TEST_PIN_RX +#else +#define S2LP_TEST_PIN_RX D5 +#endif + +#if defined(MBED_CONF_S2LP_TEST_PIN_CSMA) +#define S2LP_TEST_PIN_CSMA MBED_CONF_S2LP_TEST_PIN_CSMA +#else +#define S2LP_TEST_PIN_CSMA D4 +#endif + +#if defined(MBED_CONF_S2LP_TEST_PIN_SPARE_1) +#define S2LP_TEST_PIN_SPARE_1 MBED_CONF_S2LP_TEST_PIN_SPARE_1 +#else +#define S2LP_TEST_PIN_SPARE_1 D2 +#endif + +#if defined(MBED_CONF_S2LP_TEST_PIN_SPARE_2) +#define S2LP_TEST_PIN_SPARE_2 MBED_CONF_S2LP_TEST_PIN_SPARE_2 +#else +#define S2LP_TEST_PIN_SPARE_2 D8 +#endif + +#if defined(MBED_CONF_S2LP_SPI_GPIO0) +#define S2LP_SPI_GPIO0 MBED_CONF_S2LP_SPI_GPIO0 +#else +#define S2LP_SPI_GPIO0 A0 +#endif + +#if defined(MBED_CONF_S2LP_SPI_GPIO1) +#define S2LP_SPI_GPIO1 MBED_CONF_S2LP_SPI_GPIO1 +#else +#define S2LP_SPI_GPIO1 A2 +#endif + +#if defined(MBED_CONF_S2LP_SPI_GPIO2) +#define S2LP_SPI_GPIO2 MBED_CONF_S2LP_SPI_GPIO2 +#else +#define S2LP_SPI_GPIO2 A3 +#endif + +#if defined(MBED_CONF_S2LP_SPI_GPIO3) +#define S2LP_SPI_GPIO3 MBED_CONF_S2LP_SPI_GPIO3 +#else +#define S2LP_SPI_GPIO3 A5 +#endif + +#if defined(MBED_CONF_S2LP_I2C_SDA) +#define S2LP_I2C_SDA MBED_CONF_S2LP_I2C_SDA +#else +#define S2LP_I2C_SDA null +#endif + +#if defined(MBED_CONF_S2LP_I2C_SCL) +#define S2LP_I2C_SCL MBED_CONF_S2LP_I2C_SCL +#else +#define S2LP_I2C_SCL null +#endif + +#include "at24mac_s2lp.h" + +class RFPins; +class TestPins_S2LP; + +class NanostackRfPhys2lp : public NanostackRfPhy { +public: + NanostackRfPhys2lp(PinName spi_sdi, PinName spi_sdo, PinName spi_sclk, PinName spi_cs, PinName spi_sdn + , PinName spi_gpio0, PinName spi_gpio1, PinName spi_gpio2, PinName spi_gpio3 +#ifdef AT24MAC + , PinName i2c_sda, PinName i2c_scl +#endif //AT24MAC + ); + virtual ~NanostackRfPhys2lp(); + virtual int8_t rf_register(); + virtual void rf_unregister(); + virtual void get_mac_address(uint8_t *mac); + virtual void set_mac_address(uint8_t *mac); + +private: +#ifdef AT24MAC + AT24Mac_s2lp _mac; +#endif //AT24MAC + uint8_t _mac_addr[8]; + RFPins *_rf; + TestPins_S2LP *_test_pins; + bool _mac_set; + + const PinName _spi_sdi; + const PinName _spi_sdo; + const PinName _spi_sclk; + const PinName _spi_cs; + const PinName _spi_sdn; + const PinName _spi_gpio0; + const PinName _spi_gpio1; + const PinName _spi_gpio2; + const PinName _spi_gpio3; +}; +#endif /* MBED_CONF_NANOSTACK_CONFIGURATION && DEVICE_SPI */ +#endif /* NANOSTACK_PHY_S2LP_H_ */