drivers/wireless/lpwan/sx126x/sx126x.c (880 lines of code) (raw):
/****************************************************************************
* drivers/wireless/lpwan/sx126x/sx126x.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you 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.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include "sx126x.h"
#include <nuttx/arch.h>
#include <nuttx/config.h>
#include <debug.h>
#include <errno.h>
#include <nuttx/mutex.h>
#include <nuttx/semaphore.h>
#include <nuttx/spi/spi.h>
#include <nuttx/wireless/ioctl.h>
#include <sched.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/endian.h>
#include <unistd.h>
#include <nuttx/wireless/lpwan/sx126x.h>
#include <nuttx/wqueue.h>
/****************************************************************************
* Private prototypes for file operations
****************************************************************************/
static int sx126x_open(FAR struct file *filep);
static int sx126x_close(FAR struct file *filep);
static ssize_t sx126x_read(FAR struct file *filep,
FAR char *buffer,
size_t buflen);
static ssize_t sx126x_write(FAR struct file *filep,
FAR const char *buf,
size_t buflen);
static int sx126x_ioctl(FAR struct file *filep,
int cmd,
unsigned long arg);
/****************************************************************************
* Private data types
****************************************************************************/
struct sx126x_dev_s
{
struct spi_dev_s *spi;
const struct sx126x_lower_s *lower;
uint8_t times_opened;
mutex_t lock; /* Only let one user in at a time */
sem_t rx_sem;
sem_t tx_sem;
uint16_t irqbits;
/* Hardware settings */
bool invert_iq;
/* Common settings */
uint8_t payload_len;
enum sx126x_packet_type_e packet_type; /* This will decide what modulation to use */
uint32_t frequency_hz;
uint8_t power;
uint16_t preambles;
/* LoRa settings */
enum sx126x_lora_sf_e lora_sf;
enum sx126x_lora_bw_e lora_bw;
enum sx126x_lora_cr_e lora_cr;
bool lora_crc;
bool lora_fixed_header;
bool low_datarate_optimization;
uint8_t syncword[SX126X_REG_SYNCWORD_LEN];
/* Interrupt handling */
uint16_t irq_mask;
struct work_s irq0_work;
};
enum sx126x_cmd_status
{
SX126X_STATUS_RESERVED, SX126X_STATUS_RFU, SX126X_STATUS_DATA_AVAILABLE,
SX126X_STATUS_TIMEOUT, SX126X_STATUS_ERROR, SX126X_STATUS_EXECUTE_FAIL,
SX126X_STATUS_TX_DONE
};
enum sx126x_chip_mode
{
SX126X_MODE_UNUSED, SX126X_MODE_RFU, SX126X_MODE_STBY_RC,
SX126X_MODE_STBY_XOSC, SX126X_MODE_FS, SX126X_MODE_RX, SX126X_MODE_TX
};
struct sx126x_status_s
{
enum sx126x_cmd_status cmd;
enum sx126x_chip_mode mode;
};
static const struct file_operations sx126x_ops =
{
sx126x_open,
sx126x_close,
sx126x_read,
sx126x_write,
NULL,
sx126x_ioctl,
NULL,
NULL
};
/****************************************************************************
* Globals
****************************************************************************/
FAR struct sx126x_dev_s g_sx126x_devices[SX126X_MAX_DEVICES];
/****************************************************************************
* Private prototypes
****************************************************************************/
/* SPI and control **********************************************************/
static void sx126x_command(FAR struct sx126x_dev_s *dev,
uint8_t cmd,
FAR const uint8_t *params,
size_t paramslen,
FAR uint8_t *returns);
static void sx126x_reset(FAR struct sx126x_dev_s *dev);
static void sx126x_get_status(FAR struct sx126x_dev_s *dev,
FAR struct sx126x_status_s *status);
static void sx126x_spi_lock(FAR struct sx126x_dev_s *dev);
static void sx126x_spi_unlock(FAR struct sx126x_dev_s *dev);
static void sx126x_write_register(FAR struct sx126x_dev_s *dev,
uint16_t address,
uint8_t *data,
size_t data_length);
/* Operational modes functions **********************************************/
static void sx126x_set_standby(FAR struct sx126x_dev_s *dev,
enum sx126x_standby_mode_e mode);
static void sx126x_set_tx(FAR struct sx126x_dev_s *dev,
uint32_t timeout);
static void sx126x_set_rx(FAR struct sx126x_dev_s *dev, uint32_t timeout);
static void sx126x_set_cad(struct sx126x_dev_s *dev);
static void sx126x_set_tx_continuous_wave(FAR struct sx126x_dev_s *dev);
static void sx126x_set_regulator_mode(FAR struct sx126x_dev_s *dev,
enum sx126x_regulator_mode_e mode);
static void sx126x_set_pa_config(FAR struct sx126x_dev_s *dev,
enum sx126x_device_e model,
uint8_t hpmax,
uint8_t padutycycle);
static void sx126x_set_tx_infinite_preamble(FAR struct sx126x_dev_s *dev);
/* DIO and IRQ control functions ********************************************/
static void sx126x_set_dio_irq_params(FAR struct sx126x_dev_s *dev,
uint16_t irq_mask,
uint16_t dio1_mask,
uint16_t dio2_mask,
uint16_t dio3_mask);
static void sx126x_set_dio2_as_rf_switch(FAR struct sx126x_dev_s *dev,
bool enable);
static void sx126x_set_dio3_as_tcxo(FAR struct sx126x_dev_s *dev,
enum sx126x_tcxo_voltage_e voltage,
uint32_t delay);
static void sx126x_get_irq_status(FAR struct sx126x_dev_s *dev,
FAR uint16_t *irqstatus);
static void sx126x_clear_irq_status(FAR struct sx126x_dev_s *dev,
uint16_t clearbits);
/* RF Modulation and Packet-Related Functions *******************************/
static void sx126x_set_packet_params_lora(FAR struct sx126x_dev_s *dev,
FAR struct
sx126x_packetparams_lora_s *
pktparams);
static void sx126x_set_modulation_params_lora(FAR struct sx126x_dev_s *dev,
FAR struct
sx126x_modparams_lora_s *
modparams);
static void sx126x_set_buffer_base_address(FAR struct sx126x_dev_s *dev,
uint8_t tx,
uint8_t rx);
static void sx126x_set_tx_params(FAR struct sx126x_dev_s *dev, uint8_t power,
enum sx126x_ramp_time_e ramp_time);
static void sx126x_set_packet_type(FAR struct sx126x_dev_s *dev,
enum sx126x_packet_type_e type);
static void sx126x_set_rf_frequency(FAR struct sx126x_dev_s *dev,
uint32_t frequency_hz);
/* Communication status information *****************************************/
static void sx126x_get_rssi_inst(FAR struct sx126x_dev_s *dev,
FAR int32_t *dbm);
static void sx126x_get_rx_buffer_status(FAR struct sx126x_dev_s *dev,
uint8_t *status,
uint8_t *payload_len,
uint8_t *rx_buff_offset);
/* Registers and buffer *****************************************************/
static void sx126x_write_register(FAR struct sx126x_dev_s *dev,
uint16_t address,
uint8_t *data,
size_t data_length);
static void sx126x_write_buffer(FAR struct sx126x_dev_s *dev,
uint8_t offset,
FAR const uint8_t *payload,
uint8_t len);
static void sx126x_read_buffer(FAR struct sx126x_dev_s *dev,
uint8_t offset,
FAR uint8_t *payload,
uint8_t len);
/* Register settings ********************************************************/
static void sx126x_set_syncword(FAR struct sx126x_dev_s *dev,
uint8_t *syncword,
uint8_t syncword_length);
/* Driver specific **********************************************************/
static int sx126x_init(FAR struct sx126x_dev_s *dev);
static int sx126x_deinit(FAR struct sx126x_dev_s *dev);
static int sx126x_setup_radio(FAR struct sx126x_dev_s *dev);
static void sx126x_set_defaults(FAR struct sx126x_dev_s *dev);
/* Interrupt handlers *******************************************************/
static int sx126x_irq0handler(int irq, FAR void *context, FAR void *arg);
static inline int sx126x_attachirq0(FAR struct sx126x_dev_s *dev, xcpt_t isr,
FAR void *arg);
static void sx126x_isr0_process(FAR void *arg);
/****************************************************************************
* Private Functions
****************************************************************************/
/* File operations **********************************************************/
static int sx126x_open(FAR struct file *filep)
{
int ret = 0;
/* Get device */
struct sx126x_dev_s *dev;
dev = filep->f_inode->i_private;
wlinfo("Opening SX126x %d", dev->lower->dev_number);
/* Lock dev */
ret = nxmutex_lock(&dev->lock);
if (ret < 0)
{
return ret;
}
/* Only one can open this dev at a time */
if (dev->times_opened > 0)
{
ret = -EBUSY;
goto exit_err;
}
/* Initialize */
ret = sx126x_init(dev);
if (ret != 0)
{
goto exit_err;
}
/* Success */
dev->times_opened++;
ret = OK;
exit_err:
nxmutex_unlock(&dev->lock);
return ret;
}
static int sx126x_close(FAR struct file *filep)
{
int ret = 0;
/* Get device */
struct sx126x_dev_s *dev;
dev = filep->f_inode->i_private;
wlinfo("Closing SX126x %d", dev->lower->dev_number);
/* Lock */
ret = nxmutex_lock(&dev->lock);
if (ret < 0)
{
goto exit_err;
}
/* De-init */
ret = sx126x_deinit(dev);
if (ret != 0)
{
goto exit_err;
}
/* Success */
if (dev->times_opened > 0)
{
dev->times_opened--; /* Do not let this wrap around. */
ret = OK;
}
exit_err:
nxmutex_unlock(&dev->lock);
return ret;
}
static ssize_t sx126x_read(FAR struct file *filep,
FAR char *buf,
size_t buflen)
{
int ret = 0;
if (buf == NULL || buflen < 1)
{
return -EINVAL;
}
/* Get device */
struct sx126x_dev_s *dev;
dev = filep->f_inode->i_private;
nxmutex_lock(&dev->lock);
printf("Reading\n");
/* Get header */
struct sx126x_read_header_s *header = (struct sx126x_read_header_s *)buf;
/* Pre-RX setup */
sx126x_spi_lock(dev);
dev->irq_mask = SX126X_IRQ_RXDONE_MASK | SX126X_IRQ_CRCERR_MASK;
ret = sx126x_setup_radio(dev);
if (ret != 0)
{
goto sx126x_rx_abort;
}
/* RX mode */
sx126x_set_rx(dev, SX126X_NO_TIMEOUT);
sx126x_spi_unlock(dev);
/* Wait for a packet */
nxsem_wait(&dev->rx_sem);
/* Get payload */
uint8_t status = 0;
uint8_t offset = 0;
sx126x_spi_lock(dev);
sx126x_get_rx_buffer_status(dev, &status,
&header->payload_length,
&offset);
sx126x_read_buffer(dev, offset, header->payload,
header->payload_length);
sx126x_spi_unlock(dev);
/* Get CRC check */
header->crc_error = dev->irqbits & SX126X_IRQ_CRCERR_MASK;
/* Exit */
sx126x_rx_abort:
nxmutex_unlock(&dev->lock);
return 1;
}
static ssize_t sx126x_write(FAR struct file *filep,
FAR const char *buf,
size_t buflen)
{
int ret = 0;
/* Get device */
struct sx126x_dev_s *dev;
dev = filep->f_inode->i_private;
if (buf == NULL || buflen < 1)
{
return -EINVAL;
}
nxmutex_lock(&dev->lock);
sx126x_spi_lock(dev);
/* Data */
dev->payload_len = buflen;
sx126x_write_buffer(dev, 0, (uint8_t *)buf, buflen);
/* Pre-TX setup */
dev->irq_mask = SX126X_IRQ_TXDONE_MASK;
ret = sx126x_setup_radio(dev);
if (ret != 0)
{
sx126x_spi_unlock(dev);
goto sx126x_tx_abort;
}
/* TX */
sx126x_set_tx(dev, 0);
sx126x_spi_unlock(dev);
/* Wait for transmitting operations to be finished */
wlinfo("TXing");
ret = nxsem_wait(&dev->tx_sem);
sx126x_tx_abort:
nxmutex_unlock(&dev->lock);
return ret;
}
static int sx126x_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{
int ret = 0;
/* Get device */
struct sx126x_dev_s *dev;
dev = filep->f_inode->i_private;
wlinfo("IOCTL cmd %d arg %u SX126x dev_number %d",
cmd,
*(FAR uint32_t *)((uintptr_t)arg),
dev->lower->dev_number);
/* Lock */
ret = nxmutex_lock(&dev->lock);
if (ret < 0)
{
goto exit_err;
}
/* Do thing */
switch (cmd)
{
/* Set radio freq. Takes uint32_t *frequency in Hz */
case WLIOC_SETRADIOFREQ:
{
FAR uint32_t *freq_ptr = (FAR uint32_t *)((uintptr_t)arg);
DEBUGASSERT(freq_ptr != NULL);
dev->frequency_hz = *freq_ptr;
break;
}
/* Get radio freq. Sets uint32_t *frequency in Hz */
case WLIOC_GETRADIOFREQ:
{
FAR uint32_t *freq_ptr = (FAR uint32_t *)((uintptr_t)arg);
DEBUGASSERT(freq_ptr != NULL);
*freq_ptr = dev->frequency_hz;
break;
}
/* Set TX power. arg: Pointer to int8_t power value */
case WLIOC_SETTXPOWER:
{
FAR int8_t *ptr = (FAR int8_t *)((uintptr_t)arg);
DEBUGASSERT(ptr != NULL);
dev->power = *ptr;
break;
}
/* Get current TX power. arg: Pointer to int8_t power value */
case WLIOC_GETTXPOWER:
{
FAR int8_t *ptr = (FAR int8_t *)((uintptr_t)arg);
DEBUGASSERT(ptr != NULL);
*ptr = dev->power;
break;
}
/* TODO: Integration with new common IOCTL API */
/* Driver specific IOCTL */
/* Lora config */
case SX126XIOC_LORACONFIGSET:
{
FAR struct sx126x_lora_config_s *ptr =
(FAR struct sx126x_lora_config_s *)((uintptr_t)arg);
DEBUGASSERT(ptr != NULL);
/* Modulation params */
dev->lora_sf = ptr->modulation.spreading_factor;
dev->lora_bw = ptr->modulation.bandwidth;
dev->lora_cr = ptr->modulation.coding_rate;
dev->low_datarate_optimization =
ptr->modulation.low_datarate_optimization;
/* Packet params */
dev->lora_crc = ptr->packet.crc_enable;
dev->lora_fixed_header =
ptr->packet.fixed_length_header;
dev->payload_len = ptr->packet.payload_length;
dev->invert_iq = ptr->packet.invert_iq;
dev->preambles = ptr->packet.preambles;
break;
}
}
/* Success */
ret = OK;
exit_err:
nxmutex_unlock(&dev->lock);
return ret;
}
uint32_t sx126x_convert_freq_in_hz_to_pll_step(uint32_t freq_in_hz)
{
uint32_t steps_int;
uint32_t steps_frac;
steps_int = freq_in_hz / SX126X_PLL_STEP_SCALED;
steps_frac = freq_in_hz - (steps_int * SX126X_PLL_STEP_SCALED);
return (steps_int <<
SX126X_PLL_STEP_SHIFT_AMOUNT) + (((steps_frac <<
SX126X_PLL_STEP_SHIFT_AMOUNT) + (SX126X_PLL_STEP_SCALED >>
1)) / SX126X_PLL_STEP_SCALED);
}
/* Operational modes functions **********************************************/
static void sx126x_set_standby(FAR struct sx126x_dev_s *dev,
enum sx126x_standby_mode_e mode)
{
sx126x_command(dev, SX126X_SETSTANDBY, (uint8_t *)&mode,
SX126X_SETSTANDBY_PARAMS, NULL);
}
static void sx126x_set_tx(FAR struct sx126x_dev_s *dev, uint32_t timeout)
{
/* Convert timeout to BE24 */
timeout = htobe32(timeout << 8);
sx126x_command(dev, SX126X_SETTX, (uint8_t *)&timeout, SX126X_SETTX_PARAMS,
NULL);
}
static void sx126x_set_rx(FAR struct sx126x_dev_s *dev, uint32_t timeout)
{
/* Convert timeout to BE24 */
timeout = htobe32(timeout << 8);
sx126x_command(dev, SX126X_SETRX, (uint8_t *)&timeout, SX126X_SETRX_PARAMS,
NULL);
}
static void sx126x_stop_timer_on_preamble(FAR struct sx126x_dev_s *dev,
bool enable)
{
sx126x_command(dev, SX126X_STOPTIMERONPREAMBLE, (uint8_t *)&enable,
SX126X_STOPTIMERONPREAMBLE_PARAMS, NULL);
}
static void sx126x_set_rx_duty_cycle(FAR struct sx126x_dev_s *dev,
uint32_t rx_period,
uint32_t sleep_period)
{
uint8_t params[SX126X_SETRXDUTYCYCLE_PARAMS];
rx_period = htobe32(rx_period << 8);
sleep_period = htobe32(sleep_period << 8);
memcpy(params + SX126X_SETRXDUTYCYCLE_RXPERIOD_PARAM,
(uint8_t *)&rx_period,
SX126X_SETRXDUTYCYCLE_RXPERIOD_PARAMS);
memcpy(params + SX126X_SETRXDUTYCYCLE_SLEEPPERIOD_PARAM,
(uint8_t *)&sleep_period,
SX126X_SETRXDUTYCYCLE_SLEEPPERIOD_PARAMS);
sx126x_command(dev, SX126X_SETRXDUTYCYCLE, params,
SX126X_SETRXDUTYCYCLE_PARAMS, NULL);
}
static void sx126x_set_cad(FAR struct sx126x_dev_s *dev)
{
sx126x_command(dev, SX126X_SETCAD, NULL, 0, NULL);
}
static void sx126x_set_tx_continuous_wave(FAR struct sx126x_dev_s *dev)
{
sx126x_command(dev, SX126X_SETTXCONTINUOUSWAVE, NULL, 0, NULL);
}
static void sx126x_set_regulator_mode(FAR struct sx126x_dev_s *dev,
enum sx126x_regulator_mode_e mode)
{
sx126x_command(dev, SX126X_SETREGULATORMODE, (uint8_t *)&mode,
SX126X_SETREGULATORMODE_PARAMS, NULL);
}
/* Caution! Exceeding the limits listed in DS_SX1261/2 V2.1
* 13.1.14.1 may cause irreversible damage to the device
*/
static void sx126x_set_pa_config(FAR struct sx126x_dev_s *dev,
enum sx126x_device_e model, uint8_t hpmax,
uint8_t padutycycle)
{
uint8_t params[SX126X_SETPACONFIG_PARMS];
memset(params, 0, SX126X_SETPACONFIG_PARMS);
params[SX126X_SETPACONFIG_PADUTYCYCLE_PARAM] = padutycycle;
params[SX126X_SETPACONFIG_HPMAX_PARAM] = hpmax;
params[SX126X_SETPACONFIG_DEVICESEL_PARAM] = model;
params[SX126X_SETPACONFIG_PALUT_PARAM] = 0x01;
sx126x_command(dev, SX126X_SETPACONFIG, params, SX126X_SETPACONFIG_PARMS,
NULL);
}
static void sx126x_set_tx_infinite_preamble(FAR struct sx126x_dev_s *dev)
{
sx126x_command(dev, SX126X_SETTXINFINITEPREAMBLE, NULL, 0, NULL);
}
static void sx126x_set_rx_tx_fallback_mode(FAR struct sx126x_dev_s *dev,
enum sx126x_fallback_mode_e
fallback)
{
sx126x_command(dev, SX126X_SETRXTXFALLBACKMODE, (uint8_t *)&fallback,
SX126X_SETRXTXFALLBACKMODE_PARAMS, NULL);
}
/* DIO and IRQ control functions */
static void sx126x_set_dio_irq_params(FAR struct sx126x_dev_s *dev,
uint16_t irq_mask, uint16_t dio1_mask,
uint16_t dio2_mask, uint16_t dio3_mask)
{
irq_mask = htobe16(irq_mask);
dio1_mask = htobe16(dio1_mask);
dio2_mask = htobe16(dio2_mask);
dio3_mask = htobe16(dio3_mask);
uint8_t params[SX126X_SETDIOIRQPARAMS_PARAMS];
memcpy(params + SX126X_SETDIOIRQPARAMS_IRQMASK_PARAM, &irq_mask,
SX126X_SETDIOIRQPARAMS_IRQMASK_PARAMS);
memcpy(params + SX126X_SETDIOIRQPARAMS_DIO1MASK_PARAM, &dio1_mask,
SX126X_SETDIOIRQPARAMS_DIO1MASK_PARAMS);
memcpy(params + SX126X_SETDIOIRQPARAMS_DIO2MASK_PARAM, &dio2_mask,
SX126X_SETDIOIRQPARAMS_DIO2MASK_PARAMS);
memcpy(params + SX126X_SETDIOIRQPARAMS_DIO3MASK_PARAM, &dio3_mask,
SX126X_SETDIOIRQPARAMS_DIO3MASK_PARAMS);
sx126x_command(dev, SX126X_SETDIOIRQPARAMS, params,
SX126X_SETDIOIRQPARAMS_PARAMS, NULL);
}
static void sx126x_set_dio2_as_rf_switch(FAR struct sx126x_dev_s *dev,
bool enable)
{
sx126x_command(dev, SX126X_SETDIO2RFSWCTRL, (uint8_t *)&enable,
SX126X_SETDIO2RFSWCTRL_PARAMS, NULL);
}
static void sx126x_set_dio3_as_tcxo(FAR struct sx126x_dev_s *dev,
enum sx126x_tcxo_voltage_e voltage,
uint32_t delay)
{
uint8_t params[SX126X_SETDIO3TCXOCTRL_PARAMS];
params[SX126X_SETDIO3TCXOCTRL_TCXO_V_PARAM] = voltage;
/* Convert delay to 24 bit and convert to BE */
delay = htobe32(delay << 8);
memcpy(params + SX126X_SETDIO3TCXOCTRL_DELAY_PARAM, &delay,
SX126X_SETDIO3TCXOCTRL_DELAY_PARAMS);
sx126x_command(dev, SX126X_SETDIO3TCXOCTRL, params,
SX126X_SETDIO3TCXOCTRL_PARAMS, NULL);
}
static void sx126x_get_irq_status(FAR struct sx126x_dev_s *dev,
FAR uint16_t *irqstatus)
{
uint8_t returns[SX126X_GETIRQSTATUS_RETURNS];
sx126x_command(dev, SX126X_GETIRQSTATUS,
NULL, SX126X_GETIRQSTATUS_RETURNS,
returns);
uint16_t bits;
memcpy(&bits, returns +
SX126X_GETIRQSTATUS_IRQSTATUS_RETURN,
SX126X_GETIRQSTATUS_IRQSTATUS_RETURNS);
*irqstatus = be16toh(bits);
}
static void sx126x_clear_irq_status(FAR struct sx126x_dev_s *dev,
uint16_t clearbits)
{
uint8_t params[SX126X_CLEARIRQSTATUS_PARAMS];
clearbits = htobe16(clearbits);
memcpy(params + SX126X_CLEARIRQSTATUS_CLEAR_PARAM,
&clearbits,
SX126X_CLEARIRQSTATUS_CLEAR_PARAMS);
sx126x_command(dev, SX126X_CLEARIRQSTATUS, params,
SX126X_CLEARIRQSTATUS_PARAMS,
NULL);
}
/* RF Modulation and Packet-Related Functions *******************************/
static void sx126x_set_packet_params_lora(FAR struct sx126x_dev_s *dev,
FAR struct
sx126x_packetparams_lora_s *
pktparams)
{
uint8_t params[SX126X_SETPACKETPARMS_PARAMS];
memset(params, 0, SX126X_SETPACKETPARMS_PARAMS);
uint16_t preambles = htobe16(pktparams->preambles);
memcpy(params + SX126X_PKTPARAM1_LORA_PREAMBLELEN_PARAM, &preambles,
SX126X_PKTPARAM1_LORA_PREAMBLELEN_PARAMS);
params[SX126X_PKTPARAM3_LORA_HEADERTYPE_PARAM] =
pktparams->fixed_length_header;
params[SX126X_PKTPARAM4_LORA_PAYLOADLEN_PARAM] =
pktparams->payload_length;
params[SX126X_PKTPARAM5_LORA_CRCTYPE_PARAM] = pktparams->crc_enable;
params[SX126X_PKTPARAM6_LORA_INVERTIQ_PARAM] = pktparams->invert_iq;
sx126x_command(dev, SX126X_SETPACKETPARMS, params,
SX126X_SETPACKETPARMS_PARAMS, NULL);
}
static void sx126x_set_modulation_params_lora(FAR struct sx126x_dev_s *dev,
FAR struct
sx126x_modparams_lora_s *
modparams)
{
uint8_t params[SX126X_SETMODULATIONPARAMS_PARAMS];
memset(params, 0, SX126X_SETMODULATIONPARAMS_PARAMS);
params[SX126X_MODPARAM1_LORA_SF_PARAM] =
modparams->spreading_factor;
params[SX126X_MODPARAM2_LORA_BW_PARAM] =
modparams->bandwidth;
params[SX126X_MODPARAM3_LORA_CR_PARAM] =
modparams->coding_rate;
params[SX126X_MODPARAM4_LORA_LOWDATRATE_OPTI_PARAM] =
modparams->low_datarate_optimization;
sx126x_command(dev, SX126X_SETMODULATIONPARAMS, params,
SX126X_SETMODULATIONPARAMS_PARAMS, NULL);
}
static void sx126x_set_buffer_base_address(FAR struct sx126x_dev_s *dev,
uint8_t tx, uint8_t rx)
{
uint8_t params[SX126X_SETBUFFERBASEADDRESS_PARAMS];
memset(params, 0, SX126X_SETBUFFERBASEADDRESS_PARAMS);
params[SX126X_SETBUFFERBASEADDRESS_TX_PARAM] = tx;
params[SX126X_SETBUFFERBASEADDRESS_RX_PARAM] = rx;
sx126x_command(dev, SX126X_SETBUFFERBASEADDRESS, params,
SX126X_SETBUFFERBASEADDRESS_PARAMS, NULL);
}
static void sx126x_set_tx_params(FAR struct sx126x_dev_s *dev, uint8_t power,
enum sx126x_ramp_time_e ramp_time)
{
uint8_t params[SX126X_SETTXPARMS_PARAMS];
memset(params, 0, SX126X_SETTXPARMS_PARAMS);
params[SX126X_SETTXPARMS_RAMPTIME_PARAM] = ramp_time;
params[SX126X_SETTXPARMS_POWER_PARAM] = power;
sx126x_command(dev, SX126X_SETTXPARMS, params, SX126X_SETTXPARMS_PARAMS,
NULL);
}
static void sx126x_set_packet_type(FAR struct sx126x_dev_s *dev,
enum sx126x_packet_type_e type)
{
sx126x_command(dev, SX126X_SETPACKETTYPE, (uint8_t *)&type,
SX126X_SETPACKETTYPE_PARAMS, NULL);
}
static void sx126x_set_rf_frequency(FAR struct sx126x_dev_s *dev,
uint32_t frequency_hz)
{
uint32_t corrected_freq =
sx126x_convert_freq_in_hz_to_pll_step(frequency_hz);
corrected_freq = htobe32(corrected_freq);
sx126x_command(dev, SX126X_SETRFFREQUENCY, (uint8_t *)&corrected_freq,
SX126X_SETRFFREQUENCY_PARAMS, NULL);
}
static void sx126x_set_lora_symb_num_timout(FAR struct sx126x_dev_s *dev,
uint8_t symbnum)
{
sx126x_command(dev, SX126X_SETLORASYMBNUMTIMEOUT, &symbnum,
SX126X_SETLORASYMBNUMTIMEOUT_PARAMS, NULL);
}
/* Communication status information *****************************************/
static void sx126x_get_status(FAR struct sx126x_dev_s *dev,
FAR struct sx126x_status_s *status)
{
/* NOP param to shift out result */
uint8_t parms[1] = {
0x00
};
uint8_t rets[1];
/* Get, mask and shift result into readable mode numbers */
sx126x_command(dev, SX126X_CMD_GETSTATUS, parms, sizeof(parms), rets);
status->mode = (rets[0] & SX126X_STATUS_CHIPMODE_MASK) >>
SX126X_STATUS_CHIPMODE_SHIFT;
status->cmd = (rets[0] & SX126X_STATUS_CMD_MASK) >>
SX126X_STATUS_CMD_SHIFT;
}
static void sx126x_get_rssi_inst(FAR struct sx126x_dev_s *dev,
FAR int32_t *dbm)
{
uint8_t rets[SX126X_GETRSSIINST_RETURNS];
sx126x_command(dev, SX126X_GETRSSIINST, NULL, SX126X_GETRSSIINST_RETURNS,
rets);
/* Calculate dBm from returns */
int32_t rssi = rets[SX126X_GETRSSIINST_RSSI_RETURN];
(*dbm) = -rssi / 2.0;
}
static void sx126x_get_rx_buffer_status(FAR struct sx126x_dev_s *dev,
uint8_t *status,
uint8_t *payload_len,
uint8_t *rx_buff_offset)
{
uint8_t returns[SX126X_GETRXBUFFERSTATUS_RETURNS];
sx126x_command(dev, SX126X_GETRXBUFFERSTATUS,
NULL,
SX126X_GETRXBUFFERSTATUS_RETURNS,
returns);
*status = returns[SX126X_GETRXBUFFERSTATUS_STATUS_RETURN];
*payload_len = returns[SX126X_GETRXBUFFERSTATUS_PAYLOAD_LEN_RETURN];
*rx_buff_offset = returns[SX126X_GETRXBUFFERSTATUS_RX_START_PTR_RETURN];
}
/* Lower hardware control ***************************************************/
static void sx126x_reset(FAR struct sx126x_dev_s *dev)
{
dev->lower->reset();
}
/* SPI Communication ********************************************************/
static void sx126x_select(FAR struct sx126x_dev_s *dev)
{
SPI_SELECT(dev->spi, SPIDEV_LPWAN(dev->lower->dev_number), true);
}
static void sx126x_deselect(FAR struct sx126x_dev_s *dev)
{
SPI_SELECT(dev->spi, SPIDEV_LPWAN(dev->lower->dev_number), false);
}
static void sx126x_spi_lock(FAR struct sx126x_dev_s *dev)
{
struct spi_dev_s *spi = dev->spi;
SPI_LOCK(spi, true);
SPI_SETBITS(spi, 8);
SPI_SETMODE(spi, SPIDEV_MODE0);
SPI_SETFREQUENCY(spi, SX126X_SPI_SPEED);
}
static void sx126x_spi_unlock(FAR struct sx126x_dev_s *dev)
{
SPI_LOCK(dev->spi, false);
}
static void sx126x_command(FAR struct sx126x_dev_s *dev, uint8_t cmd,
const FAR uint8_t *params, size_t paramslen,
FAR uint8_t *returns)
{
sx126x_select(dev);
/* First send the command. This does not return anything.
* "RFU" according the manual
*/
SPI_SEND(dev->spi, cmd);
/* Send all the params and record the returning bytes */
for (size_t i = 0; i < paramslen; i++)
{
uint8_t param = SX126X_NOP;
if (params != NULL)
{
param = params[i];
}
uint8_t ret = SPI_SEND(dev->spi, param);
if (returns != NULL)
{
returns[i] = ret;
}
}
sx126x_deselect(dev);
}
/* Registers and buffer *****************************************************/
static void sx126x_write_register(FAR struct sx126x_dev_s *dev,
uint16_t address,
uint8_t *data,
size_t data_length)
{
sx126x_select(dev);
/* Send the opcode and address */
SPI_SEND(dev->spi, SX126X_WRITEREGISTER);
SPI_SEND(dev->spi, (uint8_t)(address >> 8));
SPI_SEND(dev->spi, (uint8_t)address);
/* Send data */
for (size_t i = 0; i < data_length; i++)
{
SPI_SEND(dev->spi, data[i]);
}
sx126x_deselect(dev);
}
static void sx126x_read_register(FAR struct sx126x_dev_s *dev,
uint16_t address,
uint8_t *data,
size_t data_length)
{
sx126x_select(dev);
/* Send the opcode and address */
SPI_SEND(dev->spi, SX126X_WRITEREGISTER);
SPI_SEND(dev->spi, (uint8_t)(address >> 8));
SPI_SEND(dev->spi, (uint8_t)address);
/* Send data */
for (size_t i = 0; i < data_length; i++)
{
data[i] = SPI_SEND(dev->spi, SX126X_NOP);
}
sx126x_deselect(dev);
}
static void sx126x_write_buffer(FAR struct sx126x_dev_s *dev,
uint8_t offset,
FAR const uint8_t *payload,
uint8_t len)
{
sx126x_select(dev);
/* Command */
SPI_SEND(dev->spi, SX126X_WRITEBUFFER);
/* Offset */
SPI_SEND(dev->spi, offset);
/* Data */
for (size_t i = 0; i < len; i++)
{
SPI_SEND(dev->spi, payload[i]);
}
sx126x_deselect(dev);
}
static void sx126x_read_buffer(FAR struct sx126x_dev_s *dev,
uint8_t offset,
FAR uint8_t *payload,
uint8_t len)
{
sx126x_select(dev);
/* Command */
SPI_SEND(dev->spi, SX126X_READBUFFER);
/* Offset */
SPI_SEND(dev->spi, offset);
/* NOP */
SPI_SEND(dev->spi, SX126X_NOP);
/* Data */
for (size_t i = 0; i < len; i++)
{
payload[i] = SPI_SEND(dev->spi, SX126X_NOP);
}
sx126x_deselect(dev);
}
/* Register settings ********************************************************/
static void sx126x_set_syncword(FAR struct sx126x_dev_s *dev,
uint8_t *syncword,
uint8_t syncword_length)
{
if (syncword_length > SX126X_REG_SYNCWORD_LEN)
{
syncword_length = SX126X_REG_SYNCWORD_LEN;
wlerr("Syncword length was limited to the maximum 8 bytes");
}
sx126x_write_register(dev, SX126X_REG_SYNCWORD, syncword, syncword_length);
}
/* Driver specific **********************************************************/
static int sx126x_init(FAR struct sx126x_dev_s *dev)
{
sx126x_reset(dev);
sx126x_set_defaults(dev);
return 0;
}
static int sx126x_deinit(FAR struct sx126x_dev_s *dev)
{
return 0;
}
static void sx126x_set_defaults(FAR struct sx126x_dev_s *dev)
{
/* Hardware defaults */
dev->invert_iq = SX126X_DEFAULT_INVERT_IQ;
/* Common defaults */
dev->packet_type = SX126X_DEFAULT_PACKET_TYPE;
dev->frequency_hz = SX126X_DEFAULT_FREQ;
dev->power = SX126X_DEFAULT_POWER;
dev->preambles = SX126X_DEFAULT_LORA_PREAMBLES;
/* LoRa defaults */
dev->lora_sf = SX126X_DEFAULT_LORA_SF;
dev->lora_bw = SX126X_DEFAULT_LORA_BW;
dev->lora_cr = SX126X_DEFAULT_LORA_CR;
dev->lora_fixed_header = SX126X_DEFAULT_LORA_FIXED_HEADER;
dev->lora_crc = SX126X_DEFAULT_LORA_CRC_EN;
dev->low_datarate_optimization = SX126X_DEFAULT_LORA_LDO;
uint8_t newsyncword[] = SX126X_DEFAULT_SYNCWORD;
memcpy(dev->syncword, newsyncword, sizeof(dev->syncword));
/* GFSK defaults */
}
static int sx126x_setup_radio(FAR struct sx126x_dev_s *dev)
{
/* Clear IRQ status */
sx126x_clear_irq_status(dev, 0xffff);
/* Set regulator */
sx126x_set_regulator_mode(dev, dev->lower->regulator_mode);
/* Set packet type */
sx126x_set_packet_type(dev, dev->packet_type);
/* Set RF frequency */
int illegal_freq = dev->lower->check_frequency(dev->frequency_hz);
if (illegal_freq)
{
wlerr("Board does not support %dHz", dev->frequency_hz);
return -1;
}
sx126x_set_rf_frequency(dev, dev->frequency_hz);
/* Set PA settings from lower */
uint8_t hp;
uint8_t dc;
enum sx126x_device_e model;
dev->lower->get_pa_values(&model, &hp, &dc);
sx126x_set_pa_config(dev, model, hp, dc);
/* Set TX params */
dev->lower->limit_tx_power(&dev->power); /* Limited by board */
sx126x_set_tx_params(dev, dev->power, dev->lower->tx_ramp_time);
/* Set base */
sx126x_set_buffer_base_address(dev, 0, 0);
/* Set params depending on packet type */
switch (dev->packet_type)
{
case (SX126X_PACKETTYPE_LORA):
{
/* Mod params */
struct sx126x_modparams_lora_s modparams = {
.spreading_factor = dev->lora_sf,
.bandwidth = dev->lora_bw,
.coding_rate = dev->lora_cr,
.low_datarate_optimization = dev->low_datarate_optimization
};
sx126x_set_modulation_params_lora(dev, &modparams);
/* Packet params */
struct sx126x_packetparams_lora_s pktparams = {
.crc_enable = dev->lora_crc,
.fixed_length_header = dev->lora_fixed_header,
.payload_length = dev->payload_len,
.invert_iq = dev->invert_iq,
.preambles = dev->preambles
};
sx126x_set_packet_params_lora(dev, &pktparams);
break;
}
default:
break;
}
/* Sync word */
sx126x_set_syncword(dev, dev->syncword, sizeof(dev->syncword));
/* IRQ MASK */
sx126x_set_dio_irq_params(dev, dev->irq_mask,
dev->lower->masks.dio1_mask,
dev->lower->masks.dio2_mask,
dev->lower->masks.dio3_mask);
/* DIO 2 */
sx126x_set_dio2_as_rf_switch(dev, dev->lower->use_dio2_as_rf_sw);
/* DIO 3 */
sx126x_set_dio3_as_tcxo(dev, dev->lower->dio3_voltage,
dev->lower->dio3_delay);
return 0;
}
/* Interrupt handling *******************************************************/
static int sx126x_irq0handler(int irq, FAR void *context, FAR void *arg)
{
FAR struct sx126x_dev_s *dev = (FAR struct sx126x_dev_s *)arg;
DEBUGASSERT(dev != NULL);
DEBUGASSERT(work_available(&dev->irq0_work));
return work_queue(HPWORK, &dev->irq0_work, sx126x_isr0_process, arg, 0);
}
static inline int sx126x_attachirq0(FAR struct sx126x_dev_s *dev, xcpt_t isr,
FAR void *arg)
{
DEBUGASSERT(dev->lower->irq0attach != NULL);
return dev->lower->irq0attach(isr, arg);
}
static void sx126x_isr0_process(FAR void *arg)
{
DEBUGASSERT(arg);
FAR struct sx126x_dev_s *dev = (FAR struct sx126x_dev_s *)arg;
wlinfo("SX126x ISR0 process triggered");
/* Get and clear IRQ bits */
sx126x_spi_lock(dev);
sx126x_get_irq_status(dev, &dev->irqbits);
sx126x_spi_unlock(dev);
wlinfo("IRQ status 0x%X", dev->irqbits);
/* On TX done */
if (dev->irqbits & SX126X_IRQ_TXDONE_MASK)
{
wlinfo("TX done");
/* Release writing threads */
nxsem_post(&dev->tx_sem);
}
/* On RX done */
if (dev->irqbits & SX126X_IRQ_RXDONE_MASK)
{
wlinfo("RX done");
nxsem_post(&dev->rx_sem);
}
/* On CAD done */
if (dev->irqbits & SX126X_IRQ_CADDONE_MASK)
{
wlinfo("CAD done");
}
/* On CAD detect */
if (dev->irqbits & SX126X_IRQ_CADDETECTED_MASK)
{
wlinfo("CAD detect");
}
}
/****************************************************************************
* Public Functions
****************************************************************************/
void sx126x_register(FAR struct spi_dev_s *spi,
FAR const struct sx126x_lower_s *lower,
const char *path)
{
/* Register the dev using an unique dev_number,
* so multiple radios can be registered at once
*/
if (lower->dev_number >= SX126X_MAX_DEVICES)
{
wlerr("SX126x dev_number %d is greater than \
allowed amount of SX126x devices",
lower->dev_number);
return;
}
struct sx126x_dev_s *dev;
dev = &g_sx126x_devices[lower->dev_number];
dev->lower = lower;
dev->spi = spi;
/* Initialize locks and semaphores */
nxmutex_init(&dev->lock);
nxsem_init(&dev->rx_sem, 0, 0);
nxsem_init(&dev->tx_sem, 0, 0);
sx126x_attachirq0(dev, sx126x_irq0handler, dev);
(void)register_driver(path, &sx126x_ops, 0666,
dev);
}