diff --git a/README.md b/README.md index fdb0c63..880dc97 100644 --- a/README.md +++ b/README.md @@ -5,21 +5,21 @@ Static avr-libc library providing basic support for RFM95 radio modules. Other RFM9x radios might work as well, but were not tested yet. -This is work in progress. Simple Tx-Rx with response in FSK mode works so far. - I'm impressed how well these radio modules work; the range achieved with simple wire antennas as well as the reliable packet transmission. -## TODO +This is work in progress. Currently available is (FSK and LoRa): -- Add support for LoRa mode +- Transmit a packet +- Blocking receive a single packet with timeout +- Async'ly receive a packet (MCU sleeps or does something else until reception) ## Usage 1. Include `librfm.h` and `librfm.a` in the project 2. Implement the `_rfm*` functions in `librfm.h` in the application (this is to make the library device and CPU frequency independent) -3. Route interrupts occurring on `DIO0` and `DIO4` to `rfmIrq()` +3. Route interrupts occurring on `DIO0` and `DIO4`(FSK)/`DIO1`(LoRa) to `rfmIrq()` ## Range @@ -27,7 +27,16 @@ with an RSSI of about -125 dBm at almost 2.7 km distance - with simple wire antennas, and a low hill in between. +### FSK + +Transmitting with +17 dBm, reception stopped with an RSSI of about -125 dBm at +almost 2.7 km distance - with simple wire antennas, and a low hill in between. + Compared to the [RFM69](https://github.com/gitdode/librfm) at +13 dBm, it does make it a few hundred meters further - seems fair enough! ![FieldTest4](https://github.com/user-attachments/assets/67f745c4-a47f-4cb1-a278-547a0b0e01e3) + +### LoRa + +TODO 🙂 diff --git a/librfm.c b/librfm.c index eed555a..88a9be7 100644 --- a/librfm.c +++ b/librfm.c @@ -8,9 +8,15 @@ #include "librfm.h" #include "utils.h" -static volatile bool packetSent = false; -static volatile bool payloadReady = false; -static volatile bool timeout = false; +/* FSK 'Timeout' */ +static volatile bool rxTimeout = false; +/* FSK 'PacketSent' */ +static volatile bool txDone = false; +/* FSK 'PayloadReady' */ +static volatile bool rxDone = false; + +/* Current mode: LoRa or FSK */ +static bool lora = false; /** * Writes the given value to the given register. @@ -44,210 +50,280 @@ * Sets the module to the given operating mode. */ static void setMode(uint8_t mode) { - regWrite(OP_MODE, (regRead(OP_MODE) & ~MASK_MODE) | (mode & MASK_MODE)); + regWrite(RFM_OP_MODE, (regRead(RFM_OP_MODE) & ~RFM_MASK_MODE) | (mode & RFM_MASK_MODE)); } /** - * Enables or disables timeouts. + * Enables or disables timeouts in FSK mode. * * @param enable */ -static void timeoutEnable(bool enable) { +static void timeoutEnableFSK(bool enable) { + // get "Timeout" on DIO4 + regWrite(RFM_DIO_MAP2, (regRead(RFM_DIO_MAP2) | 0x80) & ~0x40); + rxTimeout = false; if (enable) { - // get "Timeout" on DIO4 - regWrite(DIO_MAP2, (regRead(DIO_MAP2) | 0x80) & ~0x40); - timeout = false; // TODO calculate - seems to be about 50, 75, 100ms - regWrite(RX_TO_RSSI, 0x1f); - regWrite(RX_TO_PREA, 0x2f); - regWrite(RX_TO_SYNC, 0x3f); + regWrite(RFM_FSK_RX_TO_RSSI, 0x1f); + regWrite(RFM_FSK_RX_TO_PREA, 0x2f); + regWrite(RFM_FSK_RX_TO_SYNC, 0x3f); } else { - regWrite(RX_TO_RSSI, 0x00); - regWrite(RX_TO_PREA, 0x00); - regWrite(RX_TO_SYNC, 0x00); + regWrite(RFM_FSK_RX_TO_RSSI, 0x00); + regWrite(RFM_FSK_RX_TO_PREA, 0x00); + regWrite(RFM_FSK_RX_TO_SYNC, 0x00); } } -bool rfmInit(uint64_t freq, uint8_t node) { - // wait a bit after power on - _rfmDelay5(); - _rfmDelay5(); - - // pull reset high to turn on the module - _rfmOn(); - - _rfmDelay5(); - - uint8_t version = regRead(VERSION); - // printString("Version: "); - // printHex(version); - if (version == 0x00) { - return false; - } - - // FSK modulation, high frequency mode, sleep mode - regWrite(OP_MODE, 0x00); - +static bool initFSK(uint8_t node) { // bit rate 9.6 kBit/s // regWrite(BITRATE_MSB, 0x0d); // regWrite(BITRATE_LSB, 0x05); // frequency deviation 10 kHz (default 5 kHz) - regWrite(FDEV_MSB, 0x00); - regWrite(FDEV_LSB, 0xa4); - - // set the carrier frequency - uint32_t frf = freq * 1000000ULL / F_STEP; - regWrite(FRF_MSB, frf >> 16); - regWrite(FRF_MID, frf >> 8); - regWrite(FRF_LSB, frf >> 0); - - // PA level +17 dBm with PA_BOOST pin (Pmax default/not relevant) - regWrite(PA_CONFIG, 0xff); - - // LNA highest gain, no current adjustment - regWrite(LNA, 0x20); + regWrite(RFM_FSK_FDEV_MSB, 0x00); + regWrite(RFM_FSK_FDEV_LSB, 0xa4); // AgcAutoOn, receiver trigger event PreambleDetect - regWrite(RX_CONFIG, 0x0e); + regWrite(RFM_FSK_RX_CONFIG, 0x0e); // no RSSI offset, 32 samples used - regWrite(RSSI_CONFIG, 0x04); + regWrite(RFM_FSK_RSSI_CONFIG, 0x04); // 10 dB threshold for interferer detection - regWrite(RSSI_COLLIS, 0x0a); + regWrite(RFM_FSK_RSSI_COLLIS, 0x0a); // RSSI threshold (POR 0xff) - regWrite(RSSI_THRESH, 0x94); + regWrite(RFM_FSK_RSSI_THRESH, 0x94); // channel filter bandwith 20.8 kHz (default 10.4 kHz) - regWrite(RX_BW, 0x14); + regWrite(RFM_FSK_RX_BW, 0x14); // RX_BW during AFC - regWrite(AFC_BW, 0x14); + regWrite(RFM_FSK_AFC_BW, 0x14); // AFC auto on // regWrite(AFC_FEI, 0x00); // PreambleDetectorOn, PreambleDetectorSize 2 bytes, // PreambleDetectorTol 4 chips per bit - regWrite(PREA_DETECT, 0xaa); + regWrite(RFM_FSK_PREA_DETECT, 0xaa); // Preamble size - regWrite(PREA_MSB, 0x00); - regWrite(PREA_LSB, 0x03); + regWrite(RFM_FSK_PREA_MSB, 0x00); + regWrite(RFM_FSK_PREA_LSB, 0x03); // AutoRestartRxMode off, PreamblePolarity 0xaa, SyncOn, // FifoFillCondition if SyncAddress, SyncSize + 1 = 4 bytes - regWrite(SYNC_CONFIG, 0x13); + regWrite(RFM_FSK_SYNC_CONFIG, 0x13); // just set all sync word values to some really creative value - regWrite(SYNC_VAL1, 0x2f); - regWrite(SYNC_VAL2, 0x30); - regWrite(SYNC_VAL3, 0x31); - regWrite(SYNC_VAL4, 0x32); - regWrite(SYNC_VAL5, 0x33); - regWrite(SYNC_VAL6, 0x34); - regWrite(SYNC_VAL7, 0x35); - regWrite(SYNC_VAL8, 0x36); + regWrite(RFM_FSK_SYNC_VAL1, 0x2f); + regWrite(RFM_FSK_SYNC_VAL2, 0x30); + regWrite(RFM_FSK_SYNC_VAL3, 0x31); + regWrite(RFM_FSK_SYNC_VAL4, 0x32); + regWrite(RFM_FSK_SYNC_VAL5, 0x33); + regWrite(RFM_FSK_SYNC_VAL6, 0x34); + regWrite(RFM_FSK_SYNC_VAL7, 0x35); + regWrite(RFM_FSK_SYNC_VAL8, 0x36); // variable payload length, DcFree none, CrcOn, CrcAutoClearOff, // match broadcast or node address - regWrite(PCK_CONFIG1, 0x9c); + regWrite(RFM_FSK_PCK_CONFIG1, 0x9c); // Packet mode, ..., PayloadLength(10:8) - regWrite(PCK_CONFIG2, 0x40); + regWrite(RFM_FSK_PCK_CONFIG2, 0x40); // PayloadLength(7:0) - regWrite(PAYLOAD_LEN, 0x40); + regWrite(RFM_FSK_PAYLOAD_LEN, 0x40); // node and broadcast address - regWrite(NODE_ADDR, node); - regWrite(CAST_ADDR, CAST_ADDRESS); + regWrite(RFM_FSK_NODE_ADDR, node); + regWrite(RFM_FSK_CAST_ADDR, RFM_CAST_ADDRESS); // set TX start condition to "at least one byte in FIFO" - regWrite(FIFO_THRESH, 0x8f); + regWrite(RFM_FSK_FIFO_THRESH, 0x8f); // AutoImageCalOn disabled, TempThreshold 10°C, TempMonitorOff 0 - regWrite(IMAGE_CAL, 0x02); + regWrite(RFM_FSK_IMAGE_CAL, 0x02); // printString("Radio init done\r\n"); return true; } -void rfmIrq(void) { - uint8_t irqFlags1 = regRead(IRQ_FLAGS1); - uint8_t irqFlags2 = regRead(IRQ_FLAGS2); +static bool initLoRa(void) { + // SPI interface address pointer in FIFO data buffer (POR 0x00) + regWrite(RFM_LORA_FIFO_ADDR_PTR, 0x00); - if (irqFlags1 & (1 << 2)) timeout = true; - if (irqFlags2 & (1 << 3)) packetSent = true; - if (irqFlags2 & (1 << 2)) payloadReady = true; + // write base address in FIFO data buffer for TX modulator (POR 0x80) + regWrite(RFM_LORA_FIFO_TX_ADDR, 0x80); + + // read base address in FIFO data buffer for RX demodulator (POR 0x00) + regWrite(RFM_LORA_FIFO_RX_ADDR, 0x00); + + // signal bandwidth 62.5 kHz, error coding rate 4/5, explicit header mode + regWrite(RFM_LORA_MODEM_CONFIG1, 0x62); + + // spreading factor 9, TX single packet mode, CRC enable, RX timeout MSB + regWrite(RFM_LORA_MODEM_CONFIG2, 0x94); + + // RX timeout LSB + regWrite(RFM_LORA_SYMB_TIMEO_LSB, 0x64); + + // static node, AGC auto off + regWrite(RFM_LORA_MODEM_CONFIG3, 0x00); + + // preamble length MSB + regWrite(RFM_LORA_PREA_LEN_MSB, 0x00); + + // preamble length LSB + regWrite(RFM_LORA_PREA_LEN_LSB, 0x08); + + // payload length in bytes (>0 only for implicit header mode) + // regWrite(RFM_LORA_PAYLD_LEN, 0x01); + + // max payload length (CRC error if exceeded) + regWrite(RFM_LORA_PAYLD_MAX_LEN, 0xff); + + // frequency hopping disabled + regWrite(RFM_LORA_HOP_PERIOD, 0x00); + + return true; +} + +bool rfmInit(uint64_t freq, uint8_t node, bool _lora) { + lora = _lora; + + // wait a bit after power on + _rfmDelay5(); + _rfmDelay5(); + + // pull reset high to turn on the module + _rfmOn(); + _rfmDelay5(); + + uint8_t version = regRead(RFM_VERSION); + // printString("Version: "); + // printHex(version); + if (version == 0x00) { + return false; + } + + // set the carrier frequency + uint32_t frf = freq * 1000000UL / RFM_F_STEP; + regWrite(RFM_FRF_MSB, frf >> 16); + regWrite(RFM_FRF_MID, frf >> 8); + regWrite(RFM_FRF_LSB, frf >> 0); + + // PA level +17 dBm with PA_BOOST pin (Pmax default/not relevant) + regWrite(RFM_PA_CONFIG, 0xff); + + // LNA highest gain, no current adjustment + regWrite(RFM_LNA, 0x20); + + if (lora) { + // go to sleep before switching to LoRa mode + setMode(RFM_MODE_SLEEP); + _rfmDelay5(); + + // LoRa mode, high frequency mode, sleep mode + regWrite(RFM_OP_MODE, 0x80); + + return initLoRa(); + } else { + // FSK mode, FSK modulation, high frequency mode, sleep mode + regWrite(RFM_OP_MODE, 0x00); + + return initFSK(node); + } +} + +void rfmIrq(void) { + if (lora) { + uint8_t irqFlags = regRead(RFM_LORA_IRQ_FLAGS); + + if (irqFlags & (1 << 7)) rxTimeout = true; + if (irqFlags & (1 << 3)) txDone = true; + if (irqFlags & (1 << 6)) rxDone = true; + } else { + uint8_t irqFlags1 = regRead(RFM_FSK_IRQ_FLAGS1); + uint8_t irqFlags2 = regRead(RFM_FSK_IRQ_FLAGS2); + + if (irqFlags1 & (1 << 2)) rxTimeout = true; + if (irqFlags2 & (1 << 3)) txDone = true; + if (irqFlags2 & (1 << 2)) rxDone = true; + } } void rfmTimeout(void) { - timeout = true; + if (!lora) { + // workaround for timeout interrupt sometimes not occurring in FSK mode + // https://electronics.stackexchange.com/q/743099/65699 + rxTimeout = true; + } } void rfmSleep(void) { _rfmDelay5(); - setMode(MODE_SLEEP); + setMode(RFM_MODE_SLEEP); } void rfmWake(void) { - setMode(MODE_STDBY); + setMode(RFM_MODE_STDBY); // should better wait for ModeReady irq? _rfmDelay5(); } void rfmSetNodeAddress(uint8_t address) { - regWrite(NODE_ADDR, address); + if (!lora) { + regWrite(RFM_FSK_NODE_ADDR, address); + } } void rfmSetOutputPower(int8_t dBm) { uint8_t pa = 0xc0; // +2 dBm with PA_BOOST // adjust power from 2 to +17 dBm - pa |= (min(max(dBm - PA_OFF, PA_MIN), PA_MAX)) & 0x0f; - regWrite(PA_CONFIG, pa); + pa |= (min(max(dBm - RFM_PA_OFF, RFM_PA_MIN), RFM_PA_MAX)) & 0x0f; + regWrite(RFM_PA_CONFIG, pa); } int8_t rfmGetOutputPower(void) { - return (regRead(PA_CONFIG) & 0x0f) + PA_OFF; + return (regRead(RFM_PA_CONFIG) & 0x0f) + RFM_PA_OFF; } void rfmStartReceive(bool timeout) { - timeoutEnable(timeout); + timeoutEnableFSK(timeout); // get "PayloadReady" on DIO0 - regWrite(DIO_MAP1, regRead(DIO_MAP1) & ~0xc0); - payloadReady = false; + regWrite(RFM_DIO_MAP1, regRead(RFM_DIO_MAP1) & ~0xc0); + rxDone = false; - setMode(MODE_RX); + setMode(RFM_MODE_RX); } -PayloadFlags rfmPayloadReady(void) { - PayloadFlags flags = {.ready = false, .rssi = 255, .crc = false}; - if (payloadReady) { +RxFlags rfmPayloadReady(void) { + RxFlags flags = {.ready = false, .rssi = 255, .crc = false}; + if (rxDone) { flags.ready = true; - flags.rssi = regRead(RSSI_VALUE); - flags.crc = regRead(IRQ_FLAGS2) & (1 << 1); - setMode(MODE_STDBY); + flags.rssi = divRoundNearest(regRead(RFM_FSK_RSSI_VALUE), 2); + flags.crc = regRead(RFM_FSK_IRQ_FLAGS2) & (1 << 1); + setMode(RFM_MODE_STDBY); } return flags; } size_t rfmReadPayload(uint8_t *payload, size_t size) { - size_t len = regRead(FIFO); + size_t len = regRead(RFM_FIFO); len = min(len, size); // TODO assume and ignore address for now (already filtered anyway) - regRead(FIFO); + regRead(RFM_FIFO); _rfmSel(); - _rfmTx(FIFO); + _rfmTx(RFM_FIFO); for (size_t i = 0; i < len; i++) { - payload[i] = _rfmTx(FIFO); + payload[i] = _rfmTx(RFM_FIFO); } _rfmDes(); @@ -258,17 +334,17 @@ rfmStartReceive(enable); // wait until "PayloadReady" or (forced) "Timeout" - do {} while (!payloadReady && !timeout); + do {} while (!rxDone && !rxTimeout); - if (payloadReady) { - timeoutEnable(false); + if (rxDone) { + timeoutEnableFSK(false); } - setMode(MODE_STDBY); + setMode(RFM_MODE_STDBY); - if (timeout) { + if (rxTimeout) { // full power as last resort, indicate timeout - regWrite(PA_CONFIG, 0xff); + regWrite(RFM_PA_CONFIG, 0xff); return 0; } @@ -278,10 +354,10 @@ size_t rfmTransmitPayload(uint8_t *payload, size_t size, uint8_t node) { // message + address byte - size_t len = min(size, MESSAGE_SIZE) + 1; + size_t len = min(size, RFM_FSK_MSG_SIZE) + 1; _rfmSel(); - _rfmTx(FIFO | 0x80); + _rfmTx(RFM_FIFO | 0x80); _rfmTx(len); _rfmTx(node); for (size_t i = 0; i < size; i++) { @@ -290,15 +366,112 @@ _rfmDes(); // get "PacketSent" on DIO0 (default) - regWrite(DIO_MAP1, regRead(DIO_MAP1) & ~0xc0); - packetSent = false; + regWrite(RFM_DIO_MAP1, regRead(RFM_DIO_MAP1) & ~0xc0); + txDone = false; - setMode(MODE_TX); + setMode(RFM_MODE_TX); // wait until "PacketSent" - do {} while (!packetSent); + do {} while (!txDone); - setMode(MODE_STDBY); + setMode(RFM_MODE_STDBY); + + return len; +} + +void rfmLoRaStartRx(void) { + // clear "RxDone" and "PayloadCrcError" interrupt + regWrite(RFM_LORA_IRQ_FLAGS, 0x40 | 0x20); + rxDone = false; + + // get "RxDone" on DIO0 + regWrite(RFM_DIO_MAP1, regRead(RFM_DIO_MAP1) & ~0xc0); + + // set FIFO address pointer to configured TX base address + regWrite(RFM_LORA_FIFO_ADDR_PTR, regRead(RFM_LORA_FIFO_RX_ADDR)); + + // TODO already is in continuous RX mode most of the time + setMode(RFM_MODE_RX); +} + +RxFlags rfmLoRaRxDone(void) { + RxFlags flags = {.ready = false, .rssi = 255, .crc = false}; + if (rxDone) { + flags.ready = true; + flags.rssi = 157 - regRead(RFM_LORA_PCK_RSSI); + flags.crc = !(regRead(RFM_LORA_IRQ_FLAGS) & (1 << 5)); + } + + return flags; +} + +size_t rfmLoRaRxRead(uint8_t *payload, size_t size) { + // set FIFO address pointer to current RX address + regWrite(RFM_LORA_FIFO_ADDR_PTR, regRead(RFM_LORA_FIFO_CURR_ADDR)); + + size_t len = regRead(RFM_LORA_RX_BYTES_NB); + len = min(len, size); + + _rfmSel(); + _rfmTx(RFM_FIFO); + for (size_t i = 0; i < len; i++) { + payload[i] = _rfmTx(RFM_FIFO); + } + _rfmDes(); + + return len; +} + +size_t rfmLoRaRx(uint8_t *payload, size_t size) { + // clear "RxTimeout" and "RxDone" interrupt + // regWrite(RFM_LORA_IRQ_FLAGS, 0xc0); + + // get "RxTimeout" on DIO1 and "RxDone" on DIO0 + regWrite(RFM_DIO_MAP1, regRead(RFM_DIO_MAP1) & ~0xf0); + rxTimeout = false; + rxDone = false; + + // set FIFO address pointer to configured TX base address + regWrite(RFM_LORA_FIFO_ADDR_PTR, regRead(RFM_LORA_FIFO_RX_ADDR)); + + setMode(RFM_MODE_RXSINGLE); + + // wait until "RxDone" or "RxTimeout" + do {} while (!rxDone && !rxTimeout); + + if (rxTimeout) { + return 0; + } + + return rfmLoRaRxRead(payload, size); +} + +size_t rfmLoRaTx(uint8_t *payload, size_t size) { + size_t len = min(size, RFM_LORA_MSG_SIZE); + + // set FIFO address pointer to configured TX base address + regWrite(RFM_LORA_FIFO_ADDR_PTR, regRead(RFM_LORA_FIFO_TX_ADDR)); + + regWrite(RFM_LORA_PAYLD_LEN, len); + + _rfmSel(); + _rfmTx(RFM_FIFO | 0x80); + for (size_t i = 0; i < len; i++) { + _rfmTx(payload[i]); + } + _rfmDes(); + + // clear "TxDone" interrupt + regWrite(RFM_LORA_IRQ_FLAGS, 0x08); + + // get "TxDone" on DIO0 + regWrite(RFM_DIO_MAP1, (regRead(RFM_DIO_MAP1) & ~0x80) | 0x40); + txDone = false; + + setMode(RFM_MODE_TX); + + // wait until "TxDone" + do {} while (!txDone); return len; } diff --git a/librfm.h b/librfm.h index 5d80df9..1e4913b 100644 --- a/librfm.h +++ b/librfm.h @@ -12,86 +12,125 @@ #include #include -#define FIFO 0x00 -#define OP_MODE 0x01 -#define BITRATE_MSB 0x02 -#define BITRATE_LSB 0x03 -#define FDEV_MSB 0x04 -#define FDEV_LSB 0x05 -#define FRF_MSB 0x06 -#define FRF_MID 0x07 -#define FRF_LSB 0x08 -#define PA_CONFIG 0x09 -#define PA_RAMP 0x0a -#define OCP 0x0b -#define LNA 0x0c -#define RX_CONFIG 0x0d -#define RSSI_CONFIG 0x0e -#define RSSI_COLLIS 0x0f -#define RSSI_THRESH 0x10 -#define RSSI_VALUE 0x11 -#define RX_BW 0x12 -#define AFC_BW 0x13 -#define AFC_FEI 0x1a -#define PREA_DETECT 0x1f -#define RX_TO_RSSI 0x20 -#define RX_TO_PREA 0x21 -#define RX_TO_SYNC 0x22 -#define RX_DELAY 0x23 -#define OSC 0x24 -#define PREA_MSB 0x25 -#define PREA_LSB 0x26 -#define SYNC_CONFIG 0x27 -#define SYNC_VAL1 0x28 -#define SYNC_VAL2 0x29 -#define SYNC_VAL3 0x2a -#define SYNC_VAL4 0x2b -#define SYNC_VAL5 0x2c -#define SYNC_VAL6 0x2d -#define SYNC_VAL7 0x2e -#define SYNC_VAL8 0x2f -#define PCK_CONFIG1 0x30 -#define PCK_CONFIG2 0x31 -#define PAYLOAD_LEN 0x32 -#define NODE_ADDR 0x33 -#define CAST_ADDR 0x34 -#define FIFO_THRESH 0x35 -#define IMAGE_CAL 0x3b -#define TEMP 0x3c -#define LOW_BAT 0x3d -#define IRQ_FLAGS1 0x3e -#define IRQ_FLAGS2 0x3f -#define DIO_MAP1 0x40 -#define DIO_MAP2 0x41 -#define VERSION 0x42 +/* Registers shared by FSK and LoRa mode */ +#define RFM_FIFO 0x00 +#define RFM_OP_MODE 0x01 +#define RFM_FRF_MSB 0x06 +#define RFM_FRF_MID 0x07 +#define RFM_FRF_LSB 0x08 +#define RFM_PA_CONFIG 0x09 +#define RFM_PA_RAMP 0x0a +#define RFM_OCP 0x0b +#define RFM_LNA 0x0c +#define RFM_DIO_MAP1 0x40 +#define RFM_DIO_MAP2 0x41 +#define RFM_VERSION 0x42 -#define MASK_MODE 0x07 +/* FSK mode registers */ +#define RFM_FSK_BITRATE_MSB 0x02 +#define RFM_FSK_BITRATE_LSB 0x03 +#define RFM_FSK_FDEV_MSB 0x04 +#define RFM_FSK_FDEV_LSB 0x05 +#define RFM_FSK_RX_CONFIG 0x0d +#define RFM_FSK_RSSI_CONFIG 0x0e +#define RFM_FSK_RSSI_COLLIS 0x0f +#define RFM_FSK_RSSI_THRESH 0x10 +#define RFM_FSK_RSSI_VALUE 0x11 +#define RFM_FSK_RX_BW 0x12 +#define RFM_FSK_AFC_BW 0x13 +#define RFM_FSK_AFC_FEI 0x1a +#define RFM_FSK_PREA_DETECT 0x1f +#define RFM_FSK_RX_TO_RSSI 0x20 +#define RFM_FSK_RX_TO_PREA 0x21 +#define RFM_FSK_RX_TO_SYNC 0x22 +#define RFM_FSK_RX_DELAY 0x23 +#define RFM_FSK_OSC 0x24 +#define RFM_FSK_PREA_MSB 0x25 +#define RFM_FSK_PREA_LSB 0x26 +#define RFM_FSK_SYNC_CONFIG 0x27 +#define RFM_FSK_SYNC_VAL1 0x28 +#define RFM_FSK_SYNC_VAL2 0x29 +#define RFM_FSK_SYNC_VAL3 0x2a +#define RFM_FSK_SYNC_VAL4 0x2b +#define RFM_FSK_SYNC_VAL5 0x2c +#define RFM_FSK_SYNC_VAL6 0x2d +#define RFM_FSK_SYNC_VAL7 0x2e +#define RFM_FSK_SYNC_VAL8 0x2f +#define RFM_FSK_PCK_CONFIG1 0x30 +#define RFM_FSK_PCK_CONFIG2 0x31 +#define RFM_FSK_PAYLOAD_LEN 0x32 +#define RFM_FSK_NODE_ADDR 0x33 +#define RFM_FSK_CAST_ADDR 0x34 +#define RFM_FSK_FIFO_THRESH 0x35 +#define RFM_FSK_IMAGE_CAL 0x3b +#define RFM_FSK_TEMP 0x3c +#define RFM_FSK_LOW_BAT 0x3d +#define RFM_FSK_IRQ_FLAGS1 0x3e +#define RFM_FSK_IRQ_FLAGS2 0x3f -#define MODE_SLEEP 0x00 -#define MODE_STDBY 0x01 -#define MODE_FS_TX 0x02 -#define MODE_TX 0x03 -#define MODE_FS_RX 0x04 -#define MODE_RX 0x05 +/* LoRa mode registers */ +#define RFM_LORA_FIFO_ADDR_PTR 0x0d +#define RFM_LORA_FIFO_TX_ADDR 0x0e +#define RFM_LORA_FIFO_RX_ADDR 0x0f +#define RFM_LORA_FIFO_CURR_ADDR 0x10 +#define RFM_LORA_IRQ_FLAGS_MASK 0x11 +#define RFM_LORA_IRQ_FLAGS 0x12 +#define RFM_LORA_RX_BYTES_NB 0x13 +#define RFM_LORA_RX_HDR_CNT_MSB 0x14 +#define RFM_LORA_RX_HDR_CNT_LSB 0x15 +#define RFM_LORA_RX_PCK_CNT_MSB 0x16 +#define RFM_LORA_RX_PCK_CNT_LSB 0x17 +#define RFM_LORA_MODEM_STAT 0x18 +#define RFM_LORA_PCK_SNR 0x19 +#define RFM_LORA_PCK_RSSI 0x1a +#define RFM_LORA_RSSI 0x1b +#define RFM_LORA_HOP_CHANNEL 0x1c +#define RFM_LORA_MODEM_CONFIG1 0x1d +#define RFM_LORA_MODEM_CONFIG2 0x1e +#define RFM_LORA_SYMB_TIMEO_LSB 0x1f +#define RFM_LORA_PREA_LEN_MSB 0x20 +#define RFM_LORA_PREA_LEN_LSB 0x21 +#define RFM_LORA_PAYLD_LEN 0x22 +#define RFM_LORA_PAYLD_MAX_LEN 0x23 +#define RFM_LORA_HOP_PERIOD 0x24 +#define RFM_LORA_FIFO_RX_B_ADDR 0x25 +#define RFM_LORA_MODEM_CONFIG3 0x26 -#define DBM_MIN 2 -#define DBM_MAX 17 -#define PA_MIN 0 -#define PA_MAX 15 -#define PA_OFF 2 +/* Values shared by FSK and LoRa mode */ +#define RFM_MODE_SLEEP 0x00 +#define RFM_MODE_STDBY 0x01 +#define RFM_MODE_FS_TX 0x02 +#define RFM_MODE_TX 0x03 +#define RFM_MODE_FS_RX 0x04 +#define RFM_MODE_RX 0x05 // LoRa RXCONTINUOUS +#define RFM_MODE_RXSINGLE 0x06 // LoRa only +#define RFM_MODE_CAD 0x07 // LoRa only +#define RFM_MASK_MODE 0x07 -#define MESSAGE_SIZE 63 -#define F_STEP 61035 -#define CAST_ADDRESS 0x84 +#define RFM_F_STEP 61035 +#define RFM_CAST_ADDRESS 0x84 + +#define RFM_DBM_MIN 2 +#define RFM_DBM_MAX 17 +#define RFM_PA_MIN 0 +#define RFM_PA_MAX 15 +#define RFM_PA_OFF 2 + +/* FSK mode values */ +#define RFM_FSK_MSG_SIZE 63 + +/* LoRa mode values */ +// assuming 50/50 for Tx/Rx for now +#define RFM_LORA_MSG_SIZE 128 /** - * Flags for "payload ready" event. + * Flags for 'PayloadReady'/'RxDone' event. */ typedef struct { bool ready; bool crc; uint8_t rssi; -} PayloadFlags; +} RxFlags; /** * F_CPU dependent delay of 5 milliseconds. @@ -128,12 +167,13 @@ uint8_t _rfmTx(uint8_t data); /** - * Initializes the radio module with the given carrier frequency in kilohertz - * and node address. Returns true on success, false otherwise. + * Initializes the radio module in FSK or LoRa mode with the given carrier + * frequency in kilohertz and node address. Returns true on success, + * false otherwise. * * @return success */ -bool rfmInit(uint64_t freq, uint8_t node); +bool rfmInit(uint64_t freq, uint8_t node, bool lora); /** * Reads interrupt flags. Should be called when any interrupt occurs @@ -143,7 +183,8 @@ /** * Sets the "Timeout" interrupt flag, allowing to "unlock" a possibly hanging - * wait for either "PayloadReady" or "Timeout" by the radio. + * wait for either "PayloadReady" or "Timeout" by the radio in FSK mode, + * ignored in LoRa mode. */ void rfmTimeout(void); @@ -182,22 +223,25 @@ /** * Sets the radio to receive mode and maps "PayloadReady" to DIO0 and enables * or disables timeout. + * For FSK mode. * * @param timeout enable timeout */ void rfmStartReceive(bool timeout); /** - * Returns true if a "PayloadReady" interrupt arrived and clears the - * interrupt state. + * Returns true and puts the radio in standby mode if a "PayloadReady" + * interrupt arrived. + * For FSK mode. * - * @return true if "PayloadReady" + * @return flags */ -PayloadFlags rfmPayloadReady(void); +RxFlags rfmPayloadReady(void); /** * Sets the radio in standby mode, puts the payload into the given array * with the given size, and returns the length of the payload. + * For FSK mode. * * @param payload buffer for payload * @param size of payload buffer @@ -209,6 +253,7 @@ * Waits for "PayloadReady", puts the payload into the given array with the * given size, enables or disables timeout, and returns the length of the * payload, or 0 if a timeout occurred. + * For FSK mode. * * @param payload buffer for payload * @param size of payload buffer @@ -218,7 +263,8 @@ size_t rfmReceivePayload(uint8_t *payload, size_t size, bool timeout); /** - * Transmits up to 64 bytes of the given payload with the given node address. + * Transmits up to 63 bytes of the given payload with the given node address. + * For FSK mode. * * @param payload to be sent * @param size of payload @@ -227,5 +273,46 @@ */ size_t rfmTransmitPayload(uint8_t *payload, size_t size, uint8_t node); -#endif /* LIBRFM_H */ +/** + * Sets the radio in continous receive mode and maps "RxDone" to DIO0. + */ +void rfmLoRaStartRx(void); +/** + * Returns true if a "RxDone" interrupt arrived. + * + * @return flags + */ +RxFlags rfmLoRaRxDone(void); + +/** + * Puts the received payload into the given array with the given size, + * and returns the length of the payload. + * + * @param payload buffer for payload + * @param size of payload buffer + * @return payload bytes actually received + */ +size_t rfmLoRaRxRead(uint8_t *payload, size_t size); + +/** + * Sets the radio in single receive mode, waits for "RxDone" with timeout, + * puts the payload into the given array with the given size, and returns + * the length of the payload, or 0 if a timeout occurred. + * + * @param payload buffer for payload + * @param size of payload buffer + * @return payload bytes actually received + */ +size_t rfmLoRaRx(uint8_t *payload, size_t size); + +/** + * Transmits up to 128 bytes of the given payload. + * + * @param payload to be sent + * @param size of payload + * @return payload bytes actually sent + */ +size_t rfmLoRaTx(uint8_t *payload, size_t size); + +#endif /* LIBRFM_H */