LMiC LoRa Semtech + Nucleo
Fork of LMiC by
radio.cpp
- Committer:
- mluis
- Date:
- 2015-01-22
- Revision:
- 0:62d1edcc13d1
- Child:
- 1:d3b7bde3995c
File content as of revision 0:62d1edcc13d1:
/******************************************************************************* * Copyright (c) 2014 IBM Corporation. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Zurich Research Lab - initial API, implementation and documentation * Semtech Apps Team - Modified to support the MBED sx1276 driver * library. * Possibility to use original or Semtech's MBED * radio driver. The selection is done by setting * USE_SMTC_RADIO_DRIVER preprocessing directive * in lmic.h *******************************************************************************/ #include "lmic.h" #if USE_SMTC_RADIO_DRIVER #include "sx1276-hal.h" /*! * Syncword for lora networks (nibbles swapped) */ #define LORA_MAC_SYNCWORD 0x34 /* * Callback functions prototypes */ /*! * @brief Function to be executed on Radio Tx Done event */ void OnTxDone( void ); /*! * @brief Function to be executed on Radio Rx Done event */ void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ); /*! * @brief Function executed on Radio Tx Timeout event */ void OnTxTimeout( void ); /*! * @brief Function executed on Radio Rx Timeout event */ void OnRxTimeout( void ); /*! * @brief Function executed on Radio Rx Error event */ void OnRxError( void ); /*! * @brief Function executed on Radio Fhss Change Channel event */ void OnFhssChangeChannel( uint8_t channelIndex ); /*! * @brief Function executed on CAD Done event */ void OnCadDone( void ); /* * Radio object declraration */ SX1276MB1xAS Radio( OnTxDone, OnTxTimeout, OnRxDone, OnRxTimeout, OnRxError, NULL, NULL ); static const u2_t LORA_RXDONE_FIXUP[] = { [FSK] = us2osticks(0), // ( 0 ticks) [SF7] = us2osticks(0), // ( 0 ticks) [SF8] = us2osticks(1648), // ( 54 ticks) [SF9] = us2osticks(3265), // ( 107 ticks) [SF10] = us2osticks(7049), // ( 231 ticks) [SF11] = us2osticks(13641), // ( 447 ticks) [SF12] = us2osticks(31189), // (1022 ticks) }; void OnTxDone( void ) { ostime_t now = os_getTime( ); // save exact tx time LMIC.txend = now - us2osticks( 43 ); // TXDONE FIXUP // go from stanby to sleep Radio.Sleep( ); // run os job (use preset func ptr) os_setCallback( &LMIC.osjob, LMIC.osjob.func ); } void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ) { ostime_t now = os_getTime( ); // save exact rx time LMIC.rxtime = now - LORA_RXDONE_FIXUP[getSf( LMIC.rps )]; // read the PDU and inform the MAC that we received something LMIC.dataLen = size; // now read the FIFO memcpy( LMIC.frame, payload, size ); // read rx quality parameters LMIC.rxq.snr = snr; // SNR [dB] * 4 LMIC.rxq.rssi = rssi; // RSSI [dBm] (-196...+63) // go from stanby to sleep Radio.Sleep( ); // run os job (use preset func ptr) os_setCallback( &LMIC.osjob, LMIC.osjob.func ); } void OnTxTimeout( void ) { ostime_t now = os_getTime( ); // indicate error // go from stanby to sleep Radio.Sleep( ); // run os job (use preset func ptr) os_setCallback( &LMIC.osjob, LMIC.osjob.func ); } void OnRxTimeout( void ) { ostime_t now = os_getTime( ); // indicate timeout LMIC.dataLen = 0; // go from stanby to sleep Radio.Sleep( ); // run os job (use preset func ptr) os_setCallback( &LMIC.osjob, LMIC.osjob.func ); } void OnRxError( void ) { ostime_t now = os_getTime( ); // indicate error LMIC.dataLen = 0; // go from stanby to sleep Radio.Sleep( ); // run os job (use preset func ptr) os_setCallback( &LMIC.osjob, LMIC.osjob.func ); } /*! * LMIC API implementation */ // RADIO STATE // (initialized by radio_init( ), used by radio_rand1( )) static u1_t randbuf[16]; // get random seed from wideband noise rssi void radio_init( void ) { hal_disableIRQs( ); // seed 15-byte randomness via noise rssi // Set LoRa modem ON Radio.SetModem( MODEM_LORA ); // Disable LoRa modem interrupts Radio.Write( REG_LR_IRQFLAGSMASK, RFLR_IRQFLAGS_RXTIMEOUT | RFLR_IRQFLAGS_RXDONE | RFLR_IRQFLAGS_PAYLOADCRCERROR | RFLR_IRQFLAGS_VALIDHEADER | RFLR_IRQFLAGS_TXDONE | RFLR_IRQFLAGS_CADDONE | RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL | RFLR_IRQFLAGS_CADDETECTED ); // Set radio in continuous reception Radio.Rx( 0 ); for( int i = 1; i < 16; i++ ) { for( int j = 0; j < 8; j++ ) { u1_t b; // wait for two non-identical subsequent least-significant bits while( ( b = Radio.Read( REG_LR_RSSIWIDEBAND ) & 0x01 ) == ( Radio.Read( REG_LR_RSSIWIDEBAND ) & 0x01 ) ); randbuf[i] = ( randbuf[i] << 1 ) | b; } } randbuf[0] = 16; // set initial index // Change LoRa modem SyncWord Radio.Write( REG_LR_SYNCWORD, LORA_MAC_SYNCWORD ); Radio.Sleep( ); hal_enableIRQs( ); } // return next random byte derived from seed buffer // (buf[0] holds index of next byte to be returned) u1_t radio_rand1( void ) { u1_t i = randbuf[0]; ASSERT( i != 0 ); if( i == 16 ) { os_aes( AES_ENC, randbuf, 16 ); // encrypt seed with any key i = 0; } u1_t v = randbuf[i++]; randbuf[0] = i; return v; } void os_radio( u1_t mode ) { hal_disableIRQs( ); switch( mode ) { case RADIO_RST: // put radio to sleep Radio.Sleep( ); break; case RADIO_TX: // transmit frame now //ASSERT( Radio.GetState( ) == IDLE ); Radio.SetChannel( LMIC.freq ); if( getSf( LMIC.rps ) == FSK ) { // FSK modem Radio.SetTxConfig( MODEM_FSK, LMIC.txpow, 25e3, 0, 50e3, 0, 5, false, true, 0, 0, false, 3e6 ); } else { // LoRa modem Radio.SetTxConfig( MODEM_LORA, LMIC.txpow, 0, getBw( LMIC.rps ), getSf( LMIC.rps ) + 6, getCr( LMIC.rps ) + 1, 8, getIh( LMIC.rps ) ? true : false, ( getNocrc(LMIC.rps) == 0 ) ? true : false, 0, 0, false, 3e6 ); } //starttx( ); // buf=LMIC.frame, len=LMIC.dataLen Radio.Send( LMIC.frame, LMIC.dataLen ); break; case RADIO_RX: // receive frame now (exactly at rxtime) //ASSERT( Radio.GetState( ) == IDLE ); Radio.SetChannel( LMIC.freq ); if( getSf( LMIC.rps ) == FSK ) { // FSK modem //Radio.SetRxConfig( MODEM_FSK, 50e3, 50e3, 0, 83.333e3, 5, 0, false, 0, true, 0, 0, false, false ); Radio.SetRxConfig( MODEM_FSK, 50e3, 50e3, 0, 83.333e3, 5, 0, false, 0, true, 0, 0, false, true ); } else { // LoRa modem Radio.SetRxConfig( MODEM_LORA, getBw( LMIC.rps ), getSf( LMIC.rps ) + 6, getCr( LMIC.rps ) + 1, 0, 8, LMIC.rxsyms, getIh( LMIC.rps ) ? true : false, getIh(LMIC.rps), ( getNocrc(LMIC.rps) == 0 ) ? true : false, 0, 0, true, false ); } // now instruct the radio to receive hal_waitUntil( LMIC.rxtime ); // busy wait until exact rx time //startrx( RXMODE_SINGLE ); // buf = LMIC.frame, time = LMIC.rxtime, timeout=LMIC.rxsyms if( getSf( LMIC.rps ) == FSK ) { // FSK modem Radio.Rx( 50e3 ); // Max Rx window 50 ms } else { // LoRa modem Radio.Rx( 3e6 ); // Max Rx window 3 seconds } break; case RADIO_RXON: // start scanning for beacon now //ASSERT( Radio.GetState( ) == IDLE ); Radio.SetChannel( LMIC.freq ); if( getSf( LMIC.rps ) == FSK ) { // FSK modem Radio.SetRxConfig( MODEM_FSK, 50e3, 50e3, 0, 83.333e3, 5, 0, false, 0, true, 0, 0, false, true ); } else { // LoRa modem Radio.SetRxConfig( MODEM_LORA, getBw( LMIC.rps ), getSf( LMIC.rps ) + 6, getCr( LMIC.rps ) + 1, 0, 8, LMIC.rxsyms, getIh( LMIC.rps ) ? true : false, getIh(LMIC.rps), ( getNocrc(LMIC.rps) == 0 ) ? true : false, 0, 0, true, true ); } //startrx( RXMODE_SCAN) ; // buf = LMIC.frame Radio.Rx( 0 ); break; } hal_enableIRQs( ); } #else // ---------------------------------------- // Registers Mapping #define RegFifo 0x00 // common #define RegOpMode 0x01 // common #define FSKRegBitrateMsb 0x02 #define FSKRegBitrateLsb 0x03 #define FSKRegFdevMsb 0x04 #define FSKRegFdevLsb 0x05 #define RegFrfMsb 0x06 // common #define RegFrfMid 0x07 // common #define RegFrfLsb 0x08 // common #define RegPaConfig 0x09 // common #define RegPaRamp 0x0A // common #define RegOcp 0x0B // common #define RegLna 0x0C // common #define FSKRegRxConfig 0x0D #define LORARegFifoAddrPtr 0x0D #define FSKRegRssiConfig 0x0E #define LORARegFifoTxBaseAddr 0x0E #define FSKRegRssiCollision 0x0F #define LORARegFifoRxBaseAddr 0x0F #define FSKRegRssiThresh 0x10 #define LORARegFifoRxCurrentAddr 0x10 #define FSKRegRssiValue 0x11 #define LORARegIrqFlagsMask 0x11 #define FSKRegRxBw 0x12 #define LORARegIrqFlags 0x12 #define FSKRegAfcBw 0x13 #define LORARegRxNbBytes 0x13 #define FSKRegOokPeak 0x14 #define LORARegRxHeaderCntValueMsb 0x14 #define FSKRegOokFix 0x15 #define LORARegRxHeaderCntValueLsb 0x15 #define FSKRegOokAvg 0x16 #define LORARegRxPacketCntValueMsb 0x16 #define LORARegRxpacketCntValueLsb 0x17 #define LORARegModemStat 0x18 #define LORARegPktSnrValue 0x19 #define FSKRegAfcFei 0x1A #define LORARegPktRssiValue 0x1A #define FSKRegAfcMsb 0x1B #define LORARegRssiValue 0x1B #define FSKRegAfcLsb 0x1C #define LORARegHopChannel 0x1C #define FSKRegFeiMsb 0x1D #define LORARegModemConfig1 0x1D #define FSKRegFeiLsb 0x1E #define LORARegModemConfig2 0x1E #define FSKRegPreambleDetect 0x1F #define LORARegSymbTimeoutLsb 0x1F #define FSKRegRxTimeout1 0x20 #define LORARegPreambleMsb 0x20 #define FSKRegRxTimeout2 0x21 #define LORARegPreambleLsb 0x21 #define FSKRegRxTimeout3 0x22 #define LORARegPayloadLength 0x22 #define FSKRegRxDelay 0x23 #define LORARegPayloadMaxLength 0x23 #define FSKRegOsc 0x24 #define LORARegHopPeriod 0x24 #define FSKRegPreambleMsb 0x25 #define LORARegFifoRxByteAddr 0x25 #define LORARegModemConfig3 0x26 #define FSKRegPreambleLsb 0x26 #define FSKRegSyncConfig 0x27 #define LORARegFeiMsb 0x28 #define FSKRegSyncValue1 0x28 #define LORAFeiMib 0x29 #define FSKRegSyncValue2 0x29 #define LORARegFeiLsb 0x2A #define FSKRegSyncValue3 0x2A #define FSKRegSyncValue4 0x2B #define LORARegRssiWideband 0x2C #define FSKRegSyncValue5 0x2C #define FSKRegSyncValue6 0x2D #define FSKRegSyncValue7 0x2E #define FSKRegSyncValue8 0x2F #define FSKRegPacketConfig1 0x30 #define FSKRegPacketConfig2 0x31 #define LORARegDetectOptimize 0x31 #define FSKRegPayloadLength 0x32 #define FSKRegNodeAdrs 0x33 #define LORARegInvertIQ 0x33 #define FSKRegBroadcastAdrs 0x34 #define FSKRegFifoThresh 0x35 #define FSKRegSeqConfig1 0x36 #define FSKRegSeqConfig2 0x37 #define LORARegDetectionThreshold 0x37 #define FSKRegTimerResol 0x38 #define FSKRegTimer1Coef 0x39 #define LORARegSyncWord 0x39 #define FSKRegTimer2Coef 0x3A #define FSKRegImageCal 0x3B #define FSKRegTemp 0x3C #define FSKRegLowBat 0x3D #define FSKRegIrqFlags1 0x3E #define FSKRegIrqFlags2 0x3F #define RegDioMapping1 0x40 // common #define RegDioMapping2 0x41 // common #define RegVersion 0x42 // common // #define RegAgcRef 0x43 // common // #define RegAgcThresh1 0x44 // common // #define RegAgcThresh2 0x45 // common // #define RegAgcThresh3 0x46 // common // #define RegPllHop 0x4B // common // #define RegTcxo 0x58 // common #define RegPaDac 0x5A // common // #define RegPll 0x5C // common // #define RegPllLowPn 0x5E // common // #define RegFormerTemp 0x6C // common // #define RegBitRateFrac 0x70 // common // ---------------------------------------- // spread factors and mode for RegModemConfig2 #define SX1272_MC2_FSK 0x00 #define SX1272_MC2_SF7 0x70 #define SX1272_MC2_SF8 0x80 #define SX1272_MC2_SF9 0x90 #define SX1272_MC2_SF10 0xA0 #define SX1272_MC2_SF11 0xB0 #define SX1272_MC2_SF12 0xC0 // bandwidth for RegModemConfig1 #define SX1272_MC1_BW_125 0x00 #define SX1272_MC1_BW_250 0x40 #define SX1272_MC1_BW_500 0x80 // coding rate for RegModemConfig1 #define SX1272_MC1_CR_4_5 0x08 #define SX1272_MC1_CR_4_6 0x10 #define SX1272_MC1_CR_4_7 0x18 #define SX1272_MC1_CR_4_8 0x20 #define SX1272_MC1_IMPLICIT_HEADER_MODE_ON 0x04 // required for receive #define SX1272_MC1_RX_PAYLOAD_CRCON 0x02 #define SX1272_MC1_LOW_DATA_RATE_OPTIMIZE 0x01 // mandated for SF11 and SF12 // transmit power configuration for RegPaConfig #define SX1272_PAC_PA_SELECT_PA_BOOST 0x80 #define SX1272_PAC_PA_SELECT_RFIO_PIN 0x00 // sx1276 RegModemConfig1 #define SX1276_MC1_BW_125 0x70 #define SX1276_MC1_BW_250 0x80 #define SX1276_MC1_BW_500 0x90 #define SX1276_MC1_CR_4_5 0x02 #define SX1276_MC1_CR_4_6 0x04 #define SX1276_MC1_CR_4_7 0x06 #define SX1276_MC1_CR_4_8 0x08 #define SX1276_MC1_IMPLICIT_HEADER_MODE_ON 0x01 // sx1276 RegModemConfig2 #define SX1276_MC2_RX_PAYLOAD_CRCON 0x04 // sx1276 RegModemConfig3 #define SX1276_MC3_LOW_DATA_RATE_OPTIMIZE 0x08 #define SX1276_MC3_AGCAUTO 0x04 // preamble for lora networks (nibbles swapped) #define LORA_MAC_PREAMBLE 0x34 #define RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG1 0x0A #ifdef CFG_sx1276_radio #define RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG2 0x70 #elif CFG_sx1272_radio #define RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG2 0x74 #endif // ---------------------------------------- // Constants for radio registers #define OPMODE_LORA 0x80 #define OPMODE_MASK 0x07 #define OPMODE_SLEEP 0x00 #define OPMODE_STANDBY 0x01 #define OPMODE_FSTX 0x02 #define OPMODE_TX 0x03 #define OPMODE_FSRX 0x04 #define OPMODE_RX 0x05 #define OPMODE_RX_SINGLE 0x06 #define OPMODE_CAD 0x07 // ---------------------------------------- // Bits masking the corresponding IRQs from the radio #define IRQ_LORA_RXTOUT_MASK 0x80 #define IRQ_LORA_RXDONE_MASK 0x40 #define IRQ_LORA_CRCERR_MASK 0x20 #define IRQ_LORA_HEADER_MASK 0x10 #define IRQ_LORA_TXDONE_MASK 0x08 #define IRQ_LORA_CDDONE_MASK 0x04 #define IRQ_LORA_FHSSCH_MASK 0x02 #define IRQ_LORA_CDDETD_MASK 0x01 #define IRQ_FSK1_MODEREADY_MASK 0x80 #define IRQ_FSK1_RXREADY_MASK 0x40 #define IRQ_FSK1_TXREADY_MASK 0x20 #define IRQ_FSK1_PLLLOCK_MASK 0x10 #define IRQ_FSK1_RSSI_MASK 0x08 #define IRQ_FSK1_TIMEOUT_MASK 0x04 #define IRQ_FSK1_PREAMBLEDETECT_MASK 0x02 #define IRQ_FSK1_SYNCADDRESSMATCH_MASK 0x01 #define IRQ_FSK2_FIFOFULL_MASK 0x80 #define IRQ_FSK2_FIFOEMPTY_MASK 0x40 #define IRQ_FSK2_FIFOLEVEL_MASK 0x20 #define IRQ_FSK2_FIFOOVERRUN_MASK 0x10 #define IRQ_FSK2_PACKETSENT_MASK 0x08 #define IRQ_FSK2_PAYLOADREADY_MASK 0x04 #define IRQ_FSK2_CRCOK_MASK 0x02 #define IRQ_FSK2_LOWBAT_MASK 0x01 // ---------------------------------------- // DIO function mappings D0D1D2D3 #define MAP_DIO0_LORA_RXDONE 0x00 // 00------ #define MAP_DIO0_LORA_TXDONE 0x40 // 01------ #define MAP_DIO1_LORA_RXTOUT 0x00 // --00---- #define MAP_DIO1_LORA_NOP 0x30 // --11---- #define MAP_DIO2_LORA_NOP 0xC0 // ----11-- #define MAP_DIO0_FSK_READY 0x00 // 00------ (packet sent / payload ready) #define MAP_DIO1_FSK_NOP 0x30 // --11---- #define MAP_DIO2_FSK_TXNOP 0x04 // ----01-- #define MAP_DIO2_FSK_TIMEOUT 0x08 // ----10-- // FSK IMAGECAL defines #define RF_IMAGECAL_AUTOIMAGECAL_MASK 0x7F #define RF_IMAGECAL_AUTOIMAGECAL_ON 0x80 #define RF_IMAGECAL_AUTOIMAGECAL_OFF 0x00 // Default #define RF_IMAGECAL_IMAGECAL_MASK 0xBF #define RF_IMAGECAL_IMAGECAL_START 0x40 #define RF_IMAGECAL_IMAGECAL_RUNNING 0x20 #define RF_IMAGECAL_IMAGECAL_DONE 0x00 // Default // RADIO STATE // (initialized by radio_init(), used by radio_rand1()) static u1_t randbuf[16]; #ifdef CFG_sx1276_radio #define LNA_RX_GAIN (0x20|0x1) #elif CFG_sx1272_radio #define LNA_RX_GAIN (0x20|0x03) #else #error Missing CFG_sx1272_radio/CFG_sx1276_radio #endif static void writeReg (u1_t addr, u1_t data ) { hal_pin_nss(0); hal_spi(addr | 0x80); hal_spi(data); hal_pin_nss(1); } static u1_t readReg (u1_t addr) { hal_pin_nss(0); hal_spi(addr & 0x7F); u1_t val = hal_spi(0x00); hal_pin_nss(1); return val; } static void writeBuf (u1_t addr, xref2u1_t buf, u1_t len) { hal_pin_nss(0); hal_spi(addr | 0x80); for (u1_t i=0; i<len; i++) { hal_spi(buf[i]); } hal_pin_nss(1); } static void readBuf (u1_t addr, xref2u1_t buf, u1_t len) { hal_pin_nss(0); hal_spi(addr & 0x7F); for (u1_t i=0; i<len; i++) { buf[i] = hal_spi(0x00); } hal_pin_nss(1); } static void opmode (u1_t mode) { writeReg(RegOpMode, (readReg(RegOpMode) & ~OPMODE_MASK) | mode); } static void opmodeLora(void) { u1_t u = OPMODE_LORA; #ifdef CFG_sx1276_radio u |= 0x8; // TBD: sx1276 high freq #endif writeReg(RegOpMode, u); } static void opmodeFSK(void) { u1_t u = 0; #ifdef CFG_sx1276_radio u |= 0x8; // TBD: sx1276 high freq #endif writeReg(RegOpMode, u); } // configure LoRa modem (cfg1, cfg2) static void configLoraModem (void) { sf_t sf = getSf(LMIC.rps); #ifdef CFG_sx1276_radio u1_t mc1 = 0, mc2 = 0, mc3 = 0; switch (getBw(LMIC.rps)) { case BW125: mc1 |= SX1276_MC1_BW_125; break; case BW250: mc1 |= SX1276_MC1_BW_250; break; case BW500: mc1 |= SX1276_MC1_BW_500; break; default: ASSERT(0); } switch( getCr(LMIC.rps) ) { case CR_4_5: mc1 |= SX1276_MC1_CR_4_5; break; case CR_4_6: mc1 |= SX1276_MC1_CR_4_6; break; case CR_4_7: mc1 |= SX1276_MC1_CR_4_7; break; case CR_4_8: mc1 |= SX1276_MC1_CR_4_8; break; default: ASSERT(0); } if (getIh(LMIC.rps)) { mc1 |= SX1276_MC1_IMPLICIT_HEADER_MODE_ON; writeReg(LORARegPayloadLength, getIh(LMIC.rps)); // required length } // set ModemConfig1 writeReg(LORARegModemConfig1, mc1); mc2 = (SX1272_MC2_SF7 + ((sf-1)<<4)); if (getNocrc(LMIC.rps) == 0) { mc2 |= SX1276_MC2_RX_PAYLOAD_CRCON; } writeReg(LORARegModemConfig2, mc2); mc3 = SX1276_MC3_AGCAUTO; if (sf == SF11 || sf == SF12) { mc3 |= SX1276_MC3_LOW_DATA_RATE_OPTIMIZE; } writeReg(LORARegModemConfig3, mc3); #elif CFG_sx1272_radio u1_t mc1 = (getBw(LMIC.rps)<<6); switch( getCr(LMIC.rps) ) { case CR_4_5: mc1 |= SX1272_MC1_CR_4_5; break; case CR_4_6: mc1 |= SX1272_MC1_CR_4_6; break; case CR_4_7: mc1 |= SX1272_MC1_CR_4_7; break; case CR_4_8: mc1 |= SX1272_MC1_CR_4_8; break; } if (sf == SF11 || sf == SF12) { mc1 |= SX1272_MC1_LOW_DATA_RATE_OPTIMIZE; } if (getNocrc(LMIC.rps) == 0) { mc1 |= SX1272_MC1_RX_PAYLOAD_CRCON; } if (getIh(LMIC.rps)) { mc1 |= SX1272_MC1_IMPLICIT_HEADER_MODE_ON; writeReg(LORARegPayloadLength, getIh(LMIC.rps)); // required length } // set ModemConfig1 writeReg(LORARegModemConfig1, mc1); // set ModemConfig2 (sf, AgcAutoOn=1 SymbTimeoutHi=00) writeReg(LORARegModemConfig2, (SX1272_MC2_SF7 + ((sf-1)<<4)) | 0x04); #else #error Missing CFG_sx1272_radio/CFG_sx1276_radio #endif /* CFG_sx1272_radio */ } static void configChannel (void) { // set frequency: FQ = (FRF * 32 Mhz) / (2 ^ 19) u8_t frf = ((u8_t)LMIC.freq << 19) / 32000000; writeReg(RegFrfMsb, (u1_t)(frf>>16)); writeReg(RegFrfMid, (u1_t)(frf>> 8)); writeReg(RegFrfLsb, (u1_t)(frf>> 0)); } static void configPower (void) { #ifdef CFG_sx1276_radio // no boost used for now s1_t pw = (s1_t)LMIC.txpow; if(pw > 14) { pw = 14; } else if(pw < -1) { pw = -1; } // check board type for BOOST pin writeReg(RegPaConfig, (u1_t)(0x00|((pw+1)&0xf))); writeReg(RegPaDac, readReg(RegPaDac)|0x4); #elif CFG_sx1272_radio // set PA config (2-17 dBm using PA_BOOST) s1_t pw = (s1_t)LMIC.txpow; if(pw > 17) { pw = 17; } else if(pw < 2) { pw = 2; } writeReg(RegPaConfig, (u1_t)(0x80|(pw-2))); #else #error Missing CFG_sx1272_radio/CFG_sx1276_radio #endif /* CFG_sx1272_radio */ } static void txfsk (void) { // select FSK modem (from sleep mode) writeReg(RegOpMode, 0x10); // FSK, BT=0.5 ASSERT(readReg(RegOpMode) == 0x10); // enter standby mode (required for FIFO loading)) opmode(OPMODE_STANDBY); // set bitrate writeReg(FSKRegBitrateMsb, 0x02); // 50kbps writeReg(FSKRegBitrateLsb, 0x80); // set frequency deviation writeReg(FSKRegFdevMsb, 0x01); // +/- 25kHz writeReg(FSKRegFdevLsb, 0x99); // frame and packet handler settings writeReg(FSKRegPreambleMsb, 0x00); writeReg(FSKRegPreambleLsb, 0x05); writeReg(FSKRegSyncConfig, 0x12); writeReg(FSKRegPacketConfig1, 0xD0); writeReg(FSKRegPacketConfig2, 0x40); writeReg(FSKRegSyncValue1, 0xC1); writeReg(FSKRegSyncValue2, 0x94); writeReg(FSKRegSyncValue3, 0xC1); // configure frequency configChannel(); // configure output power configPower(); // set the IRQ mapping DIO0=PacketSent DIO1=NOP DIO2=NOP writeReg(RegDioMapping1, MAP_DIO0_FSK_READY|MAP_DIO1_FSK_NOP|MAP_DIO2_FSK_TXNOP); // initialize the payload size and address pointers writeReg(FSKRegPayloadLength, LMIC.dataLen+1); // (insert length byte into payload)) // download length byte and buffer to the radio FIFO writeReg(RegFifo, LMIC.dataLen); writeBuf(RegFifo, LMIC.frame, LMIC.dataLen); // enable antenna switch for TX hal_pin_rxtx(1); // now we actually start the transmission opmode(OPMODE_TX); } static void txlora (void) { // select LoRa modem (from sleep mode) //writeReg(RegOpMode, OPMODE_LORA); opmodeLora(); ASSERT((readReg(RegOpMode) & OPMODE_LORA) != 0); // enter standby mode (required for FIFO loading)) opmode(OPMODE_STANDBY); // configure LoRa modem (cfg1, cfg2) configLoraModem(); // configure frequency configChannel(); // configure output power writeReg(RegPaRamp, (readReg(RegPaRamp) & 0xF0) | 0x08); // set PA ramp-up time 50 uSec configPower(); // set sync word writeReg(LORARegSyncWord, LORA_MAC_PREAMBLE); // set the IRQ mapping DIO0=TxDone DIO1=NOP DIO2=NOP writeReg(RegDioMapping1, MAP_DIO0_LORA_TXDONE|MAP_DIO1_LORA_NOP|MAP_DIO2_LORA_NOP); // clear all radio IRQ flags writeReg(LORARegIrqFlags, 0xFF); // mask all IRQs but TxDone writeReg(LORARegIrqFlagsMask, ~IRQ_LORA_TXDONE_MASK); // initialize the payload size and address pointers writeReg(LORARegFifoTxBaseAddr, 0x00); writeReg(LORARegFifoAddrPtr, 0x00); writeReg(LORARegPayloadLength, LMIC.dataLen); // download buffer to the radio FIFO writeBuf(RegFifo, LMIC.frame, LMIC.dataLen); // enable antenna switch for TX hal_pin_rxtx(1); // now we actually start the transmission opmode(OPMODE_TX); } // start transmitter (buf=LMIC.frame, len=LMIC.dataLen) static void starttx (void) { ASSERT( (readReg(RegOpMode) & OPMODE_MASK) == OPMODE_SLEEP ); if(getSf(LMIC.rps) == FSK) { // FSK modem txfsk(); } else { // LoRa modem txlora(); } // the radio will go back to STANDBY mode as soon as the TX is finished // the corresponding IRQ will inform us about completion. } enum { RXMODE_SINGLE, RXMODE_SCAN, RXMODE_RSSI }; static const u1_t rxlorairqmask[] = { [RXMODE_SINGLE] = IRQ_LORA_RXDONE_MASK|IRQ_LORA_RXTOUT_MASK, [RXMODE_SCAN] = IRQ_LORA_RXDONE_MASK, [RXMODE_RSSI] = 0x00, }; // start LoRa receiver (time=LMIC.rxtime, timeout=LMIC.rxsyms, result=LMIC.frame[LMIC.dataLen]) static void rxlora (u1_t rxmode) { // select LoRa modem (from sleep mode) opmodeLora(); ASSERT((readReg(RegOpMode) & OPMODE_LORA) != 0); // enter standby mode (warm up)) opmode(OPMODE_STANDBY); // don't use MAC settings at startup if(rxmode == RXMODE_RSSI) { // use fixed settings for rssi scan writeReg(LORARegModemConfig1, RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG1); writeReg(LORARegModemConfig2, RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG2); } else { // single or continuous rx mode // configure LoRa modem (cfg1, cfg2) configLoraModem(); // configure frequency configChannel(); } // set LNA gain writeReg(RegLna, LNA_RX_GAIN); // set max payload size writeReg(LORARegPayloadMaxLength, 64); // use inverted I/Q signal (prevent mote-to-mote communication) writeReg(LORARegInvertIQ, readReg(LORARegInvertIQ)|(1<<6)); // set symbol timeout (for single rx) writeReg(LORARegSymbTimeoutLsb, LMIC.rxsyms); // set sync word writeReg(LORARegSyncWord, LORA_MAC_PREAMBLE); // configure DIO mapping DIO0=RxDone DIO1=RxTout DIO2=NOP writeReg(RegDioMapping1, MAP_DIO0_LORA_RXDONE|MAP_DIO1_LORA_RXTOUT|MAP_DIO2_LORA_NOP); // clear all radio IRQ flags writeReg(LORARegIrqFlags, 0xFF); // enable required radio IRQs writeReg(LORARegIrqFlagsMask, ~rxlorairqmask[rxmode]); // enable antenna switch for RX hal_pin_rxtx(0); // now instruct the radio to receive if (rxmode == RXMODE_SINGLE) { // single rx hal_waitUntil(LMIC.rxtime); // busy wait until exact rx time opmode(OPMODE_RX_SINGLE); } else { // continous rx (scan or rssi) opmode(OPMODE_RX); } } static void rxfsk (u1_t rxmode) { // only single rx (no continuous scanning, no noise sampling) ASSERT( rxmode == RXMODE_SINGLE ); // select FSK modem (from sleep mode) //writeReg(RegOpMode, 0x00); // (not LoRa) opmodeFSK(); ASSERT((readReg(RegOpMode) & OPMODE_LORA) == 0); // enter standby mode (warm up)) opmode(OPMODE_STANDBY); // configure frequency configChannel(); // set LNA gain //writeReg(RegLna, 0x20|0x03); // max gain, boost enable writeReg(RegLna, LNA_RX_GAIN); // configure receiver writeReg(FSKRegRxConfig, 0x1E); // AFC auto, AGC, trigger on preamble?!? // set receiver bandwidth writeReg(FSKRegRxBw, 0x0B); // 50kHz SSb // set AFC bandwidth writeReg(FSKRegAfcBw, 0x12); // 83.3kHz SSB // set preamble detection writeReg(FSKRegPreambleDetect, 0xAA); // enable, 2 bytes, 10 chip errors // set sync config writeReg(FSKRegSyncConfig, 0x12); // no auto restart, preamble 0xAA, enable, fill FIFO, 3 bytes sync // set packet config writeReg(FSKRegPacketConfig1, 0xD8); // var-length, whitening, crc, no auto-clear, no adr filter writeReg(FSKRegPacketConfig2, 0x40); // packet mode // set sync value writeReg(FSKRegSyncValue1, 0xC1); writeReg(FSKRegSyncValue2, 0x94); writeReg(FSKRegSyncValue3, 0xC1); // set preamble timeout writeReg(FSKRegRxTimeout2, 0xFF);//(LMIC.rxsyms+1)/2); // set bitrate writeReg(FSKRegBitrateMsb, 0x02); // 50kbps writeReg(FSKRegBitrateLsb, 0x80); // set frequency deviation writeReg(FSKRegFdevMsb, 0x01); // +/- 25kHz writeReg(FSKRegFdevLsb, 0x99); // configure DIO mapping DIO0=PayloadReady DIO1=NOP DIO2=TimeOut writeReg(RegDioMapping1, MAP_DIO0_FSK_READY|MAP_DIO1_FSK_NOP|MAP_DIO2_FSK_TIMEOUT); // enable antenna switch for RX hal_pin_rxtx(0); // now instruct the radio to receive hal_waitUntil(LMIC.rxtime); // busy wait until exact rx time opmode(OPMODE_RX); // no single rx mode available in FSK } static void startrx (u1_t rxmode) { ASSERT( (readReg(RegOpMode) & OPMODE_MASK) == OPMODE_SLEEP ); if(getSf(LMIC.rps) == FSK) { // FSK modem rxfsk(rxmode); } else { // LoRa modem rxlora(rxmode); } // the radio will go back to STANDBY mode as soon as the RX is finished // or timed out, and the corresponding IRQ will inform us about completion. } // get random seed from wideband noise rssi void radio_init (void) { hal_disableIRQs(); // manually reset radio #ifdef CFG_sx1276_radio hal_pin_rst(0); // drive RST pin low #else hal_pin_rst(1); // drive RST pin high #endif hal_waitUntil(os_getTime()+ms2osticks(1)); // wait >100us hal_pin_rst(2); // configure RST pin floating! hal_waitUntil(os_getTime()+ms2osticks(5)); // wait 5ms opmode(OPMODE_SLEEP); // some sanity checks, e.g., read version number u1_t v = readReg(RegVersion); #ifdef CFG_sx1276_radio ASSERT(v == 0x12 ); #elif CFG_sx1272_radio ASSERT(v == 0x22); #else #error Missing CFG_sx1272_radio/CFG_sx1276_radio #endif // seed 15-byte randomness via noise rssi rxlora(RXMODE_RSSI); while( (readReg(RegOpMode) & OPMODE_MASK) != OPMODE_RX ); // continuous rx for(int i=1; i<16; i++) { for(int j=0; j<8; j++) { u1_t b; // wait for two non-identical subsequent least-significant bits while( (b = readReg(LORARegRssiWideband) & 0x01) == (readReg(LORARegRssiWideband) & 0x01) ); randbuf[i] = (randbuf[i] << 1) | b; } } randbuf[0] = 16; // set initial index #ifdef CFG_sx1276_radio // chain calibration writeReg(RegPaConfig, 0); // Launch Rx chain calibration for LF band writeReg(FSKRegImageCal, (readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_MASK)|RF_IMAGECAL_IMAGECAL_START); while((readReg(FSKRegImageCal)&RF_IMAGECAL_IMAGECAL_RUNNING) == RF_IMAGECAL_IMAGECAL_RUNNING){ ; } // Sets a Frequency in HF band u4_t frf = 868000000; writeReg(RegFrfMsb, (u1_t)(frf>>16)); writeReg(RegFrfMid, (u1_t)(frf>> 8)); writeReg(RegFrfLsb, (u1_t)(frf>> 0)); // Launch Rx chain calibration for HF band writeReg(FSKRegImageCal, (readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_MASK)|RF_IMAGECAL_IMAGECAL_START); while((readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_RUNNING) == RF_IMAGECAL_IMAGECAL_RUNNING) { ; } #endif /* CFG_sx1276_radio */ opmode(OPMODE_SLEEP); hal_enableIRQs(); } // return next random byte derived from seed buffer // (buf[0] holds index of next byte to be returned) u1_t radio_rand1 (void) { u1_t i = randbuf[0]; ASSERT( i != 0 ); if( i==16 ) { os_aes(AES_ENC, randbuf, 16); // encrypt seed with any key i = 0; } u1_t v = randbuf[i++]; randbuf[0] = i; return v; } u1_t radio_rssi (void) { hal_disableIRQs(); u1_t r = readReg(LORARegRssiValue); hal_enableIRQs(); return r; } static const u2_t LORA_RXDONE_FIXUP[] = { [FSK] = us2osticks(0), // ( 0 ticks) [SF7] = us2osticks(0), // ( 0 ticks) [SF8] = us2osticks(1648), // ( 54 ticks) [SF9] = us2osticks(3265), // ( 107 ticks) [SF10] = us2osticks(7049), // ( 231 ticks) [SF11] = us2osticks(13641), // ( 447 ticks) [SF12] = us2osticks(31189), // (1022 ticks) }; // called by hal ext IRQ handler // (radio goes to stanby mode after tx/rx operations) void radio_irq_handler (u1_t dio) { ostime_t now = os_getTime(); if( (readReg(RegOpMode) & OPMODE_LORA) != 0) { // LORA modem u1_t flags = readReg(LORARegIrqFlags); if( flags & IRQ_LORA_TXDONE_MASK ) { // save exact tx time LMIC.txend = now - us2osticks(43); // TXDONE FIXUP } else if( flags & IRQ_LORA_RXDONE_MASK ) { // save exact rx time LMIC.rxtime = now - LORA_RXDONE_FIXUP[getSf(LMIC.rps)]; // read the PDU and inform the MAC that we received something LMIC.dataLen = (readReg(LORARegModemConfig1) & SX1272_MC1_IMPLICIT_HEADER_MODE_ON) ? readReg(LORARegPayloadLength) : readReg(LORARegRxNbBytes); // set FIFO read address pointer writeReg(LORARegFifoAddrPtr, readReg(LORARegFifoRxCurrentAddr)); // now read the FIFO readBuf(RegFifo, LMIC.frame, LMIC.dataLen); // read rx quality parameters LMIC.rxq.snr = readReg(LORARegPktSnrValue); // SNR [dB] * 4 LMIC.rxq.rssi = readReg(LORARegPktRssiValue) - 125 + 64; // RSSI [dBm] (-196...+63) } else if( flags & IRQ_LORA_RXTOUT_MASK ) { // indicate timeout LMIC.dataLen = 0; } // mask all radio IRQs writeReg(LORARegIrqFlagsMask, 0xFF); // clear radio IRQ flags writeReg(LORARegIrqFlags, 0xFF); } else { // FSK modem u1_t flags1 = readReg(FSKRegIrqFlags1); u1_t flags2 = readReg(FSKRegIrqFlags2); if( flags2 & IRQ_FSK2_PACKETSENT_MASK ) { // save exact tx time LMIC.txend = now; } else if( flags2 & IRQ_FSK2_PAYLOADREADY_MASK ) { // save exact rx time LMIC.rxtime = now; // read the PDU and inform the MAC that we received something LMIC.dataLen = readReg(FSKRegPayloadLength); // now read the FIFO readBuf(RegFifo, LMIC.frame, LMIC.dataLen); // read rx quality parameters LMIC.rxq.snr = 0; // determine snr LMIC.rxq.rssi = 0; // determine rssi } else if( flags1 & IRQ_FSK1_TIMEOUT_MASK ) { // indicate timeout LMIC.dataLen = 0; } else { while(1); } } // go from stanby to sleep opmode(OPMODE_SLEEP); // run os job (use preset func ptr) os_setCallback(&LMIC.osjob, LMIC.osjob.func); } void os_radio (u1_t mode) { hal_disableIRQs(); switch (mode) { case RADIO_RST: // put radio to sleep opmode(OPMODE_SLEEP); break; case RADIO_TX: // transmit frame now starttx(); // buf=LMIC.frame, len=LMIC.dataLen break; case RADIO_RX: // receive frame now (exactly at rxtime) startrx(RXMODE_SINGLE); // buf=LMIC.frame, time=LMIC.rxtime, timeout=LMIC.rxsyms break; case RADIO_RXON: // start scanning for beacon now startrx(RXMODE_SCAN); // buf=LMIC.frame break; } hal_enableIRQs(); } #endif // USE_SMTC_RADIO_DRIVER