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.
Diff: libloragw/src/loragw_radio.c
- Revision:
- 0:102b50f941d0
diff -r 000000000000 -r 102b50f941d0 libloragw/src/loragw_radio.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libloragw/src/loragw_radio.c Wed Apr 11 14:38:42 2018 +0000 @@ -0,0 +1,250 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2017 Semtech-Cycleo + +Description: + Functions used to handle LoRa concentrator radios. + +License: Revised BSD License, see LICENSE.TXT file include in the project + +*/ + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +#include <stdint.h> /* C99 types */ +#include <stdbool.h> /* bool type */ +#include <stdio.h> /* printf fprintf */ + +#include "loragw_sx125x.h" +#include "loragw_com.h" +#include "loragw_aux.h" +#include "loragw_reg.h" +#include "loragw_hal.h" +#include "loragw_radio.h" + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE MACROS ------------------------------------------------------- */ + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#if DEBUG_REG == 1 +#define DEBUG_MSG(str) fprintf(stderr, str) +#define DEBUG_PRINTF(fmt, args...) fprintf(stderr,"%s:%d: "fmt, __FUNCTION__, __LINE__, args) +#define CHECK_NULL(a) if(a==NULL){fprintf(stderr,"%s:%d: ERROR: NULL POINTER AS ARGUMENT\n", __FUNCTION__, __LINE__);return LGW_REG_ERROR;} +#else +#define DEBUG_MSG(str) +#define DEBUG_PRINTF(fmt, args...) +#define CHECK_NULL(a) if(a==NULL){return LGW_REG_ERROR;} +#endif + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE TYPES -------------------------------------------------------- */ + +/** +@struct lgw_radio_type_version_s +@brief Associate a radio type with its corresponding expected version value + read in the radio version register. +*/ +struct lgw_radio_type_version_s { + enum lgw_radio_type_e type; + uint8_t reg_version; +}; + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE CONSTANTS ---------------------------------------------------- */ + +#define PLL_LOCK_MAX_ATTEMPTS 5 + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE VARIABLES ---------------------------------------------------- */ + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS ---------------------------------------------------- */ + +void sx125x_write(uint8_t channel, uint8_t addr, uint8_t data); +uint8_t sx125x_read(uint8_t channel, uint8_t addr); + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */ + +void sx125x_write(uint8_t channel, uint8_t addr, uint8_t data) { + int reg_add, reg_dat, reg_cs; + + /* checking input parameters */ + if (channel >= LGW_RF_CHAIN_NB) { + DEBUG_MSG("ERROR: INVALID RF_CHAIN\n"); + return; + } + if (addr >= 0x7F) { + DEBUG_MSG("ERROR: ADDRESS OUT OF RANGE\n"); + return; + } + + /* selecting the target radio */ + switch (channel) { + case 0: + reg_add = LGW_SPI_RADIO_A__ADDR; + reg_dat = LGW_SPI_RADIO_A__DATA; + reg_cs = LGW_SPI_RADIO_A__CS; + break; + + case 1: + reg_add = LGW_SPI_RADIO_B__ADDR; + reg_dat = LGW_SPI_RADIO_B__DATA; + reg_cs = LGW_SPI_RADIO_B__CS; + break; + + default: + DEBUG_PRINTF("ERROR: UNEXPECTED VALUE %d IN SWITCH STATEMENT\n", channel); + return; + } + + /* SPI master data write procedure */ + lgw_reg_w(reg_cs, 0); + lgw_reg_w(reg_add, 0x80 | addr); /* MSB at 1 for write operation */ + lgw_reg_w(reg_dat, data); + lgw_reg_w(reg_cs, 1); + lgw_reg_w(reg_cs, 0); + + return; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +uint8_t sx125x_read(uint8_t channel, uint8_t addr) { + int reg_add, reg_dat, reg_cs, reg_rb; + int32_t read_value; + + /* checking input parameters */ + if (channel >= LGW_RF_CHAIN_NB) { + DEBUG_MSG("ERROR: INVALID RF_CHAIN\n"); + return 0; + } + if (addr >= 0x7F) { + DEBUG_MSG("ERROR: ADDRESS OUT OF RANGE\n"); + return 0; + } + + /* selecting the target radio */ + switch (channel) { + case 0: + reg_add = LGW_SPI_RADIO_A__ADDR; + reg_dat = LGW_SPI_RADIO_A__DATA; + reg_cs = LGW_SPI_RADIO_A__CS; + reg_rb = LGW_SPI_RADIO_A__DATA_READBACK; + break; + + case 1: + reg_add = LGW_SPI_RADIO_B__ADDR; + reg_dat = LGW_SPI_RADIO_B__DATA; + reg_cs = LGW_SPI_RADIO_B__CS; + reg_rb = LGW_SPI_RADIO_B__DATA_READBACK; + break; + + default: + DEBUG_PRINTF("ERROR: UNEXPECTED VALUE %d IN SWITCH STATEMENT\n", channel); + return 0; + } + + /* SPI master data read procedure */ + lgw_reg_w(reg_cs, 0); + lgw_reg_w(reg_add, addr); /* MSB at 0 for read operation */ + lgw_reg_w(reg_dat, 0); + lgw_reg_w(reg_cs, 1); + lgw_reg_w(reg_cs, 0); + lgw_reg_r(reg_rb, &read_value); + + return (uint8_t)read_value; +} + +/* -------------------------------------------------------------------------- */ +/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */ + +int lgw_setup_sx125x(uint8_t rf_chain, uint8_t rf_clkout, bool rf_enable, uint8_t rf_radio_type, uint32_t freq_hz) { + uint32_t part_int = 0; + uint32_t part_frac = 0; + int cpt_attempts = 0; + + if (rf_chain >= LGW_RF_CHAIN_NB) { + DEBUG_MSG("ERROR: INVALID RF_CHAIN\n"); + return -1; + } + + /* Get version to identify SX1255/57 silicon revision */ + DEBUG_PRINTF("Note: SX125x #%d version register returned 0x%02x\n", rf_chain, sx125x_read(rf_chain, 0x07)); + + /* General radio setup */ + if (rf_clkout == rf_chain) { + sx125x_write(rf_chain, 0x10, SX125x_TX_DAC_CLK_SEL + 2); + DEBUG_PRINTF("Note: SX125x #%d clock output enabled\n", rf_chain); + } else { + sx125x_write(rf_chain, 0x10, SX125x_TX_DAC_CLK_SEL); + DEBUG_PRINTF("Note: SX125x #%d clock output disabled\n", rf_chain); + } + + switch (rf_radio_type) { + case LGW_RADIO_TYPE_SX1255: + sx125x_write(rf_chain, 0x28, SX125x_XOSC_GM_STARTUP + SX125x_XOSC_DISABLE * 16); + break; + case LGW_RADIO_TYPE_SX1257: + sx125x_write(rf_chain, 0x26, SX125x_XOSC_GM_STARTUP + SX125x_XOSC_DISABLE * 16); + break; + default: + DEBUG_PRINTF("ERROR: UNEXPECTED VALUE %d FOR RADIO TYPE\n", rf_radio_type); + break; + } + + if (rf_enable == true) { + /* Tx gain and trim */ + sx125x_write(rf_chain, 0x08, SX125x_TX_MIX_GAIN + SX125x_TX_DAC_GAIN * 16); + sx125x_write(rf_chain, 0x0A, SX125x_TX_ANA_BW + SX125x_TX_PLL_BW * 32); + sx125x_write(rf_chain, 0x0B, SX125x_TX_DAC_BW); + + /* Rx gain and trim */ + sx125x_write(rf_chain, 0x0C, SX125x_LNA_ZIN + SX125x_RX_BB_GAIN * 2 + SX125x_RX_LNA_GAIN * 32); + sx125x_write(rf_chain, 0x0D, SX125x_RX_BB_BW + SX125x_RX_ADC_TRIM * 4 + SX125x_RX_ADC_BW * 32); + sx125x_write(rf_chain, 0x0E, SX125x_ADC_TEMP + SX125x_RX_PLL_BW * 2); + + /* set RX PLL frequency */ + switch (rf_radio_type) { + case LGW_RADIO_TYPE_SX1255: + part_int = freq_hz / (SX125x_32MHz_FRAC << 7); /* integer part, gives the MSB */ + part_frac = ((freq_hz % (SX125x_32MHz_FRAC << 7)) << 9) / SX125x_32MHz_FRAC; /* fractional part, gives middle part and LSB */ + break; + case LGW_RADIO_TYPE_SX1257: + part_int = freq_hz / (SX125x_32MHz_FRAC << 8); /* integer part, gives the MSB */ + part_frac = ((freq_hz % (SX125x_32MHz_FRAC << 8)) << 8) / SX125x_32MHz_FRAC; /* fractional part, gives middle part and LSB */ + break; + default: + DEBUG_PRINTF("ERROR: UNEXPECTED VALUE %d FOR RADIO TYPE\n", rf_radio_type); + break; + } + + sx125x_write(rf_chain, 0x01, 0xFF & part_int); /* Most Significant Byte */ + sx125x_write(rf_chain, 0x02, 0xFF & (part_frac >> 8)); /* middle byte */ + sx125x_write(rf_chain, 0x03, 0xFF & part_frac); /* Least Significant Byte */ + + /* start and PLL lock */ + do { + if (cpt_attempts >= PLL_LOCK_MAX_ATTEMPTS) { + DEBUG_MSG("ERROR: FAIL TO LOCK PLL\n"); + return -1; + } + sx125x_write(rf_chain, 0x00, 1); /* enable Xtal oscillator */ + sx125x_write(rf_chain, 0x00, 3); /* Enable RX (PLL+FE) */ + ++cpt_attempts; + DEBUG_PRINTF("Note: SX125x #%d PLL start (attempt %d)\n", rf_chain, cpt_attempts); + wait_ms(1); + } while((sx125x_read(rf_chain, 0x11) & 0x02) == 0); + } else { + DEBUG_PRINTF("Note: SX125x #%d kept in standby mode\n", rf_chain); + } + + return 0; +} + +/* --- EOF ------------------------------------------------------------------ */