Host driver/HAL to build a LoRa Picocell Gateway which communicates through USB with a concentrator board based on Semtech SX1308 multi-channel modem and SX1257/SX1255 RF transceivers.

Committer:
dgabino
Date:
Wed Apr 11 14:38:42 2018 +0000
Revision:
0:102b50f941d0
Initial commit

Who changed what in which revision?

UserRevisionLine numberNew contents of line
dgabino 0:102b50f941d0 1 /*
dgabino 0:102b50f941d0 2 / _____) _ | |
dgabino 0:102b50f941d0 3 ( (____ _____ ____ _| |_ _____ ____| |__
dgabino 0:102b50f941d0 4 \____ \| ___ | (_ _) ___ |/ ___) _ \
dgabino 0:102b50f941d0 5 _____) ) ____| | | || |_| ____( (___| | | |
dgabino 0:102b50f941d0 6 (______/|_____)_|_|_| \__)_____)\____)_| |_|
dgabino 0:102b50f941d0 7 (C)2017 Semtech-Cycleo
dgabino 0:102b50f941d0 8
dgabino 0:102b50f941d0 9 Description:
dgabino 0:102b50f941d0 10 LoRa concentrator Hardware Abstraction Layer
dgabino 0:102b50f941d0 11
dgabino 0:102b50f941d0 12 License: Revised BSD License, see LICENSE.TXT file include in the project
dgabino 0:102b50f941d0 13 */
dgabino 0:102b50f941d0 14
dgabino 0:102b50f941d0 15
dgabino 0:102b50f941d0 16 /* -------------------------------------------------------------------------- */
dgabino 0:102b50f941d0 17 /* --- DEPENDANCIES --------------------------------------------------------- */
dgabino 0:102b50f941d0 18
dgabino 0:102b50f941d0 19 #include <stdint.h> /* C99 types */
dgabino 0:102b50f941d0 20 #include <stdbool.h> /* bool type */
dgabino 0:102b50f941d0 21 #include <stdio.h> /* printf fprintf */
dgabino 0:102b50f941d0 22 #include <string.h> /* memcpy */
dgabino 0:102b50f941d0 23 #include <math.h> /* pow, cell */
dgabino 0:102b50f941d0 24 #include <inttypes.h> /* format macro constants... */
dgabino 0:102b50f941d0 25
dgabino 0:102b50f941d0 26 #include "loragw_reg.h"
dgabino 0:102b50f941d0 27 #include "loragw_mcu.h"
dgabino 0:102b50f941d0 28 #include "loragw_hal.h"
dgabino 0:102b50f941d0 29 #include "loragw_aux.h"
dgabino 0:102b50f941d0 30 #include "loragw_radio.h"
dgabino 0:102b50f941d0 31
dgabino 0:102b50f941d0 32 /* -------------------------------------------------------------------------- */
dgabino 0:102b50f941d0 33 /* --- PRIVATE MACROS ------------------------------------------------------- */
dgabino 0:102b50f941d0 34
dgabino 0:102b50f941d0 35 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
dgabino 0:102b50f941d0 36 #if DEBUG_HAL == 1
dgabino 0:102b50f941d0 37 #define DEBUG_MSG(str) fprintf(stderr, str)
dgabino 0:102b50f941d0 38 #define DEBUG_PRINTF(fmt, args...) fprintf(stderr,"%s:%d: "fmt, __FUNCTION__, __LINE__, args)
dgabino 0:102b50f941d0 39 #define DEBUG_ARRAY(a,b,c) for(a=0;a<b;++a) fprintf(stderr,"%x.",c[a]);fprintf(stderr,"end\n")
dgabino 0:102b50f941d0 40 #define CHECK_NULL(a) if(a==NULL){fprintf(stderr,"%s:%d: ERROR: NULL POINTER AS ARGUMENT\n", __FUNCTION__, __LINE__);return LGW_HAL_ERROR;}
dgabino 0:102b50f941d0 41 #else
dgabino 0:102b50f941d0 42 #define DEBUG_MSG(str)
dgabino 0:102b50f941d0 43 #define DEBUG_PRINTF(fmt, args...)
dgabino 0:102b50f941d0 44 #define DEBUG_ARRAY(a,b,c) for(a=0;a!=0;){}
dgabino 0:102b50f941d0 45 #define CHECK_NULL(a) if(a==NULL){return LGW_HAL_ERROR;}
dgabino 0:102b50f941d0 46 #endif
dgabino 0:102b50f941d0 47
dgabino 0:102b50f941d0 48 #define IF_HZ_TO_REG(f) (f << 5)/15625
dgabino 0:102b50f941d0 49 #define SET_PPM_ON(bw,dr) (((bw == BW_125KHZ) && ((dr == DR_LORA_SF11) || (dr == DR_LORA_SF12))) || ((bw == BW_250KHZ) && (dr == DR_LORA_SF12)))
dgabino 0:102b50f941d0 50 #define TRACE() fprintf(stderr, "@ %s %d\n", __FUNCTION__, __LINE__);
dgabino 0:102b50f941d0 51
dgabino 0:102b50f941d0 52 /* -------------------------------------------------------------------------- */
dgabino 0:102b50f941d0 53 /* --- PRIVATE CONSTANTS & TYPES -------------------------------------------- */
dgabino 0:102b50f941d0 54
dgabino 0:102b50f941d0 55 #define MCU_ARB 0
dgabino 0:102b50f941d0 56 #define MCU_AGC 1
dgabino 0:102b50f941d0 57 #define MCU_ARB_FW_BYTE 8192 /* size of the firmware IN BYTES (= twice the number of 14b words) */
dgabino 0:102b50f941d0 58 #define MCU_AGC_FW_BYTE 8192 /* size of the firmware IN BYTES (= twice the number of 14b words) */
dgabino 0:102b50f941d0 59 #define FW_VERSION_ADDR 0x20 /* Address of firmware version in data memory */
dgabino 0:102b50f941d0 60 #define FW_VERSION_CAL 2 /* Expected version of calibration firmware */
dgabino 0:102b50f941d0 61 #define FW_VERSION_AGC 4 /* Expected version of AGC firmware */
dgabino 0:102b50f941d0 62 #define FW_VERSION_ARB 1 /* Expected version of arbiter firmware */
dgabino 0:102b50f941d0 63
dgabino 0:102b50f941d0 64 #define TX_METADATA_NB 16
dgabino 0:102b50f941d0 65 #define RX_METADATA_NB 16
dgabino 0:102b50f941d0 66
dgabino 0:102b50f941d0 67 #define AGC_CMD_WAIT 16
dgabino 0:102b50f941d0 68 #define AGC_CMD_ABORT 17
dgabino 0:102b50f941d0 69
dgabino 0:102b50f941d0 70 #define MIN_LORA_PREAMBLE 4
dgabino 0:102b50f941d0 71 #define STD_LORA_PREAMBLE 6
dgabino 0:102b50f941d0 72 #define MIN_FSK_PREAMBLE 3
dgabino 0:102b50f941d0 73 #define STD_FSK_PREAMBLE 5
dgabino 0:102b50f941d0 74
dgabino 0:102b50f941d0 75 #define TX_START_DELAY 1500
dgabino 0:102b50f941d0 76
dgabino 0:102b50f941d0 77 #define RSSI_MULTI_BIAS -35 /* difference between "multi" modem RSSI offset and "stand-alone" modem RSSI offset */
dgabino 0:102b50f941d0 78 #define RSSI_FSK_POLY_0 60 /* polynomiam coefficients to linearize FSK RSSI */
dgabino 0:102b50f941d0 79 #define RSSI_FSK_POLY_1 1.5351
dgabino 0:102b50f941d0 80 #define RSSI_FSK_POLY_2 0.003
dgabino 0:102b50f941d0 81
dgabino 0:102b50f941d0 82 #define LGW_RF_RX_BANDWIDTH_125KHZ 925000 /* for 125KHz channels */
dgabino 0:102b50f941d0 83 #define LGW_RF_RX_BANDWIDTH_250KHZ 1000000 /* for 250KHz channels */
dgabino 0:102b50f941d0 84 #define LGW_RF_RX_BANDWIDTH_500KHZ 1100000 /* for 500KHz channels */
dgabino 0:102b50f941d0 85
dgabino 0:102b50f941d0 86 /* constant arrays defining hardware capability */
dgabino 0:102b50f941d0 87 const uint8_t ifmod_config[LGW_IF_CHAIN_NB] = LGW_IFMODEM_CONFIG;
dgabino 0:102b50f941d0 88
dgabino 0:102b50f941d0 89 /* Version string, used to identify the library version/options once compiled */
dgabino 0:102b50f941d0 90 const char lgw_version_string[] = "Version: " LIBLORAGW_VERSION ";";
dgabino 0:102b50f941d0 91
dgabino 0:102b50f941d0 92 /* -------------------------------------------------------------------------- */
dgabino 0:102b50f941d0 93 /* --- PRIVATE VARIABLES ---------------------------------------------------- */
dgabino 0:102b50f941d0 94
dgabino 0:102b50f941d0 95 #include "arb_fw.var" /* external definition of the variable */
dgabino 0:102b50f941d0 96 #include "agc_fw.var" /* external definition of the variable */
dgabino 0:102b50f941d0 97 #include "cal_fw.var" /* external definition of the variable */
dgabino 0:102b50f941d0 98 #include "cal_fw5-12.var" /* external definition of the variable */
dgabino 0:102b50f941d0 99
dgabino 0:102b50f941d0 100 /*
dgabino 0:102b50f941d0 101 The following static variables are the configuration set that the user can
dgabino 0:102b50f941d0 102 modify using rxrf_setconf, rxif_setconf and txgain_setconf functions.
dgabino 0:102b50f941d0 103 The functions _start and _send then use that set to configure the hardware.
dgabino 0:102b50f941d0 104
dgabino 0:102b50f941d0 105 Parameters validity and coherency is verified by the _setconf functions and
dgabino 0:102b50f941d0 106 the _start and _send functions assume they are valid.
dgabino 0:102b50f941d0 107 */
dgabino 0:102b50f941d0 108
dgabino 0:102b50f941d0 109 static bool lgw_is_started = false;
dgabino 0:102b50f941d0 110
dgabino 0:102b50f941d0 111 static bool rf_enable[LGW_RF_CHAIN_NB];
dgabino 0:102b50f941d0 112 static uint32_t rf_rx_freq[LGW_RF_CHAIN_NB]; /* absolute, in Hz */
dgabino 0:102b50f941d0 113 static float rf_rssi_offset[LGW_RF_CHAIN_NB];
dgabino 0:102b50f941d0 114 static bool rf_tx_enable[LGW_RF_CHAIN_NB];
dgabino 0:102b50f941d0 115 static enum lgw_radio_type_e rf_radio_type[LGW_RF_CHAIN_NB];
dgabino 0:102b50f941d0 116
dgabino 0:102b50f941d0 117 static bool if_enable[LGW_IF_CHAIN_NB];
dgabino 0:102b50f941d0 118 static bool if_rf_chain[LGW_IF_CHAIN_NB]; /* for each IF, 0 -> radio A, 1 -> radio B */
dgabino 0:102b50f941d0 119 static int32_t if_freq[LGW_IF_CHAIN_NB]; /* relative to radio frequency, +/- in Hz */
dgabino 0:102b50f941d0 120
dgabino 0:102b50f941d0 121 static uint8_t lora_multi_sfmask[LGW_MULTI_NB]; /* enables SF for LoRa 'multi' modems */
dgabino 0:102b50f941d0 122
dgabino 0:102b50f941d0 123 static uint8_t lora_rx_bw; /* bandwidth setting for LoRa standalone modem */
dgabino 0:102b50f941d0 124 static uint8_t lora_rx_sf; /* spreading factor setting for LoRa standalone modem */
dgabino 0:102b50f941d0 125 static bool lora_rx_ppm_offset;
dgabino 0:102b50f941d0 126
dgabino 0:102b50f941d0 127 static uint8_t fsk_rx_bw; /* bandwidth setting of FSK modem */
dgabino 0:102b50f941d0 128 static uint32_t fsk_rx_dr; /* FSK modem datarate in bauds */
dgabino 0:102b50f941d0 129 static uint8_t fsk_sync_word_size = 3; /* default number of bytes for FSK sync word */
dgabino 0:102b50f941d0 130 static uint64_t fsk_sync_word = 0xC194C1; /* default FSK sync word (ALIGNED RIGHT, MSbit first) */
dgabino 0:102b50f941d0 131
dgabino 0:102b50f941d0 132 static bool lorawan_public = false;
dgabino 0:102b50f941d0 133 static uint8_t rf_clkout = 0;
dgabino 0:102b50f941d0 134
dgabino 0:102b50f941d0 135 static struct lgw_tx_gain_lut_s txgain_lut = {
dgabino 0:102b50f941d0 136 .size = 2,
dgabino 0:102b50f941d0 137 .lut[0] = {
dgabino 0:102b50f941d0 138 .dig_gain = 0,
dgabino 0:102b50f941d0 139 .pa_gain = 2,
dgabino 0:102b50f941d0 140 .dac_gain = 3,
dgabino 0:102b50f941d0 141 .mix_gain = 10,
dgabino 0:102b50f941d0 142 .rf_power = 14
dgabino 0:102b50f941d0 143 },
dgabino 0:102b50f941d0 144 .lut[1] = {
dgabino 0:102b50f941d0 145 .dig_gain = 0,
dgabino 0:102b50f941d0 146 .pa_gain = 3,
dgabino 0:102b50f941d0 147 .dac_gain = 3,
dgabino 0:102b50f941d0 148 .mix_gain = 14,
dgabino 0:102b50f941d0 149 .rf_power = 27
dgabino 0:102b50f941d0 150 }
dgabino 0:102b50f941d0 151 };
dgabino 0:102b50f941d0 152
dgabino 0:102b50f941d0 153 /* TX I/Q imbalance coefficients for mixer gain = 8 to 15 */
dgabino 0:102b50f941d0 154 static int8_t cal_offset_a_i[8]; /* TX I offset for radio A */
dgabino 0:102b50f941d0 155 static int8_t cal_offset_a_q[8]; /* TX Q offset for radio A */
dgabino 0:102b50f941d0 156 static int8_t cal_offset_b_i[8]; /* TX I offset for radio B */
dgabino 0:102b50f941d0 157 static int8_t cal_offset_b_q[8]; /* TX Q offset for radio B */
dgabino 0:102b50f941d0 158
dgabino 0:102b50f941d0 159 /* -------------------------------------------------------------------------- */
dgabino 0:102b50f941d0 160 /* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */
dgabino 0:102b50f941d0 161
dgabino 0:102b50f941d0 162 int load_firmware(uint8_t target, uint8_t *firmware, uint16_t size);
dgabino 0:102b50f941d0 163
dgabino 0:102b50f941d0 164 void lgw_constant_adjust(void);
dgabino 0:102b50f941d0 165
dgabino 0:102b50f941d0 166 int32_t lgw_sf_getval(int x);
dgabino 0:102b50f941d0 167 int32_t lgw_bw_getval(int x);
dgabino 0:102b50f941d0 168
dgabino 0:102b50f941d0 169 int lgw_calibrate_sx125x(uint8_t *cal_fw, uint8_t idx_start, uint8_t idx_nb);
dgabino 0:102b50f941d0 170
dgabino 0:102b50f941d0 171 /* -------------------------------------------------------------------------- */
dgabino 0:102b50f941d0 172 /* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */
dgabino 0:102b50f941d0 173
dgabino 0:102b50f941d0 174 /* size is the firmware size in bytes (not 14b words) */
dgabino 0:102b50f941d0 175 int load_firmware(uint8_t target, uint8_t *firmware, uint16_t size) {
dgabino 0:102b50f941d0 176 int reg_rst;
dgabino 0:102b50f941d0 177 int reg_sel;
dgabino 0:102b50f941d0 178 uint8_t fw_check[8192];
dgabino 0:102b50f941d0 179 int32_t dummy;
dgabino 0:102b50f941d0 180
dgabino 0:102b50f941d0 181 /* check parameters */
dgabino 0:102b50f941d0 182 CHECK_NULL(firmware);
dgabino 0:102b50f941d0 183 if (target == MCU_ARB) {
dgabino 0:102b50f941d0 184 if (size != MCU_ARB_FW_BYTE) {
dgabino 0:102b50f941d0 185 DEBUG_MSG("ERROR: NOT A VALID SIZE FOR MCU ARG FIRMWARE\n");
dgabino 0:102b50f941d0 186 return -1;
dgabino 0:102b50f941d0 187 }
dgabino 0:102b50f941d0 188 reg_rst = LGW_MCU_RST_0;
dgabino 0:102b50f941d0 189 reg_sel = LGW_MCU_SELECT_MUX_0;
dgabino 0:102b50f941d0 190 } else if (target == MCU_AGC) {
dgabino 0:102b50f941d0 191 if (size != MCU_AGC_FW_BYTE) {
dgabino 0:102b50f941d0 192 DEBUG_MSG("ERROR: NOT A VALID SIZE FOR MCU AGC FIRMWARE\n");
dgabino 0:102b50f941d0 193 return -1;
dgabino 0:102b50f941d0 194 }
dgabino 0:102b50f941d0 195 reg_rst = LGW_MCU_RST_1;
dgabino 0:102b50f941d0 196 reg_sel = LGW_MCU_SELECT_MUX_1;
dgabino 0:102b50f941d0 197 } else {
dgabino 0:102b50f941d0 198 DEBUG_MSG("ERROR: NOT A VALID TARGET FOR LOADING FIRMWARE\n");
dgabino 0:102b50f941d0 199 return -1;
dgabino 0:102b50f941d0 200 }
dgabino 0:102b50f941d0 201
dgabino 0:102b50f941d0 202 /* reset the targeted MCU */
dgabino 0:102b50f941d0 203 lgw_reg_w(reg_rst, 1);
dgabino 0:102b50f941d0 204
dgabino 0:102b50f941d0 205 /* set mux to access MCU program RAM and set address to 0 */
dgabino 0:102b50f941d0 206 lgw_reg_w(reg_sel, 0);
dgabino 0:102b50f941d0 207 lgw_reg_w(LGW_MCU_PROM_ADDR, 0);
dgabino 0:102b50f941d0 208
dgabino 0:102b50f941d0 209 /* write the program in one burst */
dgabino 0:102b50f941d0 210 lgw_reg_wb(LGW_MCU_PROM_DATA, firmware, size);
dgabino 0:102b50f941d0 211
dgabino 0:102b50f941d0 212 /* Read back firmware code for check */
dgabino 0:102b50f941d0 213 lgw_reg_r( LGW_MCU_PROM_DATA, &dummy ); /* bug workaround */
dgabino 0:102b50f941d0 214 lgw_reg_rb( LGW_MCU_PROM_DATA, fw_check, size );
dgabino 0:102b50f941d0 215 if (memcmp(firmware, fw_check, size) != 0) {
dgabino 0:102b50f941d0 216
dgabino 0:102b50f941d0 217 return -1;
dgabino 0:102b50f941d0 218 }
dgabino 0:102b50f941d0 219
dgabino 0:102b50f941d0 220 /* give back control of the MCU program ram to the MCU */
dgabino 0:102b50f941d0 221 lgw_reg_w(reg_sel, 1);
dgabino 0:102b50f941d0 222
dgabino 0:102b50f941d0 223 return 0;
dgabino 0:102b50f941d0 224 }
dgabino 0:102b50f941d0 225
dgabino 0:102b50f941d0 226 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
dgabino 0:102b50f941d0 227
dgabino 0:102b50f941d0 228 void lgw_constant_adjust(void) {
dgabino 0:102b50f941d0 229
dgabino 0:102b50f941d0 230 /* I/Q path setup */
dgabino 0:102b50f941d0 231 // lgw_reg_w(LGW_RX_INVERT_IQ,0); /* default 0 */
dgabino 0:102b50f941d0 232 // lgw_reg_w(LGW_MODEM_INVERT_IQ,1); /* default 1 */
dgabino 0:102b50f941d0 233 // lgw_reg_w(LGW_CHIRP_INVERT_RX,1); /* default 1 */
dgabino 0:102b50f941d0 234 // lgw_reg_w(LGW_RX_EDGE_SELECT,0); /* default 0 */
dgabino 0:102b50f941d0 235 // lgw_reg_w(LGW_MBWSSF_MODEM_INVERT_IQ,0); /* default 0 */
dgabino 0:102b50f941d0 236 // lgw_reg_w(LGW_DC_NOTCH_EN,1); /* default 1 */
dgabino 0:102b50f941d0 237 lgw_reg_w(LGW_RSSI_BB_FILTER_ALPHA, 6); /* default 7 */
dgabino 0:102b50f941d0 238 lgw_reg_w(LGW_RSSI_DEC_FILTER_ALPHA, 7); /* default 5 */
dgabino 0:102b50f941d0 239 lgw_reg_w(LGW_RSSI_CHANN_FILTER_ALPHA, 7); /* default 8 */
dgabino 0:102b50f941d0 240 lgw_reg_w(LGW_RSSI_BB_DEFAULT_VALUE, 23); /* default 32 */
dgabino 0:102b50f941d0 241 lgw_reg_w(LGW_RSSI_CHANN_DEFAULT_VALUE, 85); /* default 100 */
dgabino 0:102b50f941d0 242 lgw_reg_w(LGW_RSSI_DEC_DEFAULT_VALUE, 66); /* default 100 */
dgabino 0:102b50f941d0 243 lgw_reg_w(LGW_DEC_GAIN_OFFSET, 7); /* default 8 */
dgabino 0:102b50f941d0 244 lgw_reg_w(LGW_CHAN_GAIN_OFFSET, 6); /* default 7 */
dgabino 0:102b50f941d0 245
dgabino 0:102b50f941d0 246 /* Correlator setup */
dgabino 0:102b50f941d0 247 // lgw_reg_w(LGW_CORR_DETECT_EN,126); /* default 126 */
dgabino 0:102b50f941d0 248 // lgw_reg_w(LGW_CORR_NUM_SAME_PEAK,4); /* default 4 */
dgabino 0:102b50f941d0 249 // lgw_reg_w(LGW_CORR_MAC_GAIN,5); /* default 5 */
dgabino 0:102b50f941d0 250 // lgw_reg_w(LGW_CORR_SAME_PEAKS_OPTION_SF6,0); /* default 0 */
dgabino 0:102b50f941d0 251 // lgw_reg_w(LGW_CORR_SAME_PEAKS_OPTION_SF7,1); /* default 1 */
dgabino 0:102b50f941d0 252 // lgw_reg_w(LGW_CORR_SAME_PEAKS_OPTION_SF8,1); /* default 1 */
dgabino 0:102b50f941d0 253 // lgw_reg_w(LGW_CORR_SAME_PEAKS_OPTION_SF9,1); /* default 1 */
dgabino 0:102b50f941d0 254 // lgw_reg_w(LGW_CORR_SAME_PEAKS_OPTION_SF10,1); /* default 1 */
dgabino 0:102b50f941d0 255 // lgw_reg_w(LGW_CORR_SAME_PEAKS_OPTION_SF11,1); /* default 1 */
dgabino 0:102b50f941d0 256 // lgw_reg_w(LGW_CORR_SAME_PEAKS_OPTION_SF12,1); /* default 1 */
dgabino 0:102b50f941d0 257 // lgw_reg_w(LGW_CORR_SIG_NOISE_RATIO_SF6,4); /* default 4 */
dgabino 0:102b50f941d0 258 // lgw_reg_w(LGW_CORR_SIG_NOISE_RATIO_SF7,4); /* default 4 */
dgabino 0:102b50f941d0 259 // lgw_reg_w(LGW_CORR_SIG_NOISE_RATIO_SF8,4); /* default 4 */
dgabino 0:102b50f941d0 260 // lgw_reg_w(LGW_CORR_SIG_NOISE_RATIO_SF9,4); /* default 4 */
dgabino 0:102b50f941d0 261 // lgw_reg_w(LGW_CORR_SIG_NOISE_RATIO_SF10,4); /* default 4 */
dgabino 0:102b50f941d0 262 // lgw_reg_w(LGW_CORR_SIG_NOISE_RATIO_SF11,4); /* default 4 */
dgabino 0:102b50f941d0 263 // lgw_reg_w(LGW_CORR_SIG_NOISE_RATIO_SF12,4); /* default 4 */
dgabino 0:102b50f941d0 264
dgabino 0:102b50f941d0 265 /* LoRa 'multi' demodulators setup */
dgabino 0:102b50f941d0 266 // lgw_reg_w(LGW_PREAMBLE_SYMB1_NB,10); /* default 10 */
dgabino 0:102b50f941d0 267 // lgw_reg_w(LGW_FREQ_TO_TIME_INVERT,29); /* default 29 */
dgabino 0:102b50f941d0 268 // lgw_reg_w(LGW_FRAME_SYNCH_GAIN,1); /* default 1 */
dgabino 0:102b50f941d0 269 // lgw_reg_w(LGW_SYNCH_DETECT_TH,1); /* default 1 */
dgabino 0:102b50f941d0 270 // lgw_reg_w(LGW_ZERO_PAD,0); /* default 0 */
dgabino 0:102b50f941d0 271 lgw_reg_w(LGW_SNR_AVG_CST, 3); /* default 2 */
dgabino 0:102b50f941d0 272 if (lorawan_public) { /* LoRa network */
dgabino 0:102b50f941d0 273 lgw_reg_w(LGW_FRAME_SYNCH_PEAK1_POS, 3); /* default 1 */
dgabino 0:102b50f941d0 274 lgw_reg_w(LGW_FRAME_SYNCH_PEAK2_POS, 4); /* default 2 */
dgabino 0:102b50f941d0 275 } else { /* private network */
dgabino 0:102b50f941d0 276 lgw_reg_w(LGW_FRAME_SYNCH_PEAK1_POS, 1); /* default 1 */
dgabino 0:102b50f941d0 277 lgw_reg_w(LGW_FRAME_SYNCH_PEAK2_POS, 2); /* default 2 */
dgabino 0:102b50f941d0 278 }
dgabino 0:102b50f941d0 279
dgabino 0:102b50f941d0 280 // lgw_reg_w(LGW_PREAMBLE_FINE_TIMING_GAIN,1); /* default 1 */
dgabino 0:102b50f941d0 281 // lgw_reg_w(LGW_ONLY_CRC_EN,1); /* default 1 */
dgabino 0:102b50f941d0 282 // lgw_reg_w(LGW_PAYLOAD_FINE_TIMING_GAIN,2); /* default 2 */
dgabino 0:102b50f941d0 283 // lgw_reg_w(LGW_TRACKING_INTEGRAL,0); /* default 0 */
dgabino 0:102b50f941d0 284 // lgw_reg_w(LGW_ADJUST_MODEM_START_OFFSET_RDX8,0); /* default 0 */
dgabino 0:102b50f941d0 285 // lgw_reg_w(LGW_ADJUST_MODEM_START_OFFSET_SF12_RDX4,4092); /* default 4092 */
dgabino 0:102b50f941d0 286 // lgw_reg_w(LGW_MAX_PAYLOAD_LEN,255); /* default 255 */
dgabino 0:102b50f941d0 287
dgabino 0:102b50f941d0 288 /* LoRa standalone 'MBWSSF' demodulator setup */
dgabino 0:102b50f941d0 289 // lgw_reg_w(LGW_MBWSSF_PREAMBLE_SYMB1_NB,10); /* default 10 */
dgabino 0:102b50f941d0 290 // lgw_reg_w(LGW_MBWSSF_FREQ_TO_TIME_INVERT,29); /* default 29 */
dgabino 0:102b50f941d0 291 // lgw_reg_w(LGW_MBWSSF_FRAME_SYNCH_GAIN,1); /* default 1 */
dgabino 0:102b50f941d0 292 // lgw_reg_w(LGW_MBWSSF_SYNCH_DETECT_TH,1); /* default 1 */
dgabino 0:102b50f941d0 293 // lgw_reg_w(LGW_MBWSSF_ZERO_PAD,0); /* default 0 */
dgabino 0:102b50f941d0 294 if (lorawan_public) { /* LoRa network */
dgabino 0:102b50f941d0 295 lgw_reg_w(LGW_MBWSSF_FRAME_SYNCH_PEAK1_POS, 3); /* default 1 */
dgabino 0:102b50f941d0 296 lgw_reg_w(LGW_MBWSSF_FRAME_SYNCH_PEAK2_POS, 4); /* default 2 */
dgabino 0:102b50f941d0 297 } else {
dgabino 0:102b50f941d0 298 lgw_reg_w(LGW_MBWSSF_FRAME_SYNCH_PEAK1_POS, 1); /* default 1 */
dgabino 0:102b50f941d0 299 lgw_reg_w(LGW_MBWSSF_FRAME_SYNCH_PEAK2_POS, 2); /* default 2 */
dgabino 0:102b50f941d0 300 }
dgabino 0:102b50f941d0 301 // lgw_reg_w(LGW_MBWSSF_ONLY_CRC_EN,1); /* default 1 */
dgabino 0:102b50f941d0 302 // lgw_reg_w(LGW_MBWSSF_PAYLOAD_FINE_TIMING_GAIN,2); /* default 2 */
dgabino 0:102b50f941d0 303 // lgw_reg_w(LGW_MBWSSF_PREAMBLE_FINE_TIMING_GAIN,1); /* default 1 */
dgabino 0:102b50f941d0 304 // lgw_reg_w(LGW_MBWSSF_TRACKING_INTEGRAL,0); /* default 0 */
dgabino 0:102b50f941d0 305 // lgw_reg_w(LGW_MBWSSF_AGC_FREEZE_ON_DETECT,1); /* default 1 */
dgabino 0:102b50f941d0 306
dgabino 0:102b50f941d0 307 /* Improvement of reference clock frequency error tolerance */
dgabino 0:102b50f941d0 308 lgw_reg_w(LGW_ADJUST_MODEM_START_OFFSET_RDX4, 1); /* default 0 */
dgabino 0:102b50f941d0 309 lgw_reg_w(LGW_ADJUST_MODEM_START_OFFSET_SF12_RDX4, 4094); /* default 4092 */
dgabino 0:102b50f941d0 310 lgw_reg_w(LGW_CORR_MAC_GAIN, 7); /* default 5 */
dgabino 0:102b50f941d0 311
dgabino 0:102b50f941d0 312 /* FSK datapath setup */
dgabino 0:102b50f941d0 313 lgw_reg_w(LGW_FSK_RX_INVERT, 1); /* default 0 */
dgabino 0:102b50f941d0 314 lgw_reg_w(LGW_FSK_MODEM_INVERT_IQ, 1); /* default 0 */
dgabino 0:102b50f941d0 315
dgabino 0:102b50f941d0 316 /* FSK demodulator setup */
dgabino 0:102b50f941d0 317 lgw_reg_w(LGW_FSK_RSSI_LENGTH, 4); /* default 0 */
dgabino 0:102b50f941d0 318 lgw_reg_w(LGW_FSK_PKT_MODE, 1); /* variable length, default 0 */
dgabino 0:102b50f941d0 319 lgw_reg_w(LGW_FSK_CRC_EN, 1); /* default 0 */
dgabino 0:102b50f941d0 320 lgw_reg_w(LGW_FSK_DCFREE_ENC, 2); /* default 0 */
dgabino 0:102b50f941d0 321 // lgw_reg_w(LGW_FSK_CRC_IBM,0); /* default 0 */
dgabino 0:102b50f941d0 322 lgw_reg_w(LGW_FSK_ERROR_OSR_TOL, 10); /* default 0 */
dgabino 0:102b50f941d0 323 lgw_reg_w(LGW_FSK_PKT_LENGTH, 255); /* max packet length in variable length mode */
dgabino 0:102b50f941d0 324 // lgw_reg_w(LGW_FSK_NODE_ADRS,0); /* default 0 */
dgabino 0:102b50f941d0 325 // lgw_reg_w(LGW_FSK_BROADCAST,0); /* default 0 */
dgabino 0:102b50f941d0 326 // lgw_reg_w(LGW_FSK_AUTO_AFC_ON,0); /* default 0 */
dgabino 0:102b50f941d0 327 lgw_reg_w(LGW_FSK_PATTERN_TIMEOUT_CFG, 128); /* sync timeout (allow 8 bytes preamble + 8 bytes sync word, default 0 */
dgabino 0:102b50f941d0 328
dgabino 0:102b50f941d0 329 /* TX general parameters */
dgabino 0:102b50f941d0 330 lgw_reg_w(LGW_TX_START_DELAY, TX_START_DELAY); /* default 0 */
dgabino 0:102b50f941d0 331
dgabino 0:102b50f941d0 332 /* TX LoRa */
dgabino 0:102b50f941d0 333 // lgw_reg_w(LGW_TX_MODE,0); /* default 0 */
dgabino 0:102b50f941d0 334 lgw_reg_w(LGW_TX_SWAP_IQ, 1); /* "normal" polarity; default 0 */
dgabino 0:102b50f941d0 335 if (lorawan_public) { /* LoRa network */
dgabino 0:102b50f941d0 336 lgw_reg_w(LGW_TX_FRAME_SYNCH_PEAK1_POS, 3); /* default 1 */
dgabino 0:102b50f941d0 337 lgw_reg_w(LGW_TX_FRAME_SYNCH_PEAK2_POS, 4); /* default 2 */
dgabino 0:102b50f941d0 338 } else { /* Private network */
dgabino 0:102b50f941d0 339 lgw_reg_w(LGW_TX_FRAME_SYNCH_PEAK1_POS, 1); /* default 1 */
dgabino 0:102b50f941d0 340 lgw_reg_w(LGW_TX_FRAME_SYNCH_PEAK2_POS, 2); /* default 2 */
dgabino 0:102b50f941d0 341 }
dgabino 0:102b50f941d0 342
dgabino 0:102b50f941d0 343 /* TX FSK */
dgabino 0:102b50f941d0 344 // lgw_reg_w(LGW_FSK_TX_GAUSSIAN_EN,1); /* default 1 */
dgabino 0:102b50f941d0 345 lgw_reg_w(LGW_FSK_TX_GAUSSIAN_SELECT_BT, 2); /* Gaussian filter always on TX, default 0 */
dgabino 0:102b50f941d0 346 // lgw_reg_w(LGW_FSK_TX_PATTERN_EN,1); /* default 1 */
dgabino 0:102b50f941d0 347 // lgw_reg_w(LGW_FSK_TX_PREAMBLE_SEQ,0); /* default 0 */
dgabino 0:102b50f941d0 348
dgabino 0:102b50f941d0 349 return;
dgabino 0:102b50f941d0 350 }
dgabino 0:102b50f941d0 351
dgabino 0:102b50f941d0 352 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
dgabino 0:102b50f941d0 353
dgabino 0:102b50f941d0 354 int32_t lgw_bw_getval(int x) {
dgabino 0:102b50f941d0 355 switch (x) {
dgabino 0:102b50f941d0 356 case BW_500KHZ:
dgabino 0:102b50f941d0 357 return 500000;
dgabino 0:102b50f941d0 358 case BW_250KHZ:
dgabino 0:102b50f941d0 359 return 250000;
dgabino 0:102b50f941d0 360 case BW_125KHZ:
dgabino 0:102b50f941d0 361 return 125000;
dgabino 0:102b50f941d0 362 case BW_62K5HZ:
dgabino 0:102b50f941d0 363 return 62500;
dgabino 0:102b50f941d0 364 case BW_31K2HZ:
dgabino 0:102b50f941d0 365 return 31200;
dgabino 0:102b50f941d0 366 case BW_15K6HZ:
dgabino 0:102b50f941d0 367 return 15600;
dgabino 0:102b50f941d0 368 case BW_7K8HZ :
dgabino 0:102b50f941d0 369 return 7800;
dgabino 0:102b50f941d0 370 default:
dgabino 0:102b50f941d0 371 return -1;
dgabino 0:102b50f941d0 372 }
dgabino 0:102b50f941d0 373 }
dgabino 0:102b50f941d0 374
dgabino 0:102b50f941d0 375 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
dgabino 0:102b50f941d0 376
dgabino 0:102b50f941d0 377 int32_t lgw_sf_getval(int x) {
dgabino 0:102b50f941d0 378 switch (x) {
dgabino 0:102b50f941d0 379 case DR_LORA_SF7:
dgabino 0:102b50f941d0 380 return 7;
dgabino 0:102b50f941d0 381 case DR_LORA_SF8:
dgabino 0:102b50f941d0 382 return 8;
dgabino 0:102b50f941d0 383 case DR_LORA_SF9:
dgabino 0:102b50f941d0 384 return 9;
dgabino 0:102b50f941d0 385 case DR_LORA_SF10:
dgabino 0:102b50f941d0 386 return 10;
dgabino 0:102b50f941d0 387 case DR_LORA_SF11:
dgabino 0:102b50f941d0 388 return 11;
dgabino 0:102b50f941d0 389 case DR_LORA_SF12:
dgabino 0:102b50f941d0 390 return 12;
dgabino 0:102b50f941d0 391 default:
dgabino 0:102b50f941d0 392 return -1;
dgabino 0:102b50f941d0 393 }
dgabino 0:102b50f941d0 394 }
dgabino 0:102b50f941d0 395
dgabino 0:102b50f941d0 396 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
dgabino 0:102b50f941d0 397
dgabino 0:102b50f941d0 398 int lgw_calibrate_sx125x(uint8_t *cal_fw, uint8_t idx_start, uint8_t idx_nb) {
dgabino 0:102b50f941d0 399 int i, err;
dgabino 0:102b50f941d0 400 int32_t read_val;
dgabino 0:102b50f941d0 401 uint8_t fw_version;
dgabino 0:102b50f941d0 402 uint8_t cal_cmd;
dgabino 0:102b50f941d0 403 uint16_t cal_time;
dgabino 0:102b50f941d0 404 uint8_t cal_status;
dgabino 0:102b50f941d0 405
dgabino 0:102b50f941d0 406 /* check parameters */
dgabino 0:102b50f941d0 407 if (cal_fw == NULL) {
dgabino 0:102b50f941d0 408 DEBUG_MSG("ERROR: invalid parameter, null pointer\n");
dgabino 0:102b50f941d0 409 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 410 }
dgabino 0:102b50f941d0 411 if ((idx_start < 5) || (idx_start > 15)) {
dgabino 0:102b50f941d0 412 DEBUG_MSG("ERROR: invalid parameter, calibration offset index start must be [5-15]\n");
dgabino 0:102b50f941d0 413 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 414 }
dgabino 0:102b50f941d0 415 if (idx_nb > 8) {
dgabino 0:102b50f941d0 416 DEBUG_MSG("ERROR: invalid parameter, calibration offset index number must be <= 8\n");
dgabino 0:102b50f941d0 417 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 418 }
dgabino 0:102b50f941d0 419
dgabino 0:102b50f941d0 420 /* reset the registers (also shuts the radios down) */
dgabino 0:102b50f941d0 421 lgw_soft_reset();
dgabino 0:102b50f941d0 422
dgabino 0:102b50f941d0 423 /* gate clocks */
dgabino 0:102b50f941d0 424 lgw_reg_w(LGW_GLOBAL_EN, 0);
dgabino 0:102b50f941d0 425 lgw_reg_w(LGW_CLK32M_EN, 0);
dgabino 0:102b50f941d0 426
dgabino 0:102b50f941d0 427 #if 0
dgabino 0:102b50f941d0 428 /* switch on and reset the radios (also starts the 32 MHz XTAL) */
dgabino 0:102b50f941d0 429 lgw_reg_w(LGW_RADIO_A_EN, 1);
dgabino 0:102b50f941d0 430 lgw_reg_w(LGW_RADIO_B_EN, 1);
dgabino 0:102b50f941d0 431 wait_ms(500); /* TODO: optimize */
dgabino 0:102b50f941d0 432 #endif
dgabino 0:102b50f941d0 433 lgw_reg_w(LGW_RADIO_RST, 1);
dgabino 0:102b50f941d0 434 wait_ms(5);
dgabino 0:102b50f941d0 435 lgw_reg_w(LGW_RADIO_RST, 0);
dgabino 0:102b50f941d0 436
dgabino 0:102b50f941d0 437 /* setup the radios */
dgabino 0:102b50f941d0 438 err = lgw_setup_sx125x(0, rf_clkout, rf_enable[0], rf_radio_type[0], rf_rx_freq[0]);
dgabino 0:102b50f941d0 439 if (err != 0) {
dgabino 0:102b50f941d0 440 DEBUG_MSG("ERROR: Failed to setup sx125x radio for RF chain 0\n");
dgabino 0:102b50f941d0 441 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 442 }
dgabino 0:102b50f941d0 443 err = lgw_setup_sx125x(1, rf_clkout, rf_enable[1], rf_radio_type[1], rf_rx_freq[1]);
dgabino 0:102b50f941d0 444 if (err != 0) {
dgabino 0:102b50f941d0 445 DEBUG_MSG("ERROR: Failed to setup sx125x radio for RF chain 0\n");
dgabino 0:102b50f941d0 446 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 447 }
dgabino 0:102b50f941d0 448
dgabino 0:102b50f941d0 449 /* Enable clocks */
dgabino 0:102b50f941d0 450 lgw_reg_w(LGW_GLOBAL_EN, 1);
dgabino 0:102b50f941d0 451 lgw_reg_w(LGW_CLK32M_EN, 1);
dgabino 0:102b50f941d0 452
dgabino 0:102b50f941d0 453 /* GPIOs table :
dgabino 0:102b50f941d0 454 DGPIO0 -> N/A
dgabino 0:102b50f941d0 455 DGPIO1 -> N/A
dgabino 0:102b50f941d0 456 DGPIO2 -> N/A
dgabino 0:102b50f941d0 457 DGPIO3 -> TX digital filter ON
dgabino 0:102b50f941d0 458 DGPIO4 -> TX ON
dgabino 0:102b50f941d0 459 */
dgabino 0:102b50f941d0 460
dgabino 0:102b50f941d0 461 /* select calibration command */
dgabino 0:102b50f941d0 462 cal_cmd = 0;
dgabino 0:102b50f941d0 463 cal_cmd |= rf_enable[0] ? 0x01 : 0x00; /* Bit 0: Calibrate Rx IQ mismatch compensation on radio A */
dgabino 0:102b50f941d0 464 cal_cmd |= rf_enable[1] ? 0x02 : 0x00; /* Bit 1: Calibrate Rx IQ mismatch compensation on radio B */
dgabino 0:102b50f941d0 465 cal_cmd |= (rf_enable[0] && rf_tx_enable[0]) ? 0x04 : 0x00; /* Bit 2: Calibrate Tx DC offset on radio A */
dgabino 0:102b50f941d0 466 cal_cmd |= (rf_enable[1] && rf_tx_enable[1]) ? 0x08 : 0x00; /* Bit 3: Calibrate Tx DC offset on radio B */
dgabino 0:102b50f941d0 467 cal_cmd |= 0x10; /* Bit 4: 0: calibrate with DAC gain=2, 1: with DAC gain=3 (use 3) */
dgabino 0:102b50f941d0 468
dgabino 0:102b50f941d0 469 switch (rf_radio_type[0]) { /* we assume that there is only one radio type on the board */
dgabino 0:102b50f941d0 470 case LGW_RADIO_TYPE_SX1255:
dgabino 0:102b50f941d0 471 cal_cmd |= 0x20; /* Bit 5: 0: SX1257, 1: SX1255 */
dgabino 0:102b50f941d0 472 break;
dgabino 0:102b50f941d0 473 case LGW_RADIO_TYPE_SX1257:
dgabino 0:102b50f941d0 474 cal_cmd |= 0x00; /* Bit 5: 0: SX1257, 1: SX1255 */
dgabino 0:102b50f941d0 475 break;
dgabino 0:102b50f941d0 476 default:
dgabino 0:102b50f941d0 477 DEBUG_PRINTF("ERROR: UNEXPECTED VALUE %d FOR RADIO TYPE\n", rf_radio_type[0]);
dgabino 0:102b50f941d0 478 break;
dgabino 0:102b50f941d0 479 }
dgabino 0:102b50f941d0 480
dgabino 0:102b50f941d0 481 cal_cmd |= 0x00; /* Bit 6-7: Board type 0: ref, 1: FPGA, 3: board X */
dgabino 0:102b50f941d0 482 cal_time = 2300; /* measured between 2.1 and 2.2 sec, because 1 TX only */
dgabino 0:102b50f941d0 483
dgabino 0:102b50f941d0 484 /* Load the calibration firmware */
dgabino 0:102b50f941d0 485 load_firmware(MCU_AGC, cal_fw, MCU_AGC_FW_BYTE);
dgabino 0:102b50f941d0 486 lgw_reg_w(LGW_FORCE_HOST_RADIO_CTRL, 0); /* gives to AGC MCU the control of the radios */
dgabino 0:102b50f941d0 487 lgw_reg_w(LGW_RADIO_SELECT, cal_cmd); /* send calibration configuration word */
dgabino 0:102b50f941d0 488 lgw_reg_w(LGW_MCU_RST_1, 0);
dgabino 0:102b50f941d0 489
dgabino 0:102b50f941d0 490 /* Check firmware version */
dgabino 0:102b50f941d0 491 lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, FW_VERSION_ADDR);
dgabino 0:102b50f941d0 492 lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val);
dgabino 0:102b50f941d0 493 fw_version = (uint8_t)read_val;
dgabino 0:102b50f941d0 494 if (fw_version != FW_VERSION_CAL) {
dgabino 0:102b50f941d0 495
dgabino 0:102b50f941d0 496 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 497 }
dgabino 0:102b50f941d0 498
dgabino 0:102b50f941d0 499 lgw_reg_w(LGW_PAGE_REG, 3); /* Calibration will start on this condition as soon as MCU can talk to concentrator registers */
dgabino 0:102b50f941d0 500 lgw_reg_w(LGW_EMERGENCY_FORCE_HOST_CTRL, 0); /* Give control of concentrator registers to MCU */
dgabino 0:102b50f941d0 501
dgabino 0:102b50f941d0 502 /* Wait for calibration to end */
dgabino 0:102b50f941d0 503 DEBUG_PRINTF("Note: calibration started (time: %u ms)\n", cal_time);
dgabino 0:102b50f941d0 504 wait_ms(cal_time); /* Wait for end of calibration */
dgabino 0:102b50f941d0 505 lgw_reg_w(LGW_EMERGENCY_FORCE_HOST_CTRL, 1); /* Take back control */
dgabino 0:102b50f941d0 506
dgabino 0:102b50f941d0 507 /* Get calibration status */
dgabino 0:102b50f941d0 508 lgw_reg_r(LGW_MCU_AGC_STATUS, &read_val);
dgabino 0:102b50f941d0 509 cal_status = (uint8_t)read_val;
dgabino 0:102b50f941d0 510 /*
dgabino 0:102b50f941d0 511 bit 7: calibration finished
dgabino 0:102b50f941d0 512 bit 0: could access SX1301 registers
dgabino 0:102b50f941d0 513 bit 1: could access radio A registers
dgabino 0:102b50f941d0 514 bit 2: could access radio B registers
dgabino 0:102b50f941d0 515 bit 3: radio A RX image rejection successful
dgabino 0:102b50f941d0 516 bit 4: radio B RX image rejection successful
dgabino 0:102b50f941d0 517 bit 5: radio A TX DC Offset correction successful
dgabino 0:102b50f941d0 518 bit 6: radio B TX DC Offset correction successful
dgabino 0:102b50f941d0 519 */
dgabino 0:102b50f941d0 520 if ((cal_status & 0x81) != 0x81) {
dgabino 0:102b50f941d0 521 DEBUG_PRINTF("ERROR: CALIBRATION FAILURE (STATUS = 0x%X)\n", cal_status);
dgabino 0:102b50f941d0 522 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 523 } else {
dgabino 0:102b50f941d0 524 DEBUG_PRINTF("Note: calibration finished (status = 0x%X)\n", cal_status);
dgabino 0:102b50f941d0 525 }
dgabino 0:102b50f941d0 526 if (rf_enable[0] && ((cal_status & 0x02) == 0)) {
dgabino 0:102b50f941d0 527 DEBUG_MSG("ERROR: calibration could not access radio A\n");
dgabino 0:102b50f941d0 528 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 529 }
dgabino 0:102b50f941d0 530 if (rf_enable[1] && ((cal_status & 0x04) == 0)) {
dgabino 0:102b50f941d0 531 DEBUG_MSG("ERROR: calibration could not access radio B\n");
dgabino 0:102b50f941d0 532 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 533 }
dgabino 0:102b50f941d0 534 if (rf_enable[0] && ((cal_status & 0x08) == 0)) {
dgabino 0:102b50f941d0 535 DEBUG_MSG("WARNING: problem in calibration of radio A for image rejection\n");
dgabino 0:102b50f941d0 536 }
dgabino 0:102b50f941d0 537 if (rf_enable[1] && ((cal_status & 0x10) == 0)) {
dgabino 0:102b50f941d0 538 DEBUG_MSG("WARNING: problem in calibration of radio B for image rejection\n");
dgabino 0:102b50f941d0 539 }
dgabino 0:102b50f941d0 540 if (rf_enable[0] && rf_tx_enable[0] && ((cal_status & 0x20) == 0)) {
dgabino 0:102b50f941d0 541 DEBUG_MSG("WARNING: problem in calibration of radio A for TX DC offset\n");
dgabino 0:102b50f941d0 542 }
dgabino 0:102b50f941d0 543 if (rf_enable[1] && rf_tx_enable[1] && ((cal_status & 0x40) == 0)) {
dgabino 0:102b50f941d0 544 DEBUG_MSG("WARNING: problem in calibration of radio B for TX DC offset\n");
dgabino 0:102b50f941d0 545 }
dgabino 0:102b50f941d0 546
dgabino 0:102b50f941d0 547 /* Get TX DC offset values */
dgabino 0:102b50f941d0 548 for(i = 0; i < (int)idx_nb; ++i) {
dgabino 0:102b50f941d0 549 lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xA0 + i);
dgabino 0:102b50f941d0 550 lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val);
dgabino 0:102b50f941d0 551 cal_offset_a_i[i] = (int8_t)read_val;
dgabino 0:102b50f941d0 552 lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xA8 + i);
dgabino 0:102b50f941d0 553 lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val);
dgabino 0:102b50f941d0 554 cal_offset_a_q[i] = (int8_t)read_val;
dgabino 0:102b50f941d0 555 lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xB0 + i);
dgabino 0:102b50f941d0 556 lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val);
dgabino 0:102b50f941d0 557 cal_offset_b_i[i] = (int8_t)read_val;
dgabino 0:102b50f941d0 558 lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xB8 + i);
dgabino 0:102b50f941d0 559 lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val);
dgabino 0:102b50f941d0 560 cal_offset_b_q[i] = (int8_t)read_val;
dgabino 0:102b50f941d0 561 DEBUG_PRINTF("calibration a_i = %d\n", cal_offset_a_i[i]);
dgabino 0:102b50f941d0 562 }
dgabino 0:102b50f941d0 563
dgabino 0:102b50f941d0 564 /* Require MCU to capture calibration values */
dgabino 0:102b50f941d0 565 lgw_mcu_commit_radio_calibration(idx_start, idx_nb);
dgabino 0:102b50f941d0 566
dgabino 0:102b50f941d0 567 return LGW_HAL_SUCCESS;
dgabino 0:102b50f941d0 568 }
dgabino 0:102b50f941d0 569
dgabino 0:102b50f941d0 570 /* -------------------------------------------------------------------------- */
dgabino 0:102b50f941d0 571 /* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */
dgabino 0:102b50f941d0 572
dgabino 0:102b50f941d0 573 int lgw_board_setconf(struct lgw_conf_board_s conf) {
dgabino 0:102b50f941d0 574 /* check if the concentrator is running */
dgabino 0:102b50f941d0 575 if (lgw_is_started == true) {
dgabino 0:102b50f941d0 576 DEBUG_MSG("ERROR: CONCENTRATOR IS RUNNING, STOP IT BEFORE TOUCHING CONFIGURATION\n");
dgabino 0:102b50f941d0 577 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 578 }
dgabino 0:102b50f941d0 579
dgabino 0:102b50f941d0 580 /* set internal config according to parameters */
dgabino 0:102b50f941d0 581 lorawan_public = conf.lorawan_public;
dgabino 0:102b50f941d0 582 rf_clkout = conf.clksrc;
dgabino 0:102b50f941d0 583
dgabino 0:102b50f941d0 584 DEBUG_PRINTF("Note: board configuration; lorawan_public:%d, clksrc:%d\n", lorawan_public, rf_clkout);
dgabino 0:102b50f941d0 585
dgabino 0:102b50f941d0 586 /* send configuration to concentrator MCU */
dgabino 0:102b50f941d0 587 return lgw_mcu_board_setconf(conf);
dgabino 0:102b50f941d0 588 }
dgabino 0:102b50f941d0 589
dgabino 0:102b50f941d0 590 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
dgabino 0:102b50f941d0 591
dgabino 0:102b50f941d0 592 int lgw_rxrf_setconf(uint8_t rf_chain, struct lgw_conf_rxrf_s conf) {
dgabino 0:102b50f941d0 593 /* check if the concentrator is running */
dgabino 0:102b50f941d0 594 if (lgw_is_started == true) {
dgabino 0:102b50f941d0 595 DEBUG_MSG("ERROR: CONCENTRATOR IS RUNNING, STOP IT BEFORE TOUCHING CONFIGURATION\n");
dgabino 0:102b50f941d0 596 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 597 }
dgabino 0:102b50f941d0 598
dgabino 0:102b50f941d0 599 /* check input range (segfault prevention) */
dgabino 0:102b50f941d0 600 if (rf_chain >= LGW_RF_CHAIN_NB) {
dgabino 0:102b50f941d0 601 DEBUG_MSG("ERROR: NOT A VALID RF_CHAIN NUMBER\n");
dgabino 0:102b50f941d0 602 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 603 }
dgabino 0:102b50f941d0 604
dgabino 0:102b50f941d0 605 /* check if radio type is supported */
dgabino 0:102b50f941d0 606 if ((conf.type != LGW_RADIO_TYPE_SX1255) && (conf.type != LGW_RADIO_TYPE_SX1257)) {
dgabino 0:102b50f941d0 607 DEBUG_MSG("ERROR: NOT A VALID RADIO TYPE\n");
dgabino 0:102b50f941d0 608 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 609 }
dgabino 0:102b50f941d0 610
dgabino 0:102b50f941d0 611 /* set internal config according to parameters */
dgabino 0:102b50f941d0 612 rf_enable[rf_chain] = conf.enable;
dgabino 0:102b50f941d0 613 rf_rx_freq[rf_chain] = conf.freq_hz;
dgabino 0:102b50f941d0 614 rf_rssi_offset[rf_chain] = conf.rssi_offset;
dgabino 0:102b50f941d0 615 rf_radio_type[rf_chain] = conf.type;
dgabino 0:102b50f941d0 616 rf_tx_enable[rf_chain] = conf.tx_enable;
dgabino 0:102b50f941d0 617
dgabino 0:102b50f941d0 618 DEBUG_PRINTF("Note: rf_chain %d configuration; en:%d freq:%d rssi_offset:%f radio_type:%d tx_enable:%d\n", rf_chain, rf_enable [rf_chain], rf_rx_freq[rf_chain], rf_rssi_offset[rf_chain], rf_radio_type[rf_chain], rf_tx_enable[rf_chain]);
dgabino 0:102b50f941d0 619
dgabino 0:102b50f941d0 620 /* send configuration to concentrator MCU */
dgabino 0:102b50f941d0 621 return lgw_mcu_rxrf_setconf(rf_chain, conf);
dgabino 0:102b50f941d0 622 }
dgabino 0:102b50f941d0 623
dgabino 0:102b50f941d0 624 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
dgabino 0:102b50f941d0 625
dgabino 0:102b50f941d0 626 int lgw_rxif_setconf(uint8_t if_chain, struct lgw_conf_rxif_s conf) {
dgabino 0:102b50f941d0 627 int32_t bw_hz;
dgabino 0:102b50f941d0 628 uint32_t rf_rx_bandwidth;
dgabino 0:102b50f941d0 629
dgabino 0:102b50f941d0 630 /* check if the concentrator is running */
dgabino 0:102b50f941d0 631 if (lgw_is_started == true) {
dgabino 0:102b50f941d0 632 DEBUG_MSG("ERROR: CONCENTRATOR IS RUNNING, STOP IT BEFORE TOUCHING CONFIGURATION\n");
dgabino 0:102b50f941d0 633 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 634 }
dgabino 0:102b50f941d0 635
dgabino 0:102b50f941d0 636 /* check input range (segfault prevention) */
dgabino 0:102b50f941d0 637 if (if_chain >= LGW_IF_CHAIN_NB) {
dgabino 0:102b50f941d0 638 DEBUG_PRINTF("ERROR: %d NOT A VALID IF_CHAIN NUMBER\n", if_chain);
dgabino 0:102b50f941d0 639 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 640 }
dgabino 0:102b50f941d0 641
dgabino 0:102b50f941d0 642 /* if chain is disabled, don't care about most parameters */
dgabino 0:102b50f941d0 643 if (conf.enable == false) {
dgabino 0:102b50f941d0 644 if_enable[if_chain] = false;
dgabino 0:102b50f941d0 645 if_freq[if_chain] = 0;
dgabino 0:102b50f941d0 646 DEBUG_PRINTF("Note: if_chain %d disabled\n", if_chain);
dgabino 0:102b50f941d0 647 return LGW_HAL_SUCCESS;
dgabino 0:102b50f941d0 648 }
dgabino 0:102b50f941d0 649
dgabino 0:102b50f941d0 650 /* check 'general' parameters */
dgabino 0:102b50f941d0 651 if (ifmod_config[if_chain] == IF_UNDEFINED) {
dgabino 0:102b50f941d0 652 DEBUG_PRINTF("ERROR: IF CHAIN %d NOT CONFIGURABLE\n", if_chain);
dgabino 0:102b50f941d0 653 }
dgabino 0:102b50f941d0 654 if (conf.rf_chain >= LGW_RF_CHAIN_NB) {
dgabino 0:102b50f941d0 655 DEBUG_MSG("ERROR: INVALID RF_CHAIN TO ASSOCIATE WITH A LORA_STD IF CHAIN\n");
dgabino 0:102b50f941d0 656 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 657 }
dgabino 0:102b50f941d0 658 switch (conf.bandwidth) {
dgabino 0:102b50f941d0 659 case BW_250KHZ:
dgabino 0:102b50f941d0 660 rf_rx_bandwidth = LGW_RF_RX_BANDWIDTH_250KHZ;
dgabino 0:102b50f941d0 661 break;
dgabino 0:102b50f941d0 662 case BW_500KHZ:
dgabino 0:102b50f941d0 663 rf_rx_bandwidth = LGW_RF_RX_BANDWIDTH_500KHZ;
dgabino 0:102b50f941d0 664 break;
dgabino 0:102b50f941d0 665 default:
dgabino 0:102b50f941d0 666 rf_rx_bandwidth = LGW_RF_RX_BANDWIDTH_125KHZ;
dgabino 0:102b50f941d0 667 break;
dgabino 0:102b50f941d0 668 }
dgabino 0:102b50f941d0 669
dgabino 0:102b50f941d0 670 bw_hz = lgw_bw_getval(conf.bandwidth);
dgabino 0:102b50f941d0 671 if ((conf.freq_hz + ((bw_hz == -1) ? LGW_REF_BW : bw_hz) / 2) > ((int32_t)rf_rx_bandwidth / 2)) {
dgabino 0:102b50f941d0 672 DEBUG_PRINTF("ERROR: IF FREQUENCY %d TOO HIGH\n", conf.freq_hz);
dgabino 0:102b50f941d0 673 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 674 } else if ((conf.freq_hz - ((bw_hz == -1) ? LGW_REF_BW : bw_hz) / 2) < -((int32_t)rf_rx_bandwidth / 2)) {
dgabino 0:102b50f941d0 675 DEBUG_PRINTF("ERROR: IF FREQUENCY %d TOO LOW\n", conf.freq_hz);
dgabino 0:102b50f941d0 676 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 677 }
dgabino 0:102b50f941d0 678
dgabino 0:102b50f941d0 679 /* check parameters according to the type of IF chain + modem,
dgabino 0:102b50f941d0 680 fill default if necessary, and commit configuration if everything is OK */
dgabino 0:102b50f941d0 681 switch (ifmod_config[if_chain]) {
dgabino 0:102b50f941d0 682 case IF_LORA_STD:
dgabino 0:102b50f941d0 683 /* fill default parameters if needed */
dgabino 0:102b50f941d0 684 if (conf.bandwidth == BW_UNDEFINED) {
dgabino 0:102b50f941d0 685 conf.bandwidth = BW_250KHZ;
dgabino 0:102b50f941d0 686 }
dgabino 0:102b50f941d0 687 if (conf.datarate == DR_UNDEFINED) {
dgabino 0:102b50f941d0 688 conf.datarate = DR_LORA_SF9;
dgabino 0:102b50f941d0 689 }
dgabino 0:102b50f941d0 690 /* check BW & DR */
dgabino 0:102b50f941d0 691 if (!IS_LORA_BW(conf.bandwidth)) {
dgabino 0:102b50f941d0 692 DEBUG_MSG("ERROR: BANDWIDTH NOT SUPPORTED BY LORA_STD IF CHAIN\n");
dgabino 0:102b50f941d0 693 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 694 }
dgabino 0:102b50f941d0 695 if (!IS_LORA_STD_DR(conf.datarate)) {
dgabino 0:102b50f941d0 696 DEBUG_MSG("ERROR: DATARATE NOT SUPPORTED BY LORA_STD IF CHAIN\n");
dgabino 0:102b50f941d0 697 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 698 }
dgabino 0:102b50f941d0 699 /* set internal configuration */
dgabino 0:102b50f941d0 700 if_enable[if_chain] = conf.enable;
dgabino 0:102b50f941d0 701 if_rf_chain[if_chain] = conf.rf_chain;
dgabino 0:102b50f941d0 702 if_freq[if_chain] = conf.freq_hz;
dgabino 0:102b50f941d0 703 lora_rx_bw = conf.bandwidth;
dgabino 0:102b50f941d0 704 lora_rx_sf = (uint8_t)(DR_LORA_MULTI & conf.datarate); /* filter SF out of the 7-12 range */
dgabino 0:102b50f941d0 705 if (SET_PPM_ON(conf.bandwidth, conf.datarate)) {
dgabino 0:102b50f941d0 706 lora_rx_ppm_offset = true;
dgabino 0:102b50f941d0 707 } else {
dgabino 0:102b50f941d0 708 lora_rx_ppm_offset = false;
dgabino 0:102b50f941d0 709 }
dgabino 0:102b50f941d0 710
dgabino 0:102b50f941d0 711 DEBUG_PRINTF("Note: LoRa 'std' if_chain %d configuration; en:%d freq:%d bw:%d dr:%d\n", if_chain, if_enable[if_chain], if_freq[if_chain], lora_rx_bw, lora_rx_sf);
dgabino 0:102b50f941d0 712 break;
dgabino 0:102b50f941d0 713
dgabino 0:102b50f941d0 714 case IF_LORA_MULTI:
dgabino 0:102b50f941d0 715 /* fill default parameters if needed */
dgabino 0:102b50f941d0 716 if (conf.bandwidth == BW_UNDEFINED) {
dgabino 0:102b50f941d0 717 conf.bandwidth = BW_125KHZ;
dgabino 0:102b50f941d0 718 }
dgabino 0:102b50f941d0 719 if (conf.datarate == DR_UNDEFINED) {
dgabino 0:102b50f941d0 720 conf.datarate = DR_LORA_MULTI;
dgabino 0:102b50f941d0 721 }
dgabino 0:102b50f941d0 722 /* check BW & DR */
dgabino 0:102b50f941d0 723 if (conf.bandwidth != BW_125KHZ) {
dgabino 0:102b50f941d0 724 DEBUG_MSG("ERROR: BANDWIDTH NOT SUPPORTED BY LORA_MULTI IF CHAIN\n");
dgabino 0:102b50f941d0 725 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 726 }
dgabino 0:102b50f941d0 727 if (!IS_LORA_MULTI_DR(conf.datarate)) {
dgabino 0:102b50f941d0 728 DEBUG_MSG("ERROR: DATARATE(S) NOT SUPPORTED BY LORA_MULTI IF CHAIN\n");
dgabino 0:102b50f941d0 729 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 730 }
dgabino 0:102b50f941d0 731 /* set internal configuration */
dgabino 0:102b50f941d0 732 if_enable[if_chain] = conf.enable;
dgabino 0:102b50f941d0 733 if_rf_chain[if_chain] = conf.rf_chain;
dgabino 0:102b50f941d0 734 if_freq[if_chain] = conf.freq_hz;
dgabino 0:102b50f941d0 735 lora_multi_sfmask[if_chain] = (uint8_t)(DR_LORA_MULTI & conf.datarate); /* filter SF out of the 7-12 range */
dgabino 0:102b50f941d0 736
dgabino 0:102b50f941d0 737 DEBUG_PRINTF("Note: LoRa 'multi' if_chain %d configuration; en:%d freq:%d SF_mask:0x%02x\n", if_chain, if_enable[if_chain], if_freq[if_chain], lora_multi_sfmask[if_chain]);
dgabino 0:102b50f941d0 738 break;
dgabino 0:102b50f941d0 739
dgabino 0:102b50f941d0 740 case IF_FSK_STD:
dgabino 0:102b50f941d0 741 /* fill default parameters if needed */
dgabino 0:102b50f941d0 742 if (conf.bandwidth == BW_UNDEFINED) {
dgabino 0:102b50f941d0 743 conf.bandwidth = BW_250KHZ;
dgabino 0:102b50f941d0 744 }
dgabino 0:102b50f941d0 745 if (conf.datarate == DR_UNDEFINED) {
dgabino 0:102b50f941d0 746 conf.datarate = 64000; /* default datarate */
dgabino 0:102b50f941d0 747 }
dgabino 0:102b50f941d0 748 /* check BW & DR */
dgabino 0:102b50f941d0 749 if(!IS_FSK_BW(conf.bandwidth)) {
dgabino 0:102b50f941d0 750 DEBUG_MSG("ERROR: BANDWIDTH NOT SUPPORTED BY FSK IF CHAIN\n");
dgabino 0:102b50f941d0 751 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 752 }
dgabino 0:102b50f941d0 753 if(!IS_FSK_DR(conf.datarate)) {
dgabino 0:102b50f941d0 754 DEBUG_MSG("ERROR: DATARATE NOT SUPPORTED BY FSK IF CHAIN\n");
dgabino 0:102b50f941d0 755 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 756 }
dgabino 0:102b50f941d0 757 /* set internal configuration */
dgabino 0:102b50f941d0 758 if_enable[if_chain] = conf.enable;
dgabino 0:102b50f941d0 759 if_rf_chain[if_chain] = conf.rf_chain;
dgabino 0:102b50f941d0 760 if_freq[if_chain] = conf.freq_hz;
dgabino 0:102b50f941d0 761 fsk_rx_bw = conf.bandwidth;
dgabino 0:102b50f941d0 762 fsk_rx_dr = conf.datarate;
dgabino 0:102b50f941d0 763 if (conf.sync_word > 0) {
dgabino 0:102b50f941d0 764 fsk_sync_word_size = conf.sync_word_size;
dgabino 0:102b50f941d0 765 fsk_sync_word = conf.sync_word;
dgabino 0:102b50f941d0 766 }
dgabino 0:102b50f941d0 767 DEBUG_PRINTF("Note: FSK if_chain %d configuration; en:%d freq:%d bw:%u dr:%u (%u real dr) sync:0x%0*" PRIx64 "\n", if_chain, if_enable[if_chain], if_freq[if_chain], fsk_rx_bw, fsk_rx_dr, LGW_XTAL_FREQU / (LGW_XTAL_FREQU / fsk_rx_dr), 2 * fsk_sync_word_size, fsk_sync_word);
dgabino 0:102b50f941d0 768 break;
dgabino 0:102b50f941d0 769
dgabino 0:102b50f941d0 770 default:
dgabino 0:102b50f941d0 771 DEBUG_PRINTF("ERROR: IF CHAIN %d TYPE NOT SUPPORTED\n", if_chain);
dgabino 0:102b50f941d0 772 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 773 }
dgabino 0:102b50f941d0 774
dgabino 0:102b50f941d0 775 /* send configuration to concentrator MCU */
dgabino 0:102b50f941d0 776 return lgw_mcu_rxif_setconf(if_chain, conf);
dgabino 0:102b50f941d0 777 }
dgabino 0:102b50f941d0 778
dgabino 0:102b50f941d0 779 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
dgabino 0:102b50f941d0 780
dgabino 0:102b50f941d0 781 int lgw_txgain_setconf(struct lgw_tx_gain_lut_s *conf) {
dgabino 0:102b50f941d0 782 int i;
dgabino 0:102b50f941d0 783
dgabino 0:102b50f941d0 784 /* Check LUT size */
dgabino 0:102b50f941d0 785 if ((conf->size < 1) || (conf->size > TX_GAIN_LUT_SIZE_MAX)) {
dgabino 0:102b50f941d0 786 DEBUG_PRINTF("ERROR: TX gain LUT must have at least one entry and maximum %d entries\n", TX_GAIN_LUT_SIZE_MAX);
dgabino 0:102b50f941d0 787 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 788 }
dgabino 0:102b50f941d0 789
dgabino 0:102b50f941d0 790 txgain_lut.size = conf->size;
dgabino 0:102b50f941d0 791
dgabino 0:102b50f941d0 792 for (i = 0; i < txgain_lut.size; i++) {
dgabino 0:102b50f941d0 793 /* Check gain range */
dgabino 0:102b50f941d0 794 if (conf->lut[i].dig_gain > 3) {
dgabino 0:102b50f941d0 795 DEBUG_MSG("ERROR: TX gain LUT: SX1301 digital gain must be between 0 and 3\n");
dgabino 0:102b50f941d0 796 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 797 }
dgabino 0:102b50f941d0 798 if (conf->lut[i].dac_gain != 3) {
dgabino 0:102b50f941d0 799 DEBUG_MSG("ERROR: TX gain LUT: SX1257 DAC gains != 3 are not supported\n");
dgabino 0:102b50f941d0 800 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 801 }
dgabino 0:102b50f941d0 802 if (conf->lut[i].mix_gain > 15) {
dgabino 0:102b50f941d0 803 DEBUG_MSG("ERROR: TX gain LUT: SX1257 mixer gain must not exceed 15\n");
dgabino 0:102b50f941d0 804 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 805 }
dgabino 0:102b50f941d0 806 if (conf->lut[i].pa_gain > 3) {
dgabino 0:102b50f941d0 807 DEBUG_MSG("ERROR: TX gain LUT: External PA gain must not exceed 3\n");
dgabino 0:102b50f941d0 808 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 809 }
dgabino 0:102b50f941d0 810
dgabino 0:102b50f941d0 811 /* Set internal LUT */
dgabino 0:102b50f941d0 812 txgain_lut.lut[i].dig_gain = conf->lut[i].dig_gain;
dgabino 0:102b50f941d0 813 txgain_lut.lut[i].dac_gain = conf->lut[i].dac_gain;
dgabino 0:102b50f941d0 814 txgain_lut.lut[i].mix_gain = conf->lut[i].mix_gain;
dgabino 0:102b50f941d0 815 txgain_lut.lut[i].pa_gain = conf->lut[i].pa_gain;
dgabino 0:102b50f941d0 816 txgain_lut.lut[i].rf_power = conf->lut[i].rf_power;
dgabino 0:102b50f941d0 817 }
dgabino 0:102b50f941d0 818
dgabino 0:102b50f941d0 819 /* send configuration to concentrator MCU */
dgabino 0:102b50f941d0 820 return lgw_mcu_txgain_setconf(conf);
dgabino 0:102b50f941d0 821 }
dgabino 0:102b50f941d0 822
dgabino 0:102b50f941d0 823 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
dgabino 0:102b50f941d0 824
dgabino 0:102b50f941d0 825 int lgw_start(void) {
dgabino 0:102b50f941d0 826 int i, err;
dgabino 0:102b50f941d0 827 unsigned x;
dgabino 0:102b50f941d0 828 uint8_t radio_select;
dgabino 0:102b50f941d0 829 int32_t read_val;
dgabino 0:102b50f941d0 830 uint8_t load_val;
dgabino 0:102b50f941d0 831 uint8_t fw_version;
dgabino 0:102b50f941d0 832 uint64_t fsk_sync_word_reg;
dgabino 0:102b50f941d0 833
dgabino 0:102b50f941d0 834 if (lgw_is_started == true) {
dgabino 0:102b50f941d0 835 DEBUG_MSG("Note: LoRa concentrator already started, restarting it now\n");
dgabino 0:102b50f941d0 836 }
dgabino 0:102b50f941d0 837
dgabino 0:102b50f941d0 838 /* Calibrate radios */
dgabino 0:102b50f941d0 839 err = lgw_calibrate_sx125x(callow_firmware, 5, 8);
dgabino 0:102b50f941d0 840 if (err != LGW_HAL_SUCCESS) {
dgabino 0:102b50f941d0 841 DEBUG_MSG("ERROR: Failed to calibrate sx125x radios (5-12)\n");
dgabino 0:102b50f941d0 842 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 843 }
dgabino 0:102b50f941d0 844 wait_ms(5);
dgabino 0:102b50f941d0 845 err = lgw_calibrate_sx125x(cal_firmware, 8, 8);
dgabino 0:102b50f941d0 846 if (err != LGW_HAL_SUCCESS) {
dgabino 0:102b50f941d0 847 DEBUG_MSG("ERROR: Failed to calibrate sx125x radios (8-15)\n");
dgabino 0:102b50f941d0 848 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 849 }
dgabino 0:102b50f941d0 850
dgabino 0:102b50f941d0 851 /* RX and TX packets signalling through GPIOs */
dgabino 0:102b50f941d0 852 lgw_reg_w(LGW_GPIO_MODE, 31); /* Set all GPIOs as output */
dgabino 0:102b50f941d0 853 lgw_reg_w(LGW_GPIO_SELECT_OUTPUT, 0);
dgabino 0:102b50f941d0 854
dgabino 0:102b50f941d0 855 /* load adjusted parameters */
dgabino 0:102b50f941d0 856 lgw_constant_adjust();
dgabino 0:102b50f941d0 857
dgabino 0:102b50f941d0 858 /* Sanity check for RX frequency */
dgabino 0:102b50f941d0 859 if (rf_rx_freq[0] == 0) {
dgabino 0:102b50f941d0 860 DEBUG_MSG("ERROR: wrong configuration, rf_rx_freq[0] is not set\n");
dgabino 0:102b50f941d0 861 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 862 }
dgabino 0:102b50f941d0 863
dgabino 0:102b50f941d0 864 /* Freq-to-time-drift calculation */
dgabino 0:102b50f941d0 865 x = 4096000000 / (rf_rx_freq[0] >> 1); /* dividend: (4*2048*1000000) >> 1, rescaled to avoid 32b overflow */
dgabino 0:102b50f941d0 866 x = ( x > 63 ) ? 63 : x; /* saturation */
dgabino 0:102b50f941d0 867 lgw_reg_w(LGW_FREQ_TO_TIME_DRIFT, x); /* default 9 */
dgabino 0:102b50f941d0 868
dgabino 0:102b50f941d0 869 x = 4096000000 / (rf_rx_freq[0] >> 3); /* dividend: (16*2048*1000000) >> 3, rescaled to avoid 32b overflow */
dgabino 0:102b50f941d0 870 x = ( x > 63 ) ? 63 : x; /* saturation */
dgabino 0:102b50f941d0 871 lgw_reg_w(LGW_MBWSSF_FREQ_TO_TIME_DRIFT, x); /* default 36 */
dgabino 0:102b50f941d0 872
dgabino 0:102b50f941d0 873 /* configure LoRa 'multi' demodulators aka. LoRa 'sensor' channels (IF0-3) */
dgabino 0:102b50f941d0 874 radio_select = 0; /* IF mapping to radio A/B (per bit, 0=A, 1=B) */
dgabino 0:102b50f941d0 875 for(i = 0; i < LGW_MULTI_NB; ++i) {
dgabino 0:102b50f941d0 876 radio_select += (if_rf_chain[i] == 1 ? 1 << i : 0); /* transform bool array into binary word */
dgabino 0:102b50f941d0 877 }
dgabino 0:102b50f941d0 878 /*
dgabino 0:102b50f941d0 879 lgw_reg_w(LGW_RADIO_SELECT, radio_select);
dgabino 0:102b50f941d0 880
dgabino 0:102b50f941d0 881 LGW_RADIO_SELECT is used for communication with the firmware, "radio_select"
dgabino 0:102b50f941d0 882 will be loaded in LGW_RADIO_SELECT at the end of start procedure.
dgabino 0:102b50f941d0 883 */
dgabino 0:102b50f941d0 884
dgabino 0:102b50f941d0 885 lgw_reg_w(LGW_IF_FREQ_0, IF_HZ_TO_REG(if_freq[0])); /* default -384 */
dgabino 0:102b50f941d0 886 lgw_reg_w(LGW_IF_FREQ_1, IF_HZ_TO_REG(if_freq[1])); /* default -128 */
dgabino 0:102b50f941d0 887 lgw_reg_w(LGW_IF_FREQ_2, IF_HZ_TO_REG(if_freq[2])); /* default 128 */
dgabino 0:102b50f941d0 888 lgw_reg_w(LGW_IF_FREQ_3, IF_HZ_TO_REG(if_freq[3])); /* default 384 */
dgabino 0:102b50f941d0 889 lgw_reg_w(LGW_IF_FREQ_4, IF_HZ_TO_REG(if_freq[4])); /* default -384 */
dgabino 0:102b50f941d0 890 lgw_reg_w(LGW_IF_FREQ_5, IF_HZ_TO_REG(if_freq[5])); /* default -128 */
dgabino 0:102b50f941d0 891 lgw_reg_w(LGW_IF_FREQ_6, IF_HZ_TO_REG(if_freq[6])); /* default 128 */
dgabino 0:102b50f941d0 892 lgw_reg_w(LGW_IF_FREQ_7, IF_HZ_TO_REG(if_freq[7])); /* default 384 */
dgabino 0:102b50f941d0 893
dgabino 0:102b50f941d0 894 lgw_reg_w(LGW_CORR0_DETECT_EN, (if_enable[0] == true) ? lora_multi_sfmask[0] : 0); /* default 0 */
dgabino 0:102b50f941d0 895 lgw_reg_w(LGW_CORR1_DETECT_EN, (if_enable[1] == true) ? lora_multi_sfmask[1] : 0); /* default 0 */
dgabino 0:102b50f941d0 896 lgw_reg_w(LGW_CORR2_DETECT_EN, (if_enable[2] == true) ? lora_multi_sfmask[2] : 0); /* default 0 */
dgabino 0:102b50f941d0 897 lgw_reg_w(LGW_CORR3_DETECT_EN, (if_enable[3] == true) ? lora_multi_sfmask[3] : 0); /* default 0 */
dgabino 0:102b50f941d0 898 lgw_reg_w(LGW_CORR4_DETECT_EN, (if_enable[4] == true) ? lora_multi_sfmask[4] : 0); /* default 0 */
dgabino 0:102b50f941d0 899 lgw_reg_w(LGW_CORR5_DETECT_EN, (if_enable[5] == true) ? lora_multi_sfmask[5] : 0); /* default 0 */
dgabino 0:102b50f941d0 900 lgw_reg_w(LGW_CORR6_DETECT_EN, (if_enable[6] == true) ? lora_multi_sfmask[6] : 0); /* default 0 */
dgabino 0:102b50f941d0 901 lgw_reg_w(LGW_CORR7_DETECT_EN, (if_enable[7] == true) ? lora_multi_sfmask[7] : 0); /* default 0 */
dgabino 0:102b50f941d0 902
dgabino 0:102b50f941d0 903 lgw_reg_w(LGW_PPM_OFFSET, 0x60); /* as the threshold is 16ms, use 0x60 to enable ppm_offset for SF12 and SF11 @125kHz*/
dgabino 0:102b50f941d0 904
dgabino 0:102b50f941d0 905 lgw_reg_w(LGW_CONCENTRATOR_MODEM_ENABLE, 1); /* default 0 */
dgabino 0:102b50f941d0 906
dgabino 0:102b50f941d0 907 /* configure LoRa 'stand-alone' modem (IF8) */
dgabino 0:102b50f941d0 908 lgw_reg_w(LGW_IF_FREQ_8, IF_HZ_TO_REG(if_freq[8])); /* MBWSSF modem (default 0) */
dgabino 0:102b50f941d0 909 if (if_enable[8] == true) {
dgabino 0:102b50f941d0 910 lgw_reg_w(LGW_MBWSSF_RADIO_SELECT, if_rf_chain[8]);
dgabino 0:102b50f941d0 911 switch(lora_rx_bw) {
dgabino 0:102b50f941d0 912 case BW_125KHZ:
dgabino 0:102b50f941d0 913 lgw_reg_w(LGW_MBWSSF_MODEM_BW, 0);
dgabino 0:102b50f941d0 914 break;
dgabino 0:102b50f941d0 915 case BW_250KHZ:
dgabino 0:102b50f941d0 916 lgw_reg_w(LGW_MBWSSF_MODEM_BW, 1);
dgabino 0:102b50f941d0 917 break;
dgabino 0:102b50f941d0 918 case BW_500KHZ:
dgabino 0:102b50f941d0 919 lgw_reg_w(LGW_MBWSSF_MODEM_BW, 2);
dgabino 0:102b50f941d0 920 break;
dgabino 0:102b50f941d0 921 default:
dgabino 0:102b50f941d0 922 DEBUG_PRINTF("ERROR: UNEXPECTED VALUE %d IN SWITCH STATEMENT\n", lora_rx_bw);
dgabino 0:102b50f941d0 923 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 924 }
dgabino 0:102b50f941d0 925 switch(lora_rx_sf) {
dgabino 0:102b50f941d0 926 case DR_LORA_SF7:
dgabino 0:102b50f941d0 927 lgw_reg_w(LGW_MBWSSF_RATE_SF, 7);
dgabino 0:102b50f941d0 928 break;
dgabino 0:102b50f941d0 929 case DR_LORA_SF8:
dgabino 0:102b50f941d0 930 lgw_reg_w(LGW_MBWSSF_RATE_SF, 8);
dgabino 0:102b50f941d0 931 break;
dgabino 0:102b50f941d0 932 case DR_LORA_SF9:
dgabino 0:102b50f941d0 933 lgw_reg_w(LGW_MBWSSF_RATE_SF, 9);
dgabino 0:102b50f941d0 934 break;
dgabino 0:102b50f941d0 935 case DR_LORA_SF10:
dgabino 0:102b50f941d0 936 lgw_reg_w(LGW_MBWSSF_RATE_SF, 10);
dgabino 0:102b50f941d0 937 break;
dgabino 0:102b50f941d0 938 case DR_LORA_SF11:
dgabino 0:102b50f941d0 939 lgw_reg_w(LGW_MBWSSF_RATE_SF, 11);
dgabino 0:102b50f941d0 940 break;
dgabino 0:102b50f941d0 941 case DR_LORA_SF12:
dgabino 0:102b50f941d0 942 lgw_reg_w(LGW_MBWSSF_RATE_SF, 12);
dgabino 0:102b50f941d0 943 break;
dgabino 0:102b50f941d0 944 default:
dgabino 0:102b50f941d0 945 DEBUG_PRINTF("ERROR: UNEXPECTED VALUE %d IN SWITCH STATEMENT\n", lora_rx_sf);
dgabino 0:102b50f941d0 946 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 947 }
dgabino 0:102b50f941d0 948 lgw_reg_w(LGW_MBWSSF_PPM_OFFSET, lora_rx_ppm_offset); /* default 0 */
dgabino 0:102b50f941d0 949 lgw_reg_w(LGW_MBWSSF_MODEM_ENABLE, 1); /* default 0 */
dgabino 0:102b50f941d0 950 } else {
dgabino 0:102b50f941d0 951 lgw_reg_w(LGW_MBWSSF_MODEM_ENABLE, 0);
dgabino 0:102b50f941d0 952 }
dgabino 0:102b50f941d0 953
dgabino 0:102b50f941d0 954 /* configure FSK modem (IF9) */
dgabino 0:102b50f941d0 955 lgw_reg_w(LGW_IF_FREQ_9, IF_HZ_TO_REG(if_freq[9])); /* FSK modem, default 0 */
dgabino 0:102b50f941d0 956 lgw_reg_w(LGW_FSK_PSIZE, fsk_sync_word_size - 1);
dgabino 0:102b50f941d0 957 lgw_reg_w(LGW_FSK_TX_PSIZE, fsk_sync_word_size - 1);
dgabino 0:102b50f941d0 958 fsk_sync_word_reg = fsk_sync_word << (8 * (8 - fsk_sync_word_size));
dgabino 0:102b50f941d0 959 lgw_reg_w(LGW_FSK_REF_PATTERN_LSB, (uint32_t)(0xFFFFFFFF & fsk_sync_word_reg));
dgabino 0:102b50f941d0 960 lgw_reg_w(LGW_FSK_REF_PATTERN_MSB, (uint32_t)(0xFFFFFFFF & (fsk_sync_word_reg >> 32)));
dgabino 0:102b50f941d0 961 if (if_enable[9] == true) {
dgabino 0:102b50f941d0 962 lgw_reg_w(LGW_FSK_RADIO_SELECT, if_rf_chain[9]);
dgabino 0:102b50f941d0 963 lgw_reg_w(LGW_FSK_BR_RATIO, LGW_XTAL_FREQU / fsk_rx_dr); /* setting the dividing ratio for datarate */
dgabino 0:102b50f941d0 964 lgw_reg_w(LGW_FSK_CH_BW_EXPO, fsk_rx_bw);
dgabino 0:102b50f941d0 965 lgw_reg_w(LGW_FSK_MODEM_ENABLE, 1); /* default 0 */
dgabino 0:102b50f941d0 966 } else {
dgabino 0:102b50f941d0 967 lgw_reg_w(LGW_FSK_MODEM_ENABLE, 0);
dgabino 0:102b50f941d0 968 }
dgabino 0:102b50f941d0 969
dgabino 0:102b50f941d0 970 /* Load firmware */
dgabino 0:102b50f941d0 971 load_firmware(MCU_ARB, arb_firmware, MCU_ARB_FW_BYTE);
dgabino 0:102b50f941d0 972 load_firmware(MCU_AGC, agc_firmware, MCU_AGC_FW_BYTE);
dgabino 0:102b50f941d0 973
dgabino 0:102b50f941d0 974 /* gives the AGC MCU control over radio, RF front-end and filter gain */
dgabino 0:102b50f941d0 975 lgw_reg_w(LGW_FORCE_HOST_RADIO_CTRL, 0);
dgabino 0:102b50f941d0 976 lgw_reg_w(LGW_FORCE_HOST_FE_CTRL, 0);
dgabino 0:102b50f941d0 977 lgw_reg_w(LGW_FORCE_DEC_FILTER_GAIN, 0);
dgabino 0:102b50f941d0 978
dgabino 0:102b50f941d0 979 /* Get MCUs out of reset */
dgabino 0:102b50f941d0 980 lgw_reg_w(LGW_RADIO_SELECT, 0); /* MUST not be = to 1 or 2 at firmware init */
dgabino 0:102b50f941d0 981 lgw_reg_w(LGW_MCU_RST_0, 0);
dgabino 0:102b50f941d0 982 lgw_reg_w(LGW_MCU_RST_1, 0);
dgabino 0:102b50f941d0 983
dgabino 0:102b50f941d0 984 /* Check firmware version */
dgabino 0:102b50f941d0 985 lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, FW_VERSION_ADDR);
dgabino 0:102b50f941d0 986 lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val);
dgabino 0:102b50f941d0 987 fw_version = (uint8_t)read_val;
dgabino 0:102b50f941d0 988 if (fw_version != FW_VERSION_AGC) {
dgabino 0:102b50f941d0 989 DEBUG_PRINTF("ERROR: Version of AGC firmware not expected, actual:%d expected:%d\n", fw_version, FW_VERSION_AGC);
dgabino 0:102b50f941d0 990 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 991 }
dgabino 0:102b50f941d0 992 lgw_reg_w(LGW_DBG_ARB_MCU_RAM_ADDR, FW_VERSION_ADDR);
dgabino 0:102b50f941d0 993 lgw_reg_r(LGW_DBG_ARB_MCU_RAM_DATA, &read_val);
dgabino 0:102b50f941d0 994 fw_version = (uint8_t)read_val;
dgabino 0:102b50f941d0 995 if (fw_version != FW_VERSION_ARB) {
dgabino 0:102b50f941d0 996 DEBUG_PRINTF("ERROR: Version of arbiter firmware not expected, actual:%d expected:%d\n", fw_version, FW_VERSION_ARB);
dgabino 0:102b50f941d0 997 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 998 }
dgabino 0:102b50f941d0 999
dgabino 0:102b50f941d0 1000 DEBUG_MSG("Info: Initialising AGC firmware...\n");
dgabino 0:102b50f941d0 1001 wait_ms(10);
dgabino 0:102b50f941d0 1002
dgabino 0:102b50f941d0 1003 lgw_reg_r(LGW_MCU_AGC_STATUS, &read_val);
dgabino 0:102b50f941d0 1004 if (read_val != 0x10) {
dgabino 0:102b50f941d0 1005 DEBUG_PRINTF("ERROR: AGC FIRMWARE INITIALIZATION FAILURE, STATUS 0x%02X\n", (uint8_t)read_val);
dgabino 0:102b50f941d0 1006 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 1007 }
dgabino 0:102b50f941d0 1008
dgabino 0:102b50f941d0 1009 /* Update Tx gain LUT and start AGC */
dgabino 0:102b50f941d0 1010 for (i = 0; i < txgain_lut.size; ++i) {
dgabino 0:102b50f941d0 1011 lgw_reg_w(LGW_RADIO_SELECT, AGC_CMD_WAIT); /* start a transaction */
dgabino 0:102b50f941d0 1012 wait_ms(1);
dgabino 0:102b50f941d0 1013 load_val = txgain_lut.lut[i].mix_gain + (16 * txgain_lut.lut[i].dac_gain) + (64 * txgain_lut.lut[i].pa_gain);
dgabino 0:102b50f941d0 1014 lgw_reg_w(LGW_RADIO_SELECT, load_val);
dgabino 0:102b50f941d0 1015 wait_ms(1);
dgabino 0:102b50f941d0 1016 lgw_reg_r(LGW_MCU_AGC_STATUS, &read_val);
dgabino 0:102b50f941d0 1017 if (read_val != (0x30 + i)) {
dgabino 0:102b50f941d0 1018 DEBUG_PRINTF("ERROR: AGC FIRMWARE INITIALIZATION FAILURE, STATUS 0x%02X\n", (uint8_t)read_val);
dgabino 0:102b50f941d0 1019 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 1020 }
dgabino 0:102b50f941d0 1021 }
dgabino 0:102b50f941d0 1022 /* As the AGC fw is waiting for 16 entries, we need to abort the transaction if we get less entries */
dgabino 0:102b50f941d0 1023 if (txgain_lut.size < TX_GAIN_LUT_SIZE_MAX) {
dgabino 0:102b50f941d0 1024 lgw_reg_w(LGW_RADIO_SELECT, AGC_CMD_WAIT);
dgabino 0:102b50f941d0 1025 wait_ms(10);
dgabino 0:102b50f941d0 1026 load_val = AGC_CMD_ABORT;
dgabino 0:102b50f941d0 1027 lgw_reg_w(LGW_RADIO_SELECT, load_val);
dgabino 0:102b50f941d0 1028 wait_ms(10);
dgabino 0:102b50f941d0 1029 lgw_reg_r(LGW_MCU_AGC_STATUS, &read_val);
dgabino 0:102b50f941d0 1030 if (read_val != 0x30) {
dgabino 0:102b50f941d0 1031 DEBUG_PRINTF("ERROR: AGC FIRMWARE INITIALIZATION FAILURE, STATUS 0x%02X\n", (uint8_t)read_val);
dgabino 0:102b50f941d0 1032 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 1033 }
dgabino 0:102b50f941d0 1034 }
dgabino 0:102b50f941d0 1035
dgabino 0:102b50f941d0 1036 /* Load Tx freq MSBs (always 3 if f > 768 for SX1257 or f > 384 for SX1255 */
dgabino 0:102b50f941d0 1037 lgw_reg_w(LGW_RADIO_SELECT, AGC_CMD_WAIT);
dgabino 0:102b50f941d0 1038 wait_ms(10);
dgabino 0:102b50f941d0 1039 lgw_reg_w(LGW_RADIO_SELECT, 3);
dgabino 0:102b50f941d0 1040 wait_ms(10);
dgabino 0:102b50f941d0 1041 lgw_reg_r(LGW_MCU_AGC_STATUS, &read_val);
dgabino 0:102b50f941d0 1042 if (read_val != 0x33) {
dgabino 0:102b50f941d0 1043 DEBUG_PRINTF("ERROR: AGC FIRMWARE INITIALIZATION FAILURE, STATUS 0x%02X\n", (uint8_t)read_val);
dgabino 0:102b50f941d0 1044 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 1045 }
dgabino 0:102b50f941d0 1046
dgabino 0:102b50f941d0 1047 /* Load chan_select firmware option */
dgabino 0:102b50f941d0 1048 lgw_reg_w(LGW_RADIO_SELECT, AGC_CMD_WAIT);
dgabino 0:102b50f941d0 1049 wait_ms(10);
dgabino 0:102b50f941d0 1050 lgw_reg_w(LGW_RADIO_SELECT, 0);
dgabino 0:102b50f941d0 1051 wait_ms(10);
dgabino 0:102b50f941d0 1052 lgw_reg_r(LGW_MCU_AGC_STATUS, &read_val);
dgabino 0:102b50f941d0 1053 if (read_val != 0x30) {
dgabino 0:102b50f941d0 1054 DEBUG_PRINTF("ERROR: AGC FIRMWARE INITIALIZATION FAILURE, STATUS 0x%02X\n", (uint8_t)read_val);
dgabino 0:102b50f941d0 1055 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 1056 }
dgabino 0:102b50f941d0 1057
dgabino 0:102b50f941d0 1058 /* End AGC firmware init and check status */
dgabino 0:102b50f941d0 1059 lgw_reg_w(LGW_RADIO_SELECT, AGC_CMD_WAIT);
dgabino 0:102b50f941d0 1060 wait_ms(10);
dgabino 0:102b50f941d0 1061 lgw_reg_w(LGW_RADIO_SELECT, radio_select); /* Load intended value of RADIO_SELECT */
dgabino 0:102b50f941d0 1062 wait_ms(10);
dgabino 0:102b50f941d0 1063 DEBUG_MSG("Info: putting back original RADIO_SELECT value\n");
dgabino 0:102b50f941d0 1064 lgw_reg_r(LGW_MCU_AGC_STATUS, &read_val);
dgabino 0:102b50f941d0 1065 if (read_val != 0x40) {
dgabino 0:102b50f941d0 1066 DEBUG_PRINTF("ERROR: AGC FIRMWARE INITIALIZATION FAILURE, STATUS 0x%02X\n", (uint8_t)read_val);
dgabino 0:102b50f941d0 1067 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 1068 }
dgabino 0:102b50f941d0 1069
dgabino 0:102b50f941d0 1070 /* enable GPS event capture */
dgabino 0:102b50f941d0 1071 lgw_reg_w(LGW_GPS_EN, 0);
dgabino 0:102b50f941d0 1072
dgabino 0:102b50f941d0 1073 lgw_is_started = true;
dgabino 0:102b50f941d0 1074 return LGW_HAL_SUCCESS;
dgabino 0:102b50f941d0 1075 }
dgabino 0:102b50f941d0 1076
dgabino 0:102b50f941d0 1077 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
dgabino 0:102b50f941d0 1078
dgabino 0:102b50f941d0 1079 int lgw_stop(void) {
dgabino 0:102b50f941d0 1080 lgw_soft_reset();
dgabino 0:102b50f941d0 1081 lgw_disconnect();
dgabino 0:102b50f941d0 1082
dgabino 0:102b50f941d0 1083 lgw_is_started = false;
dgabino 0:102b50f941d0 1084 return LGW_HAL_SUCCESS;
dgabino 0:102b50f941d0 1085 }
dgabino 0:102b50f941d0 1086
dgabino 0:102b50f941d0 1087 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
dgabino 0:102b50f941d0 1088
dgabino 0:102b50f941d0 1089 int lgw_receive(uint8_t max_pkt, struct lgw_pkt_rx_s *pkt_data) {
dgabino 0:102b50f941d0 1090 /* check input variables */
dgabino 0:102b50f941d0 1091 if ((max_pkt == 0) || (max_pkt > LGW_PKT_FIFO_SIZE)) {
dgabino 0:102b50f941d0 1092 DEBUG_PRINTF("ERROR: %d = INVALID MAX NUMBER OF PACKETS TO FETCH\n", max_pkt);
dgabino 0:102b50f941d0 1093 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 1094 }
dgabino 0:102b50f941d0 1095
dgabino 0:102b50f941d0 1096 /* send packet data to concentrator MCU */
dgabino 0:102b50f941d0 1097 return lgw_mcu_receive( max_pkt, pkt_data);
dgabino 0:102b50f941d0 1098 }
dgabino 0:102b50f941d0 1099
dgabino 0:102b50f941d0 1100 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
dgabino 0:102b50f941d0 1101
dgabino 0:102b50f941d0 1102 int lgw_send(struct lgw_pkt_tx_s pkt_data) {
dgabino 0:102b50f941d0 1103 /* check input range (segfault prevention) */
dgabino 0:102b50f941d0 1104 if (pkt_data.rf_chain >= LGW_RF_CHAIN_NB) {
dgabino 0:102b50f941d0 1105 DEBUG_MSG("ERROR: INVALID RF_CHAIN TO SEND PACKETS\n");
dgabino 0:102b50f941d0 1106 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 1107 }
dgabino 0:102b50f941d0 1108
dgabino 0:102b50f941d0 1109 /* check input variables */
dgabino 0:102b50f941d0 1110 if (rf_tx_enable[pkt_data.rf_chain] == false) {
dgabino 0:102b50f941d0 1111 DEBUG_MSG("ERROR: SELECTED RF_CHAIN IS DISABLED FOR TX ON SELECTED BOARD\n");
dgabino 0:102b50f941d0 1112 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 1113 }
dgabino 0:102b50f941d0 1114 if (rf_enable[pkt_data.rf_chain] == false) {
dgabino 0:102b50f941d0 1115 DEBUG_MSG("ERROR: SELECTED RF_CHAIN IS DISABLED\n");
dgabino 0:102b50f941d0 1116 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 1117 }
dgabino 0:102b50f941d0 1118 if (!IS_TX_MODE(pkt_data.tx_mode)) {
dgabino 0:102b50f941d0 1119 DEBUG_MSG("ERROR: TX_MODE NOT SUPPORTED\n");
dgabino 0:102b50f941d0 1120 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 1121 }
dgabino 0:102b50f941d0 1122 if (pkt_data.modulation == MOD_LORA) {
dgabino 0:102b50f941d0 1123 if (!IS_LORA_BW(pkt_data.bandwidth)) {
dgabino 0:102b50f941d0 1124 DEBUG_MSG("ERROR: BANDWIDTH NOT SUPPORTED BY LORA TX\n");
dgabino 0:102b50f941d0 1125 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 1126 }
dgabino 0:102b50f941d0 1127 if (!IS_LORA_STD_DR(pkt_data.datarate)) {
dgabino 0:102b50f941d0 1128 DEBUG_MSG("ERROR: DATARATE NOT SUPPORTED BY LORA TX\n");
dgabino 0:102b50f941d0 1129 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 1130 }
dgabino 0:102b50f941d0 1131 if (!IS_LORA_CR(pkt_data.coderate)) {
dgabino 0:102b50f941d0 1132 DEBUG_MSG("ERROR: CODERATE NOT SUPPORTED BY LORA TX\n");
dgabino 0:102b50f941d0 1133 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 1134 }
dgabino 0:102b50f941d0 1135 if (pkt_data.size > 255) {
dgabino 0:102b50f941d0 1136 DEBUG_MSG("ERROR: PAYLOAD LENGTH TOO BIG FOR LORA TX\n");
dgabino 0:102b50f941d0 1137 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 1138 }
dgabino 0:102b50f941d0 1139 } else if (pkt_data.modulation == MOD_FSK) {
dgabino 0:102b50f941d0 1140 if ((pkt_data.f_dev < 1) || (pkt_data.f_dev > 200)) {
dgabino 0:102b50f941d0 1141 DEBUG_MSG("ERROR: TX FREQUENCY DEVIATION OUT OF ACCEPTABLE RANGE\n");
dgabino 0:102b50f941d0 1142 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 1143 }
dgabino 0:102b50f941d0 1144 if (!IS_FSK_DR(pkt_data.datarate)) {
dgabino 0:102b50f941d0 1145 DEBUG_MSG("ERROR: DATARATE NOT SUPPORTED BY FSK IF CHAIN\n");
dgabino 0:102b50f941d0 1146 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 1147 }
dgabino 0:102b50f941d0 1148 if (pkt_data.size > 255) {
dgabino 0:102b50f941d0 1149 DEBUG_MSG("ERROR: PAYLOAD LENGTH TOO BIG FOR FSK TX\n");
dgabino 0:102b50f941d0 1150 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 1151 }
dgabino 0:102b50f941d0 1152 } else {
dgabino 0:102b50f941d0 1153 DEBUG_MSG("ERROR: INVALID TX MODULATION\n");
dgabino 0:102b50f941d0 1154 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 1155 }
dgabino 0:102b50f941d0 1156
dgabino 0:102b50f941d0 1157 /* send packet data to concentrator MCU */
dgabino 0:102b50f941d0 1158 return lgw_mcu_send(pkt_data);
dgabino 0:102b50f941d0 1159 }
dgabino 0:102b50f941d0 1160
dgabino 0:102b50f941d0 1161 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
dgabino 0:102b50f941d0 1162
dgabino 0:102b50f941d0 1163 int lgw_status(uint8_t select, uint8_t *code) {
dgabino 0:102b50f941d0 1164 int32_t read_value;
dgabino 0:102b50f941d0 1165
dgabino 0:102b50f941d0 1166 /* check input variables */
dgabino 0:102b50f941d0 1167 CHECK_NULL(code);
dgabino 0:102b50f941d0 1168
dgabino 0:102b50f941d0 1169 if (select == TX_STATUS) {
dgabino 0:102b50f941d0 1170 lgw_reg_r(LGW_TX_STATUS, &read_value);
dgabino 0:102b50f941d0 1171 if (lgw_is_started == false) {
dgabino 0:102b50f941d0 1172 *code = TX_OFF;
dgabino 0:102b50f941d0 1173 } else if ((read_value & 0x10) == 0) { /* bit 4 @1: TX programmed */
dgabino 0:102b50f941d0 1174 *code = TX_FREE;
dgabino 0:102b50f941d0 1175 } else if ((read_value & 0x60) != 0) { /* bit 5 or 6 @1: TX sequence */
dgabino 0:102b50f941d0 1176 *code = TX_EMITTING;
dgabino 0:102b50f941d0 1177 } else {
dgabino 0:102b50f941d0 1178 *code = TX_SCHEDULED;
dgabino 0:102b50f941d0 1179 }
dgabino 0:102b50f941d0 1180 return LGW_HAL_SUCCESS;
dgabino 0:102b50f941d0 1181
dgabino 0:102b50f941d0 1182 } else if (select == RX_STATUS) {
dgabino 0:102b50f941d0 1183 *code = RX_STATUS_UNKNOWN; /* todo */
dgabino 0:102b50f941d0 1184 return LGW_HAL_SUCCESS;
dgabino 0:102b50f941d0 1185
dgabino 0:102b50f941d0 1186 } else {
dgabino 0:102b50f941d0 1187 DEBUG_MSG("ERROR: SELECTION INVALID, NO STATUS TO RETURN\n");
dgabino 0:102b50f941d0 1188 return LGW_HAL_ERROR;
dgabino 0:102b50f941d0 1189 }
dgabino 0:102b50f941d0 1190 }
dgabino 0:102b50f941d0 1191
dgabino 0:102b50f941d0 1192 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
dgabino 0:102b50f941d0 1193
dgabino 0:102b50f941d0 1194 int lgw_abort_tx(void) {
dgabino 0:102b50f941d0 1195 return lgw_reg_w(LGW_TX_TRIG_ALL, 0);
dgabino 0:102b50f941d0 1196 }
dgabino 0:102b50f941d0 1197
dgabino 0:102b50f941d0 1198 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
dgabino 0:102b50f941d0 1199
dgabino 0:102b50f941d0 1200 int lgw_get_trigcnt(uint32_t* trig_cnt_us) {
dgabino 0:102b50f941d0 1201 return lgw_mcu_get_trigcnt(trig_cnt_us);
dgabino 0:102b50f941d0 1202 }
dgabino 0:102b50f941d0 1203
dgabino 0:102b50f941d0 1204 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
dgabino 0:102b50f941d0 1205
dgabino 0:102b50f941d0 1206 const char* lgw_version_info() {
dgabino 0:102b50f941d0 1207 return lgw_version_string;
dgabino 0:102b50f941d0 1208 }
dgabino 0:102b50f941d0 1209
dgabino 0:102b50f941d0 1210 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
dgabino 0:102b50f941d0 1211
dgabino 0:102b50f941d0 1212 int lgw_mcu_version_info() {
dgabino 0:102b50f941d0 1213 return (int)(STM32FWVERSION);
dgabino 0:102b50f941d0 1214 }
dgabino 0:102b50f941d0 1215
dgabino 0:102b50f941d0 1216 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
dgabino 0:102b50f941d0 1217
dgabino 0:102b50f941d0 1218 uint32_t lgw_time_on_air(struct lgw_pkt_tx_s *packet) {
dgabino 0:102b50f941d0 1219 int32_t val;
dgabino 0:102b50f941d0 1220 uint8_t SF, H, DE;
dgabino 0:102b50f941d0 1221 uint16_t BW;
dgabino 0:102b50f941d0 1222 uint32_t payloadSymbNb, Tpacket;
dgabino 0:102b50f941d0 1223 double Tsym, Tpreamble, Tpayload, Tfsk;
dgabino 0:102b50f941d0 1224
dgabino 0:102b50f941d0 1225 if (packet == NULL) {
dgabino 0:102b50f941d0 1226 DEBUG_MSG("ERROR: Failed to compute time on air, wrong parameter\n");
dgabino 0:102b50f941d0 1227 return 0;
dgabino 0:102b50f941d0 1228 }
dgabino 0:102b50f941d0 1229
dgabino 0:102b50f941d0 1230 if (packet->modulation == MOD_LORA) {
dgabino 0:102b50f941d0 1231 /* Get bandwidth */
dgabino 0:102b50f941d0 1232 val = lgw_bw_getval(packet->bandwidth);
dgabino 0:102b50f941d0 1233 if (val != -1) {
dgabino 0:102b50f941d0 1234 BW = (uint16_t)(val / 1E3);
dgabino 0:102b50f941d0 1235 } else {
dgabino 0:102b50f941d0 1236 DEBUG_PRINTF("ERROR: Cannot compute time on air for this packet, unsupported bandwidth (0x%02X)\n", packet->bandwidth);
dgabino 0:102b50f941d0 1237 return 0;
dgabino 0:102b50f941d0 1238 }
dgabino 0:102b50f941d0 1239
dgabino 0:102b50f941d0 1240 /* Get datarate */
dgabino 0:102b50f941d0 1241 val = lgw_sf_getval(packet->datarate);
dgabino 0:102b50f941d0 1242 if (val != -1) {
dgabino 0:102b50f941d0 1243 SF = (uint8_t)val;
dgabino 0:102b50f941d0 1244 } else {
dgabino 0:102b50f941d0 1245 DEBUG_PRINTF("ERROR: Cannot compute time on air for this packet, unsupported datarate (0x%02X)\n", packet->datarate);
dgabino 0:102b50f941d0 1246 return 0;
dgabino 0:102b50f941d0 1247 }
dgabino 0:102b50f941d0 1248
dgabino 0:102b50f941d0 1249 /* Duration of 1 symbol */
dgabino 0:102b50f941d0 1250 Tsym = pow(2, SF) / BW;
dgabino 0:102b50f941d0 1251
dgabino 0:102b50f941d0 1252 /* Duration of preamble */
dgabino 0:102b50f941d0 1253 Tpreamble = (8 + 4.25) * Tsym; /* 8 programmed symbols in preamble */
dgabino 0:102b50f941d0 1254
dgabino 0:102b50f941d0 1255 /* Duration of payload */
dgabino 0:102b50f941d0 1256 H = (packet->no_header == false) ? 0 : 1; /* header is always enabled, except for beacons */
dgabino 0:102b50f941d0 1257 DE = (SF >= 11) ? 1 : 0; /* Low datarate optimization enabled for SF11 and SF12 */
dgabino 0:102b50f941d0 1258
dgabino 0:102b50f941d0 1259 payloadSymbNb = 8 + (ceil((double)(8 * packet->size - 4 * SF + 28 + 16 - 20 * H) / (double)(4 * (SF - 2 * DE))) * (packet->coderate + 4)); /* Explicitely cast to double to keep precision of the division */
dgabino 0:102b50f941d0 1260
dgabino 0:102b50f941d0 1261 Tpayload = payloadSymbNb * Tsym;
dgabino 0:102b50f941d0 1262
dgabino 0:102b50f941d0 1263 /* Duration of packet */
dgabino 0:102b50f941d0 1264 Tpacket = Tpreamble + Tpayload;
dgabino 0:102b50f941d0 1265 } else if (packet->modulation == MOD_FSK) {
dgabino 0:102b50f941d0 1266 /* PREAMBLE + SYNC_WORD + PKT_LEN + PKT_PAYLOAD + CRC
dgabino 0:102b50f941d0 1267 PREAMBLE: default 5 bytes
dgabino 0:102b50f941d0 1268 SYNC_WORD: default 3 bytes
dgabino 0:102b50f941d0 1269 PKT_LEN: 1 byte (variable length mode)
dgabino 0:102b50f941d0 1270 PKT_PAYLOAD: x bytes
dgabino 0:102b50f941d0 1271 CRC: 0 or 2 bytes
dgabino 0:102b50f941d0 1272 */
dgabino 0:102b50f941d0 1273 Tfsk = (8 * (double)(packet->preamble + fsk_sync_word_size + 1 + packet->size + ((packet->no_crc == true) ? 0 : 2)) / (double)packet->datarate) * 1E3;
dgabino 0:102b50f941d0 1274
dgabino 0:102b50f941d0 1275 /* Duration of packet */
dgabino 0:102b50f941d0 1276 Tpacket = (uint32_t)Tfsk + 1; /* add margin for rounding */
dgabino 0:102b50f941d0 1277 } else {
dgabino 0:102b50f941d0 1278 Tpacket = 0;
dgabino 0:102b50f941d0 1279 DEBUG_PRINTF("ERROR: Cannot compute time on air for this packet, unsupported modulation (0x%02X)\n", packet->modulation);
dgabino 0:102b50f941d0 1280 }
dgabino 0:102b50f941d0 1281
dgabino 0:102b50f941d0 1282 return Tpacket;
dgabino 0:102b50f941d0 1283 }
dgabino 0:102b50f941d0 1284
dgabino 0:102b50f941d0 1285 /* --- EOF ------------------------------------------------------------------ */