/* * THE FOLLOWING FIRMWARE IS PROVIDED: (1) "AS IS" WITH NO WARRANTY; AND * (2)TO ENABLE ACCESS TO CODING INFORMATION TO GUIDE AND FACILITATE CUSTOMER. * CONSEQUENTLY, SEMTECH SHALL NOT BE HELD LIABLE FOR ANY DIRECT, INDIRECT OR * CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE CONTENT * OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING INFORMATION * CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. * * Copyright (C) SEMTECH S.A. */ /*! * \file sx1276-LoRa.c * \brief SX1276 RF chip driver mode LoRa * * \version 2.0.0 * \date May 6 2013 * \author Gregory Cristian * * Last modified by Miguel Luis on Jun 19 2013 */ #include #include "hal_radio.h" #include "sx1276-Fsk.h" #include "sx1276-LoRaMisc.h" #include "sx1276-LoRa.h" /*! * Frequency hopping frequencies table */ #if defined (HIGH_FREQUENCY) const int32_t HoppingFrequencies[] = { 916500000, 923500000, 906500000, 917500000, 917500000, 909000000, 903000000, 916000000, 912500000, 926000000, 925000000, 909500000, 913000000, 918500000, 918500000, 902500000, 911500000, 926500000, 902500000, 922000000, 924000000, 903500000, 913000000, 922000000, 926000000, 910000000, 920000000, 922500000, 911000000, 922000000, 909500000, 926000000, 922000000, 918000000, 925500000, 908000000, 917500000, 926500000, 908500000, 916000000, 905500000, 916000000, 903000000, 905000000, 915000000, 913000000, 907000000, 910000000, 926500000, 925500000, 911000000, }; #else const int32_t HoppingFrequencies[] = { #if 0 479000000, 484700000, 471500000, 479300000, 471700000, 479500000, 471900000, 479700000, 472100000, 479900000, 472300000, 480100000, 472500000, 480300000, 472700000, 480500000, 472900000, 480700000, 473100000, 480900000, 473300000, 481100000, 473500000, 481300000, 473700000, 481500000, 473900000, 481700000, 474100000, 481900000, 474300000, 482100000, 474500000, 482300000, 474700000, 482500000, 474900000, 482700000, 475100000, 482900000, 475300000, 483100000, 475500000, 483300000, 475700000, 483500000, 475900000, 483700000, 476100000, 483900000, 476500000, 484100000, 476700000, 484300000, 476900000, 484500000, 477100000, 484900000, 477300000, 485100000, 477500000, 485300000, 477700000, 485500000, 477900000, 485700000, #endif 492000000, 492100000, 492200000, 492300000, 492400000, 492500000, 492600000, 492700000, 492800000, 492900000, 493000000, 493100000, 493200000, 493300000, 493400000, 493500000, 493600000, 493700000, 493800000, 493900000, 494000000, 494100000, 494200000, 494300000, 494400000, 494500000, 494600000, 494700000, 494800000, 494900000, 495000000, 495100000, 495200000, 495300000, 495400000, 495500000, 495600000, 495700000, 495800000, 495900000, 496000000, 496100000, 496200000, 496300000, 496400000, 496500000, 496600000, 496700000, 496800000, 496900000, }; #endif #define NO_HOP_LOW_FREQUENCY 479000000 #ifdef HOP_CHSS #define HOP_CHANNELS 50 #endif static u8 g_hopChannel = 0; static u32 LoraSymbolTs; static u32 loraWokePreambleLenth; u8 SX1276Regs[0x70]; tSX1276LR* SX1276LR = (tSX1276LR*)SX1276Regs; u32 g_SignalBw[10] = {7800, 10400, 15600, 20800, 31200, 41600, 62500, 125000, 250000, 500000}; tLoRaSettings LoRaSettings = { #ifdef HIGH_FREQUENCY .RFFrequency = 920000000, // RFFrequency #else .RFFrequency = NO_HOP_LOW_FREQUENCY, #endif .Power = 0, // Power .SignalBw = 6, // SignalBw [0: 7.8kHz, 1: 10.4 kHz, 2: 15.6 kHz, 3: 20.8 kHz, 4: 31.2 kHz, // 5: 41.6 kHz, 6: 62.5 kHz, 7: 125 kHz, 8: 250 kHz, 9: 500 kHz, other: Reserved] .SpreadingFactor = 7, // SpreadingFactor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips] .ErrorCoding = 2, // ErrorCoding [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] .CrcOn = true, // CrcOn [0: OFF, 1: ON] .ImplicitHeaderOn = false, // ImplicitHeaderOn [0: OFF, 1: ON] .RxSingleOn = false, // RxSingleOn [0: Continuous, 1 Single] .FreqHopOn = false, // FreqHopOn [0: OFF, 1: ON] .HopPeriod = 7, // HopPeriod Hops every frequency hopping period symbols .TxPacketTimeout = 800, // TxPacketTimeout .RxPacketTimeout = 800, // RxPacketTimeout .PayloadLength = 254, // PayloadLength (used for implicit header mode) .LowDatarateOptimize = false, // LowDatarateOptimize option .PreambleLen = NORMALSYMBOLSLENGTH, // Preamble length }; st_RF_LoRa_DypeDef g_RF_LoRa = { .rf_DataBufferValid = false, .rf_state = RFLR_STATE_IDLE, .rf_RxPacketSize = 0, .rf_TxPacketSize = 0, .rf_HeaderValid = false, }; /* typedef struct { u32 loraWokePreambleLenth; uint8_t LoraChl; st_RF_LoRa_DypeDef data; u32 (*readLoraSymbolTs)(void); int8_t (*getPacketSnr)(void); u8 (*getRxPacketRssi)(); void (*LoRa_NormalTx)(u8 channel, u8 *PBuffer, u8 length); void (*LoRa_WakeUpTx)(u8 channel, u8 *PBuffer, u8 length); void (*LoRa_Receive_Packet)(u8 channel, bool wakeUp); }st_Lora_port; u32 readLoraSymbolTs(void) { return set_LoraSymbolTs(LoRaSettings.SignalBw, LoRaSettings.SpreadingFactor); } */ /***************************************************************************** * Function : set_LoraSymbolTs * Description : none * Input : uint8_t BwFlag uint8_t SF * Output : None * Return : unit ms * Others : * Record * 1.Date : 20170320 * Author : barry * Modification: Created function *****************************************************************************/ u32 set_LoraSymbolTs(uint8_t BwFlag ,uint8_t SF) { return (1 << SF)*1000/g_SignalBw[BwFlag]; } u32 get_LoraSymbolTs(void) { return LoraSymbolTs; } /***************************************************************************** * Function : set_LoRaWokeUpPreambleLenth * Description : none * Input : u32 period * Output : None * Return : * Others : * Record * 1.Date : 20170320 * Author : barry * Modification: Created function *****************************************************************************/ u32 set_LoRaWokeUpPreambleLenth(u32 period, uint8_t BwFlag ,uint8_t SF) { return (u32)(period * g_SignalBw[BwFlag] / ((1 << SF) *1000)) + 6; } u32 get_LoRaWokeUpPreambleLenth(void) { return loraWokePreambleLenth; } /***************************************************************************** Prototype : SX1276LoRaSetDefaults Description : none Input : void Output : None Return Value : Date : 2014/3/15 Author : Barry *****************************************************************************/ void SX1276LoRaSetDefaults( void ) { /* REMARK: See SX1276 datasheet for modified default values */ SX1276Read( REG_LR_VERSION, &SX1276LR->RegVersion ); } /***************************************************************************** Prototype : SX1276LoRaSetOpMode Description : none Input : uint8_t opMode Output : None Return Value : Date : 2014/3/15 Author : Barry *****************************************************************************/ void SX1276LoRaSetOpMode( uint8_t opMode ) { SX1276LR->RegOpMode = ( SX1276LR->RegOpMode & RFLR_OPMODE_MASK ) | opMode; /* BEGIN: Added by Barry, 2014/3/14 */ #ifdef HIGH_FREQUENCY SX1276LR->RegOpMode = ( SX1276LR->RegOpMode & RFLR_OPMODE_FREQMODE_ACCESS_MASK ) | RFLR_OPMODE_FREQMODE_ACCESS_HF; //Elvis #endif /* END: Added by Barry, 2014/3/14 */ SX1276Write( REG_LR_OPMODE, SX1276LR->RegOpMode ); } /***************************************************************************** Prototype : SX1276LoRaGetOpMode Description : none Input : void Output : None Return Value : Date : 2014/3/15 Author : Barry *****************************************************************************/ uint8_t SX1276LoRaGetOpMode( void ) { SX1276Read( REG_LR_OPMODE, &SX1276LR->RegOpMode ); return SX1276LR->RegOpMode & ~RFLR_OPMODE_MASK; } /***************************************************************************** Prototype : SX1276LoRaReadRxGain Description : none Input : void Output : None Return Value : Date : 2014/3/15 Author : Barry *****************************************************************************/ uint8_t SX1276LoRaReadRxGain( void ) { SX1276Read( REG_LR_LNA, &SX1276LR->RegLna ); return( SX1276LR->RegLna >> 5 ) & 0x07; } /***************************************************************************** Prototype : config_GDOx_Map Description : none Input : st_GDOx_Config GDOx_Map Output : None Return Value : Date : 2014/3/15 Author : Barry *****************************************************************************/ void config_GDOx_Map(st_GDOx_Config GDOx_Map) { SX1276LR->RegDioMapping1 = GDOx_Map.GDO0Config| GDOx_Map.GDO1Config| GDOx_Map.GDO2Config| GDOx_Map.GDO3Config; SX1276LR->RegDioMapping2 = GDOx_Map.GDO4Config| GDOx_Map.GDO5Config; SX1276WriteBuffer( REG_LR_DIOMAPPING1, &SX1276LR->RegDioMapping1, 2 ); } /***************************************************************************** Prototype : getPacketSnr Description : none Input : void Output : None Return Value : Date : 2014/3/15 Author : Barry *****************************************************************************/ int8_t getPacketSnr(void) { uint8_t rxSnrEstimate; int8_t RxPacketSnrEstimate; SX1276Read( REG_LR_PKTSNRVALUE, &rxSnrEstimate ); /* The SNR sign bit is 1 */ if( rxSnrEstimate & 0x80 ) { /* Invert and divide by 4 */ RxPacketSnrEstimate = ( ( ~rxSnrEstimate + 1 ) & 0xFF ) >> 2; RxPacketSnrEstimate = -RxPacketSnrEstimate; } else { /* Divide by 4 */ RxPacketSnrEstimate = ( rxSnrEstimate & 0xFF ) >> 2; } return RxPacketSnrEstimate; } /***************************************************************************** Prototype : get_RxPacketRssi Description : none Input : void Output : None Return Value : Date : 2014/3/15 Author : Barry *****************************************************************************/ double get_RxPacketRssi(int8_t RxPacketSnr) { #define RSSI_OFFSET_LF -164.0 #define RSSI_OFFSET_HF -157.0 double RxPacketRssi; SX1276Read( REG_LR_PKTRSSIVALUE, &SX1276LR->RegPktRssiValue ); if( LoRaSettings.RFFrequency < 860000000 ) // LF { if( RxPacketSnr < 0 ) { RxPacketRssi = RSSI_OFFSET_LF + ((double)SX1276LR->RegPktRssiValue) + RxPacketSnr; } else { RxPacketRssi = RSSI_OFFSET_LF + ( 1.0666 * ((double)SX1276LR->RegPktRssiValue) ); } } else // HF { if( RxPacketSnr < 0 ) { RxPacketRssi = RSSI_OFFSET_HF + ( ( double )SX1276LR->RegPktRssiValue ) + RxPacketSnr; } else { RxPacketRssi = RSSI_OFFSET_HF + ( 1.0666 * ((double)SX1276LR->RegPktRssiValue) ); } } return RxPacketRssi; } /***************************************************************************** Prototype : read_Lora_Rssi Description : none Input : tRFLRStates state Output : None Return Value : Date : 2014/3/15 Author : Barry *****************************************************************************/ u8 read_Lora_Rssi(void) { int8_t pSnr = getPacketSnr(); u8 rssi = (u8)get_RxPacketRssi(pSnr); if (rssi & 0x80) { rssi = (~rssi + 1); } return rssi; } /***************************************************************************** Prototype : SX1276LoRaInit Description : none Input : void Output : None Return Value : Date : 2014/3/15 Author : Barry *****************************************************************************/ void SX1276LoRaInit( void ) { SX1276LoRaSetOpMode( RFLR_OPMODE_SLEEP ); SX1276LR->RegOpMode = ( SX1276LR->RegOpMode & RFLR_OPMODE_LONGRANGEMODE_MASK ) | RFLR_OPMODE_LONGRANGEMODE_ON; SX1276Write( REG_LR_OPMODE, SX1276LR->RegOpMode ); SX1276LoRaSetOpMode( RFLR_OPMODE_STANDBY ); SX1276LoRaSetDefaults( ); SX1276ReadBuffer( REG_LR_OPMODE, SX1276Regs + 1, 0x70 - 1 ); SX1276LR->RegLna = RFLR_LNA_GAIN_G1; SX1276Write( REG_LR_LNA, SX1276LR->RegLna ); /* Set the device in Sleep Mode */ SX1276LoRaSetOpMode( RFLR_OPMODE_SLEEP ); SX1276LoRaSetRFFrequency( LoRaSettings.RFFrequency ); SX1276LoRaSetSpreadingFactor( LoRaSettings.SpreadingFactor ); // SF6 only operates in implicit header mode. SX1276LoRaSetErrorCoding( LoRaSettings.ErrorCoding ); SX1276LoRaSetPacketCrcOn( LoRaSettings.CrcOn ); SX1276LoRaSetSignalBandwidth( LoRaSettings.SignalBw ); SX1276LoRaSetImplicitHeaderOn( LoRaSettings.ImplicitHeaderOn ); SX1276LoRaSetSymbTimeout( 0x3FF ); SX1276LoRaSetPayloadLength( LoRaSettings.PayloadLength ); SX1276LoRaSetPreambleLength(LoRaSettings.PreambleLen); /* seted true when a symble time over 16ms */ if (get_LoraSymbolTs() >= 16) { SX1276LoRaSetLowDatarateOptimize(true); } else { SX1276LoRaSetLowDatarateOptimize(false); } SX1276LoRaSetPAOutput( RFLR_PACONFIG_PASELECT_RFO ); //SX1276LoRaSetPAOutput( RFLR_PACONFIG_PASELECT_PABOOST ); /* set max power 17DBm */ SX1276LoRaSetPa20dBm( false ); /* set power */ SX1276LoRaSetRFPower( LoRaSettings.Power ); SX1276StartSleep(); } /***************************************************************************** * Function : Lora_lowPower_Init * Description : none * Input : u32 period * Output : None * Return : * Others : * Record * 1.Date : 20170526 * Author : barry * Modification: Created function *****************************************************************************/ void Lora_lowPower_Init(u32 period) { LoraSymbolTs = set_LoraSymbolTs(LoRaSettings.SignalBw, LoRaSettings.SpreadingFactor); loraWokePreambleLenth = set_LoRaWokeUpPreambleLenth(period, LoRaSettings.SignalBw, LoRaSettings.SpreadingFactor); } u32 get_LoraWokeUpPreambleT(void) { return LoraSymbolTs*loraWokePreambleLenth; } /***************************************************************************** Prototype : receiveRxData Description : none Input : bool spiDMA Output : None Return Value : Date : 2014/3/15 Author : Barry *****************************************************************************/ void receiveRxData(u8 *buf, u8 *length) { if( LoRaSettings.RxSingleOn == true ) { SX1276LR->RegFifoAddrPtr = SX1276LR->RegFifoRxBaseAddr; } else { SX1276Read( REG_LR_FIFORXCURRENTADDR, &SX1276LR->RegFifoRxCurrentAddr ); SX1276LR->RegFifoAddrPtr = SX1276LR->RegFifoRxCurrentAddr; } if( LoRaSettings.ImplicitHeaderOn == true ) { *length = SX1276LR->RegPayloadLength; } else { SX1276Read( REG_LR_NBRXBYTES, &SX1276LR->RegNbRxBytes ); *length = SX1276LR->RegNbRxBytes; } SX1276Write( REG_LR_FIFOADDRPTR, SX1276LR->RegFifoAddrPtr ); SX1276ReadFifo(buf, *length); } /***************************************************************************** * Function : SX1276LoRa_hopTx_config * Description : none * Input : void * Output : None * Return : * Others : * Record * 1.Date : 20170320 * Author : barry * Modification: Created function *****************************************************************************/ void SX1276LoRa_hopTx_config(st_GDOx_Config *DIO_map_ptr) { SX1276LR->RegIrqFlagsMask &= (~RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL); SX1276LR->RegHopPeriod = LoRaSettings.HopPeriod; DIO_map_ptr->GDO1Config = DIO1_FhssCC; hal_DIOx_ITConfig(1,ENABLE); } /***************************************************************************** Prototype : SX1276LoRa_Send_Packet Description : none Input : u8 *PBuffer u8 length bool FreqHop bool spiDMA Output : None Return Value : Date : 2014/3/15 Author : Barry *****************************************************************************/ void SX1276LoRa_Send_Packet(u8 channel, bool wakeUp, u8 *PBuffer,u8 length) { st_GDOx_Config DIO_map_conf = DEFAULT_DIO_config; SX1276LoRaSetOpMode( RFLR_OPMODE_STANDBY ); hal_DIOx_ITConfig(all, DISABLE); switch_Tx(); DIO_map_conf.GDO0Config = DIO0_TxDone; SX1276LR->RegIrqFlagsMask = RFLR_IRQFLAGS_ALL_MASK & (~RFLR_IRQFLAGS_TXDONE); SX1276LoRaSetOpMode( RFLR_OPMODE_SYNTHESIZER_TX ); SX1276LR->RegHopPeriod = 0; #ifdef HOP_CHSS SX1276LoRa_hopTx_config(DIO_map_conf); #endif LoRaSettings.PreambleLen = wakeUp? get_LoRaWokeUpPreambleLenth():NORMALSYMBOLSLENGTH; SX1276LoRaSetPreambleLength(LoRaSettings.PreambleLen); config_GDOx_Map(DIO_map_conf); SX1276Write( REG_LR_HOPPERIOD, SX1276LR->RegHopPeriod ); SX1276Write( REG_LR_IRQFLAGSMASK, SX1276LR->RegIrqFlagsMask ); SX1276LoRaSetRFFrequency(HoppingFrequencies[channel]); SX1276LR->RegPayloadLength = length; SX1276Write( REG_LR_PAYLOADLENGTH, SX1276LR->RegPayloadLength); SX1276LR->RegFifoTxBaseAddr = 0x00; SX1276Write( REG_LR_FIFOTXBASEADDR, SX1276LR->RegFifoTxBaseAddr ); SX1276LR->RegFifoAddrPtr = SX1276LR->RegFifoTxBaseAddr; SX1276Write(REG_LR_FIFOADDRPTR, SX1276LR->RegFifoAddrPtr); g_RF_LoRa.rf_state = RFLR_STATE_TX_RUNNING; MemCpy(g_RF_LoRa.rf_DataBuffer, PBuffer, length); g_RF_LoRa.rf_TxPacketSize = length; SX1276WriteFifo(g_RF_LoRa.rf_DataBuffer,length); SX1276LoRaSetOpMode(RFLR_OPMODE_TRANSMITTER); hal_DIOx_ITConfig(0,ENABLE); } /***************************************************************************** Prototype : SX1276LoRa_Receive_Packet Description : none Input : bool FreqHop Output : None Return Value : Date : 2014/3/15 Author : Barry *****************************************************************************/ void SX1276LoRa_Receive_Packet(u8 channel, bool wakeUp) { st_GDOx_Config DIO_map_conf = DEFAULT_DIO_config; SX1276LoRaSetOpMode( RFLR_OPMODE_STANDBY ); SX1276LR->RegIrqFlagsMask = RFLR_IRQFLAGS_ALL_MASK; SX1276LR->RegIrqFlagsMask &= (~(RFLR_IRQFLAGS_RXDONE | RFLR_IRQFLAGS_PAYLOADCRCERROR| RFLR_IRQFLAGS_VALIDHEADER /*|RFLR_IRQFLAGS_RXTIMEOUT*/ )); DIO_map_conf.GDO0Config = DIO0_RxDone; DIO_map_conf.GDO3Config = DIO3_ValidHeader; SX1276LR->RegHopPeriod = 0; switch_Rx(); #ifdef HOP_CHSS SX1276LoRa_hopTx_config(DIO_map_conf); #endif LoRaSettings.PreambleLen = wakeUp? get_LoRaWokeUpPreambleLenth():NORMALSYMBOLSLENGTH; SX1276LoRaSetPreambleLength(LoRaSettings.PreambleLen); SX1276LoRaSetSymbTimeout(0x3FF); config_GDOx_Map(DIO_map_conf); SX1276Write( REG_LR_HOPPERIOD, SX1276LR->RegHopPeriod ); SX1276Write( REG_LR_IRQFLAGSMASK, SX1276LR->RegIrqFlagsMask ); SX1276LoRaSetRFFrequency(HoppingFrequencies[channel]); if (wakeUp) { SX1276LoRaSetOpMode( RFLR_OPMODE_RECEIVER_SINGLE ); } else { SX1276LoRaSetOpMode( RFLR_OPMODE_RECEIVER ); } g_RF_LoRa.rf_state = RFLR_STATE_RX_RUNNING; hal_DIOx_ITConfig(0,ENABLE); hal_DIOx_ITConfig(3,ENABLE); } /***************************************************************************** Prototype : SX1276StartSleep Description : none Date : 2014/3/15 Author : Barry *****************************************************************************/ void SX1276StartSleep(void) { switch_Rx(); hal_DIOx_ITConfig(all, DISABLE); hal_sRF_ClearAllRF_IT(); SX1276LoRaSetOpMode( RFLR_OPMODE_SLEEP ); g_RF_LoRa.rf_state = RFLR_STATE_SLEEP; } /***************************************************************************** Prototype : SX1276LoRa_CAD_Scan Description : none Input : void Output : None Return Value : Date : 2014/3/15 Author : Barry *****************************************************************************/ void SX1276LoRa_CAD_Scan(u8 channel) { st_GDOx_Config DIO_map_conf = DEFAULT_DIO_config; switch_Rx(); SX1276LoRaSetOpMode( RFLR_OPMODE_STANDBY ); SX1276LR->RegIrqFlagsMask |= RFLR_IRQFLAGS_ALL_MASK; SX1276LR->RegIrqFlagsMask &= (~(RFLR_IRQFLAGS_CADDETECTED | RFLR_IRQFLAGS_CADDONE)); SX1276Write( REG_LR_IRQFLAGSMASK, SX1276LR->RegIrqFlagsMask ); SX1276LoRaSetRFFrequency(HoppingFrequencies[channel]); DIO_map_conf.GDO0Config = DIO0_CadDone; DIO_map_conf.GDO1Config = DIO1_CadDetected; config_GDOx_Map(DIO_map_conf); hal_DIOx_ITConfig(0,ENABLE); SX1276LoRaSetOpMode( RFLR_OPMODE_CAD ); g_RF_LoRa.rf_state = RFLR_STATE_CAD_RUNNING; } /***************************************************************************** * Function : set_hop_Channel * Description : none * Input : u8 channel * Output : None * Return : * Others : * Record * 1.Date : 20170320 * Author : barry * Modification: Created function *****************************************************************************/ void set_hop_Channel(u8 channel) { SX1276LoRaSetRFFrequency( HoppingFrequencies[ (g_hopChannel + channel&RFLR_HOPCHANNEL_CHANNEL_MASK)%HOP_CHANNELS] ); }