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 Functions used to handle LoRa concentrator radios.
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
dgabino 0:102b50f941d0 23 #include "loragw_sx125x.h"
dgabino 0:102b50f941d0 24 #include "loragw_com.h"
dgabino 0:102b50f941d0 25 #include "loragw_aux.h"
dgabino 0:102b50f941d0 26 #include "loragw_reg.h"
dgabino 0:102b50f941d0 27 #include "loragw_hal.h"
dgabino 0:102b50f941d0 28 #include "loragw_radio.h"
dgabino 0:102b50f941d0 29
dgabino 0:102b50f941d0 30 /* -------------------------------------------------------------------------- */
dgabino 0:102b50f941d0 31 /* --- PRIVATE MACROS ------------------------------------------------------- */
dgabino 0:102b50f941d0 32
dgabino 0:102b50f941d0 33 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
dgabino 0:102b50f941d0 34 #if DEBUG_REG == 1
dgabino 0:102b50f941d0 35 #define DEBUG_MSG(str) fprintf(stderr, str)
dgabino 0:102b50f941d0 36 #define DEBUG_PRINTF(fmt, args...) fprintf(stderr,"%s:%d: "fmt, __FUNCTION__, __LINE__, args)
dgabino 0:102b50f941d0 37 #define CHECK_NULL(a) if(a==NULL){fprintf(stderr,"%s:%d: ERROR: NULL POINTER AS ARGUMENT\n", __FUNCTION__, __LINE__);return LGW_REG_ERROR;}
dgabino 0:102b50f941d0 38 #else
dgabino 0:102b50f941d0 39 #define DEBUG_MSG(str)
dgabino 0:102b50f941d0 40 #define DEBUG_PRINTF(fmt, args...)
dgabino 0:102b50f941d0 41 #define CHECK_NULL(a) if(a==NULL){return LGW_REG_ERROR;}
dgabino 0:102b50f941d0 42 #endif
dgabino 0:102b50f941d0 43
dgabino 0:102b50f941d0 44 /* -------------------------------------------------------------------------- */
dgabino 0:102b50f941d0 45 /* --- PRIVATE TYPES -------------------------------------------------------- */
dgabino 0:102b50f941d0 46
dgabino 0:102b50f941d0 47 /**
dgabino 0:102b50f941d0 48 @struct lgw_radio_type_version_s
dgabino 0:102b50f941d0 49 @brief Associate a radio type with its corresponding expected version value
dgabino 0:102b50f941d0 50 read in the radio version register.
dgabino 0:102b50f941d0 51 */
dgabino 0:102b50f941d0 52 struct lgw_radio_type_version_s {
dgabino 0:102b50f941d0 53 enum lgw_radio_type_e type;
dgabino 0:102b50f941d0 54 uint8_t reg_version;
dgabino 0:102b50f941d0 55 };
dgabino 0:102b50f941d0 56
dgabino 0:102b50f941d0 57 /* -------------------------------------------------------------------------- */
dgabino 0:102b50f941d0 58 /* --- PRIVATE CONSTANTS ---------------------------------------------------- */
dgabino 0:102b50f941d0 59
dgabino 0:102b50f941d0 60 #define PLL_LOCK_MAX_ATTEMPTS 5
dgabino 0:102b50f941d0 61
dgabino 0:102b50f941d0 62 /* -------------------------------------------------------------------------- */
dgabino 0:102b50f941d0 63 /* --- PRIVATE VARIABLES ---------------------------------------------------- */
dgabino 0:102b50f941d0 64
dgabino 0:102b50f941d0 65 /* -------------------------------------------------------------------------- */
dgabino 0:102b50f941d0 66 /* --- PRIVATE FUNCTIONS ---------------------------------------------------- */
dgabino 0:102b50f941d0 67
dgabino 0:102b50f941d0 68 void sx125x_write(uint8_t channel, uint8_t addr, uint8_t data);
dgabino 0:102b50f941d0 69 uint8_t sx125x_read(uint8_t channel, uint8_t addr);
dgabino 0:102b50f941d0 70
dgabino 0:102b50f941d0 71 /* -------------------------------------------------------------------------- */
dgabino 0:102b50f941d0 72 /* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */
dgabino 0:102b50f941d0 73
dgabino 0:102b50f941d0 74 void sx125x_write(uint8_t channel, uint8_t addr, uint8_t data) {
dgabino 0:102b50f941d0 75 int reg_add, reg_dat, reg_cs;
dgabino 0:102b50f941d0 76
dgabino 0:102b50f941d0 77 /* checking input parameters */
dgabino 0:102b50f941d0 78 if (channel >= LGW_RF_CHAIN_NB) {
dgabino 0:102b50f941d0 79 DEBUG_MSG("ERROR: INVALID RF_CHAIN\n");
dgabino 0:102b50f941d0 80 return;
dgabino 0:102b50f941d0 81 }
dgabino 0:102b50f941d0 82 if (addr >= 0x7F) {
dgabino 0:102b50f941d0 83 DEBUG_MSG("ERROR: ADDRESS OUT OF RANGE\n");
dgabino 0:102b50f941d0 84 return;
dgabino 0:102b50f941d0 85 }
dgabino 0:102b50f941d0 86
dgabino 0:102b50f941d0 87 /* selecting the target radio */
dgabino 0:102b50f941d0 88 switch (channel) {
dgabino 0:102b50f941d0 89 case 0:
dgabino 0:102b50f941d0 90 reg_add = LGW_SPI_RADIO_A__ADDR;
dgabino 0:102b50f941d0 91 reg_dat = LGW_SPI_RADIO_A__DATA;
dgabino 0:102b50f941d0 92 reg_cs = LGW_SPI_RADIO_A__CS;
dgabino 0:102b50f941d0 93 break;
dgabino 0:102b50f941d0 94
dgabino 0:102b50f941d0 95 case 1:
dgabino 0:102b50f941d0 96 reg_add = LGW_SPI_RADIO_B__ADDR;
dgabino 0:102b50f941d0 97 reg_dat = LGW_SPI_RADIO_B__DATA;
dgabino 0:102b50f941d0 98 reg_cs = LGW_SPI_RADIO_B__CS;
dgabino 0:102b50f941d0 99 break;
dgabino 0:102b50f941d0 100
dgabino 0:102b50f941d0 101 default:
dgabino 0:102b50f941d0 102 DEBUG_PRINTF("ERROR: UNEXPECTED VALUE %d IN SWITCH STATEMENT\n", channel);
dgabino 0:102b50f941d0 103 return;
dgabino 0:102b50f941d0 104 }
dgabino 0:102b50f941d0 105
dgabino 0:102b50f941d0 106 /* SPI master data write procedure */
dgabino 0:102b50f941d0 107 lgw_reg_w(reg_cs, 0);
dgabino 0:102b50f941d0 108 lgw_reg_w(reg_add, 0x80 | addr); /* MSB at 1 for write operation */
dgabino 0:102b50f941d0 109 lgw_reg_w(reg_dat, data);
dgabino 0:102b50f941d0 110 lgw_reg_w(reg_cs, 1);
dgabino 0:102b50f941d0 111 lgw_reg_w(reg_cs, 0);
dgabino 0:102b50f941d0 112
dgabino 0:102b50f941d0 113 return;
dgabino 0:102b50f941d0 114 }
dgabino 0:102b50f941d0 115
dgabino 0:102b50f941d0 116 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
dgabino 0:102b50f941d0 117
dgabino 0:102b50f941d0 118 uint8_t sx125x_read(uint8_t channel, uint8_t addr) {
dgabino 0:102b50f941d0 119 int reg_add, reg_dat, reg_cs, reg_rb;
dgabino 0:102b50f941d0 120 int32_t read_value;
dgabino 0:102b50f941d0 121
dgabino 0:102b50f941d0 122 /* checking input parameters */
dgabino 0:102b50f941d0 123 if (channel >= LGW_RF_CHAIN_NB) {
dgabino 0:102b50f941d0 124 DEBUG_MSG("ERROR: INVALID RF_CHAIN\n");
dgabino 0:102b50f941d0 125 return 0;
dgabino 0:102b50f941d0 126 }
dgabino 0:102b50f941d0 127 if (addr >= 0x7F) {
dgabino 0:102b50f941d0 128 DEBUG_MSG("ERROR: ADDRESS OUT OF RANGE\n");
dgabino 0:102b50f941d0 129 return 0;
dgabino 0:102b50f941d0 130 }
dgabino 0:102b50f941d0 131
dgabino 0:102b50f941d0 132 /* selecting the target radio */
dgabino 0:102b50f941d0 133 switch (channel) {
dgabino 0:102b50f941d0 134 case 0:
dgabino 0:102b50f941d0 135 reg_add = LGW_SPI_RADIO_A__ADDR;
dgabino 0:102b50f941d0 136 reg_dat = LGW_SPI_RADIO_A__DATA;
dgabino 0:102b50f941d0 137 reg_cs = LGW_SPI_RADIO_A__CS;
dgabino 0:102b50f941d0 138 reg_rb = LGW_SPI_RADIO_A__DATA_READBACK;
dgabino 0:102b50f941d0 139 break;
dgabino 0:102b50f941d0 140
dgabino 0:102b50f941d0 141 case 1:
dgabino 0:102b50f941d0 142 reg_add = LGW_SPI_RADIO_B__ADDR;
dgabino 0:102b50f941d0 143 reg_dat = LGW_SPI_RADIO_B__DATA;
dgabino 0:102b50f941d0 144 reg_cs = LGW_SPI_RADIO_B__CS;
dgabino 0:102b50f941d0 145 reg_rb = LGW_SPI_RADIO_B__DATA_READBACK;
dgabino 0:102b50f941d0 146 break;
dgabino 0:102b50f941d0 147
dgabino 0:102b50f941d0 148 default:
dgabino 0:102b50f941d0 149 DEBUG_PRINTF("ERROR: UNEXPECTED VALUE %d IN SWITCH STATEMENT\n", channel);
dgabino 0:102b50f941d0 150 return 0;
dgabino 0:102b50f941d0 151 }
dgabino 0:102b50f941d0 152
dgabino 0:102b50f941d0 153 /* SPI master data read procedure */
dgabino 0:102b50f941d0 154 lgw_reg_w(reg_cs, 0);
dgabino 0:102b50f941d0 155 lgw_reg_w(reg_add, addr); /* MSB at 0 for read operation */
dgabino 0:102b50f941d0 156 lgw_reg_w(reg_dat, 0);
dgabino 0:102b50f941d0 157 lgw_reg_w(reg_cs, 1);
dgabino 0:102b50f941d0 158 lgw_reg_w(reg_cs, 0);
dgabino 0:102b50f941d0 159 lgw_reg_r(reg_rb, &read_value);
dgabino 0:102b50f941d0 160
dgabino 0:102b50f941d0 161 return (uint8_t)read_value;
dgabino 0:102b50f941d0 162 }
dgabino 0:102b50f941d0 163
dgabino 0:102b50f941d0 164 /* -------------------------------------------------------------------------- */
dgabino 0:102b50f941d0 165 /* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */
dgabino 0:102b50f941d0 166
dgabino 0:102b50f941d0 167 int lgw_setup_sx125x(uint8_t rf_chain, uint8_t rf_clkout, bool rf_enable, uint8_t rf_radio_type, uint32_t freq_hz) {
dgabino 0:102b50f941d0 168 uint32_t part_int = 0;
dgabino 0:102b50f941d0 169 uint32_t part_frac = 0;
dgabino 0:102b50f941d0 170 int cpt_attempts = 0;
dgabino 0:102b50f941d0 171
dgabino 0:102b50f941d0 172 if (rf_chain >= LGW_RF_CHAIN_NB) {
dgabino 0:102b50f941d0 173 DEBUG_MSG("ERROR: INVALID RF_CHAIN\n");
dgabino 0:102b50f941d0 174 return -1;
dgabino 0:102b50f941d0 175 }
dgabino 0:102b50f941d0 176
dgabino 0:102b50f941d0 177 /* Get version to identify SX1255/57 silicon revision */
dgabino 0:102b50f941d0 178 DEBUG_PRINTF("Note: SX125x #%d version register returned 0x%02x\n", rf_chain, sx125x_read(rf_chain, 0x07));
dgabino 0:102b50f941d0 179
dgabino 0:102b50f941d0 180 /* General radio setup */
dgabino 0:102b50f941d0 181 if (rf_clkout == rf_chain) {
dgabino 0:102b50f941d0 182 sx125x_write(rf_chain, 0x10, SX125x_TX_DAC_CLK_SEL + 2);
dgabino 0:102b50f941d0 183 DEBUG_PRINTF("Note: SX125x #%d clock output enabled\n", rf_chain);
dgabino 0:102b50f941d0 184 } else {
dgabino 0:102b50f941d0 185 sx125x_write(rf_chain, 0x10, SX125x_TX_DAC_CLK_SEL);
dgabino 0:102b50f941d0 186 DEBUG_PRINTF("Note: SX125x #%d clock output disabled\n", rf_chain);
dgabino 0:102b50f941d0 187 }
dgabino 0:102b50f941d0 188
dgabino 0:102b50f941d0 189 switch (rf_radio_type) {
dgabino 0:102b50f941d0 190 case LGW_RADIO_TYPE_SX1255:
dgabino 0:102b50f941d0 191 sx125x_write(rf_chain, 0x28, SX125x_XOSC_GM_STARTUP + SX125x_XOSC_DISABLE * 16);
dgabino 0:102b50f941d0 192 break;
dgabino 0:102b50f941d0 193 case LGW_RADIO_TYPE_SX1257:
dgabino 0:102b50f941d0 194 sx125x_write(rf_chain, 0x26, SX125x_XOSC_GM_STARTUP + SX125x_XOSC_DISABLE * 16);
dgabino 0:102b50f941d0 195 break;
dgabino 0:102b50f941d0 196 default:
dgabino 0:102b50f941d0 197 DEBUG_PRINTF("ERROR: UNEXPECTED VALUE %d FOR RADIO TYPE\n", rf_radio_type);
dgabino 0:102b50f941d0 198 break;
dgabino 0:102b50f941d0 199 }
dgabino 0:102b50f941d0 200
dgabino 0:102b50f941d0 201 if (rf_enable == true) {
dgabino 0:102b50f941d0 202 /* Tx gain and trim */
dgabino 0:102b50f941d0 203 sx125x_write(rf_chain, 0x08, SX125x_TX_MIX_GAIN + SX125x_TX_DAC_GAIN * 16);
dgabino 0:102b50f941d0 204 sx125x_write(rf_chain, 0x0A, SX125x_TX_ANA_BW + SX125x_TX_PLL_BW * 32);
dgabino 0:102b50f941d0 205 sx125x_write(rf_chain, 0x0B, SX125x_TX_DAC_BW);
dgabino 0:102b50f941d0 206
dgabino 0:102b50f941d0 207 /* Rx gain and trim */
dgabino 0:102b50f941d0 208 sx125x_write(rf_chain, 0x0C, SX125x_LNA_ZIN + SX125x_RX_BB_GAIN * 2 + SX125x_RX_LNA_GAIN * 32);
dgabino 0:102b50f941d0 209 sx125x_write(rf_chain, 0x0D, SX125x_RX_BB_BW + SX125x_RX_ADC_TRIM * 4 + SX125x_RX_ADC_BW * 32);
dgabino 0:102b50f941d0 210 sx125x_write(rf_chain, 0x0E, SX125x_ADC_TEMP + SX125x_RX_PLL_BW * 2);
dgabino 0:102b50f941d0 211
dgabino 0:102b50f941d0 212 /* set RX PLL frequency */
dgabino 0:102b50f941d0 213 switch (rf_radio_type) {
dgabino 0:102b50f941d0 214 case LGW_RADIO_TYPE_SX1255:
dgabino 0:102b50f941d0 215 part_int = freq_hz / (SX125x_32MHz_FRAC << 7); /* integer part, gives the MSB */
dgabino 0:102b50f941d0 216 part_frac = ((freq_hz % (SX125x_32MHz_FRAC << 7)) << 9) / SX125x_32MHz_FRAC; /* fractional part, gives middle part and LSB */
dgabino 0:102b50f941d0 217 break;
dgabino 0:102b50f941d0 218 case LGW_RADIO_TYPE_SX1257:
dgabino 0:102b50f941d0 219 part_int = freq_hz / (SX125x_32MHz_FRAC << 8); /* integer part, gives the MSB */
dgabino 0:102b50f941d0 220 part_frac = ((freq_hz % (SX125x_32MHz_FRAC << 8)) << 8) / SX125x_32MHz_FRAC; /* fractional part, gives middle part and LSB */
dgabino 0:102b50f941d0 221 break;
dgabino 0:102b50f941d0 222 default:
dgabino 0:102b50f941d0 223 DEBUG_PRINTF("ERROR: UNEXPECTED VALUE %d FOR RADIO TYPE\n", rf_radio_type);
dgabino 0:102b50f941d0 224 break;
dgabino 0:102b50f941d0 225 }
dgabino 0:102b50f941d0 226
dgabino 0:102b50f941d0 227 sx125x_write(rf_chain, 0x01, 0xFF & part_int); /* Most Significant Byte */
dgabino 0:102b50f941d0 228 sx125x_write(rf_chain, 0x02, 0xFF & (part_frac >> 8)); /* middle byte */
dgabino 0:102b50f941d0 229 sx125x_write(rf_chain, 0x03, 0xFF & part_frac); /* Least Significant Byte */
dgabino 0:102b50f941d0 230
dgabino 0:102b50f941d0 231 /* start and PLL lock */
dgabino 0:102b50f941d0 232 do {
dgabino 0:102b50f941d0 233 if (cpt_attempts >= PLL_LOCK_MAX_ATTEMPTS) {
dgabino 0:102b50f941d0 234 DEBUG_MSG("ERROR: FAIL TO LOCK PLL\n");
dgabino 0:102b50f941d0 235 return -1;
dgabino 0:102b50f941d0 236 }
dgabino 0:102b50f941d0 237 sx125x_write(rf_chain, 0x00, 1); /* enable Xtal oscillator */
dgabino 0:102b50f941d0 238 sx125x_write(rf_chain, 0x00, 3); /* Enable RX (PLL+FE) */
dgabino 0:102b50f941d0 239 ++cpt_attempts;
dgabino 0:102b50f941d0 240 DEBUG_PRINTF("Note: SX125x #%d PLL start (attempt %d)\n", rf_chain, cpt_attempts);
dgabino 0:102b50f941d0 241 wait_ms(1);
dgabino 0:102b50f941d0 242 } while((sx125x_read(rf_chain, 0x11) & 0x02) == 0);
dgabino 0:102b50f941d0 243 } else {
dgabino 0:102b50f941d0 244 DEBUG_PRINTF("Note: SX125x #%d kept in standby mode\n", rf_chain);
dgabino 0:102b50f941d0 245 }
dgabino 0:102b50f941d0 246
dgabino 0:102b50f941d0 247 return 0;
dgabino 0:102b50f941d0 248 }
dgabino 0:102b50f941d0 249
dgabino 0:102b50f941d0 250 /* --- EOF ------------------------------------------------------------------ */