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.
libloragw/src/loragw_radio.c@0:102b50f941d0, 2018-04-11 (annotated)
- Committer:
- dgabino
- Date:
- Wed Apr 11 14:38:42 2018 +0000
- Revision:
- 0:102b50f941d0
Initial commit
Who changed what in which revision?
User | Revision | Line number | New 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 ------------------------------------------------------------------ */ |