mirror of
https://bitbucket.org/librepilot/librepilot.git
synced 2024-11-29 07:24:13 +01:00
1590 lines
59 KiB
C
1590 lines
59 KiB
C
/**
|
|
******************************************************************************
|
|
* @addtogroup PIOS PIOS Core hardware abstraction layer
|
|
* @{
|
|
* @addtogroup PIOS_RFM22B Radio Functions
|
|
* @brief PIOS OpenLRS interface for for the RFM22B radio
|
|
* @{
|
|
*
|
|
* @file pios_openlrs.c
|
|
* @author Tau Labs, http://taulabs.org, Copyright (C) 2015
|
|
* @author dRonin, http://dronin.org Copyright (C) 2015
|
|
* @author LibrePilot, http://librepilot.org Copyright (C) 2016
|
|
* @brief Implements an OpenLRS driver for the RFM22B
|
|
* @see The GNU Public License (GPL) Version 3
|
|
*
|
|
*****************************************************************************/
|
|
/*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "pios.h"
|
|
|
|
#ifdef PIOS_INCLUDE_OPENLRS
|
|
|
|
#include <pios_spi_priv.h>
|
|
#include <pios_openlrs_priv.h>
|
|
#include <pios_openlrs_rcvr_priv.h>
|
|
#include <pios_semaphore.h>
|
|
#include <pios_thread.h>
|
|
#include <taskinfo.h>
|
|
|
|
#include "flightstatus.h"
|
|
#include "flightbatterystate.h"
|
|
#include "oplinksettings.h"
|
|
#include "oplinkstatus.h"
|
|
|
|
#include "pios_rfm22b_regs.h"
|
|
|
|
#define STACK_SIZE_BYTES 800
|
|
#define TASK_PRIORITY PIOS_THREAD_PRIO_NORMAL
|
|
|
|
|
|
static void rx_reset(struct pios_openlrs_dev *openlrs_dev);
|
|
static void rfmSetCarrierFrequency(struct pios_openlrs_dev *openlrs_dev, uint32_t f);
|
|
static uint8_t rfmGetRSSI(struct pios_openlrs_dev *openlrs_dev);
|
|
static void to_rx_mode(struct pios_openlrs_dev *openlrs_dev);
|
|
static void tx_packet(struct pios_openlrs_dev *openlrs_dev, uint8_t *pkt, uint8_t size);
|
|
|
|
static struct pios_openlrs_dev *pios_openlrs_alloc();
|
|
static bool pios_openlrs_validate(struct pios_openlrs_dev *openlrs_dev);
|
|
|
|
// SPI read/write functions
|
|
static void rfm22_assertCs(struct pios_openlrs_dev *openlrs_dev);
|
|
static void rfm22_deassertCs(struct pios_openlrs_dev *openlrs_dev);
|
|
static void rfm22_claimBus(struct pios_openlrs_dev *openlrs_dev);
|
|
static void rfm22_releaseBus(struct pios_openlrs_dev *openlrs_dev);
|
|
static void rfm22_write_claim(struct pios_openlrs_dev *openlrs_dev,
|
|
uint8_t addr, uint8_t data);
|
|
static void rfm22_write(struct pios_openlrs_dev *openlrs_dev, uint8_t addr,
|
|
uint8_t data);
|
|
static uint8_t rfm22_read_claim(struct pios_openlrs_dev *openlrs_dev,
|
|
uint8_t addr);
|
|
static uint8_t rfm22_read(struct pios_openlrs_dev *openlrs_dev,
|
|
uint8_t addr);
|
|
|
|
// Private constants
|
|
const struct rfm22_modem_regs {
|
|
uint32_t bps;
|
|
uint8_t r_1c, r_1d, r_1e, r_20, r_21, r_22, r_23, r_24, r_25, r_2a, r_6e, r_6f, r_70, r_71, r_72;
|
|
} modem_params[] = {
|
|
{ 4800, 0x1a, 0x40, 0x0a, 0xa1, 0x20, 0x4e, 0xa5, 0x00, 0x1b, 0x1e, 0x27, 0x52, 0x2c, 0x23, 0x30 }, // 50000 0x00
|
|
{ 9600, 0x05, 0x40, 0x0a, 0xa1, 0x20, 0x4e, 0xa5, 0x00, 0x20, 0x24, 0x4e, 0xa5, 0x2c, 0x23, 0x30 }, // 25000 0x00
|
|
{ 19200, 0x06, 0x40, 0x0a, 0xd0, 0x00, 0x9d, 0x49, 0x00, 0x7b, 0x28, 0x9d, 0x49, 0x2c, 0x23, 0x30 }, // 25000 0x01
|
|
{ 57600, 0x05, 0x40, 0x0a, 0x45, 0x01, 0xd7, 0xdc, 0x03, 0xb8, 0x1e, 0x0e, 0xbf, 0x00, 0x23, 0x2e },
|
|
{ 125000, 0x8a, 0x40, 0x0a, 0x60, 0x01, 0x55, 0x55, 0x02, 0xad, 0x1e, 0x20, 0x00, 0x00, 0x23, 0xc8 },
|
|
};
|
|
|
|
static const uint8_t pktsizes[8] = { 0, 7, 11, 12, 16, 17, 21, 0 };
|
|
|
|
static const uint8_t OUT_FF[64] = {
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
|
};
|
|
|
|
const uint8_t default_hop_list[] = { DEFAULT_HOPLIST };
|
|
|
|
const uint32_t packet_advance_time_us = 1500;
|
|
const uint32_t packet_timeout_us = 1000;
|
|
|
|
const struct rfm22_modem_regs bind_params =
|
|
{ 9600, 0x05, 0x40, 0x0a, 0xa1, 0x20, 0x4e, 0xa5, 0x00, 0x20, 0x24, 0x4e, 0xa5, 0x2c, 0x23, 0x30 };
|
|
|
|
static uint32_t minFreq()
|
|
{
|
|
return MIN_RFM_FREQUENCY_433;
|
|
}
|
|
|
|
static uint32_t maxFreq()
|
|
{
|
|
return MAX_RFM_FREQUENCY_433;
|
|
}
|
|
|
|
static uint32_t defCarrierFreq()
|
|
{
|
|
return DEFAULT_CARRIER_FREQUENCY_433;
|
|
}
|
|
|
|
static uint32_t bindingFreq()
|
|
{
|
|
return BINDING_FREQUENCY_433;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* OpenLRS data formatting utilities
|
|
*****************************************************************************/
|
|
|
|
static uint8_t getPacketSize(struct bind_data *bd)
|
|
{
|
|
return pktsizes[(bd->flags & 0x07)];
|
|
}
|
|
|
|
static uint32_t getInterval(struct bind_data *bd)
|
|
{
|
|
uint32_t ret;
|
|
|
|
// Sending a x byte packet on bps y takes about (emperical)
|
|
// usec = (x + 15) * 8200000 / baudrate
|
|
#define BYTES_AT_BAUD_TO_USEC(bytes, bps, div) ((uint32_t)((bytes) + (div ? 20 : 15)) * 8200000L / (uint32_t)(bps))
|
|
|
|
ret = (BYTES_AT_BAUD_TO_USEC(getPacketSize(bd), modem_params[bd->modem_params].bps, bd->flags & DIVERSITY_ENABLED) + 2000);
|
|
|
|
if (bd->flags & TELEMETRY_MASK) {
|
|
ret += (BYTES_AT_BAUD_TO_USEC(TELEMETRY_PACKETSIZE, modem_params[bd->modem_params].bps, bd->flags & DIVERSITY_ENABLED) + 1000);
|
|
}
|
|
|
|
// round up to ms
|
|
ret = ((ret + 999) / 1000) * 1000;
|
|
|
|
// enable following to limit packet rate to 50Hz at most
|
|
#ifdef LIMIT_RATE_TO_50HZ
|
|
if (ret < 20000) {
|
|
ret = 20000;
|
|
}
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void unpackChannels(uint8_t config, int16_t PPM[], uint8_t *p)
|
|
{
|
|
uint8_t i;
|
|
|
|
for (i = 0; i <= (config / 2); i++) { // 4ch packed in 5 bytes
|
|
PPM[0] = (((uint16_t)p[4] & 0x03) << 8) + p[0];
|
|
PPM[1] = (((uint16_t)p[4] & 0x0c) << 6) + p[1];
|
|
PPM[2] = (((uint16_t)p[4] & 0x30) << 4) + p[2];
|
|
PPM[3] = (((uint16_t)p[4] & 0xc0) << 2) + p[3];
|
|
p += 5;
|
|
PPM += 4;
|
|
}
|
|
if (config & 1) { // 4ch packed in 1 byte;
|
|
PPM[0] = (((uint16_t)p[0] >> 6) & 3) * 333 + 12;
|
|
PPM[1] = (((uint16_t)p[0] >> 4) & 3) * 333 + 12;
|
|
PPM[2] = (((uint16_t)p[0] >> 2) & 3) * 333 + 12;
|
|
PPM[3] = (((uint16_t)p[0] >> 0) & 3) * 333 + 12;
|
|
}
|
|
}
|
|
|
|
// ! Apply the OpenLRS rescaling to the channels
|
|
static void rescaleChannels(int16_t PPM[])
|
|
{
|
|
for (uint32_t i = 0; i < OPENLRS_PPM_NUM_CHANNELS; i++) {
|
|
int16_t x = PPM[i];
|
|
int16_t ret;
|
|
|
|
if (x < 12) {
|
|
ret = 808 + x * 16;
|
|
} else if (x < 1012) {
|
|
ret = x + 988;
|
|
} else if (x < 1024) {
|
|
ret = 2000 + (x - 1011) * 16;
|
|
} else {
|
|
ret = 2192;
|
|
}
|
|
|
|
PPM[i] = ret;
|
|
}
|
|
}
|
|
|
|
static uint8_t countSetBits(uint16_t x)
|
|
{
|
|
x = x - ((x >> 1) & 0x5555);
|
|
x = (x & 0x3333) + ((x >> 2) & 0x3333);
|
|
x = x + (x >> 4);
|
|
x &= 0x0F0F;
|
|
return (x * 0x0101) >> 8;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* OpenLRS hardware access
|
|
*****************************************************************************/
|
|
|
|
#define NOP() __asm__ __volatile__ ("nop")
|
|
|
|
#define RF22B_PWRSTATE_POWERDOWN 0x00
|
|
#define RF22B_PWRSTATE_READY RFM22_opfc1_xton
|
|
#define RF22B_PWRSTATE_RX (RFM22_opfc1_rxon | RFM22_opfc1_xton)
|
|
#define RF22B_PWRSTATE_TX (RFM22_opfc1_txon | RFM22_opfc1_xton)
|
|
|
|
#define RF22B_PACKET_SENT_INTERRUPT RFM22_ie1_enpksent
|
|
#define RF22B_RX_PACKET_RECEIVED_IRQ RFM22_ie1_enpkvalid
|
|
|
|
static void rfmSetChannel(struct pios_openlrs_dev *openlrs_dev, uint8_t ch)
|
|
{
|
|
// DEBUG_PRINTF(3,"rfmSetChannel %d\r\n", ch);
|
|
uint8_t magicLSB = (openlrs_dev->bind_data.rf_magic & 0xff) ^ ch;
|
|
|
|
rfm22_claimBus(openlrs_dev);
|
|
rfm22_write(openlrs_dev, RFM22_frequency_hopping_channel_select, openlrs_dev->bind_data.hopchannel[ch]);
|
|
rfm22_write(openlrs_dev, RFM22_transmit_header3 + 3, magicLSB);
|
|
rfm22_write(openlrs_dev, RFM22_check_header3 + 3, magicLSB);
|
|
rfm22_releaseBus(openlrs_dev);
|
|
}
|
|
|
|
static uint8_t rfmGetRSSI(struct pios_openlrs_dev *openlrs_dev)
|
|
{
|
|
return rfm22_read_claim(openlrs_dev, 0x26);
|
|
}
|
|
|
|
static uint16_t rfmGetAFCC(struct pios_openlrs_dev *openlrs_dev)
|
|
{
|
|
return ((uint16_t)rfm22_read_claim(openlrs_dev, 0x2B) << 2) | ((uint16_t)rfm22_read_claim(openlrs_dev, 0x2C) >> 6);
|
|
}
|
|
|
|
static void setModemRegs(struct pios_openlrs_dev *openlrs_dev, const struct rfm22_modem_regs *r)
|
|
{
|
|
DEBUG_PRINTF(3, "setModemRegs\r\n");
|
|
rfm22_claimBus(openlrs_dev);
|
|
rfm22_write(openlrs_dev, RFM22_if_filter_bandwidth, r->r_1c);
|
|
rfm22_write(openlrs_dev, RFM22_afc_loop_gearshift_override, r->r_1d);
|
|
rfm22_write(openlrs_dev, RFM22_afc_timing_control, r->r_1e);
|
|
rfm22_write(openlrs_dev, RFM22_clk_recovery_oversampling_ratio, r->r_20);
|
|
rfm22_write(openlrs_dev, RFM22_clk_recovery_offset2, r->r_21);
|
|
rfm22_write(openlrs_dev, RFM22_clk_recovery_offset1, r->r_22);
|
|
rfm22_write(openlrs_dev, RFM22_clk_recovery_offset0, r->r_23);
|
|
rfm22_write(openlrs_dev, RFM22_clk_recovery_timing_loop_gain1, r->r_24);
|
|
rfm22_write(openlrs_dev, RFM22_clk_recovery_timing_loop_gain0, r->r_25);
|
|
rfm22_write(openlrs_dev, RFM22_afc_limiter, r->r_2a);
|
|
rfm22_write(openlrs_dev, RFM22_tx_data_rate1, r->r_6e);
|
|
rfm22_write(openlrs_dev, RFM22_tx_data_rate0, r->r_6f);
|
|
rfm22_write(openlrs_dev, RFM22_modulation_mode_control1, r->r_70);
|
|
rfm22_write(openlrs_dev, RFM22_modulation_mode_control2, r->r_71);
|
|
rfm22_write(openlrs_dev, RFM22_frequency_deviation, r->r_72);
|
|
rfm22_releaseBus(openlrs_dev);
|
|
}
|
|
|
|
static void rfmSetCarrierFrequency(struct pios_openlrs_dev *openlrs_dev, uint32_t f)
|
|
{
|
|
/* Protect ourselves from out-of-band frequencies. Ideally we'd latch
|
|
* an error here and prevent tx, but this is good enough to protect
|
|
* the hardware. */
|
|
if ((f < minFreq(openlrs_dev->band)) ||
|
|
(f > maxFreq(openlrs_dev->band))) {
|
|
f = defCarrierFreq(openlrs_dev->band);
|
|
}
|
|
|
|
// DEBUG_PRINTF(3,"rfmSetCarrierFrequency %d\r\n", f);
|
|
uint16_t fb, fc, hbsel;
|
|
if (f < 480000000) {
|
|
hbsel = 0;
|
|
fb = f / 10000000 - 24;
|
|
fc = (f - (fb + 24) * 10000000) * 4 / 625;
|
|
} else {
|
|
hbsel = 1;
|
|
fb = f / 20000000 - 24;
|
|
fc = (f - (fb + 24) * 20000000) * 2 / 625;
|
|
}
|
|
rfm22_claimBus(openlrs_dev);
|
|
rfm22_write(openlrs_dev, RFM22_frequency_band_select, RFM22_fbs_sbse + (hbsel ? RFM22_fbs_hbsel : 0) + (fb & RFM22_fb_mask));
|
|
rfm22_write(openlrs_dev, RFM22_nominal_carrier_frequency1, (fc >> 8));
|
|
rfm22_write(openlrs_dev, RFM22_nominal_carrier_frequency0, (fc & 0xff));
|
|
rfm22_releaseBus(openlrs_dev);
|
|
}
|
|
|
|
static void init_rfm(struct pios_openlrs_dev *openlrs_dev, uint8_t isbind)
|
|
{
|
|
DEBUG_PRINTF(2, "init_rfm %d\r\n", isbind);
|
|
|
|
if (!isbind) {
|
|
DEBUG_PRINTF(2, "Binding settings:\r\n");
|
|
PIOS_Thread_Sleep(10);
|
|
DEBUG_PRINTF(2, " version: %d\r\n", openlrs_dev->bind_data.version);
|
|
PIOS_Thread_Sleep(10);
|
|
DEBUG_PRINTF(2, " serial_baudrate: %d\r\n", openlrs_dev->bind_data.serial_baudrate);
|
|
PIOS_Thread_Sleep(10);
|
|
DEBUG_PRINTF(2, " rf_frequency: %d\r\n", openlrs_dev->bind_data.rf_frequency);
|
|
PIOS_Thread_Sleep(10);
|
|
DEBUG_PRINTF(2, " rf_power: %d\r\n", openlrs_dev->bind_data.rf_power);
|
|
PIOS_Thread_Sleep(10);
|
|
DEBUG_PRINTF(2, " rf_channel_spacing: %d\r\n", openlrs_dev->bind_data.rf_channel_spacing);
|
|
PIOS_Thread_Sleep(10);
|
|
DEBUG_PRINTF(2, " modem_params: %d\r\n", openlrs_dev->bind_data.modem_params);
|
|
PIOS_Thread_Sleep(10);
|
|
DEBUG_PRINTF(2, " flags: %d\r\n", openlrs_dev->bind_data.flags);
|
|
PIOS_Thread_Sleep(10);
|
|
}
|
|
|
|
rfm22_claimBus(openlrs_dev);
|
|
openlrs_dev->it_status1 = rfm22_read(openlrs_dev, RFM22_interrupt_status1); // read status, clear interrupt
|
|
openlrs_dev->it_status2 = rfm22_read(openlrs_dev, RFM22_interrupt_status2);
|
|
rfm22_write(openlrs_dev, RFM22_interrupt_enable2, 0x00); // disable interrupts
|
|
rfm22_write(openlrs_dev, RFM22_op_and_func_ctrl1, RF22B_PWRSTATE_READY); // disable lbd, wakeup timer, use internal 32768,xton = 1; in ready mode
|
|
rfm22_write(openlrs_dev, RFM22_xtal_osc_load_cap, 0x7f); // c = 12.5p
|
|
rfm22_write(openlrs_dev, RFM22_cpu_output_clk, 0x05);
|
|
switch (openlrs_dev->cfg.gpio_direction) {
|
|
case GPIO0_TX_GPIO1_RX:
|
|
rfm22_write(openlrs_dev, RFM22_gpio0_config, RFM22_gpio0_config_txstate); // gpio0 TX State
|
|
rfm22_write(openlrs_dev, RFM22_gpio1_config, RFM22_gpio1_config_rxstate); // gpio1 RX State
|
|
break;
|
|
case GPIO0_RX_GPIO1_TX:
|
|
rfm22_write(openlrs_dev, RFM22_gpio0_config, RFM22_gpio0_config_rxstate); // gpio0 RX State
|
|
rfm22_write(openlrs_dev, RFM22_gpio1_config, RFM22_gpio1_config_txstate); // gpio1 TX State
|
|
break;
|
|
}
|
|
rfm22_write(openlrs_dev, RFM22_gpio2_config, 0xfd); // gpio 2 VDD
|
|
rfm22_write(openlrs_dev, RFM22_io_port_config, RFM22_io_port_default); // gpio 0, 1,2 NO OTHER FUNCTION.
|
|
rfm22_releaseBus(openlrs_dev);
|
|
|
|
if (isbind) {
|
|
setModemRegs(openlrs_dev, &bind_params);
|
|
} else {
|
|
setModemRegs(openlrs_dev, &modem_params[openlrs_dev->bind_data.modem_params]);
|
|
}
|
|
|
|
// Packet settings
|
|
rfm22_claimBus(openlrs_dev);
|
|
rfm22_write(openlrs_dev, RFM22_data_access_control, 0x8c); // enable packet handler, msb first, enable crc,
|
|
rfm22_write(openlrs_dev, RFM22_header_control1, 0x0f); // no broadcast, check header bytes 3,2,1,0
|
|
rfm22_write(openlrs_dev, RFM22_header_control2, 0x42); // 4 byte header, 2 byte synch, variable pkt size
|
|
rfm22_write(openlrs_dev, RFM22_preamble_length, (openlrs_dev->bind_data.flags & DIVERSITY_ENABLED) ? 0x14 : 0x0a); // 40 bit preamble, 80 with diversity
|
|
rfm22_write(openlrs_dev, RFM22_preamble_detection_ctrl1, 0x2a); // preath = 5 (20bits), rssioff = 2
|
|
rfm22_write(openlrs_dev, RFM22_sync_word3, 0x2d); // synchronize word 3
|
|
rfm22_write(openlrs_dev, RFM22_sync_word2, 0xd4); // synchronize word 2
|
|
rfm22_write(openlrs_dev, RFM22_sync_word1, 0x00); // synch word 1 (not used)
|
|
rfm22_write(openlrs_dev, RFM22_sync_word0, 0x00); // synch word 0 (not used)
|
|
|
|
uint32_t magic = isbind ? BIND_MAGIC : openlrs_dev->bind_data.rf_magic;
|
|
for (uint8_t i = 0; i < 4; i++) {
|
|
rfm22_write(openlrs_dev, RFM22_transmit_header3 + i, (magic >> 24) & 0xff); // tx header
|
|
rfm22_write(openlrs_dev, RFM22_check_header3 + i, (magic >> 24) & 0xff); // rx header
|
|
magic = magic << 8; // advance to next byte
|
|
}
|
|
|
|
rfm22_write(openlrs_dev, RFM22_header_enable3, 0xff); // all the bit to be checked
|
|
rfm22_write(openlrs_dev, RFM22_header_enable2, 0xff); // all the bit to be checked
|
|
rfm22_write(openlrs_dev, RFM22_header_enable1, 0xff); // all the bit to be checked
|
|
rfm22_write(openlrs_dev, RFM22_header_enable0, 0xff); // all the bit to be checked
|
|
|
|
if (isbind) {
|
|
rfm22_write(openlrs_dev, RFM22_tx_power, BINDING_POWER);
|
|
} else {
|
|
rfm22_write(openlrs_dev, RFM22_tx_power, openlrs_dev->bind_data.rf_power);
|
|
}
|
|
|
|
rfm22_write(openlrs_dev, RFM22_frequency_hopping_channel_select, 0);
|
|
rfm22_write(openlrs_dev, RFM22_frequency_hopping_step_size, openlrs_dev->bind_data.rf_channel_spacing); // channel spacing
|
|
|
|
rfm22_write(openlrs_dev, RFM22_frequency_offset1, 0x00);
|
|
rfm22_write(openlrs_dev, RFM22_frequency_offset2, 0x00); // no offset
|
|
|
|
rfm22_releaseBus(openlrs_dev);
|
|
|
|
rfmSetCarrierFrequency(openlrs_dev, isbind ? bindingFreq(openlrs_dev->band) : openlrs_dev->bind_data.rf_frequency);
|
|
}
|
|
|
|
static void to_rx_mode(struct pios_openlrs_dev *openlrs_dev)
|
|
{
|
|
// DEBUG_PRINTF(3,"to_rx_mode\r\n");
|
|
rfm22_claimBus(openlrs_dev);
|
|
openlrs_dev->it_status1 = rfm22_read(openlrs_dev, RFM22_interrupt_status1);
|
|
openlrs_dev->it_status2 = rfm22_read(openlrs_dev, RFM22_interrupt_status2);
|
|
rfm22_write(openlrs_dev, RFM22_op_and_func_ctrl1, RF22B_PWRSTATE_READY);
|
|
rfm22_releaseBus(openlrs_dev);
|
|
PIOS_Thread_Sleep(10);
|
|
rx_reset(openlrs_dev);
|
|
NOP();
|
|
}
|
|
|
|
static void clearFIFO(struct pios_openlrs_dev *openlrs_dev)
|
|
{
|
|
// DEBUG_PRINTF(3,"clearFIFO\r\n");
|
|
rfm22_claimBus(openlrs_dev);
|
|
rfm22_write(openlrs_dev, RFM22_op_and_func_ctrl2, 0x03);
|
|
rfm22_write(openlrs_dev, RFM22_op_and_func_ctrl2, 0x00);
|
|
rfm22_releaseBus(openlrs_dev);
|
|
}
|
|
|
|
static void rx_reset(struct pios_openlrs_dev *openlrs_dev)
|
|
{
|
|
// DEBUG_PRINTF(3,"rx_reset\r\n");
|
|
rfm22_write_claim(openlrs_dev, RFM22_op_and_func_ctrl1, RF22B_PWRSTATE_READY);
|
|
rfm22_write_claim(openlrs_dev, RFM22_rx_fifo_control, 36); // threshold for rx almost full, interrupt when 1 byte received
|
|
clearFIFO(openlrs_dev);
|
|
rfm22_claimBus(openlrs_dev);
|
|
rfm22_write(openlrs_dev, RFM22_op_and_func_ctrl1, RF22B_PWRSTATE_RX); // to rx mode
|
|
rfm22_write(openlrs_dev, RFM22_interrupt_enable1, RF22B_RX_PACKET_RECEIVED_IRQ);
|
|
openlrs_dev->it_status1 = rfm22_read(openlrs_dev, RFM22_interrupt_status1); // read the Interrupt Status1 register
|
|
openlrs_dev->it_status2 = rfm22_read(openlrs_dev, RFM22_interrupt_status2);
|
|
rfm22_releaseBus(openlrs_dev);
|
|
}
|
|
|
|
// TODO: move into dev structure
|
|
uint32_t tx_start = 0;
|
|
|
|
static void tx_packet_async(struct pios_openlrs_dev *openlrs_dev, uint8_t *pkt, uint8_t size)
|
|
{
|
|
rfm22_claimBus(openlrs_dev);
|
|
rfm22_write(openlrs_dev, RFM22_transmit_packet_length, size); // total tx size
|
|
|
|
for (uint8_t i = 0; i < size; i++) {
|
|
rfm22_write(openlrs_dev, RFM22_fifo_access, pkt[i]);
|
|
}
|
|
|
|
rfm22_write(openlrs_dev, RFM22_interrupt_enable1, RF22B_PACKET_SENT_INTERRUPT);
|
|
openlrs_dev->it_status1 = rfm22_read(openlrs_dev, RFM22_interrupt_status1); // read the Interrupt Status1 register
|
|
openlrs_dev->it_status2 = rfm22_read(openlrs_dev, RFM22_interrupt_status2);
|
|
tx_start = PIOS_DELAY_GetuS();
|
|
rfm22_write(openlrs_dev, RFM22_op_and_func_ctrl1, RF22B_PWRSTATE_TX); // to tx mode
|
|
rfm22_releaseBus(openlrs_dev);
|
|
|
|
openlrs_dev->rf_mode = Transmit;
|
|
}
|
|
|
|
static void tx_packet(struct pios_openlrs_dev *openlrs_dev, uint8_t *pkt, uint8_t size)
|
|
{
|
|
tx_packet_async(openlrs_dev, pkt, size);
|
|
PIOS_Semaphore_Take(openlrs_dev->sema_isr, 25);
|
|
|
|
#if defined(PIOS_INCLUDE_WDG) && defined(PIOS_WDG_RFM22B)
|
|
// Update the watchdog timer
|
|
PIOS_WDG_UpdateFlag(PIOS_WDG_RFM22B);
|
|
#endif /* PIOS_WDG_RFM22B */
|
|
|
|
if (openlrs_dev->rf_mode == Transmit) {
|
|
DEBUG_PRINTF(2, "OLRS ERR: tx_packet timeout\r\n");
|
|
init_rfm(openlrs_dev, false); // reset modem
|
|
}
|
|
}
|
|
|
|
#ifdef OPENLRS_SIMPLE_BEACON_TONE
|
|
// Basic tone
|
|
static void beacon_simpletone(struct pios_openlrs_dev *openlrs_dev, uint8_t tone, int16_t len __attribute__((unused)))
|
|
{
|
|
DEBUG_PRINTF(2, "beacon_morse: %d\r\n", len);
|
|
|
|
#if defined(PIOS_LED_LINK)
|
|
PIOS_LED_On(PIOS_LED_LINK);
|
|
#endif /* PIOS_LED_LINK */
|
|
|
|
rfm22_claimBus(openlrs_dev);
|
|
|
|
GPIO_TypeDef *gpio = openlrs_dev->cfg.spi_cfg->mosi.gpio;
|
|
uint16_t pin_source = openlrs_dev->cfg.spi_cfg->mosi.init.GPIO_Pin;
|
|
uint8_t remap = openlrs_dev->cfg.spi_cfg->remap;
|
|
|
|
GPIO_InitTypeDef init = {
|
|
.GPIO_Speed = GPIO_Speed_50MHz,
|
|
.GPIO_Mode = GPIO_Mode_OUT,
|
|
.GPIO_OType = GPIO_OType_PP,
|
|
.GPIO_PuPd = GPIO_PuPd_UP
|
|
};
|
|
init.GPIO_Pin = pin_source;
|
|
|
|
// Set MOSI to digital out for bit banging
|
|
GPIO_PinAFConfig(gpio, pin_source, 0);
|
|
GPIO_Init(gpio, &init);
|
|
|
|
int16_t cycles = (len * 50) / tone;
|
|
for (int16_t i = 0; i < cycles; i++) {
|
|
GPIO_SetBits(gpio, pin_source);
|
|
PIOS_Thread_Sleep(tone);
|
|
GPIO_ResetBits(gpio, pin_source);
|
|
PIOS_Thread_Sleep(tone);
|
|
}
|
|
GPIO_Init(gpio, (GPIO_InitTypeDef *)&openlrs_dev->cfg.spi_cfg->mosi.init);
|
|
GPIO_PinAFConfig(gpio, pin_source, remap);
|
|
rfm22_releaseBus(openlrs_dev);
|
|
|
|
#if defined(PIOS_LED_LINK)
|
|
PIOS_LED_Off(PIOS_LED_LINK);
|
|
#endif /* PIOS_LED_LINK */
|
|
|
|
#if defined(PIOS_INCLUDE_WDG) && defined(PIOS_WDG_RFM22B)
|
|
// Update the watchdog timer
|
|
PIOS_WDG_UpdateFlag(PIOS_WDG_RFM22B);
|
|
#endif /* PIOS_WDG_RFM22B */
|
|
}
|
|
|
|
#else /* OPENLRS_SIMPLE_BEACON_TONE */
|
|
|
|
static void beacon_tone(struct pios_openlrs_dev *openlrs_dev, int16_t hz, int16_t len __attribute__((unused))) // duration is now in half seconds.
|
|
{
|
|
DEBUG_PRINTF(2, "beacon_tone: %d %d\r\n", hz, len * 2);
|
|
int16_t d = 500000 / hz; // better resolution
|
|
|
|
#if defined(PIOS_LED_LINK)
|
|
PIOS_LED_On(PIOS_LED_LINK);
|
|
#endif /* PIOS_LED_LINK */
|
|
|
|
if (d < 1) {
|
|
d = 1;
|
|
}
|
|
|
|
rfm22_claimBus(openlrs_dev);
|
|
|
|
// This need fixed for F1
|
|
#ifdef GPIO_MODE_OUT
|
|
GPIO_TypeDef *gpio = openlrs_dev->cfg.spi_cfg->mosi.gpio;
|
|
uint16_t pin_source = openlrs_dev->cfg.spi_cfg->mosi.init.GPIO_Pin;
|
|
uint8_t remap = openlrs_dev->cfg.spi_cfg->remap;
|
|
|
|
GPIO_InitTypeDef init = {
|
|
.GPIO_Speed = GPIO_Speed_50MHz,
|
|
.GPIO_Mode = GPIO_Mode_OUT,
|
|
.GPIO_OType = GPIO_OType_PP,
|
|
.GPIO_PuPd = GPIO_PuPd_UP
|
|
};
|
|
init.GPIO_Pin = pin_source;
|
|
|
|
// Set MOSI to digital out for bit banging
|
|
GPIO_PinAFConfig(gpio, pin_source, 0);
|
|
GPIO_Init(gpio, &init);
|
|
|
|
uint32_t raw_time = PIOS_DELAY_GetRaw();
|
|
int16_t cycles = (len * 500000 / d);
|
|
for (int16_t i = 0; i < cycles; i++) {
|
|
GPIO_SetBits(gpio, pin_source);
|
|
PIOS_DELAY_WaituS(d);
|
|
GPIO_ResetBits(gpio, pin_source);
|
|
PIOS_DELAY_WaituS(d);
|
|
|
|
// Make sure to give other tasks time to do things
|
|
if (PIOS_DELAY_DiffuS(raw_time) > 10000) {
|
|
PIOS_Thread_Sleep(1);
|
|
raw_time = PIOS_DELAY_GetRaw();
|
|
}
|
|
}
|
|
|
|
GPIO_Init(gpio, (GPIO_InitTypeDef *)&openlrs_dev->cfg.spi_cfg->mosi.init);
|
|
GPIO_PinAFConfig(gpio, pin_source, remap);
|
|
#endif /* ifdef GPIO_MODE_OUT */
|
|
rfm22_releaseBus(openlrs_dev);
|
|
|
|
#if defined(PIOS_LED_LINK)
|
|
PIOS_LED_Off(PIOS_LED_LINK);
|
|
#endif /* PIOS_LED_LINK */
|
|
|
|
#if defined(PIOS_INCLUDE_WDG) && defined(PIOS_WDG_RFM22B)
|
|
// Update the watchdog timer
|
|
PIOS_WDG_UpdateFlag(PIOS_WDG_RFM22B);
|
|
#endif /* PIOS_WDG_RFM22B */
|
|
}
|
|
|
|
#endif /* OPENLRS_SIMPLE_BEACON_TONE */
|
|
|
|
static uint8_t beaconGetRSSI(struct pios_openlrs_dev *openlrs_dev)
|
|
{
|
|
uint16_t rssiSUM = 0;
|
|
|
|
rfmSetCarrierFrequency(openlrs_dev, openlrs_dev->beacon_frequency);
|
|
rfm22_write_claim(openlrs_dev, RFM22_frequency_hopping_channel_select, 0); // ch 0 to avoid offset
|
|
PIOS_Thread_Sleep(1);
|
|
rssiSUM += rfmGetRSSI(openlrs_dev);
|
|
PIOS_Thread_Sleep(1);
|
|
rssiSUM += rfmGetRSSI(openlrs_dev);
|
|
PIOS_Thread_Sleep(1);
|
|
rssiSUM += rfmGetRSSI(openlrs_dev);
|
|
PIOS_Thread_Sleep(1);
|
|
rssiSUM += rfmGetRSSI(openlrs_dev);
|
|
|
|
return rssiSUM >> 2;
|
|
}
|
|
|
|
static void beacon_send(struct pios_openlrs_dev *openlrs_dev, bool static_tone)
|
|
{
|
|
DEBUG_PRINTF(2, "beacon_send\r\n");
|
|
rfm22_claimBus(openlrs_dev);
|
|
openlrs_dev->it_status1 = rfm22_read(openlrs_dev, 0x03); // read status, clear interrupt
|
|
openlrs_dev->it_status2 = rfm22_read(openlrs_dev, 0x04);
|
|
rfm22_write(openlrs_dev, 0x06, 0x00); // no wakeup up, lbd,
|
|
rfm22_write(openlrs_dev, 0x07, RF22B_PWRSTATE_READY); // disable lbd, wakeup timer, use internal 32768,xton = 1; in ready mode
|
|
rfm22_write(openlrs_dev, 0x09, 0x7f); // (default) c = 12.5p
|
|
rfm22_write(openlrs_dev, 0x0a, 0x05);
|
|
rfm22_write(openlrs_dev, 0x0b, 0x12); // gpio0 TX State
|
|
rfm22_write(openlrs_dev, 0x0c, 0x15); // gpio1 RX State
|
|
rfm22_write(openlrs_dev, 0x0d, 0xfd); // gpio 2 micro-controller clk output
|
|
rfm22_write(openlrs_dev, 0x0e, 0x00); // gpio 0, 1,2 NO OTHER FUNCTION.
|
|
|
|
rfm22_write(openlrs_dev, 0x70, 0x2C); // disable manchest
|
|
|
|
rfm22_write(openlrs_dev, 0x30, 0x00); // disable packet handling
|
|
|
|
rfm22_write(openlrs_dev, 0x79, 0); // start channel
|
|
|
|
rfm22_write(openlrs_dev, 0x7a, 0x05); // 50khz step size (10khz x value) // no hopping
|
|
|
|
rfm22_write(openlrs_dev, 0x71, 0x12); // trclk=[00] no clock, dtmod=[01] direct using SPI, fd8=0 eninv=0 modtyp=[10] FSK
|
|
rfm22_write(openlrs_dev, 0x72, 0x02); // fd (frequency deviation) 2*625Hz == 1.25kHz
|
|
|
|
rfm22_write(openlrs_dev, 0x73, 0x00);
|
|
rfm22_write(openlrs_dev, 0x74, 0x00); // no offset
|
|
rfm22_releaseBus(openlrs_dev);
|
|
|
|
rfmSetCarrierFrequency(openlrs_dev, openlrs_dev->beacon_frequency);
|
|
|
|
rfm22_write_claim(openlrs_dev, 0x6d, 0x07); // 7 set max power 100mW
|
|
|
|
PIOS_Thread_Sleep(10);
|
|
rfm22_write_claim(openlrs_dev, 0x07, RF22B_PWRSTATE_TX); // to tx mode
|
|
PIOS_Thread_Sleep(10);
|
|
|
|
if (static_tone) {
|
|
uint8_t i = 0;
|
|
while (i++ < 20) {
|
|
#ifdef OPENLRS_SIMPLE_BEACON_TONE
|
|
beacon_simpletone(openlrs_dev, 2, 3);
|
|
#else
|
|
beacon_tone(openlrs_dev, 440, 1);
|
|
#endif
|
|
}
|
|
} else {
|
|
#ifdef OPENLRS_SIMPLE_BEACON_TONE
|
|
for (uint32_t tone = 1; tone < 4; tone++) {
|
|
if (tone == 1) {
|
|
rfm22_write(openlrs_dev, 0x6d, 0x05); // 5 set min power 25mW
|
|
} else {
|
|
rfm22_write(openlrs_dev, 0x6d, 0x00); // 0 set min power 1.3mW
|
|
}
|
|
// L - --- - -
|
|
beacon_simpletone(openlrs_dev, tone, 1);
|
|
PIOS_Thread_Sleep(100);
|
|
beacon_simpletone(openlrs_dev, tone, 3);
|
|
PIOS_Thread_Sleep(100);
|
|
beacon_simpletone(openlrs_dev, tone, 1);
|
|
PIOS_Thread_Sleep(100);
|
|
beacon_simpletone(openlrs_dev, tone, 1);
|
|
PIOS_Thread_Sleep(600);
|
|
// P - --- --- -
|
|
beacon_simpletone(openlrs_dev, tone, 1);
|
|
PIOS_Thread_Sleep(100);
|
|
beacon_simpletone(openlrs_dev, tone, 3);
|
|
PIOS_Thread_Sleep(100);
|
|
beacon_simpletone(openlrs_dev, tone, 3);
|
|
PIOS_Thread_Sleep(100);
|
|
beacon_simpletone(openlrs_dev, tone, 1);
|
|
PIOS_Thread_Sleep(2000);
|
|
}
|
|
#else /* ifdef OPENLRS_SIMPLE_BEACON_TONE */
|
|
// close encounters tune
|
|
// G, A, F, F(lower octave), C
|
|
// octave 3: 392 440 349 175 261
|
|
|
|
beacon_tone(openlrs_dev, 392, 1);
|
|
|
|
rfm22_write(openlrs_dev, 0x6d, 0x05); // 5 set mid power 25mW
|
|
PIOS_Thread_Sleep(10);
|
|
beacon_tone(openlrs_dev, 440, 1);
|
|
|
|
rfm22_write(openlrs_dev, 0x6d, 0x04); // 4 set mid power 13mW
|
|
PIOS_Thread_Sleep(10);
|
|
beacon_tone(openlrs_dev, 349, 1);
|
|
|
|
rfm22_write(openlrs_dev, 0x6d, 0x02); // 2 set min power 3mW
|
|
PIOS_Thread_Sleep(10);
|
|
beacon_tone(openlrs_dev, 175, 1);
|
|
|
|
rfm22_write(openlrs_dev, 0x6d, 0x00); // 0 set min power 1.3mW
|
|
PIOS_Thread_Sleep(10);
|
|
beacon_tone(openlrs_dev, 261, 2);
|
|
#endif /* #ifdef OPENLRS_SIMPLE_BEACON_TONE */
|
|
}
|
|
rfm22_write_claim(openlrs_dev, 0x07, RF22B_PWRSTATE_READY);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* High level OpenLRS functions
|
|
*****************************************************************************/
|
|
|
|
// TODO: these should move into device structure, or deleted
|
|
// if not useful to be reported via GCS
|
|
|
|
#define ntohl(v) \
|
|
( \
|
|
(((v) & 0xFF000000) >> 24) | \
|
|
(((v) & 0x00FF0000) >> 8) | \
|
|
(((v) & 0x0000FF00) << 8) | \
|
|
(((v) & 0x000000FF) << 24))
|
|
|
|
static uint8_t pios_openlrs_bind_receive(struct pios_openlrs_dev *openlrs_dev, uint32_t timeout)
|
|
{
|
|
uint32_t start = PIOS_Thread_Systime();
|
|
uint8_t rxb;
|
|
|
|
init_rfm(openlrs_dev, true);
|
|
// TODO: move openlrs_dev->rf_mode into dev structure
|
|
openlrs_dev->rf_mode = Receive;
|
|
to_rx_mode(openlrs_dev);
|
|
DEBUG_PRINTF(2, "Waiting bind\r\n");
|
|
DEBUG_PRINTF(2, "timeout: %d\r\n", timeout);
|
|
DEBUG_PRINTF(2, "start: %d\r\n", start);
|
|
|
|
uint32_t i = 0;
|
|
|
|
while ((!timeout) || ((PIOS_Thread_Systime() - start) < timeout)) {
|
|
PIOS_Thread_Sleep(1);
|
|
#if defined(PIOS_INCLUDE_WDG) && defined(PIOS_WDG_RFM22B)
|
|
// Update the watchdog timer
|
|
PIOS_WDG_UpdateFlag(PIOS_WDG_RFM22B);
|
|
#endif /* PIOS_WDG_RFM22B */
|
|
|
|
if (i++ % 100 == 0) {
|
|
// DEBUG_PRINTF(2,"Waiting bind\r\n");
|
|
|
|
#if defined(PIOS_LED_LINK)
|
|
PIOS_LED_Toggle(PIOS_LED_LINK);
|
|
#endif /* PIOS_LED_LINK */
|
|
}
|
|
if (openlrs_dev->rf_mode == Received) {
|
|
DEBUG_PRINTF(2, "Got pkt\r\n");
|
|
|
|
// TODO: parse data packet (write command for that)
|
|
rfm22_claimBus(openlrs_dev);
|
|
rfm22_assertCs(openlrs_dev);
|
|
PIOS_SPI_TransferByte(openlrs_dev->spi_id, 0x7f);
|
|
rxb = PIOS_SPI_TransferByte(openlrs_dev->spi_id, 0x00);
|
|
if (rxb == 'b') {
|
|
PIOS_SPI_TransferBlock(openlrs_dev->spi_id, OUT_FF,
|
|
(uint8_t *)&openlrs_dev->bind_data, sizeof(struct bind_data), NULL);
|
|
rfm22_deassertCs(openlrs_dev);
|
|
rfm22_releaseBus(openlrs_dev);
|
|
|
|
#if defined(PIOS_INCLUDE_DEBUG_CONSOLE)
|
|
if (2 <= DEBUG_LEVEL && pios_com_debug_id > 0) {
|
|
DEBUG_PRINTF(2, "Binding settings:\r\n");
|
|
PIOS_Thread_Sleep(10);
|
|
DEBUG_PRINTF(2, " version: %d\r\n", openlrs_dev->bind_data.version);
|
|
PIOS_Thread_Sleep(10);
|
|
DEBUG_PRINTF(2, " serial_baudrate: %d\r\n", openlrs_dev->bind_data.serial_baudrate);
|
|
PIOS_Thread_Sleep(10);
|
|
DEBUG_PRINTF(2, " rf_frequency: %d\r\n", openlrs_dev->bind_data.rf_frequency);
|
|
PIOS_Thread_Sleep(10);
|
|
DEBUG_PRINTF(2, " rf_power: %d\r\n", openlrs_dev->bind_data.rf_power);
|
|
PIOS_Thread_Sleep(10);
|
|
DEBUG_PRINTF(2, " rf_channel_spacing: %d\r\n", openlrs_dev->bind_data.rf_channel_spacing);
|
|
PIOS_Thread_Sleep(10);
|
|
DEBUG_PRINTF(2, " modem_params: %d\r\n", openlrs_dev->bind_data.modem_params);
|
|
PIOS_Thread_Sleep(10);
|
|
DEBUG_PRINTF(2, " flags: %d\r\n", openlrs_dev->bind_data.flags);
|
|
PIOS_Thread_Sleep(10);
|
|
|
|
for (uint32_t j = 0; j < MAXHOPS; j++) {
|
|
DEBUG_PRINTF(2, " hop channel: %d\r\n", openlrs_dev->bind_data.hopchannel[j]);
|
|
PIOS_Thread_Sleep(10);
|
|
}
|
|
}
|
|
#endif /* PIOS_INCLUDE_DEBUG_CONSOLE */
|
|
|
|
if (openlrs_dev->bind_data.version == BINDING_VERSION) {
|
|
DEBUG_PRINTF(2, "data good\r\n");
|
|
rxb = 'B';
|
|
tx_packet(openlrs_dev, &rxb, 1); // ACK that we got bound
|
|
|
|
OPLinkSettingsData binding;
|
|
OPLinkSettingsGet(&binding);
|
|
binding.Version = openlrs_dev->bind_data.version;
|
|
binding.SerialBaudrate = openlrs_dev->bind_data.serial_baudrate;
|
|
binding.RFFrequency = openlrs_dev->bind_data.rf_frequency;
|
|
binding.CoordID = openlrs_dev->bind_data.rf_magic;
|
|
binding.RFPower = openlrs_dev->bind_data.rf_power;
|
|
binding.RFChannelSpacing = openlrs_dev->bind_data.rf_channel_spacing;
|
|
binding.ModemParams = openlrs_dev->bind_data.modem_params;
|
|
binding.Flags = openlrs_dev->bind_data.flags;
|
|
for (uint32_t j = 0; j < OPLINKSETTINGS_HOPCHANNEL_NUMELEM; j++) {
|
|
binding.HopChannel[j] = openlrs_dev->bind_data.hopchannel[j];
|
|
}
|
|
binding.BeaconFrequency = openlrs_dev->beacon_frequency;
|
|
binding.BeaconDelay = openlrs_dev->beacon_delay;
|
|
binding.BeaconPeriod = openlrs_dev->beacon_period;
|
|
OPLinkSettingsSet(&binding);
|
|
UAVObjSave(OPLinkSettingsHandle(), 0);
|
|
|
|
#if defined(PIOS_LED_LINK)
|
|
PIOS_LED_Toggle(PIOS_LED_LINK);
|
|
#endif /* PIOS_LED_LINK */
|
|
|
|
return 1;
|
|
}
|
|
} else {
|
|
rfm22_deassertCs(openlrs_dev);
|
|
rfm22_releaseBus(openlrs_dev);
|
|
}
|
|
|
|
openlrs_dev->rf_mode = Receive;
|
|
rx_reset(openlrs_dev);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#if defined(PIOS_INCLUDE_DEBUG_CONSOLE)
|
|
static void printVersion(uint16_t v)
|
|
{
|
|
char ver[8];
|
|
|
|
ver[0] = '0' + ((v >> 8) & 0x0f);
|
|
ver[1] = '.';
|
|
ver[2] = '0' + ((v >> 4) & 0x0f);
|
|
if (v & 0x0f) {
|
|
ver[3] = '.';
|
|
ver[4] = '0' + (v & 0x0f);
|
|
ver[5] = '\r';
|
|
ver[6] = '\n';
|
|
ver[7] = '\0';
|
|
} else {
|
|
ver[3] = '\r';
|
|
ver[4] = '\n';
|
|
ver[5] = '\0';
|
|
}
|
|
DEBUG_PRINTF(2, ver);
|
|
}
|
|
#endif /* if defined(PIOS_INCLUDE_DEBUG_CONSOLE) */
|
|
|
|
static void pios_openlrs_setup(struct pios_openlrs_dev *openlrs_dev, bool bind)
|
|
{
|
|
DEBUG_PRINTF(2, "OpenLRSng RX setup starting. Binding: %e\r\n", bind);
|
|
PIOS_Thread_Sleep(5);
|
|
#if defined(PIOS_INCLUDE_DEBUG_CONSOLE)
|
|
printVersion(OPENLRSNG_VERSION);
|
|
#endif
|
|
|
|
// Get the OPLinkStatus UAVO
|
|
OPLinkStatusData oplink_status;
|
|
OPLinkStatusGet(&oplink_status);
|
|
if (bind) {
|
|
oplink_status.LinkState = OPLINKSTATUS_LINKSTATE_BINDING;
|
|
if (pios_openlrs_bind_receive(openlrs_dev, 0)) {
|
|
// TODO: save binding settings bindWriteEeprom();
|
|
DEBUG_PRINTF(2, "Saved bind data to EEPROM (not really yet -- TODO)\r\n");
|
|
}
|
|
}
|
|
oplink_status.LinkState = OPLINKSTATUS_LINKSTATE_BOUND;
|
|
|
|
DEBUG_PRINTF(2, "Entering normal mode\r\n");
|
|
|
|
init_rfm(openlrs_dev, 0); // Configure the RFM22B's registers for normal operation
|
|
openlrs_dev->rf_channel = 0;
|
|
rfmSetChannel(openlrs_dev, openlrs_dev->rf_channel);
|
|
|
|
// Count hopchannels as we need it later
|
|
openlrs_dev->hopcount = 0;
|
|
while ((openlrs_dev->hopcount < MAXHOPS) && (openlrs_dev->bind_data.hopchannel[openlrs_dev->hopcount] != 0)) {
|
|
openlrs_dev->hopcount++;
|
|
}
|
|
|
|
// ################### RX SYNC AT STARTUP #################
|
|
openlrs_dev->rf_mode = Receive;
|
|
to_rx_mode(openlrs_dev);
|
|
|
|
openlrs_dev->link_acquired = 0;
|
|
openlrs_dev->lastPacketTimeUs = PIOS_DELAY_GetuS();
|
|
|
|
// Update the OPLinkStatus UAVO
|
|
OPLinkStatusSet(&oplink_status);
|
|
|
|
DEBUG_PRINTF(2, "OpenLRSng RX setup complete\r\n");
|
|
}
|
|
|
|
static void pios_openlrs_rx_loop(struct pios_openlrs_dev *openlrs_dev)
|
|
{
|
|
uint32_t timeUs, timeMs;
|
|
|
|
#if defined(PIOS_INCLUDE_WDG) && defined(PIOS_WDG_RFM22B)
|
|
// Update the watchdog timer
|
|
PIOS_WDG_UpdateFlag(PIOS_WDG_RFM22B);
|
|
#endif /* PIOS_WDG_RFM22B */
|
|
|
|
if (rfm22_read_claim(openlrs_dev, 0x0C) == 0) { // detect the locked module and reboot
|
|
DEBUG_PRINTF(2, "OLRS ERR: RX hang\r\n");
|
|
init_rfm(openlrs_dev, 0);
|
|
to_rx_mode(openlrs_dev);
|
|
}
|
|
|
|
// Get the OPLinkStatus UAVO
|
|
OPLinkStatusData oplink_status;
|
|
OPLinkStatusGet(&oplink_status);
|
|
|
|
timeUs = PIOS_DELAY_GetuS();
|
|
timeMs = PIOS_Thread_Systime();
|
|
|
|
// DEBUG_PRINTF(2,"Time: %d\r\n", timeUs);
|
|
|
|
uint8_t *tx_buf = openlrs_dev->tx_buf; // convenient variable
|
|
|
|
if (openlrs_dev->rf_mode == Received) {
|
|
DEBUG_PRINTF(2, "Packet Received. Dt=%d\r\n", timeUs - openlrs_dev->lastPacketTimeUs);
|
|
|
|
// Read the packet from RFM22b
|
|
rfm22_claimBus(openlrs_dev);
|
|
rfm22_assertCs(openlrs_dev);
|
|
PIOS_SPI_TransferByte(openlrs_dev->spi_id, 0x7F);
|
|
uint32_t packet_size = getPacketSize(&openlrs_dev->bind_data);
|
|
PIOS_SPI_TransferBlock(openlrs_dev->spi_id, OUT_FF,
|
|
openlrs_dev->rx_buf, packet_size, NULL);
|
|
rfm22_deassertCs(openlrs_dev);
|
|
rfm22_releaseBus(openlrs_dev);
|
|
|
|
openlrs_dev->lastAFCCvalue = rfmGetAFCC(openlrs_dev);
|
|
|
|
#if defined(PIOS_LED_LINK)
|
|
PIOS_LED_Toggle(PIOS_LED_LINK);
|
|
#endif /* PIOS_LED_LINK */
|
|
|
|
openlrs_dev->lastPacketTimeUs = timeUs;
|
|
openlrs_dev->numberOfLostPackets = 0;
|
|
openlrs_dev->link_quality <<= 1;
|
|
openlrs_dev->link_quality |= 1;
|
|
|
|
if ((openlrs_dev->rx_buf[0] & 0x3e) == 0x00) {
|
|
// This flag indicates receiving PPM data
|
|
|
|
unpackChannels(openlrs_dev->bind_data.flags & 7, openlrs_dev->ppm, openlrs_dev->rx_buf + 1);
|
|
rescaleChannels(openlrs_dev->ppm);
|
|
|
|
// Call the PPM received callback if it's available.
|
|
if (openlrs_dev->ppm_callback) {
|
|
openlrs_dev->ppm_callback(openlrs_dev->ppm_context, openlrs_dev->ppm);
|
|
}
|
|
} else {
|
|
// Not PPM data. Push into serial RX buffer.
|
|
if ((openlrs_dev->rx_buf[0] & 0x38) == 0x38) {
|
|
if ((openlrs_dev->rx_buf[0] ^ tx_buf[0]) & 0x80) {
|
|
// We got new data... (not retransmission)
|
|
tx_buf[0] ^= 0x80; // signal that we got it
|
|
bool rx_need_yield;
|
|
uint8_t data_len = openlrs_dev->rx_buf[0] & 7;
|
|
if (openlrs_dev->rx_in_cb && (data_len > 0)) {
|
|
(openlrs_dev->rx_in_cb)(openlrs_dev->rx_in_context, &openlrs_dev->rx_buf[1], data_len, NULL, &rx_need_yield);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Flag to indicate ever got a link
|
|
openlrs_dev->link_acquired |= true;
|
|
oplink_status.LinkState = OPLINKSTATUS_LINKSTATE_CONNECTED;
|
|
openlrs_dev->beacon_armed = false; // when receiving packets make sure beacon cannot emit
|
|
|
|
// When telemetry is enabled we ack packets and send info about FC back
|
|
if (openlrs_dev->bind_data.flags & TELEMETRY_MASK) {
|
|
if ((tx_buf[0] ^ openlrs_dev->rx_buf[0]) & 0x40) {
|
|
// resend last message
|
|
} else {
|
|
tx_buf[0] &= 0xc0;
|
|
tx_buf[0] ^= 0x40; // swap sequence as we have new data
|
|
|
|
// Check for data on serial link
|
|
uint8_t bytes = 0;
|
|
// Append data from the com interface if applicable.
|
|
if (openlrs_dev->tx_out_cb) {
|
|
// Try to get some data to send
|
|
bool need_yield = false;
|
|
bytes = (openlrs_dev->tx_out_cb)(openlrs_dev->tx_out_context, &tx_buf[1], 8, NULL, &need_yield);
|
|
}
|
|
|
|
if (bytes > 0) {
|
|
tx_buf[0] |= (0x37 + bytes);
|
|
} else {
|
|
// tx_buf[0] lowest 6 bits left at 0
|
|
tx_buf[1] = openlrs_dev->rssi;
|
|
if (FlightBatteryStateHandle()) {
|
|
FlightBatteryStateData bat;
|
|
FlightBatteryStateGet(&bat);
|
|
// FrSky protocol normally uses 3.3V at 255 but
|
|
// divider from display can be set internally
|
|
tx_buf[2] = (uint8_t)bat.Voltage / 25.0f * 255;
|
|
tx_buf[3] = (uint8_t)bat.Current / 60.0f * 255;
|
|
} else {
|
|
tx_buf[2] = 0; // these bytes carry analog info. package
|
|
tx_buf[3] = 0; // battery here
|
|
}
|
|
tx_buf[4] = (openlrs_dev->lastAFCCvalue >> 8);
|
|
tx_buf[5] = openlrs_dev->lastAFCCvalue & 0xff;
|
|
tx_buf[6] = countSetBits(openlrs_dev->link_quality & 0x7fff);
|
|
}
|
|
}
|
|
|
|
// This will block until sent
|
|
tx_packet(openlrs_dev, tx_buf, 9);
|
|
}
|
|
|
|
// Once a packet has been processed, flip back into receiving mode
|
|
openlrs_dev->rf_mode = Receive;
|
|
rx_reset(openlrs_dev);
|
|
|
|
openlrs_dev->willhop = 1;
|
|
}
|
|
|
|
if (openlrs_dev->link_acquired) {
|
|
// For missing packets to be properly trigger a well timed channel hop, this method should be called fairly close (but not sooner)
|
|
// than 1ms after the packet was expected to trigger this path
|
|
if ((openlrs_dev->numberOfLostPackets < openlrs_dev->hopcount) && (PIOS_DELAY_GetuSSince(openlrs_dev->lastPacketTimeUs) > (getInterval(&openlrs_dev->bind_data) + packet_timeout_us))) {
|
|
DEBUG_PRINTF(2, "OLRS WARN: Lost packet: %d\r\n", openlrs_dev->numberOfLostPackets);
|
|
// we lost packet, hop to next channel
|
|
openlrs_dev->link_quality <<= 1;
|
|
openlrs_dev->willhop = 1;
|
|
if (openlrs_dev->numberOfLostPackets == 0) {
|
|
openlrs_dev->linkLossTimeMs = timeMs;
|
|
openlrs_dev->nextBeaconTimeMs = 0;
|
|
}
|
|
openlrs_dev->numberOfLostPackets++;
|
|
openlrs_dev->lastPacketTimeUs += getInterval(&openlrs_dev->bind_data);
|
|
openlrs_dev->willhop = 1;
|
|
} else if ((openlrs_dev->numberOfLostPackets >= openlrs_dev->hopcount) && (PIOS_DELAY_GetuSSince(openlrs_dev->lastPacketTimeUs) > (getInterval(&openlrs_dev->bind_data) * openlrs_dev->hopcount))) {
|
|
DEBUG_PRINTF(2, "ORLS WARN: Trying to resync\r\n");
|
|
// hop slowly to allow resync with TX
|
|
openlrs_dev->link_quality = 0;
|
|
openlrs_dev->willhop = 1;
|
|
openlrs_dev->lastPacketTimeUs = timeUs;
|
|
}
|
|
|
|
if (openlrs_dev->numberOfLostPackets) {
|
|
#if defined(PIOS_LED_LINK)
|
|
PIOS_LED_Off(PIOS_LED_LINK);
|
|
#endif /* PIOS_LED_LINK */
|
|
|
|
if (openlrs_dev->failsafeDelay &&
|
|
(oplink_status.LinkState == OPLINKSTATUS_LINKSTATE_CONNECTED) &&
|
|
((timeMs - openlrs_dev->linkLossTimeMs) > ((uint32_t)openlrs_dev->failsafeDelay))) {
|
|
DEBUG_PRINTF(2, "Failsafe activated: %d %d\r\n", timeMs, openlrs_dev->linkLossTimeMs);
|
|
oplink_status.LinkState = OPLINKSTATUS_LINKSTATE_DISCONNECTED;
|
|
// failsafeApply();
|
|
openlrs_dev->nextBeaconTimeMs = (timeMs + (1000UL * openlrs_dev->beacon_delay)) | 1; // beacon activating...
|
|
}
|
|
|
|
if ((openlrs_dev->beacon_frequency) && (openlrs_dev->nextBeaconTimeMs) &&
|
|
((timeMs - openlrs_dev->nextBeaconTimeMs) < 0x80000000)) {
|
|
// Indicate that the beacon is now active so we can trigger extra ones below
|
|
openlrs_dev->beacon_armed = true;
|
|
|
|
DEBUG_PRINTF(2, "Beacon time: %d\r\n", openlrs_dev->nextBeaconTimeMs);
|
|
// Only beacon when disarmed
|
|
uint8_t armed;
|
|
FlightStatusArmedGet(&armed);
|
|
if (armed == FLIGHTSTATUS_ARMED_DISARMED) {
|
|
beacon_send(openlrs_dev, false); // play cool tune
|
|
init_rfm(openlrs_dev, 0); // go back to normal RX
|
|
rx_reset(openlrs_dev);
|
|
openlrs_dev->nextBeaconTimeMs = (timeMs + (1000UL * openlrs_dev->beacon_period)) | 1; // avoid 0 in time
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// Waiting for first packet, hop slowly
|
|
if (PIOS_DELAY_GetuSSince(openlrs_dev->lastPacketTimeUs) > (getInterval(&openlrs_dev->bind_data) * openlrs_dev->hopcount)) {
|
|
// DEBUG_PRINTF(3,"Trying to get first packet\r\n");
|
|
openlrs_dev->lastPacketTimeUs = timeUs;
|
|
openlrs_dev->willhop = 1;
|
|
}
|
|
}
|
|
|
|
if (openlrs_dev->willhop == 1) {
|
|
openlrs_dev->rf_channel++;
|
|
|
|
if ((openlrs_dev->rf_channel == MAXHOPS) || (openlrs_dev->bind_data.hopchannel[openlrs_dev->rf_channel] == 0)) {
|
|
openlrs_dev->rf_channel = 0;
|
|
}
|
|
|
|
if ((openlrs_dev->beacon_frequency) && (openlrs_dev->nextBeaconTimeMs) && openlrs_dev->beacon_armed) {
|
|
// Listen for RSSI on beacon channel briefly for 'trigger'
|
|
uint8_t brssi = beaconGetRSSI(openlrs_dev);
|
|
if (brssi > ((openlrs_dev->beacon_rssi_avg >> 2) + 20)) {
|
|
openlrs_dev->nextBeaconTimeMs = timeMs + 1000L;
|
|
}
|
|
openlrs_dev->beacon_rssi_avg = (openlrs_dev->beacon_rssi_avg * 3 + brssi * 4) >> 2;
|
|
|
|
rfmSetCarrierFrequency(openlrs_dev, openlrs_dev->bind_data.rf_frequency);
|
|
}
|
|
|
|
rfmSetChannel(openlrs_dev, openlrs_dev->rf_channel);
|
|
rx_reset(openlrs_dev);
|
|
openlrs_dev->willhop = 0;
|
|
}
|
|
|
|
if (oplink_status.LinkState > OPLINKSTATUS_LINKSTATE_DISCONNECTED) {
|
|
// Convert raw Rssi to dBm
|
|
oplink_status.RSSI = (int8_t)(openlrs_dev->rssi >> 1) - 122;
|
|
|
|
// Count number of bits set in link_quality
|
|
uint8_t linkquality_bits = countSetBits(openlrs_dev->link_quality & 0x7fff);
|
|
// Translate link quality to 0 - 128 range
|
|
oplink_status.LinkQuality = (linkquality_bits + 1) * 8;
|
|
} else {
|
|
oplink_status.LinkQuality = 0;
|
|
oplink_status.RSSI = -127;
|
|
}
|
|
|
|
// Update UAVO
|
|
OPLinkStatusSet(&oplink_status);
|
|
}
|
|
|
|
uint8_t PIOS_OpenLRS_RSSI_Get(void)
|
|
{
|
|
// Get the OPLinkStatus UAVO
|
|
OPLinkStatusData oplink_status;
|
|
|
|
OPLinkStatusGet(&oplink_status);
|
|
|
|
if (oplink_status.LinkState != OPLINKSTATUS_LINKSTATE_CONNECTED) {
|
|
return 0;
|
|
} else {
|
|
OPLinkStatusData openlrs_status;
|
|
OPLinkStatusGet(&openlrs_status);
|
|
OPLinkSettingsData openlrs_data;
|
|
OPLinkSettingsGet(&openlrs_data);
|
|
|
|
uint16_t LQ = oplink_status.LinkQuality & 0x7fff;
|
|
// count number of 1s in LinkQuality
|
|
LQ = LQ - ((LQ >> 1) & 0x5555);
|
|
LQ = (LQ & 0x3333) + ((LQ >> 2) & 0x3333);
|
|
LQ = LQ + (LQ >> 4);
|
|
LQ &= 0x0F0F;
|
|
LQ = (LQ * 0x0101) >> 8;
|
|
|
|
switch (openlrs_data.RSSIType) {
|
|
case OPLINKSETTINGS_RSSITYPE_COMBINED:
|
|
if ((uint8_t)LQ == 15) {
|
|
return (uint8_t)((oplink_status.RSSI >> 1) + 128);
|
|
} else {
|
|
return LQ * 9;
|
|
}
|
|
case OPLINKSETTINGS_RSSITYPE_RSSI:
|
|
return openlrs_status.RSSI;
|
|
|
|
case OPLINKSETTINGS_RSSITYPE_LINKQUALITY:
|
|
return (uint8_t)(LQ << 4);
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* PPM Code
|
|
*****************************************************************************/
|
|
|
|
/**
|
|
* Register a OpenLRS_Rcvr interface to inform of PPM packets using a generic callback.
|
|
*
|
|
* @param[in] openlrs_id The OpenLRS device ID.
|
|
* @param[in] callback The callback function.
|
|
*/
|
|
void PIOS_OpenLRS_RegisterPPMCallback(uint32_t openlrs_id, PIOS_OpenLRS_PPMReceivedCallback callback, uint32_t context)
|
|
{
|
|
struct pios_openlrs_dev *openlrs_dev =
|
|
(struct pios_openlrs_dev *)openlrs_id;
|
|
|
|
if (!pios_openlrs_validate(openlrs_dev)) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Order is important in these assignments since openlrs_task uses ppm_callback
|
|
* field to determine if it's ok to dereference ppm_callback and ppm_context
|
|
*/
|
|
openlrs_dev->ppm_context = context;
|
|
openlrs_dev->ppm_callback = callback;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Task and device setup
|
|
*****************************************************************************/
|
|
|
|
static void pios_openlrs_task(void *parameters);
|
|
|
|
// ! Global device handle, required for IRQ handler
|
|
static struct pios_openlrs_dev *g_openlrs_dev;
|
|
|
|
/**
|
|
* Initialise an OPENLRS device
|
|
*
|
|
* @param[out] openlrs_id A pointer to store the device ID in.
|
|
* @param[in] spi_id The SPI bus index.
|
|
* @param[in] slave_num The SPI bus slave number.
|
|
* @param[in] cfg The device configuration.
|
|
*/
|
|
int32_t PIOS_OpenLRS_Init(uint32_t *openlrs_id, uint32_t spi_id,
|
|
uint32_t slave_num,
|
|
const struct pios_openlrs_cfg *cfg)
|
|
{
|
|
PIOS_DEBUG_Assert(openlrs_id);
|
|
PIOS_DEBUG_Assert(cfg);
|
|
|
|
// Allocate the device structure.
|
|
struct pios_openlrs_dev *openlrs_dev = pios_openlrs_alloc();
|
|
if (!openlrs_dev) {
|
|
return -1;
|
|
}
|
|
*openlrs_id = (uint32_t)openlrs_dev;
|
|
g_openlrs_dev = openlrs_dev;
|
|
|
|
// Hardcode the band for now.
|
|
openlrs_dev->band = 430000000;
|
|
|
|
// Store the SPI handle
|
|
openlrs_dev->slave_num = slave_num;
|
|
openlrs_dev->spi_id = spi_id;
|
|
|
|
// Before initializing everything, make sure device found
|
|
uint8_t device_type = rfm22_read(openlrs_dev, RFM22_DEVICE_TYPE) & RFM22_DT_MASK;
|
|
if (device_type != 0x08) {
|
|
return -1;
|
|
}
|
|
|
|
// Initialize the com callbacks.
|
|
openlrs_dev->rx_in_cb = NULL;
|
|
openlrs_dev->tx_out_cb = NULL;
|
|
|
|
// Initialize the "PPM" callback.
|
|
openlrs_dev->ppm_context = 0;
|
|
openlrs_dev->ppm_callback = 0;
|
|
|
|
|
|
OPLinkStatusInitialize();
|
|
DEBUG_PRINTF(2, "OpenLRS UAVOs Initialized\r\n");
|
|
OPLinkSettingsData binding;
|
|
OPLinkSettingsGet(&binding);
|
|
if (binding.Version == BINDING_VERSION) {
|
|
openlrs_dev->bind_data.version = binding.Version;
|
|
openlrs_dev->bind_data.serial_baudrate = binding.SerialBaudrate;
|
|
openlrs_dev->bind_data.rf_frequency = binding.RFFrequency;
|
|
openlrs_dev->bind_data.rf_magic = binding.CoordID;
|
|
openlrs_dev->bind_data.rf_power = binding.RFPower;
|
|
openlrs_dev->bind_data.rf_channel_spacing = binding.RFChannelSpacing;
|
|
openlrs_dev->bind_data.modem_params = binding.ModemParams;
|
|
openlrs_dev->bind_data.flags = binding.Flags;
|
|
for (uint32_t i = 0; i < OPLINKSETTINGS_HOPCHANNEL_NUMELEM; i++) {
|
|
openlrs_dev->bind_data.hopchannel[i] = binding.HopChannel[i];
|
|
}
|
|
}
|
|
|
|
// Copy beacon settings over
|
|
openlrs_dev->beacon_frequency = binding.BeaconFrequency;
|
|
openlrs_dev->beacon_delay = binding.BeaconDelay;
|
|
openlrs_dev->beacon_period = binding.BeaconPeriod;
|
|
|
|
openlrs_dev->failsafeDelay = binding.FailsafeDelay;
|
|
|
|
// Bind the configuration to the device instance
|
|
openlrs_dev->cfg = *cfg;
|
|
|
|
// Initialize the external interrupt.
|
|
PIOS_EXTI_Init(cfg->exti_cfg);
|
|
|
|
// Register the watchdog timer for the radio driver task
|
|
#if defined(PIOS_INCLUDE_WDG) && defined(PIOS_WDG_RFM22B)
|
|
PIOS_WDG_RegisterFlag(PIOS_WDG_RFM22B);
|
|
#endif /* PIOS_WDG_RFM22B */
|
|
|
|
// Start the driver task. This task controls the radio state machine and removed all of the IO from the IRQ handler.
|
|
openlrs_dev->taskHandle = PIOS_Thread_Create(pios_openlrs_task, "PIOS_OpenLRS_Task", STACK_SIZE_BYTES, (void *)openlrs_dev, TASK_PRIORITY);
|
|
|
|
// TaskMonitorAdd(TASKINFO_RUNNING_MODEMRX, openlrs_dev->taskHandle);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* The task that controls the radio state machine.
|
|
*
|
|
* @param[in] paramters The task parameters.
|
|
*/
|
|
static void pios_openlrs_task(void *parameters)
|
|
{
|
|
struct pios_openlrs_dev *openlrs_dev = (struct pios_openlrs_dev *)parameters;
|
|
|
|
if (!pios_openlrs_validate(openlrs_dev)) {
|
|
return;
|
|
}
|
|
|
|
DEBUG_PRINTF(2, "Setup started\r\n");
|
|
|
|
if (openlrs_dev->bind_data.version == BINDING_VERSION) {
|
|
pios_openlrs_setup(openlrs_dev, false);
|
|
} else {
|
|
pios_openlrs_setup(openlrs_dev, true);
|
|
}
|
|
|
|
DEBUG_PRINTF(2, "Setup complete\r\n");
|
|
|
|
bool rssi_sampled = false;
|
|
while (1) {
|
|
#if defined(PIOS_INCLUDE_WDG) && defined(PIOS_WDG_RFM22B)
|
|
// Update the watchdog timer
|
|
PIOS_WDG_UpdateFlag(PIOS_WDG_RFM22B);
|
|
#endif /* PIOS_WDG_RFM22B */
|
|
|
|
|
|
/* This block of code determines the timing of when to call the loop method. It reaches a bit into
|
|
* the internal state of that method to get the optimal timings. This is to keep the loop method as
|
|
* similar as possible to the openLRSng implementation (for easier maintenance of compatibility)
|
|
* while minimizing overhead spinning in a while loop.
|
|
*
|
|
* There are three reasons to go into loop:
|
|
* 1. the ISR was triggered (packet was received)
|
|
* 2. a little before the expected packet (to sample the RSSI while receiving packet)
|
|
* 3. a little after expected packet (to channel hop when a packet was missing)
|
|
*/
|
|
|
|
|
|
uint32_t delay_ms = 0;
|
|
|
|
uint32_t time_since_packet_us = PIOS_DELAY_GetuSSince(openlrs_dev->lastPacketTimeUs);
|
|
|
|
if (!rssi_sampled) {
|
|
// If we had not sampled RSSI yet, schedule a bit early to try and catch while "packet is in the air"
|
|
uint32_t time_till_measure_rssi_us = (getInterval(&openlrs_dev->bind_data) - packet_advance_time_us) - time_since_packet_us;
|
|
delay_ms = (time_till_measure_rssi_us + 999) / 1000;
|
|
// DEBUG_PRINTF(3, "T1: %d\r\n", delay_ms);
|
|
} else {
|
|
// If we have sampled RSSI we want to schedule to hop when a packet has been missed
|
|
uint32_t time_till_timeout_us = (getInterval(&openlrs_dev->bind_data) + packet_timeout_us) - time_since_packet_us;
|
|
delay_ms = (time_till_timeout_us + 999) / 1000;
|
|
// DEBUG_PRINTF(3, "T2: %d %d\r\n", time_till_timeout_us,delay_ms, time_since_packet_us);
|
|
}
|
|
|
|
// Maximum delay based on packet time
|
|
const uint32_t max_delay = (getInterval(&openlrs_dev->bind_data) + packet_timeout_us) / 1000;
|
|
if (delay_ms > max_delay) {
|
|
delay_ms = max_delay;
|
|
}
|
|
|
|
if (PIOS_Semaphore_Take(openlrs_dev->sema_isr, delay_ms) == false) {
|
|
if (!rssi_sampled) {
|
|
// We timed out to sample RSSI
|
|
if (openlrs_dev->numberOfLostPackets < 2) {
|
|
openlrs_dev->lastRSSITimeUs = openlrs_dev->lastPacketTimeUs;
|
|
openlrs_dev->rssi = rfmGetRSSI(openlrs_dev); // Read the RSSI value
|
|
|
|
// DEBUG_PRINTF(3, "Sampled RSSI: %d %d\r\n", openlrs_dev->RSSI, delay);
|
|
}
|
|
} else {
|
|
// We timed out because packet was missed
|
|
// DEBUG_PRINTF(3, "ISR Timeout. Missed packet: %d %d %d\r\n", delay_ms, getInterval(&openlrs_dev->bind_data), time_since_packet_us);
|
|
pios_openlrs_rx_loop(openlrs_dev);
|
|
}
|
|
|
|
rssi_sampled = true;
|
|
} else {
|
|
// DEBUG_PRINTF(3, "ISR %d %d %d\r\n", delay_ms, getInterval(&openlrs_dev->bind_data), time_since_packet_us);
|
|
|
|
// Process incoming data
|
|
pios_openlrs_rx_loop(openlrs_dev);
|
|
|
|
// When a packet has been received (processed below) indicate we need to sample a new RSSI
|
|
rssi_sampled = false;
|
|
}
|
|
|
|
|
|
// DEBUG_PRINTF(3, "Processing time %d\r\n", PIOS_DELAY_GetuSSince(openlrs_dev->lastPacketTimeUs));
|
|
}
|
|
}
|
|
|
|
bool PIOS_OpenLRS_EXT_Int(void)
|
|
{
|
|
struct pios_openlrs_dev *openlrs_dev = g_openlrs_dev;
|
|
|
|
if (!pios_openlrs_validate(openlrs_dev)) {
|
|
return false;
|
|
}
|
|
|
|
if (openlrs_dev->rf_mode == Transmit) {
|
|
openlrs_dev->rf_mode = Transmitted;
|
|
} else if (openlrs_dev->rf_mode == Receive) {
|
|
openlrs_dev->rf_mode = Received;
|
|
}
|
|
|
|
// Indicate to main task that an ISR occurred
|
|
bool woken = false;
|
|
PIOS_Semaphore_Give_FromISR(openlrs_dev->sema_isr, &woken);
|
|
|
|
return woken;
|
|
}
|
|
|
|
|
|
/**
|
|
* Allocate the device structure
|
|
*/
|
|
static struct pios_openlrs_dev *pios_openlrs_alloc(void)
|
|
{
|
|
struct pios_openlrs_dev *openlrs_dev;
|
|
|
|
openlrs_dev = (struct pios_openlrs_dev *)pios_malloc(sizeof(*openlrs_dev));
|
|
if (!openlrs_dev) {
|
|
return NULL;
|
|
}
|
|
|
|
openlrs_dev->spi_id = 0;
|
|
|
|
// Create the ISR signal
|
|
openlrs_dev->sema_isr = PIOS_Semaphore_Create();
|
|
if (!openlrs_dev->sema_isr) {
|
|
pios_free(openlrs_dev);
|
|
return NULL;
|
|
}
|
|
|
|
openlrs_dev->magic = PIOS_OPENLRS_DEV_MAGIC;
|
|
return openlrs_dev;
|
|
}
|
|
|
|
|
|
/**
|
|
* Validate that the device structure is valid.
|
|
*
|
|
* @param[in] openlrs_dev The OpenLRS device structure pointer.
|
|
*/
|
|
static bool pios_openlrs_validate(struct pios_openlrs_dev *openlrs_dev)
|
|
{
|
|
return openlrs_dev != NULL
|
|
&& openlrs_dev->magic == PIOS_OPENLRS_DEV_MAGIC;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* SPI Read/Write Functions
|
|
*****************************************************************************/
|
|
|
|
/**
|
|
* Assert the chip select line.
|
|
*
|
|
* @param[in] rfm22b_dev The RFM22B device.
|
|
*/
|
|
static void rfm22_assertCs(struct pios_openlrs_dev *openlrs_dev)
|
|
{
|
|
PIOS_DELAY_WaituS(1);
|
|
if (openlrs_dev->spi_id != 0) {
|
|
PIOS_SPI_RC_PinSet(openlrs_dev->spi_id,
|
|
openlrs_dev->slave_num, 0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deassert the chip select line.
|
|
*
|
|
* @param[in] rfm22b_dev The RFM22B device structure pointer.
|
|
*/
|
|
static void rfm22_deassertCs(struct pios_openlrs_dev *openlrs_dev)
|
|
{
|
|
if (openlrs_dev->spi_id != 0) {
|
|
PIOS_SPI_RC_PinSet(openlrs_dev->spi_id,
|
|
openlrs_dev->slave_num, 1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Claim the SPI bus.
|
|
*
|
|
* @param[in] rfm22b_dev The RFM22B device structure pointer.
|
|
*/
|
|
static void rfm22_claimBus(struct pios_openlrs_dev *openlrs_dev)
|
|
{
|
|
if (openlrs_dev->spi_id != 0) {
|
|
PIOS_SPI_ClaimBus(openlrs_dev->spi_id);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Release the SPI bus.
|
|
*
|
|
* @param[in] rfm22b_dev The RFM22B device structure pointer.
|
|
*/
|
|
static void rfm22_releaseBus(struct pios_openlrs_dev *openlrs_dev)
|
|
{
|
|
if (openlrs_dev->spi_id != 0) {
|
|
PIOS_SPI_ReleaseBus(openlrs_dev->spi_id);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Claim the semaphore and write a byte to a register
|
|
*
|
|
* @param[in] rfm22b_dev The RFM22B device.
|
|
* @param[in] addr The address to write to
|
|
* @param[in] data The datat to write to that address
|
|
*/
|
|
static void rfm22_write_claim(struct pios_openlrs_dev *openlrs_dev,
|
|
uint8_t addr, uint8_t data)
|
|
{
|
|
rfm22_claimBus(openlrs_dev);
|
|
rfm22_assertCs(openlrs_dev);
|
|
uint8_t buf[2] = { addr | 0x80, data };
|
|
PIOS_SPI_TransferBlock(openlrs_dev->spi_id, buf, NULL, sizeof(buf),
|
|
NULL);
|
|
rfm22_deassertCs(openlrs_dev);
|
|
rfm22_releaseBus(openlrs_dev);
|
|
}
|
|
|
|
/**
|
|
* Claim the semaphore and write a byte to a register
|
|
*
|
|
* @param[in] rfm22b_dev The RFM22B device.
|
|
* @param[in] addr The address to write to
|
|
* @param[in] data The datat to write to that address
|
|
*/
|
|
static uint8_t rfm22_read_claim(struct pios_openlrs_dev *openlrs_dev,
|
|
uint8_t addr)
|
|
{
|
|
uint8_t out[2] = { addr &0x7F, 0xFF };
|
|
uint8_t in[2];
|
|
|
|
rfm22_claimBus(openlrs_dev);
|
|
rfm22_assertCs(openlrs_dev);
|
|
PIOS_SPI_TransferBlock(openlrs_dev->spi_id, out, in, sizeof(out),
|
|
NULL);
|
|
rfm22_deassertCs(openlrs_dev);
|
|
rfm22_releaseBus(openlrs_dev);
|
|
return in[1];
|
|
}
|
|
|
|
/**
|
|
* Write a byte to a register without claiming the semaphore
|
|
*
|
|
* @param[in] rfm22b_dev The RFM22B device.
|
|
* @param[in] addr The address to write to
|
|
* @param[in] data The datat to write to that address
|
|
*/
|
|
static void rfm22_write(struct pios_openlrs_dev *openlrs_dev, uint8_t addr,
|
|
uint8_t data)
|
|
{
|
|
rfm22_assertCs(openlrs_dev);
|
|
uint8_t buf[2] = { addr | 0x80, data };
|
|
PIOS_SPI_TransferBlock(openlrs_dev->spi_id, buf, NULL, sizeof(buf),
|
|
NULL);
|
|
rfm22_deassertCs(openlrs_dev);
|
|
}
|
|
|
|
/**
|
|
* Read a byte from an RFM22b register without claiming the bus
|
|
*
|
|
* @param[in] rfm22b_dev The RFM22B device structure pointer.
|
|
* @param[in] addr The address to read from
|
|
* @return Returns the result of the register read
|
|
*/
|
|
static uint8_t rfm22_read(struct pios_openlrs_dev *openlrs_dev, uint8_t addr)
|
|
{
|
|
uint8_t out[2] = { addr &0x7F, 0xFF };
|
|
uint8_t in[2];
|
|
|
|
rfm22_assertCs(openlrs_dev);
|
|
PIOS_SPI_TransferBlock(openlrs_dev->spi_id, out, in, sizeof(out),
|
|
NULL);
|
|
rfm22_deassertCs(openlrs_dev);
|
|
return in[1];
|
|
}
|
|
|
|
#endif /* PIOS_INCLUDE_OPENLRS */
|
|
|
|
/**
|
|
* @}
|
|
* @}
|
|
*/
|