Hardware Abstraction Layer, permitting any LoRa application to use any LoRa radio chip
Dependents: alarm_slave alarm_master lora_p2p lorawan1v1 ... more
radio chip selection
Radio chip driver is not included, allowing choice of radio device.
If you're using SX1272 or SX1276, then import sx127x driver into your program.
if you're using SX1261 or SX1262, then import sx126x driver into your program.
if you're using SX1280, then import sx1280 driver into your program.
if you're using LR1110, then import LR1110 driver into your program.
If you're using NAmote72 or Murata discovery, then you must import only sx127x driver.
If you're using Type1SJ select target DISCO_L072CZ_LRWAN1
and import sx126x driver into your program.
Pin assigned to arduino LoRa radio shield form-factor
- SX1272 shield.
- SX1276 shield.
- SX126x kit with color touch screen, or sx126x radio-only shield.
- Exceptions with their own pin assignments: NAMote-72 and Murata discovery board.
- TODO: pin assign XDOT.
radio_sx127x.cpp
- Committer:
- Wayne Roberts
- Date:
- 2020-06-25
- Revision:
- 19:94b5382d3fc6
- Parent:
- 17:5f34cbe2ac53
- Child:
- 20:75635d50262e
File content as of revision 19:94b5382d3fc6:
#include "radio.h" #ifdef SX127x_H #ifdef DEVICE_LPTICKER LowPowerTimer Radio::lpt; LowPowerTimeout TxTimeoutEvent; #else Timer Radio::lpt; Timeout TxTimeoutEvent; #endif void Radio::Sleep() { radio.set_opmode(RF_OPMODE_SLEEP); } void Radio::Standby() { radio.set_opmode(RF_OPMODE_STANDBY); } bool Radio::CheckRfFrequency(unsigned hz) { return true; } void Radio::SetChannel(unsigned hz) { radio.set_frf_MHz(hz / 1000000.0); } float Radio::getFrfMHz() { return radio.get_frf_MHz(); } void SX1272OnTimeoutIrq( void ) { Radio::radio.set_opmode(RF_OPMODE_STANDBY); } void Radio::SetTxContinuousWave(unsigned hz, int8_t dbm, unsigned timeout_us) { fsk.enable(true); fsk.RegPktConfig2.word = radio.read_u16(REG_FSK_PACKETCONFIG2); fsk.RegPktConfig2.bits.DataModePacket = 0; // continuous mode radio.write_u16(REG_FSK_PACKETCONFIG2, fsk.RegPktConfig2.word); fsk.set_tx_fdev_hz(0); // unmodulated carrier, aka dead carrier SetChannel(hz); set_tx_dbm(dbm); if (timeout_us != 0) TxTimeoutEvent.attach_us(SX1272OnTimeoutIrq, timeout_us); radio.set_opmode(RF_OPMODE_TRANSMITTER); } #define LORA_MAC_PRIVATE_SYNCWORD 0x12 #define LORA_MAC_PUBLIC_SYNCWORD 0x34 void Radio::SetPublicNetwork(bool en) { radio.write_reg(REG_LR_SYNC_BYTE, en ? LORA_MAC_PUBLIC_SYNCWORD : LORA_MAC_PRIVATE_SYNCWORD); } uint32_t Radio::Random(void) { uint32_t ret = 0; unsigned i; radio.set_opmode(RF_OPMODE_RECEIVER); for (i = 0; i < 32; i++) { uint32_t r; wait_us(3000); r = radio.read_reg(REG_LR_WIDEBAND_RSSI); r <<= ((i & 7) << 2); ret ^= r; } return ret; } void Radio::LoRaPacketConfig(unsigned preambleLen, bool fixLen, bool crcOn, bool invIQ) { lora.RegPreamble = preambleLen; radio.write_u16(REG_LR_PREAMBLEMSB, lora.RegPreamble); if (radio.type == SX1276) { lora.RegModemConfig.sx1276bits.ImplicitHeaderModeOn = fixLen; lora.RegModemConfig2.sx1276bits.RxPayloadCrcOn = crcOn; radio.write_reg(REG_LR_MODEMCONFIG2, lora.RegModemConfig2.octet); } else if (radio.type == SX1272) { lora.RegModemConfig.sx1272bits.ImplicitHeaderModeOn = fixLen; lora.RegModemConfig.sx1272bits.RxPayloadCrcOn = crcOn; } radio.write_reg(REG_LR_MODEMCONFIG, lora.RegModemConfig.octet); lora.invert_tx(invIQ); lora.invert_rx(invIQ); } void Radio::GFSKModemConfig(unsigned bps, unsigned bw_hz, unsigned fdev_hz) { if (radio.RegOpMode.bits.LongRangeMode) fsk.enable(false); fsk.set_bitrate(bps); fsk.set_rx_dcc_bw_hz(bw_hz, 0); fsk.set_rx_dcc_bw_hz(bw_hz * 1.5, 1); fsk.set_tx_fdev_hz(fdev_hz); } void Radio::GFSKPacketConfig(unsigned preambleLen, bool fixLen, bool crcOn) { if (radio.RegOpMode.bits.LongRangeMode) fsk.enable(false); radio.write_u16(REG_FSK_PREAMBLEMSB, preambleLen); fsk.RegPktConfig1.bits.PacketFormatVariable = fixLen ? 0 : 1; fsk.RegPktConfig1.bits.CrcOn = crcOn; radio.write_reg(REG_FSK_PACKETCONFIG1, fsk.RegPktConfig1.octet); } void Radio::SetLoRaSymbolTimeout(uint16_t symbs) { if (!radio.RegOpMode.bits.LongRangeMode) lora.enable(); radio.write_reg(REG_LR_SYMBTIMEOUTLSB, symbs & 0xff); symbs >>= 8; lora.RegModemConfig2.sx1272bits.SymbTimeoutMsb = symbs; radio.write_reg(REG_LR_MODEMCONFIG2, lora.RegModemConfig2.octet); } void Radio::LoRaModemConfig(unsigned bwKHz, uint8_t sf, uint8_t coderate) { float sp; if (!radio.RegOpMode.bits.LongRangeMode) lora.enable(); lora.RegModemConfig2.sx1276bits.SpreadingFactor = sf; radio.write_reg(REG_LR_MODEMCONFIG2, lora.RegModemConfig2.octet); lora.setBw_KHz(bwKHz); if (radio.type == SX1276) { lora.RegModemConfig.sx1276bits.CodingRate = coderate; sp = lora.get_symbol_period(); if (sp > 16) lora.RegModemConfig3.sx1276bits.LowDataRateOptimize = 1; else lora.RegModemConfig3.sx1276bits.LowDataRateOptimize = 0; radio.write_reg(REG_LR_MODEMCONFIG3, lora.RegModemConfig3.octet); } else if (radio.type == SX1272) { lora.RegModemConfig.sx1272bits.CodingRate = coderate; if (lora.get_symbol_period() > 16) lora.RegModemConfig.sx1272bits.LowDataRateOptimize = 1; else lora.RegModemConfig.sx1272bits.LowDataRateOptimize = 0; } radio.write_reg(REG_LR_MODEMCONFIG, lora.RegModemConfig.octet); } void Radio::SetRxMaxPayloadLength(uint8_t max) { if (radio.RegOpMode.bits.LongRangeMode) radio.write_reg(REG_LR_RX_MAX_PAYLOADLENGTH, max); else radio.write_reg(REG_FSK_PAYLOADLENGTH, max); } void Radio::SetFixedPayloadLength(uint8_t len) { lora.RegPayloadLength = len; radio.write_reg(REG_LR_PAYLOADLENGTH, lora.RegPayloadLength); } const RadioEvents_t* RadioEvents; volatile struct pe { uint8_t dio0 : 1; uint8_t dio1 : 1; uint8_t txing : 1; } pinEvent; void Radio::dio0UserContext() { service_action_e act = lora.service(); if (!pinEvent.txing) { if (act == SERVICE_READ_FIFO && RadioEvents->RxDone) { int8_t rssi; float snr = lora.RegPktSnrValue / 4.0; rssi = lora.get_pkt_rssi(); if (snr < 0) rssi += snr; RadioEvents->RxDone(lora.RegRxNbBytes, rssi, snr); } } else if (act == SERVICE_TX_DONE) { if (RadioEvents->TxDone_botHalf) RadioEvents->TxDone_botHalf(); } } volatile us_timestamp_t Radio::irqAt; void Radio::dio0isr() { irqAt = lpt.read_us(); if (RadioEvents->DioPin_top_half) RadioEvents->DioPin_top_half(); if (pinEvent.txing) { /* TxDone handling requires low latency */ if (RadioEvents->TxDone_topHalf) RadioEvents->TxDone_topHalf(); // TODO in callback read irqAt for timestamp of interrupt } pinEvent.dio0 = 1; } void Radio::dio1UserContext() { lora.RegIrqFlags.octet = radio.read_reg(REG_LR_IRQFLAGS); if (RadioEvents->RxTimeout) RadioEvents->RxTimeout(); radio.write_reg(REG_LR_IRQFLAGS, 0x80); // ensure RxTimeout is cleared } void Radio::dio1isr() { pinEvent.dio1 = 1; if (RadioEvents->DioPin_top_half) RadioEvents->DioPin_top_half(); } void Radio::Init(const RadioEvents_t* e, unsigned spi_hz) { radio.m_spi.frequency(spi_hz); while (radio.dio0.read() || radio.dio1.read()) { radio.write_reg(REG_LR_IRQFLAGS, 0xff); // clear stagnant interrupt } dio0.rise(dio0isr); dio1.rise(dio1isr); radio.rf_switch = rfsw_callback; boardInit(); RadioEvents = e; lpt.start(); } float Radio::GetRssiInst() { return lora.get_current_rssi(); } int Radio::Send(uint8_t size, timestamp_t maxListenTime, timestamp_t channelFreeTime, int rssiThresh) { if (radio.RegOpMode.bits.Mode == RF_OPMODE_SLEEP) { radio.set_opmode(RF_OPMODE_STANDBY); wait_us(1000); } radio.write_reg(REG_LR_IRQFLAGS, 0x08); // ensure TxDone is cleared lora.RegPayloadLength = size; radio.write_reg(REG_LR_PAYLOADLENGTH, lora.RegPayloadLength); if (maxListenTime > 0) { int rssi; us_timestamp_t startAt, chFreeAt, now; lora.start_rx(RF_OPMODE_RECEIVER); startAt = lpt.read_us(); Lstart: do { now = lpt.read_us(); if ((now - startAt) > maxListenTime) { return -1; } rssi = lora.get_current_rssi(); } while (rssi > rssiThresh); chFreeAt = lpt.read_us(); do { now = lpt.read_us(); rssi = lora.get_current_rssi(); if (rssi > rssiThresh) { goto Lstart; } } while ((now - chFreeAt) < channelFreeTime); } lora.start_tx(size); pinEvent.txing = 1; return 0; } void Radio::Rx(unsigned timeout) { if (timeout == 0) { lora.start_rx(RF_OPMODE_RECEIVER); } else { lora.start_rx(RF_OPMODE_RECEIVER_SINGLE); } pinEvent.txing = 0; } void Radio::ocp(uint8_t ma) { if (ma < 130) radio.RegOcp.bits.OcpTrim = (ma - 45) / 5; else radio.RegOcp.bits.OcpTrim = (ma + 30) / 10; radio.write_reg(REG_OCP, radio.RegOcp.octet); radio.RegOcp.octet = radio.read_reg(REG_OCP); if (radio.RegOcp.bits.OcpTrim < 16) ma = 45 + (5 * radio.RegOcp.bits.OcpTrim); else if (radio.RegOcp.bits.OcpTrim < 28) ma = (10 * radio.RegOcp.bits.OcpTrim) - 30; else ma = 240; } #if 0 void Radio::PrintStatus() { #ifdef MAC_DEBUG radio.RegOpMode.octet = radio.read_reg(REG_OPMODE); switch (radio.RegOpMode.bits.Mode) { case RF_OPMODE_SLEEP: printf("SLEEP "); break; case RF_OPMODE_STANDBY: printf("STBY "); break; case RF_OPMODE_SYNTHESIZER_TX: printf("FSTX "); break; case RF_OPMODE_TRANSMITTER: printf("TX "); break; case RF_OPMODE_SYNTHESIZER_RX: printf("FSRX "); break; case RF_OPMODE_RECEIVER: printf("RXC "); break; case RF_OPMODE_RECEIVER_SINGLE: printf("RXS "); break; case RF_OPMODE_CAD: printf("CAD "); break; } printf("dio:%u:%u opmode:%02x %.2fMHz sf%ubw%u ", radio.dio0.read(), radio.dio1.read(), radio.RegOpMode.octet, radio.get_frf_MHz(), lora.getSf(), lora.getBw()); lora.RegIrqFlags.octet = radio.read_reg(REG_LR_IRQFLAGS); printf("irqFlags:%02x\r\n", lora.RegIrqFlags.octet); #endif /* MAC_DEBUG */ } #endif /* if 0 */ #ifdef DUTY_ENABLE us_timestamp_t Radio::TimeOnAir(RadioModems_t m, uint8_t pktLen) { uint32_t airTime = 0; switch (m) { case MODEM_FSK: { /* TODO airTime = round( ( 8 * ( SX1272.Settings.Fsk.PreambleLen + ( ( SX1272Read( REG_SYNCCONFIG ) & ~RF_SYNCCONFIG_SYNCSIZE_MASK ) + 1 ) + ( ( SX1272.Settings.Fsk.FixLen == 0x01 ) ? 0.0 : 1.0 ) + ( ( ( SX1272Read( REG_PACKETCONFIG1 ) & ~RF_PACKETCONFIG1_ADDRSFILTERING_MASK ) != 0x00 ) ? 1.0 : 0 ) + pktLen + ( ( SX1272.Settings.Fsk.CrcOn == 0x01 ) ? 2.0 : 0 ) ) / SX1272.Settings.Fsk.Datarate ) * 1e3 ); */ } break; case MODEM_LORA: { double bw = 0.0; uint8_t fixLen, bandwidth, LowDatarateOptimize, coderate, crcOn ; if (radio.type == SX1276) { coderate = lora.RegModemConfig.sx1276bits.CodingRate; LowDatarateOptimize = lora.RegModemConfig3.sx1276bits.LowDataRateOptimize; fixLen = lora.RegModemConfig.sx1276bits.ImplicitHeaderModeOn; bandwidth = lora.RegModemConfig.sx1276bits.Bw - 7; crcOn = lora.RegModemConfig2.sx1276bits.RxPayloadCrcOn; } else if (radio.type == SX1272) { coderate = lora.RegModemConfig.sx1272bits.CodingRate; LowDatarateOptimize = lora.RegModemConfig.sx1272bits.LowDataRateOptimize; fixLen = lora.RegModemConfig.sx1272bits.ImplicitHeaderModeOn; bandwidth = lora.RegModemConfig.sx1272bits.Bw; crcOn = lora.RegModemConfig.sx1272bits.RxPayloadCrcOn; } else return 0; switch( bandwidth ) { case 0: // 125 kHz bw = 125;//ms: 125e3; break; case 1: // 250 kHz bw = 250;//ms:250e3; break; case 2: // 500 kHz bw = 500;//ms:500e3; break; } // Symbol rate : time for one symbol (secs) double rs = bw / ( 1 << lora.RegModemConfig2.sx1276bits.SpreadingFactor ); double ts = 1 / rs; // time of preamble double tPreamble; tPreamble = (lora.RegPreamble + 4.25 ) * ts; // Symbol length of payload and time double tmp = ceil( ( 8 * pktLen - 4 * lora.RegModemConfig2.sx1276bits.SpreadingFactor + 28 + 16 * crcOn - ( fixLen ? 20 : 0 ) ) / ( double )( 4 * ( lora.RegModemConfig2.sx1276bits.SpreadingFactor - ( ( LowDatarateOptimize > 0 ) ? 2 : 0 ) ) ) ) * ( coderate + 4 ); double nPayload = 8 + ( ( tmp > 0 ) ? tmp : 0 ); double tPayload = nPayload * ts; // Time on air double tOnAir = tPreamble + tPayload; // return ms secs airTime = floor( tOnAir * 1e3 + 0.999 ); } break; } return airTime; } #endif /* DUTY_ENABLE */ void Radio::service() { if (pinEvent.dio0) { dio0UserContext(); pinEvent.txing = 0; pinEvent.dio0 = 0; } else if (radio.dio0.read()) { /* fail: missed interrupt */ dio0isr(); } if (pinEvent.dio1) { dio1UserContext(); pinEvent.dio1 = 0; } } uint32_t Radio::lora_toa_us( uint8_t pktLen ) { uint32_t airTime = 0; uint8_t chipBW = Radio::lora.getBw(); double bwKHz; uint8_t LowDataRateOptimize; bool FixLen; uint8_t crcOn; uint16_t preambleLen = Radio::radio.read_u16(REG_LR_PREAMBLEMSB); Radio::lora.RegModemConfig2.octet = Radio::radio.read_reg(REG_LR_MODEMCONFIG2); Radio::lora.RegModemConfig.octet = Radio::radio.read_reg(REG_LR_MODEMCONFIG); if (Radio::radio.type == SX1276) { chipBW -= 7; Radio::lora.RegModemConfig3.octet = Radio::radio.read_reg(REG_LR_MODEMCONFIG3); LowDataRateOptimize = Radio::lora.RegModemConfig3.sx1276bits.LowDataRateOptimize; FixLen = Radio::lora.RegModemConfig.sx1276bits.ImplicitHeaderModeOn; crcOn = Radio::lora.RegModemConfig2.sx1276bits.RxPayloadCrcOn; } else if (Radio::radio.type == SX1272) { LowDataRateOptimize = Radio::lora.RegModemConfig.sx1272bits.LowDataRateOptimize; FixLen = Radio::lora.RegModemConfig.sx1272bits.ImplicitHeaderModeOn; crcOn = Radio::lora.RegModemConfig.sx1272bits.RxPayloadCrcOn; } else return 0; switch (chipBW) { case 0: bwKHz = 125; break; case 1: bwKHz = 250; break; case 2: bwKHz = 500; break; default: return 0; } // Symbol rate : time for one symbol (secs) double rs = bwKHz / ( 1 << Radio::lora.RegModemConfig2.sx1276bits.SpreadingFactor ); double ts = 1 / rs; // time of preamble double tPreamble = ( preambleLen + 4.25 ) * ts; // Symbol length of payload and time double tmp = ceil( ( 8 * pktLen - 4 * Radio::lora.RegModemConfig2.sx1276bits.SpreadingFactor + 28 + 16 * crcOn - ( FixLen ? 20 : 0 ) ) / ( double )( 4 * ( Radio::lora.RegModemConfig2.sx1276bits.SpreadingFactor - ( ( LowDataRateOptimize > 0 ) ? 2 : 0 ) ) ) ) * ( Radio::lora.getCodingRate(false) + 4 ); double nPayload = 8 + ( ( tmp > 0 ) ? tmp : 0 ); double tPayload = nPayload * ts; // Time on air double tOnAir = tPreamble + tPayload; // return microseconds airTime = floor( tOnAir * 1000 + 0.999 ); return airTime; } #endif /* ..SX127x_H */