diff --git a/software/firmware/source/SoftRF/src/driver/RF.cpp b/software/firmware/source/SoftRF/src/driver/RF.cpp index f5909d970..12a7242ed 100644 --- a/software/firmware/source/SoftRF/src/driver/RF.cpp +++ b/software/firmware/source/SoftRF/src/driver/RF.cpp @@ -116,6 +116,13 @@ static bool sa8x8_receive(void); static bool sa8x8_transmit(void); static void sa8x8_shutdown(void); +static bool lr112x_probe(void); +static void lr112x_setup(void); +static void lr112x_channel(int8_t); +static bool lr112x_receive(void); +static bool lr112x_transmit(void); +static void lr112x_shutdown(void); + #if !defined(EXCLUDE_NRF905) const rfchip_ops_t nrf905_ops = { RF_IC_NRF905, @@ -211,6 +218,19 @@ const rfchip_ops_t sa8x8_ops = { }; #endif /* USE_SA8X8 */ +#if defined(USE_RADIOLIB) +const rfchip_ops_t lr112x_ops = { + RF_IC_LR112X, + "LR112x", + lr112x_probe, + lr112x_setup, + lr112x_channel, + lr112x_receive, + lr112x_transmit, + lr112x_shutdown +}; +#endif /* USE_RADIOLIB */ + String Bin2Hex(byte *buffer, size_t size) { String str = ""; @@ -267,6 +287,10 @@ byte RF_setup(void) } else if (cc13xx_ops.probe()) { rf_chip = &cc13xx_ops; #endif /* EXCLUDE_CC13XX */ +#if defined(USE_RADIOLIB) + } else if (lr112x_ops.probe()) { + rf_chip = &lr112x_ops; +#endif /* USE_RADIOLIB */ #if defined(USE_SA8X8) } else if (sa8x8_ops.probe()) { rf_chip = &sa8x8_ops; @@ -2788,3 +2812,555 @@ static void sa8x8_shutdown() } #endif /* USE_SA8X8 */ + +#if defined(USE_RADIOLIB) + +#include + +#define USE_SX1262 1 +#define USE_LR1121 0 + +#if USE_SX1262 +#define RADIO_TYPE SX1262 +#elif USE_LR1121 +#define RADIO_TYPE LR1121 +#endif + +RADIO_TYPE radio = new Module(lmic_pins.nss, + lmic_pins.dio[0], + lmic_pins.rst, + lmic_pins.busy); + +const rf_proto_desc_t *rl_protocol = &ogntp_proto_desc; + +static int8_t lr112x_channel_prev = (int8_t) -1; + +static bool lr112x_receive_complete = false; +static bool lr112x_receive_active = false; +static bool lr112x_transmit_complete = false; + +#if USE_SX1262 && !defined(USE_BASICMAC) + +#define CMD_READREGISTER 0x1D +#define REG_LORASYNCWORDLSB 0x0741 +#define SX126X_DEF_LORASYNCWORDLSB 0x24 + +#define RADIOLIB_MAX_DATA_LENGTH 128 + + +typedef struct +{ + uint8_t dstAddr[8]; //!< Destination address + uint32_t absTime; //!< Absolute time to Tx packet (0 for immediate) + //!< Layer will use last SeqNum used + 1 + uint8_t len; //!< Payload Length + uint8_t payload[RADIOLIB_MAX_DATA_LENGTH]; //!< Payload +} RadioLib_TxPacket; + +RadioLib_TxPacket txPacket; + +static const SPISettings spi_settings(1000000UL, MSBFIRST, SPI_MODE0); + +static void hal_spi_select (int on) { + +#if defined(SPI_HAS_TRANSACTION) + if (on) + SPI.beginTransaction(spi_settings); + else + SPI.endTransaction(); +#endif + + //Serial.println(val?">>":"<<"); + digitalWrite(lmic_pins.nss, !on ? HIGH : LOW); +} + +// Datasheet defins typical times until busy goes low. Most are < 200us, +// except when waking up from sleep, which typically takes 3500us. Since +// we cannot know here if we are in sleep, we'll have to assume we are. +// Since 3500 is typical, not maximum, wait a bit more than that. +static unsigned long MAX_BUSY_TIME = 5000; + +static void hal_pin_busy_wait (void) { + if (lmic_pins.busy == LMIC_UNUSED_PIN) { + // TODO: We could probably keep some state so we know the chip + // is in sleep, since otherwise the delay can be much shorter. + // Also, all delays after commands (rather than waking up from + // sleep) are measured from the *end* of the previous SPI + // transaction, so we could wait shorter if we remember when + // that was. + delayMicroseconds(MAX_BUSY_TIME); + } else { + unsigned long start = micros(); + + while((micros() - start) < MAX_BUSY_TIME && digitalRead(lmic_pins.busy)) /* wait */; + } +} + +static void sx1262_ReadRegs (uint16_t addr, uint8_t* data, uint8_t len) { + hal_spi_select(1); + hal_pin_busy_wait(); + hal_spi(CMD_READREGISTER); + hal_spi(addr >> 8); + hal_spi(addr); + hal_spi(0x00); // NOP + for (uint8_t i = 0; i < len; i++) { + data[i] = hal_spi(0x00); + } + hal_spi_select(0); +} + +static uint8_t sx1262_ReadReg (uint16_t addr) { + uint8_t val; + sx1262_ReadRegs(addr, &val, 1); + return val; +} +#endif + +static bool lr112x_probe() +{ +#if USE_SX1262 + u1_t v, v_reset; + + SoC->SPI_begin(); + + lmic_hal_init (nullptr); + + // manually reset radio + hal_pin_rst(0); // drive RST pin low + hal_waitUntil(os_getTime()+ms2osticks(1)); // wait >100us + + v_reset = sx1262_ReadReg(REG_LORASYNCWORDLSB); + + hal_pin_rst(2); // configure RST pin floating! + hal_waitUntil(os_getTime()+ms2osticks(5)); // wait 5ms + + v = sx1262_ReadReg(REG_LORASYNCWORDLSB); + + pinMode(lmic_pins.nss, INPUT); + SPI.end(); + + u1_t fanet_sw_lsb = ((fanet_proto_desc.syncword[0] & 0x0F) << 4) | 0x04; + if (v == SX126X_DEF_LORASYNCWORDLSB || v == fanet_sw_lsb) { + + if (v_reset == SX126X_DEF_LORASYNCWORDLSB || v == fanet_sw_lsb) { + RF_SX12XX_RST_is_connected = false; + } + + return true; + } else { + return false; + } +#endif + +#if USE_LR1121 + bool success = false; + + /* TBD */ + + return success; +#endif +} + +static void lr112x_channel(int8_t channel) +{ + if (channel != -1 && channel != lr112x_channel_prev) { + uint32_t frequency = RF_FreqPlan.getChanFrequency((uint8_t) channel); + + if (settings->rf_protocol == RF_PROTOCOL_LEGACY) { + nRF905_band_t nrf_band; + uint32_t nrf_freq_resolution; + + nrf_band = (frequency >= 844800000UL ? NRF905_BAND_868 : NRF905_BAND_433); + nrf_freq_resolution = (nrf_band == NRF905_BAND_433 ? 100000UL : 200000UL); + frequency -= (frequency % nrf_freq_resolution); + } + + int state = radio.setFrequency(frequency / 1000000.0); + + lr112x_channel_prev = channel; + /* restart Rx upon a channel switch */ + lr112x_receive_active = false; + } +} + +static void lr112x_setup() +{ + int state; + + switch (settings->rf_protocol) + { + case RF_PROTOCOL_OGNTP: + rl_protocol = &ogntp_proto_desc; + protocol_encode = &ogntp_encode; + protocol_decode = &ogntp_decode; + break; + case RF_PROTOCOL_P3I: + rl_protocol = &p3i_proto_desc; + protocol_encode = &p3i_encode; + protocol_decode = &p3i_decode; + break; + case RF_PROTOCOL_FANET: + rl_protocol = &fanet_proto_desc; + protocol_encode = &fanet_encode; + protocol_decode = &fanet_decode; + break; +#if defined(ENABLE_PROL) + case RF_PROTOCOL_APRS: + rl_protocol = &prol_proto_desc; + protocol_encode = &aprs_encode; + protocol_decode = &aprs_decode; + break; +#endif /* ENABLE_PROL */ +#if defined(ENABLE_ADSL) + case RF_PROTOCOL_ADSL_860: + rl_protocol = &adsl_proto_desc; + protocol_encode = &adsl_encode; + protocol_decode = &adsl_decode; + break; +#endif /* ENABLE_ADSL */ + case RF_PROTOCOL_LEGACY: + default: + rl_protocol = &legacy_proto_desc; + protocol_encode = &legacy_encode; + protocol_decode = &legacy_decode; + /* + * Enforce legacy protocol setting for SX1276 + * if other value (UAT) left in EEPROM from other (UATM) radio + */ + settings->rf_protocol = RF_PROTOCOL_LEGACY; + break; + } + + RF_FreqPlan.setPlan(settings->band, settings->rf_protocol); + + switch (rl_protocol->modulation_type) + { + case RF_MODULATION_TYPE_LORA: + state = radio.begin(); // start LoRa mode (and disable FSK) + /* TBD */ + break; + case RF_MODULATION_TYPE_2FSK: + case RF_MODULATION_TYPE_PPM: /* TBD */ + default: + float br, fdev, bw; + +#if USE_SX1262 + state = radio.beginFSK(); // start FSK mode (and disable LoRa) +#endif +#if USE_LR1121 + state = radio.beginGFSK(); +#endif + + switch (rl_protocol->bitrate) + { + case RF_BITRATE_38400: + br = 38.4; + break; + case RF_BITRATE_100KBPS: + default: + br = 100.0; + break; + } + state = radio.setBitRate(br); + + switch (rl_protocol->deviation) + { + case RF_FREQUENCY_DEVIATION_9_6KHZ: + fdev = 9.6; + break; + case RF_FREQUENCY_DEVIATION_19_2KHZ: + fdev = 19.2; + break; + case RF_FREQUENCY_DEVIATION_25KHZ: + fdev = 25.0; + break; + case RF_FREQUENCY_DEVIATION_50KHZ: + case RF_FREQUENCY_DEVIATION_NONE: + default: + fdev = 50.0; + break; + } + state = radio.setFrequencyDeviation(fdev); + + switch (rl_protocol->bandwidth) + { + case RF_RX_BANDWIDTH_SS_50KHZ: + bw = 117.3; + break; + case RF_RX_BANDWIDTH_SS_100KHZ: + bw = 234.3; + break; + case RF_RX_BANDWIDTH_SS_166KHZ: + bw = 312.0; + break; + case RF_RX_BANDWIDTH_SS_200KHZ: + case RF_RX_BANDWIDTH_SS_250KHZ: + case RF_RX_BANDWIDTH_SS_1567KHZ: + bw = 467.0; + break; + case RF_RX_BANDWIDTH_SS_125KHZ: + default: + bw = 234.3; + break; + } + state = radio.setRxBandwidth(bw); + + state = radio.setPreambleLength(rl_protocol->preamble_size * 8); + state = radio.setDataShaping(RADIOLIB_SHAPING_0_5); + + switch (rl_protocol->crc_type) + { + case RF_CHECKSUM_TYPE_CCITT_FFFF: + case RF_CHECKSUM_TYPE_CCITT_0000: + case RF_CHECKSUM_TYPE_CCITT_1D02: + case RF_CHECKSUM_TYPE_CRC8_107: + case RF_CHECKSUM_TYPE_RS: + /* TBD */ + break; + case RF_CHECKSUM_TYPE_GALLAGER: + case RF_CHECKSUM_TYPE_CRC_MODES: + case RF_CHECKSUM_TYPE_NONE: + default: + state = radio.setCRC(0, 0); + break; + } + + size_t pkt_size = rl_protocol->payload_offset + rl_protocol->payload_size + + rl_protocol->crc_size; + + switch (rl_protocol->whitening) + { + case RF_WHITENING_MANCHESTER: + pkt_size += pkt_size; + break; + case RF_WHITENING_PN9: + case RF_CHECKSUM_TYPE_CRC_MODES: + case RF_WHITENING_NICERF: + default: + break; + } + state = radio.fixedPacketLengthMode(pkt_size); + + state = radio.disableAddressFiltering(); + state = radio.setSyncWord((uint8_t *) rl_protocol->syncword, + (size_t) rl_protocol->syncword_size); + break; + } + + float txpow; + + switch(settings->txpower) + { + case RF_TX_POWER_FULL: + + /* Load regional max. EIRP at first */ + txpow = RF_FreqPlan.MaxTxPower; + + if (txpow > 22) + txpow = 22; + +#if 1 + /* + * Enforce Tx power limit until confirmation + * that LR112x is doing well + * when antenna is not connected + */ + if (txpow > 17) + txpow = 17; +#endif + break; + case RF_TX_POWER_OFF: + case RF_TX_POWER_LOW: + default: + txpow = 2; + break; + } + + state = radio.setOutputPower(txpow); + +#if USE_SX1262 + state = radio.setCurrentLimit(100.0); + state = radio.setRxBoostedGainMode(true); +#endif +} + +static bool lr112x_receive() +{ + bool success = false; + + if (settings->power_save & POWER_SAVE_NORECEIVE) { + return success; + } + + return success; +} + +static bool lr112x_transmit() +{ + u1_t crc8; + u2_t crc16; + u1_t i; + + bool success = false; + + if (RF_tx_size <= 0) { + return success; + } + + lr112x_receive_active = false; + lr112x_transmit_complete = false; + + size_t PayloadLen = 0; + + switch (rl_protocol->crc_type) + { + case RF_CHECKSUM_TYPE_GALLAGER: + case RF_CHECKSUM_TYPE_CRC_MODES: + case RF_CHECKSUM_TYPE_NONE: + /* crc16 left not initialized */ + break; + case RF_CHECKSUM_TYPE_CRC8_107: + crc8 = 0x71; /* seed value */ + break; + case RF_CHECKSUM_TYPE_CCITT_0000: + crc16 = 0x0000; /* seed value */ + break; + case RF_CHECKSUM_TYPE_CCITT_FFFF: + default: + crc16 = 0xffff; /* seed value */ + break; + } + + switch (rl_protocol->type) + { + case RF_PROTOCOL_LEGACY: + /* take in account NRF905/FLARM "address" bytes */ + crc16 = update_crc_ccitt(crc16, 0x31); + crc16 = update_crc_ccitt(crc16, 0xFA); + crc16 = update_crc_ccitt(crc16, 0xB6); + break; + case RF_PROTOCOL_P3I: + /* insert Net ID */ + txPacket.payload[PayloadLen++] = (u1_t) ((rl_protocol->net_id >> 24) & 0x000000FF); + txPacket.payload[PayloadLen++] = (u1_t) ((rl_protocol->net_id >> 16) & 0x000000FF); + txPacket.payload[PayloadLen++] = (u1_t) ((rl_protocol->net_id >> 8) & 0x000000FF); + txPacket.payload[PayloadLen++] = (u1_t) ((rl_protocol->net_id >> 0) & 0x000000FF); + /* insert byte with payload size */ + txPacket.payload[PayloadLen++] = rl_protocol->payload_size; + + /* insert byte with CRC-8 seed value when necessary */ + if (rl_protocol->crc_type == RF_CHECKSUM_TYPE_CRC8_107) { + txPacket.payload[PayloadLen++] = crc8; + } + + break; + case RF_PROTOCOL_OGNTP: + case RF_PROTOCOL_ADSL_860: + default: + break; + } + + for (i=0; i < RF_tx_size; i++) { + + switch (rl_protocol->whitening) + { + case RF_WHITENING_NICERF: + txPacket.payload[PayloadLen] = TxBuffer[i] ^ pgm_read_byte(&whitening_pattern[i]); + break; + case RF_WHITENING_MANCHESTER: + txPacket.payload[PayloadLen] = pgm_read_byte(&ManchesterEncode[(TxBuffer[i] >> 4) & 0x0F]); + PayloadLen++; + txPacket.payload[PayloadLen] = pgm_read_byte(&ManchesterEncode[(TxBuffer[i] ) & 0x0F]); + break; + case RF_WHITENING_NONE: + default: + txPacket.payload[PayloadLen] = TxBuffer[i]; + break; + } + + switch (rl_protocol->crc_type) + { + case RF_CHECKSUM_TYPE_GALLAGER: + case RF_CHECKSUM_TYPE_CRC_MODES: + case RF_CHECKSUM_TYPE_NONE: + break; + case RF_CHECKSUM_TYPE_CRC8_107: + update_crc8(&crc8, (u1_t)(txPacket.payload[PayloadLen])); + break; + case RF_CHECKSUM_TYPE_CCITT_FFFF: + case RF_CHECKSUM_TYPE_CCITT_0000: + default: + if (rl_protocol->whitening == RF_WHITENING_MANCHESTER) { + crc16 = update_crc_ccitt(crc16, (u1_t)(TxBuffer[i])); + } else { + crc16 = update_crc_ccitt(crc16, (u1_t)(txPacket.payload[PayloadLen])); + } + break; + } + + PayloadLen++; + } + + switch (rl_protocol->crc_type) + { + case RF_CHECKSUM_TYPE_GALLAGER: + case RF_CHECKSUM_TYPE_CRC_MODES: + case RF_CHECKSUM_TYPE_NONE: + break; + case RF_CHECKSUM_TYPE_CRC8_107: + txPacket.payload[PayloadLen++] = crc8; + break; + case RF_CHECKSUM_TYPE_CCITT_FFFF: + case RF_CHECKSUM_TYPE_CCITT_0000: + default: + if (rl_protocol->whitening == RF_WHITENING_MANCHESTER) { + txPacket.payload[PayloadLen++] = pgm_read_byte(&ManchesterEncode[(((crc16 >> 8) & 0xFF) >> 4) & 0x0F]); + txPacket.payload[PayloadLen++] = pgm_read_byte(&ManchesterEncode[(((crc16 >> 8) & 0xFF) ) & 0x0F]); + txPacket.payload[PayloadLen++] = pgm_read_byte(&ManchesterEncode[(((crc16 ) & 0xFF) >> 4) & 0x0F]); + txPacket.payload[PayloadLen++] = pgm_read_byte(&ManchesterEncode[(((crc16 ) & 0xFF) ) & 0x0F]); + PayloadLen++; + } else { + txPacket.payload[PayloadLen++] = (crc16 >> 8) & 0xFF; + txPacket.payload[PayloadLen++] = (crc16 ) & 0xFF; + } + break; + } + + txPacket.len = PayloadLen; + + int state = radio.transmit((uint8_t *) &txPacket.payload, (size_t) txPacket.len); + + if (state == RADIOLIB_ERR_NONE) { + // the packet was successfully transmitted + Serial.println(F("success!")); + + // print measured data rate + Serial.print(F("[SX1262] Datarate:\t")); + Serial.print(radio.getDataRate()); + Serial.println(F(" bps")); + + success = true; + + } else if (state == RADIOLIB_ERR_PACKET_TOO_LONG) { + // the supplied packet was longer than 256 bytes + Serial.println(F("too long!")); + + } else if (state == RADIOLIB_ERR_TX_TIMEOUT) { + // timeout occured while transmitting packet + Serial.println(F("timeout!")); + + } else { + // some other error occurred + Serial.print(F("failed, code ")); + Serial.println(state); + } + + return success; +} + +static void lr112x_shutdown() +{ + int state = radio.sleep(false); +} + +#endif /* USE_RADIOLIB */ diff --git a/software/firmware/source/SoftRF/src/driver/RF.h b/software/firmware/source/SoftRF/src/driver/RF.h index 8d28d270e..ed0859a48 100644 --- a/software/firmware/source/SoftRF/src/driver/RF.h +++ b/software/firmware/source/SoftRF/src/driver/RF.h @@ -33,6 +33,9 @@ #include #include #include +#if defined(USE_RADIOLIB) +#include +#endif #include "GNSS.h" #include "../protocol/radio/Legacy.h" @@ -76,13 +79,14 @@ enum RF_IC_R820T, RF_IC_MSI001, RF_IC_SA8X8, + RF_IC_LR112X, }; enum { RF_TX_POWER_FULL, RF_TX_POWER_LOW, - RF_TX_POWER_OFF + RF_TX_POWER_OFF, }; typedef struct rfchip_ops_struct { diff --git a/software/firmware/source/SoftRF/src/platform/SAMD.cpp b/software/firmware/source/SoftRF/src/platform/SAMD.cpp index 2fd3d9dd7..dde2b8371 100644 --- a/software/firmware/source/SoftRF/src/platform/SAMD.cpp +++ b/software/firmware/source/SoftRF/src/platform/SAMD.cpp @@ -194,7 +194,7 @@ static void SAMD_post_init() Serial.println(F("Built-in components:")); Serial.print(F("RADIO : ")); - Serial.println(hw_info.rf == RF_IC_SX1276 ? F("PASS") : F("FAIL")); + Serial.println(hw_info.rf != RF_IC_NONE ? F("PASS") : F("FAIL")); Serial.print(F("GNSS : ")); Serial.println(hw_info.gnss != GNSS_MODULE_NONE ? F("PASS") : F("FAIL")); diff --git a/software/firmware/source/SoftRF/src/protocol/radio/ADSL.cpp b/software/firmware/source/SoftRF/src/protocol/radio/ADSL.cpp index c9bb9f88d..6dca12d2e 100644 --- a/software/firmware/source/SoftRF/src/protocol/radio/ADSL.cpp +++ b/software/firmware/source/SoftRF/src/protocol/radio/ADSL.cpp @@ -65,6 +65,35 @@ const rf_proto_desc_t adsl_proto_desc = { .slot1 = {800, 1200} }; +const rf_proto_desc_t adsl_proto_desc_oband = { + .name = {'A','D','S','-','L', 0}, + .type = RF_PROTOCOL_ADSL_860, + .modulation_type = RF_MODULATION_TYPE_2FSK, + .preamble_type = P3I_PREAMBLE_TYPE, + .preamble_size = P3I_PREAMBLE_SIZE, + .syncword = {0x2d, 0xd4}, + .syncword_size = P3I_SYNCWORD_SIZE, + .net_id = 0x0000, /* not in use */ + .payload_type = RF_PAYLOAD_DIRECT, + .payload_size = ADSL_PAYLOAD_SIZE, + .payload_offset = 0, + .crc_type = ADSL_CRC_TYPE, + .crc_size = ADSL_CRC_SIZE, + + .bitrate = RF_BITRATE_38400, + .deviation = P3I_FDEV, + .whitening = RF_WHITENING_NONE, + .bandwidth = P3I_BANDWIDTH, + + .air_time = P3I_AIR_TIME, + + .tm_type = RF_TIMING_INTERVAL, + .tx_interval_min = P3I_TX_INTERVAL_MIN, + .tx_interval_max = P3I_TX_INTERVAL_MAX, + .slot0 = {0, 0}, + .slot1 = {0, 0} +}; + static GPS_Position pos; static ADSL_Packet r __attribute__((aligned(sizeof(uint32_t)))); /* Rx */ static ADSL_Packet t __attribute__((aligned(sizeof(uint32_t)))); /* Tx */ diff --git a/software/firmware/source/libraries/OGN/manchester.h b/software/firmware/source/libraries/OGN/manchester.h index 69eb99fbd..0ea8161fa 100644 --- a/software/firmware/source/libraries/OGN/manchester.h +++ b/software/firmware/source/libraries/OGN/manchester.h @@ -5,7 +5,7 @@ defined(ENERGIA_ARCH_CC13XX) || defined(ENERGIA_ARCH_CC13X2) || \ (defined(ARDUINO_ARCH_RP2040) && defined(ARDUINO_ARCH_MBED)) || \ (defined(ARDUINO_ARCH_NRF52840) && defined(ARDUINO_ARCH_MBED)) || \ - defined(ARDUINO_ARCH_RENESAS) + defined(ARDUINO_ARCH_RENESAS) || defined(ARDUINO_ARCH_SAMD) #include #endif