mirror of https://github.com/ARMmbed/mbed-os.git
1403 lines
48 KiB
C++
1403 lines
48 KiB
C++
/*
|
|
* Copyright (c) 2018 ARM Limited. All rights reserved.
|
|
* 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 <string.h>
|
|
#if defined(MBED_CONF_NANOSTACK_CONFIGURATION) && DEVICE_SPI
|
|
#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 <Timer.h>
|
|
|
|
#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 rf->TEST1 = 1;
|
|
#define TEST_TX_DONE rf->TEST1 = 0;
|
|
#define TEST_RX_STARTED rf->TEST2 = 1;
|
|
#define TEST_RX_DONE rf->TEST2 = 0;
|
|
#define TEST_ACK_TX_STARTED rf->TEST3 = 1;
|
|
#define TEST_ACK_TX_DONE rf->TEST3 = 0;
|
|
#define TEST1_ON rf->TEST4 = 1;
|
|
#define TEST1_OFF rf->TEST4 = 0;
|
|
#define TEST2_ON rf->TEST5 = 1;
|
|
#define TEST2_OFF rf->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_ACK_TX_STARTED
|
|
#define TEST_ACK_TX_DONE
|
|
#define TEST1_ON
|
|
#define TEST1_OFF
|
|
#define TEST2_ON
|
|
#define TEST2_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 MAC_FRAME_VERSION_2 (2)
|
|
#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,
|
|
#ifdef TEST_GPIOS_ENABLED
|
|
PinName spi_test1, PinName spi_test2, PinName spi_test3, PinName spi_test4, PinName spi_test5,
|
|
#endif //TEST_GPIOS_ENABLED
|
|
PinName spi_gpio0, PinName spi_gpio1, PinName spi_gpio2,
|
|
PinName spi_gpio3);
|
|
UnlockedSPI spi;
|
|
DigitalOut CS;
|
|
DigitalOut SDN;
|
|
#ifdef TEST_GPIOS_ENABLED
|
|
DigitalOut TEST1;
|
|
DigitalOut TEST2;
|
|
DigitalOut TEST3;
|
|
DigitalOut TEST4;
|
|
DigitalOut TEST5;
|
|
#endif //TEST_GPIOS_ENABLED
|
|
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;
|
|
#ifdef MBED_CONF_RTOS_PRESENT
|
|
Thread irq_thread;
|
|
Mutex mutex;
|
|
void rf_irq_task();
|
|
#endif //MBED_CONF_RTOS_PRESENT
|
|
};
|
|
|
|
RFPins::RFPins(PinName spi_sdi, PinName spi_sdo,
|
|
PinName spi_sclk, PinName spi_cs, PinName spi_sdn,
|
|
#ifdef TEST_GPIOS_ENABLED
|
|
PinName spi_test1, PinName spi_test2, PinName spi_test3, PinName spi_test4, PinName spi_test5,
|
|
#endif //TEST_GPIOS_ENABLED
|
|
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),
|
|
#ifdef TEST_GPIOS_ENABLED
|
|
TEST1(spi_test1),
|
|
TEST2(spi_test2),
|
|
TEST3(spi_test3),
|
|
TEST4(spi_test4),
|
|
TEST5(spi_test5),
|
|
#endif //TEST_GPIOS_ENABLED
|
|
RF_S2LP_GPIO0(spi_gpio0),
|
|
RF_S2LP_GPIO1(spi_gpio1),
|
|
RF_S2LP_GPIO2(spi_gpio2),
|
|
RF_S2LP_GPIO3(spi_gpio3)
|
|
#ifdef MBED_CONF_RTOS_PRESENT
|
|
, irq_thread(osPriorityRealtime, 1024)
|
|
#endif //MBED_CONF_RTOS_PRESENT
|
|
{
|
|
#ifdef MBED_CONF_RTOS_PRESENT
|
|
irq_thread.start(mbed::callback(this, &RFPins::rf_irq_task));
|
|
#endif //MBED_CONF_RTOS_PRESENT
|
|
}
|
|
|
|
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_print_registers(void);
|
|
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 RFPins *rf;
|
|
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 symbols_in_seconds;
|
|
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;
|
|
|
|
/* Channel configurations for sub-GHz */
|
|
static const phy_rf_channel_configuration_s phy_subghz = {868300000U, 500000U, 250000U, 11U, M_UNDEFINED};
|
|
|
|
static const phy_device_channel_page_s phy_channel_pages[] = {
|
|
{ CHANNEL_PAGE_2, &phy_subghz},
|
|
{ CHANNEL_PAGE_0, NULL}
|
|
};
|
|
|
|
#ifdef MBED_CONF_RTOS_PRESENT
|
|
#include "mbed.h"
|
|
#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
|
|
|
|
#endif //MBED_CONF_RTOS_PRESENT
|
|
|
|
#define ACK_FRAME_LENGTH 3
|
|
// Give some additional time for processing, PHY headers, CRC etc.
|
|
#define PACKET_SENDING_EXTRA_TIME 10000
|
|
#define MAX_PACKET_SENDING_TIME (uint32_t)(8000000/phy_subghz.datarate)*RF_MTU + 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
|
|
void test1_toggle(void)
|
|
{
|
|
if (rf->TEST4) {
|
|
rf->TEST4 = 0;
|
|
} else {
|
|
rf->TEST4 = 1;
|
|
}
|
|
}
|
|
void test2_toggle(void)
|
|
{
|
|
if (rf->TEST5) {
|
|
rf->TEST5 = 0;
|
|
} else {
|
|
rf->TEST5 = 1;
|
|
}
|
|
}
|
|
#endif //TEST_GPIOS_ENABLED
|
|
|
|
static void rf_calculate_symbols_in_seconds(uint32_t baudrate, phy_modulation_e modulation)
|
|
{
|
|
(void) modulation;
|
|
uint8_t bits_in_symbols = 1;
|
|
symbols_in_seconds = baudrate / bits_in_symbols;
|
|
}
|
|
|
|
static uint32_t rf_get_timestamp(void)
|
|
{
|
|
return (uint32_t)rf->tx_timer.read_us();
|
|
}
|
|
|
|
static void rf_lock(void)
|
|
{
|
|
platform_enter_critical();
|
|
}
|
|
|
|
static void rf_unlock(void)
|
|
{
|
|
platform_exit_critical();
|
|
}
|
|
|
|
#ifdef MBED_CONF_RTOS_PRESENT
|
|
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);
|
|
}
|
|
#endif //MBED_CONF_RTOS_PRESENT
|
|
|
|
/*
|
|
* \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<const char *>(tx), tx_len, static_cast<char *>(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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
rf_write_register(IRQ_MASK0, 0);
|
|
rf_write_register(IRQ_MASK1, 0);
|
|
rf_write_register(IRQ_MASK2, 0);
|
|
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 max_write)
|
|
{
|
|
const uint8_t spi_header[SPI_HEADER_LENGTH] = {SPI_WR_REG, TX_FIFO};
|
|
uint8_t written_length = length;
|
|
if (length > max_write) {
|
|
written_length = max_write;
|
|
}
|
|
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_write_packet_length(uint16_t packet_length)
|
|
{
|
|
rf_write_register(PCKTLEN1, packet_length / 256);
|
|
rf_write_register(PCKTLEN0, packet_length % 256);
|
|
}
|
|
|
|
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_init_registers(void)
|
|
{
|
|
rf_write_register_field(PCKTCTRL3, PCKT_FORMAT_FIELD, PCKT_FORMAT_802_15_4);
|
|
// Set 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);
|
|
rf_write_register_field(MOD2, MOD_TYPE_FIELD, MOD_2FSK);
|
|
// 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
|
|
uint8_t chflt_m, chflt_e;
|
|
rf_conf_calculate_rx_filter_bandwidth_registers(RX_FILTER_BANDWIDTH, &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);
|
|
rf_write_register(PCKT_FLT_OPTIONS, 0);
|
|
// 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);
|
|
rf_write_register_field(PCKTCTRL1, PCKT_CRCMODE_FIELD, PCKT_CRCMODE_0X1021);
|
|
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(PCKTCTRL3, PCKT_RXMODE_FIELD, PCKT_RXMODE_NORMAL);
|
|
rf_write_register(PCKTCTRL5, PCKT_PREAMBLE_LEN);
|
|
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);
|
|
// Set RSSI threshold
|
|
uint8_t rssi_th;
|
|
rf_conf_calculate_rssi_threshold_registers(RSSI_THRESHOLD, &rssi_th);
|
|
rf_write_register(RSSI_TH, rssi_th);
|
|
}
|
|
|
|
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;
|
|
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 = symbols_in_seconds;
|
|
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:
|
|
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)
|
|
{
|
|
rf_backup_timer_stop();
|
|
rf_disable_interrupt(TX_DATA_SENT);
|
|
if (rf_state != RF_TX_ACK) {
|
|
tx_finnish_time = rf_get_timestamp();
|
|
TEST_TX_DONE
|
|
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 {
|
|
TEST_ACK_TX_DONE
|
|
rf_receive(rf_rx_channel);
|
|
}
|
|
}
|
|
|
|
static void rf_tx_threshold_handler(void)
|
|
{
|
|
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, FIFO_SIZE / 2);
|
|
if (written_length < tx_data_length) {
|
|
tx_data_ptr += written_length;
|
|
tx_data_length -= written_length;
|
|
rf_enable_interrupt(TX_FIFO_ALMOST_EMPTY);
|
|
}
|
|
}
|
|
|
|
static void rf_start_tx(void)
|
|
{
|
|
rf_disable_all_interrupts();
|
|
// 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_state_change(S2LP_STATE_READY, false);
|
|
rf_state_change(S2LP_STATE_LOCK, true);
|
|
rf_state_change(S2LP_STATE_TX, false);
|
|
rf_backup_timer_start(MAX_PACKET_SENDING_TIME);
|
|
}
|
|
|
|
static void rf_cca_timer_interrupt(void)
|
|
{
|
|
if (device_driver.phy_tx_done_cb(rf_radio_driver_id, mac_tx_handle, PHY_LINK_CCA_PREPARE, 0, 0) != 0) {
|
|
if (rf_read_register(TX_FIFO_STATUS)) {
|
|
rf_send_command(S2LP_CMD_FLUSHTXFIFO);
|
|
}
|
|
rf_state = RF_IDLE;
|
|
return;
|
|
}
|
|
if ((cca_enabled == true) && (rf_read_register(LINK_QUALIF1) & CARRIER_SENSE || (rf_state != RF_CSMA_STARTED && rf_state != RF_IDLE))) {
|
|
if (rf_state == RF_CSMA_STARTED) {
|
|
rf_state = RF_IDLE;
|
|
}
|
|
if (rf_read_register(TX_FIFO_STATUS)) {
|
|
rf_send_command(S2LP_CMD_FLUSHTXFIFO);
|
|
}
|
|
tx_finnish_time = rf_get_timestamp();
|
|
if (device_driver.phy_tx_done_cb) {
|
|
device_driver.phy_tx_done_cb(rf_radio_driver_id, mac_tx_handle, PHY_LINK_CCA_FAIL, 0, 0);
|
|
}
|
|
} else {
|
|
rf_start_tx();
|
|
rf_state = RF_TX_STARTED;
|
|
TEST_TX_STARTED
|
|
}
|
|
}
|
|
|
|
static void rf_cca_timer_stop(void)
|
|
{
|
|
rf->cca_timer.detach();
|
|
}
|
|
|
|
static void rf_cca_timer_start(uint32_t slots)
|
|
{
|
|
#ifdef MBED_CONF_RTOS_PRESENT
|
|
rf->cca_timer.attach_us(rf_cca_timer_signal, slots);
|
|
#else //MBED_CONF_RTOS_PRESENT
|
|
rf->cca_timer.attach_us(rf_cca_timer_interrupt, slots);
|
|
#endif //MBED_CONF_RTOS_PRESENT
|
|
}
|
|
|
|
static void rf_backup_timer_interrupt(void)
|
|
{
|
|
tx_finnish_time = rf_get_timestamp();
|
|
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);
|
|
}
|
|
}
|
|
if (rf_read_register(TX_FIFO_STATUS)) {
|
|
rf_send_command(S2LP_CMD_FLUSHTXFIFO);
|
|
}
|
|
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 MBED_CONF_RTOS_PRESENT
|
|
rf->backup_timer.attach_us(rf_backup_timer_signal, slots);
|
|
#else //MBED_CONF_RTOS_PRESENT
|
|
rf->backup_timer.attach_us(rf_backup_timer_interrupt, slots);
|
|
#endif //MBED_CONF_RTOS_PRESENT
|
|
}
|
|
|
|
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, FIFO_SIZE);
|
|
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;
|
|
}
|
|
}
|
|
rf_cca_timer_interrupt();
|
|
rf_unlock();
|
|
return 0;
|
|
}
|
|
|
|
static void rf_send_ack(uint8_t seq)
|
|
{
|
|
// If the reception started during CCA process, the TX FIFO may already contain a data packet
|
|
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);
|
|
}
|
|
rf_state = RF_TX_ACK;
|
|
uint8_t ack_frame[3] = {MAC_TYPE_ACK, 0, seq};
|
|
rf_write_tx_fifo(ack_frame, sizeof(ack_frame), FIFO_SIZE);
|
|
rf_write_packet_length(sizeof(ack_frame) + 4);
|
|
tx_data_ptr = NULL;
|
|
rf_start_tx();
|
|
TEST_ACK_TX_STARTED
|
|
rf_backup_timer_start(ACK_SENDING_TIME);
|
|
}
|
|
|
|
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
|
|
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) {
|
|
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)) {
|
|
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);
|
|
}
|
|
// Check Ack request
|
|
if ((version != MAC_FRAME_VERSION_2) && (rx_buffer[0] & FC_AR)) {
|
|
rf_send_ack(rx_buffer[2]);
|
|
}
|
|
}
|
|
} 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)
|
|
{
|
|
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;
|
|
}
|
|
|
|
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_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_disable_all_interrupts();
|
|
rf_state_change(S2LP_STATE_READY, false);
|
|
rf_send_command(S2LP_CMD_FLUSHRXFIFO);
|
|
rf_poll_state_change(S2LP_STATE_READY);
|
|
if (rx_channel != rf_rx_channel) {
|
|
rf_write_register(CHNUM, rx_channel * rf_channel_multiplier);
|
|
rf_rx_channel = rf_new_channel = rx_channel;
|
|
}
|
|
rf_state_change(S2LP_STATE_LOCK, false);
|
|
rf_state_change(S2LP_STATE_RX, false);
|
|
rf_enable_interrupt(SYNC_WORD);
|
|
rf_enable_interrupt(RX_FIFO_ALMOST_FULL);
|
|
rf_enable_interrupt(RX_DATA_READY);
|
|
rf_enable_interrupt(RX_FIFO_UNF_OVF);
|
|
rx_data_length = 0;
|
|
rf_state = RF_IDLE;
|
|
rf_unlock();
|
|
}
|
|
|
|
#ifdef MBED_CONF_RTOS_PRESENT
|
|
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)
|
|
#else //MBED_CONF_RTOS_PRESENT
|
|
static void rf_interrupt_handler(void)
|
|
#endif //MBED_CONF_RTOS_PRESENT
|
|
{
|
|
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))) {
|
|
tx_finnish_time = rf_get_timestamp();
|
|
TEST_TX_DONE
|
|
device_driver.phy_tx_done_cb(rf_radio_driver_id, mac_tx_handle, PHY_LINK_TX_FAIL, 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_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))) {
|
|
if (!(irq_status & (1 << CRC_ERROR))) {
|
|
rf_rx_ready_handler();
|
|
} else {
|
|
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 ((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))) {
|
|
TEST_RX_DONE
|
|
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_state = RF_IDLE;
|
|
rf_receive(rf_rx_channel);
|
|
}
|
|
rf_unlock();
|
|
}
|
|
|
|
static void rf_reset(void)
|
|
{
|
|
// Shutdown
|
|
rf->SDN = 1;
|
|
wait_ms(10);
|
|
// Wake up
|
|
rf->SDN = 0;
|
|
// Wait until GPIO0 (RESETN) goes high
|
|
while (rf->RF_S2LP_GPIO0 == 0);
|
|
}
|
|
|
|
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();
|
|
if (PARTNUM != rf_read_register(DEVICE_INFO1)) {
|
|
tr_err("Invalid part number: %x", rf_read_register(DEVICE_INFO1));
|
|
}
|
|
if (VERSION != rf_read_register(DEVICE_INFO0)) {
|
|
tr_err("Invalid version: %x", rf_read_register(DEVICE_INFO0));
|
|
}
|
|
rf_init_registers();
|
|
rf_enable_gpio_interrupt();
|
|
rf_calculate_symbols_in_seconds(phy_subghz.datarate, phy_subghz.modulation);
|
|
rf->tx_timer.start();
|
|
rf_print_registers();
|
|
}
|
|
|
|
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)
|
|
{
|
|
|
|
}
|
|
|
|
void NanostackRfPhys2lp::set_mac_address(uint8_t *mac)
|
|
{
|
|
|
|
}
|
|
|
|
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;
|
|
}
|
|
rf = _rf;
|
|
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,
|
|
#ifdef TEST_GPIOS_ENABLED
|
|
PinName spi_test1, PinName spi_test2, PinName spi_test3, PinName spi_test4, PinName spi_test5,
|
|
#endif //TEST_GPIOS_ENABLED
|
|
PinName spi_gpio0, PinName spi_gpio1, PinName spi_gpio2, PinName spi_gpio3)
|
|
: _mac_addr(), _rf(NULL), _mac_set(false),
|
|
_spi_sdi(spi_sdi), _spi_sdo(spi_sdo), _spi_sclk(spi_sclk), _spi_cs(spi_cs), _spi_sdn(spi_sdn),
|
|
#ifdef TEST_GPIOS_ENABLED
|
|
_spi_test1(spi_test1), _spi_test2(spi_test2), _spi_test3(spi_test3), _spi_test4(spi_test4), _spi_test5(spi_test5),
|
|
#endif //TEST_GPIOS_ENABLED
|
|
_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,
|
|
#ifdef TEST_GPIOS_ENABLED
|
|
_spi_test1, _spi_test2, _spi_test3, _spi_test4, _spi_test5,
|
|
#endif //TEST_GPIOS_ENABLED
|
|
_spi_gpio0, _spi_gpio1, _spi_gpio2, _spi_gpio3);
|
|
}
|
|
|
|
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_v2_filter(uint8_t *ptr, uint8_t *pan_id, uint8_t dst_mode, uint8_t src_mode, uint8_t seq_compressed, uint8_t panid_compressed, uint8_t frame_type)
|
|
{
|
|
if ((dst_mode == FC_DST_ADDR_NONE) && (frame_type == MAC_TYPE_DATA || frame_type == MAC_TYPE_COMMAND)) {
|
|
return true;
|
|
}
|
|
if ((dst_mode == FC_DST_64_BITS) && (src_mode == FC_SRC_64_BITS) && panid_compressed) {
|
|
return true;
|
|
}
|
|
if (seq_compressed) {
|
|
ptr--;
|
|
}
|
|
return rf_panid_filter_common(ptr, pan_id, frame_type);
|
|
}
|
|
|
|
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_v2_filter(uint8_t *ptr, uint8_t *mac_64bit_addr, uint8_t *mac_16bit_addr, uint8_t dst_mode, uint8_t seq_compressed, uint8_t panid_compressed)
|
|
{
|
|
if (seq_compressed) {
|
|
ptr--;
|
|
}
|
|
if (panid_compressed) {
|
|
ptr -= 2;
|
|
}
|
|
return rf_addr_filter_common(ptr, dst_mode, mac_64bit_addr, mac_16bit_addr);
|
|
}
|
|
|
|
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 src_mode = (mac_header[1] & FC_SRC_MODE);
|
|
uint8_t seq_compressed = ((mac_header[1] & FC_SEQUENCE_COMPRESSION) >> SHIFT_SEQ_COMP_FIELD);
|
|
uint8_t panid_compressed = ((mac_header[0] & FC_PAN_ID_COMPRESSION) >> SHIFT_PANID_COMP_FIELD);
|
|
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_v2_filter(mac_header + OFFSET_DST_PAN_ID, pan_id, dst_mode, src_mode, seq_compressed, panid_compressed, frame_type)) {
|
|
return false;
|
|
}
|
|
if (!rf_addr_v2_filter(mac_header + OFFSET_DST_ADDR, mac_64bit_addr, mac_16bit_addr, dst_mode, seq_compressed, panid_compressed)) {
|
|
return false;
|
|
}
|
|
} else {
|
|
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;
|
|
}
|
|
|
|
static void rf_print_registers(void)
|
|
{
|
|
tr_debug("GPIO0_CONF: %x", rf_read_register(GPIO0_CONF));
|
|
tr_debug("GPIO1_CONF: %x", rf_read_register(GPIO1_CONF));
|
|
tr_debug("GPIO2_CONF: %x", rf_read_register(GPIO2_CONF));
|
|
tr_debug("GPIO3_CONF: %x", rf_read_register(GPIO3_CONF));
|
|
tr_debug("SYNT3: %x", rf_read_register(SYNT3));
|
|
tr_debug("SYNT2: %x", rf_read_register(SYNT2));
|
|
tr_debug("SYNT1: %x", rf_read_register(SYNT1));
|
|
tr_debug("SYNT0: %x", rf_read_register(SYNT0));
|
|
tr_debug("IF_OFFSET_ANA: %x", rf_read_register(IF_OFFSET_ANA));
|
|
tr_debug("IF_OFFSET_DIG: %x", rf_read_register(IF_OFFSET_DIG));
|
|
tr_debug("CHSPACE: %x", rf_read_register(CHSPACE));
|
|
tr_debug("CHNUM: %x", rf_read_register(CHNUM));
|
|
tr_debug("MOD4: %x", rf_read_register(MOD4));
|
|
tr_debug("MOD3: %x", rf_read_register(MOD3));
|
|
tr_debug("MOD2: %x", rf_read_register(MOD2));
|
|
tr_debug("MOD1: %x", rf_read_register(MOD1));
|
|
tr_debug("MOD0: %x", rf_read_register(MOD0));
|
|
tr_debug("CHFLT: %x", rf_read_register(CHFLT));
|
|
tr_debug("AFC2: %x", rf_read_register(AFC2));
|
|
tr_debug("AFC1: %x", rf_read_register(AFC1));
|
|
tr_debug("AFC0: %x", rf_read_register(AFC0));
|
|
tr_debug("RSSI_FLT: %x", rf_read_register(RSSI_FLT));
|
|
tr_debug("RSSI_TH: %x", rf_read_register(RSSI_TH));
|
|
tr_debug("AGCCTRL4: %x", rf_read_register(AGCCTRL4));
|
|
tr_debug("AGCCTRL3: %x", rf_read_register(AGCCTRL3));
|
|
tr_debug("AGCCTRL2: %x", rf_read_register(AGCCTRL2));
|
|
tr_debug("AGCCTRL1: %x", rf_read_register(AGCCTRL1));
|
|
tr_debug("AGCCTRL0: %x", rf_read_register(AGCCTRL0));
|
|
tr_debug("ANT_SELECT_CONF: %x", rf_read_register(ANT_SELECT_CONF));
|
|
tr_debug("CLOCKREC2: %x", rf_read_register(CLOCKREC2));
|
|
tr_debug("CLOCKREC1: %x", rf_read_register(CLOCKREC1));
|
|
tr_debug("PCKTCTRL6: %x", rf_read_register(PCKTCTRL6));
|
|
tr_debug("PCKTCTRL5: %x", rf_read_register(PCKTCTRL5));
|
|
tr_debug("PCKTCTRL4: %x", rf_read_register(PCKTCTRL4));
|
|
tr_debug("PCKTCTRL3: %x", rf_read_register(PCKTCTRL3));
|
|
tr_debug("PCKTCTRL2: %x", rf_read_register(PCKTCTRL2));
|
|
tr_debug("PCKTCTRL1: %x", rf_read_register(PCKTCTRL1));
|
|
tr_debug("PCKTLEN1: %x", rf_read_register(PCKTLEN1));
|
|
tr_debug("PCKTLEN0: %x", rf_read_register(PCKTLEN0));
|
|
tr_debug("SYNC3: %x", rf_read_register(SYNC3));
|
|
tr_debug("SYNC2: %x", rf_read_register(SYNC2));
|
|
tr_debug("SYNC1: %x", rf_read_register(SYNC1));
|
|
tr_debug("SYNC0: %x", rf_read_register(SYNC0));
|
|
tr_debug("QI: %x", rf_read_register(QI));
|
|
tr_debug("PCKT_PSTMBL: %x", rf_read_register(PCKT_PSTMBL));
|
|
tr_debug("PROTOCOL2: %x", rf_read_register(PROTOCOL2));
|
|
tr_debug("PROTOCOL1: %x", rf_read_register(PROTOCOL1));
|
|
tr_debug("PROTOCOL0: %x", rf_read_register(PROTOCOL0));
|
|
tr_debug("FIFO_CONFIG3: %x", rf_read_register(FIFO_CONFIG3));
|
|
tr_debug("FIFO_CONFIG2: %x", rf_read_register(FIFO_CONFIG2));
|
|
tr_debug("FIFO_CONFIG1: %x", rf_read_register(FIFO_CONFIG1));
|
|
tr_debug("FIFO_CONFIG0: %x", rf_read_register(FIFO_CONFIG0));
|
|
tr_debug("PCKT_FLT_OPTIONS: %x", rf_read_register(PCKT_FLT_OPTIONS));
|
|
tr_debug("PCKT_FLT_GOALS4: %x", rf_read_register(PCKT_FLT_GOALS4));
|
|
tr_debug("PCKT_FLT_GOALS3: %x", rf_read_register(PCKT_FLT_GOALS3));
|
|
tr_debug("PCKT_FLT_GOALS2: %x", rf_read_register(PCKT_FLT_GOALS2));
|
|
tr_debug("PCKT_FLT_GOALS1: %x", rf_read_register(PCKT_FLT_GOALS1));
|
|
tr_debug("PCKT_FLT_GOALS0: %x", rf_read_register(PCKT_FLT_GOALS0));
|
|
tr_debug("TIMERS5: %x", rf_read_register(TIMERS5));
|
|
tr_debug("TIMERS4: %x", rf_read_register(TIMERS4));
|
|
tr_debug("TIMERS3: %x", rf_read_register(TIMERS3));
|
|
tr_debug("TIMERS2: %x", rf_read_register(TIMERS2));
|
|
tr_debug("TIMERS1: %x", rf_read_register(TIMERS1));
|
|
tr_debug("TIMERS0: %x", rf_read_register(TIMERS0));
|
|
tr_debug("CSMA_CONF3: %x", rf_read_register(CSMA_CONF3));
|
|
tr_debug("CSMA_CONF2: %x", rf_read_register(CSMA_CONF2));
|
|
tr_debug("CSMA_CONF1: %x", rf_read_register(CSMA_CONF1));
|
|
tr_debug("CSMA_CONF0: %x", rf_read_register(CSMA_CONF0));
|
|
tr_debug("IRQ_MASK3: %x", rf_read_register(IRQ_MASK3));
|
|
tr_debug("IRQ_MASK2: %x", rf_read_register(IRQ_MASK2));
|
|
tr_debug("IRQ_MASK1: %x", rf_read_register(IRQ_MASK1));
|
|
tr_debug("IRQ_MASK0: %x", rf_read_register(IRQ_MASK0));
|
|
tr_debug("FAST_RX_TIMER: %x", rf_read_register(FAST_RX_TIMER));
|
|
tr_debug("PA_POWER8: %x", rf_read_register(PA_POWER8));
|
|
tr_debug("PA_POWER7: %x", rf_read_register(PA_POWER7));
|
|
tr_debug("PA_POWER6: %x", rf_read_register(PA_POWER6));
|
|
tr_debug("PA_POWER5: %x", rf_read_register(PA_POWER5));
|
|
tr_debug("PA_POWER4: %x", rf_read_register(PA_POWER4));
|
|
tr_debug("PA_POWER3: %x", rf_read_register(PA_POWER3));
|
|
tr_debug("PA_POWER2: %x", rf_read_register(PA_POWER2));
|
|
tr_debug("PA_POWER1: %x", rf_read_register(PA_POWER1));
|
|
tr_debug("PA_POWER0: %x", rf_read_register(PA_POWER0));
|
|
tr_debug("PA_CONFIG1: %x", rf_read_register(PA_CONFIG1));
|
|
tr_debug("PA_CONFIG0: %x", rf_read_register(PA_CONFIG0));
|
|
tr_debug("SYNTH_CONFIG2: %x", rf_read_register(SYNTH_CONFIG2));
|
|
tr_debug("VCO_CONFIG: %x", rf_read_register(VCO_CONFIG));
|
|
tr_debug("VCO_CALIBR_IN2: %x", rf_read_register(VCO_CALIBR_IN2));
|
|
tr_debug("VCO_CALIBR_IN1: %x", rf_read_register(VCO_CALIBR_IN1));
|
|
tr_debug("VCO_CALIBR_IN0: %x", rf_read_register(VCO_CALIBR_IN0));
|
|
tr_debug("XO_RCO_CONF1: %x", rf_read_register(XO_RCO_CONF1));
|
|
tr_debug("XO_RCO_CONF0: %x", rf_read_register(XO_RCO_CONF0));
|
|
tr_debug("RCO_CALIBR_CONF3: %x", rf_read_register(RCO_CALIBR_CONF3));
|
|
tr_debug("RCO_CALIBR_CONF2: %x", rf_read_register(RCO_CALIBR_CONF2));
|
|
tr_debug("PM_CONF4: %x", rf_read_register(PM_CONF4));
|
|
tr_debug("PM_CONF3: %x", rf_read_register(PM_CONF3));
|
|
tr_debug("PM_CONF2: %x", rf_read_register(PM_CONF2));
|
|
tr_debug("PM_CONF1: %x", rf_read_register(PM_CONF1));
|
|
tr_debug("PM_CONF0: %x", rf_read_register(PM_CONF0));
|
|
tr_debug("MC_STATE1: %x", rf_read_register(MC_STATE1));
|
|
tr_debug("MC_STATE0: %x", rf_read_register(MC_STATE0));
|
|
tr_debug("TX_FIFO_STATUS: %x", rf_read_register(TX_FIFO_STATUS));
|
|
tr_debug("RX_FIFO_STATUS: %x", rf_read_register(RX_FIFO_STATUS));
|
|
tr_debug("RCO_CALIBR_OUT4: %x", rf_read_register(RCO_CALIBR_OUT4));
|
|
tr_debug("RCO_CALIBR_OUT3: %x", rf_read_register(RCO_CALIBR_OUT3));
|
|
tr_debug("VCO_CALIBR_OUT1: %x", rf_read_register(VCO_CALIBR_OUT1));
|
|
tr_debug("VCO_CALIBR_OUT0: %x", rf_read_register(VCO_CALIBR_OUT0));
|
|
tr_debug("TX_PCKT_INFO: %x", rf_read_register(TX_PCKT_INFO));
|
|
tr_debug("RX_PCKT_INFO: %x", rf_read_register(RX_PCKT_INFO));
|
|
tr_debug("AFC_CORR: %x", rf_read_register(AFC_CORR));
|
|
tr_debug("LINK_QUALIF2: %x", rf_read_register(LINK_QUALIF2));
|
|
tr_debug("LINK_QUALIF1: %x", rf_read_register(LINK_QUALIF1));
|
|
tr_debug("RSSI_LEVEL: %x", rf_read_register(RSSI_LEVEL));
|
|
tr_debug("RX_PCKT_LEN1: %x", rf_read_register(RX_PCKT_LEN1));
|
|
tr_debug("RX_PCKT_LEN0: %x", rf_read_register(RX_PCKT_LEN0));
|
|
tr_debug("CRC_FIELD3: %x", rf_read_register(CRC_FIELD3));
|
|
tr_debug("CRC_FIELD2: %x", rf_read_register(CRC_FIELD2));
|
|
tr_debug("CRC_FIELD1: %x", rf_read_register(CRC_FIELD1));
|
|
tr_debug("CRC_FIELD0: %x", rf_read_register(CRC_FIELD0));
|
|
tr_debug("RX_ADDRE_FIELD1: %x", rf_read_register(RX_ADDRE_FIELD1));
|
|
tr_debug("RX_ADDRE_FIELD0: %x", rf_read_register(RX_ADDRE_FIELD0));
|
|
tr_debug("RSSI_LEVEL_RUN: %x", rf_read_register(RSSI_LEVEL_RUN));
|
|
tr_debug("DEVICE_INFO1: %x", rf_read_register(DEVICE_INFO1));
|
|
tr_debug("DEVICE_INFO0: %x", rf_read_register(DEVICE_INFO0));
|
|
tr_debug("IRQ_STATUS3: %x", rf_read_register(IRQ_STATUS3));
|
|
tr_debug("IRQ_STATUS2: %x", rf_read_register(IRQ_STATUS2));
|
|
tr_debug("IRQ_STATUS1: %x", rf_read_register(IRQ_STATUS1));
|
|
tr_debug("IRQ_STATUS0: %x", rf_read_register(IRQ_STATUS0));
|
|
}
|
|
|
|
#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,
|
|
#ifdef TEST_GPIOS_ENABLED
|
|
S2LP_SPI_TEST1, S2LP_SPI_TEST2, S2LP_SPI_TEST3, S2LP_SPI_TEST4, S2LP_SPI_TEST5,
|
|
#endif //TEST_GPIOS_ENABLED
|
|
S2LP_SPI_GPIO0, S2LP_SPI_GPIO1, S2LP_SPI_GPIO2, S2LP_SPI_GPIO3);
|
|
return rf_phy;
|
|
}
|
|
#endif // MBED_CONF_S2LP_PROVIDE_DEFAULT
|
|
#endif // MBED_CONF_NANOSTACK_CONFIGURATION && DEVICE_SPI
|