diff --git a/Makefile b/Makefile index 3e66925..0ac793b 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ -# Makefile to build librfm +# Makefile to build librfm95 # # Simplified version from: https://github.com/hexagon5un/AVR-Programming -MAIN = librfm.c +MAIN = librfm95.c CC = avr-gcc AR = avr-ar @@ -25,7 +25,7 @@ OBJ = $(SRC:.c=.o) OBJ = $(SRC:.S=.o) -$(TARGET).o: librfm.h utils.h Makefile +$(TARGET).o: librfm95.h utils.h Makefile all: $(TARGET).a diff --git a/librfm.c b/librfm.c deleted file mode 100644 index 88a9be7..0000000 --- a/librfm.c +++ /dev/null @@ -1,477 +0,0 @@ -/* - * File: librfm.c - * Author: torsten.roemer@luniks.net - * - * Created on 28. Januar 2025, 19:57 - */ - -#include "librfm.h" -#include "utils.h" - -/* 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. - * - * @param reg - * @param value - */ -static void regWrite(uint8_t reg, uint8_t value) { - _rfmSel(); - _rfmTx(reg | 0x80); - _rfmTx(value); - _rfmDes(); -} - -/** - * Reads and returns the value of the given register. - * - * @param reg - * @return value - */ -static uint8_t regRead(uint8_t reg) { - _rfmSel(); - _rfmTx(reg & 0x7f); - uint8_t value = _rfmTx(0x00); - _rfmDes(); - - return value; -} - -/** - * Sets the module to the given operating mode. - */ -static void setMode(uint8_t mode) { - regWrite(RFM_OP_MODE, (regRead(RFM_OP_MODE) & ~RFM_MASK_MODE) | (mode & RFM_MASK_MODE)); -} - -/** - * Enables or disables timeouts in FSK mode. - * - * @param enable - */ -static void timeoutEnableFSK(bool enable) { - // get "Timeout" on DIO4 - regWrite(RFM_DIO_MAP2, (regRead(RFM_DIO_MAP2) | 0x80) & ~0x40); - rxTimeout = false; - if (enable) { - // TODO calculate - seems to be about 50, 75, 100ms - regWrite(RFM_FSK_RX_TO_RSSI, 0x1f); - regWrite(RFM_FSK_RX_TO_PREA, 0x2f); - regWrite(RFM_FSK_RX_TO_SYNC, 0x3f); - } else { - regWrite(RFM_FSK_RX_TO_RSSI, 0x00); - regWrite(RFM_FSK_RX_TO_PREA, 0x00); - regWrite(RFM_FSK_RX_TO_SYNC, 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(RFM_FSK_FDEV_MSB, 0x00); - regWrite(RFM_FSK_FDEV_LSB, 0xa4); - - // AgcAutoOn, receiver trigger event PreambleDetect - regWrite(RFM_FSK_RX_CONFIG, 0x0e); - - // no RSSI offset, 32 samples used - regWrite(RFM_FSK_RSSI_CONFIG, 0x04); - - // 10 dB threshold for interferer detection - regWrite(RFM_FSK_RSSI_COLLIS, 0x0a); - - // RSSI threshold (POR 0xff) - regWrite(RFM_FSK_RSSI_THRESH, 0x94); - - // channel filter bandwith 20.8 kHz (default 10.4 kHz) - regWrite(RFM_FSK_RX_BW, 0x14); - - // RX_BW during AFC - regWrite(RFM_FSK_AFC_BW, 0x14); - - // AFC auto on - // regWrite(AFC_FEI, 0x00); - - // PreambleDetectorOn, PreambleDetectorSize 2 bytes, - // PreambleDetectorTol 4 chips per bit - regWrite(RFM_FSK_PREA_DETECT, 0xaa); - - // Preamble size - 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(RFM_FSK_SYNC_CONFIG, 0x13); - - // just set all sync word values to some really creative value - 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(RFM_FSK_PCK_CONFIG1, 0x9c); - - // Packet mode, ..., PayloadLength(10:8) - regWrite(RFM_FSK_PCK_CONFIG2, 0x40); - - // PayloadLength(7:0) - regWrite(RFM_FSK_PAYLOAD_LEN, 0x40); - - // node and broadcast 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(RFM_FSK_FIFO_THRESH, 0x8f); - - // AutoImageCalOn disabled, TempThreshold 10°C, TempMonitorOff 0 - regWrite(RFM_FSK_IMAGE_CAL, 0x02); - - // printString("Radio init done\r\n"); - - return true; -} - -static bool initLoRa(void) { - // SPI interface address pointer in FIFO data buffer (POR 0x00) - regWrite(RFM_LORA_FIFO_ADDR_PTR, 0x00); - - // 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) { - 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(RFM_MODE_SLEEP); -} - -void rfmWake(void) { - setMode(RFM_MODE_STDBY); - // should better wait for ModeReady irq? - _rfmDelay5(); -} - -void rfmSetNodeAddress(uint8_t 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 - RFM_PA_OFF, RFM_PA_MIN), RFM_PA_MAX)) & 0x0f; - regWrite(RFM_PA_CONFIG, pa); -} - -int8_t rfmGetOutputPower(void) { - return (regRead(RFM_PA_CONFIG) & 0x0f) + RFM_PA_OFF; -} - -void rfmStartReceive(bool timeout) { - timeoutEnableFSK(timeout); - - // get "PayloadReady" on DIO0 - regWrite(RFM_DIO_MAP1, regRead(RFM_DIO_MAP1) & ~0xc0); - rxDone = false; - - setMode(RFM_MODE_RX); -} - -RxFlags rfmPayloadReady(void) { - RxFlags flags = {.ready = false, .rssi = 255, .crc = false}; - if (rxDone) { - flags.ready = true; - 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(RFM_FIFO); - len = min(len, size); - - // TODO assume and ignore address for now (already filtered anyway) - regRead(RFM_FIFO); - - _rfmSel(); - _rfmTx(RFM_FIFO); - for (size_t i = 0; i < len; i++) { - payload[i] = _rfmTx(RFM_FIFO); - } - _rfmDes(); - - return len; -} - -size_t rfmReceivePayload(uint8_t *payload, size_t size, bool enable) { - rfmStartReceive(enable); - - // wait until "PayloadReady" or (forced) "Timeout" - do {} while (!rxDone && !rxTimeout); - - if (rxDone) { - timeoutEnableFSK(false); - } - - setMode(RFM_MODE_STDBY); - - if (rxTimeout) { - // full power as last resort, indicate timeout - regWrite(RFM_PA_CONFIG, 0xff); - - return 0; - } - - return rfmReadPayload(payload, size); -} - -size_t rfmTransmitPayload(uint8_t *payload, size_t size, uint8_t node) { - // message + address byte - size_t len = min(size, RFM_FSK_MSG_SIZE) + 1; - - _rfmSel(); - _rfmTx(RFM_FIFO | 0x80); - _rfmTx(len); - _rfmTx(node); - for (size_t i = 0; i < size; i++) { - _rfmTx(payload[i]); - } - _rfmDes(); - - // get "PacketSent" on DIO0 (default) - regWrite(RFM_DIO_MAP1, regRead(RFM_DIO_MAP1) & ~0xc0); - txDone = false; - - setMode(RFM_MODE_TX); - - // wait until "PacketSent" - do {} while (!txDone); - - 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 deleted file mode 100644 index 1e4913b..0000000 --- a/librfm.h +++ /dev/null @@ -1,318 +0,0 @@ -/* - * File: librfm.h - * Author: torsten.roemer@luniks.net - * - * Created on 30. März 2025 - */ - -#ifndef LIBRFM_H -#define LIBRFM_H - -#include -#include -#include - -/* 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 - -/* 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 - -/* 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 - -/* 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 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 'PayloadReady'/'RxDone' event. - */ -typedef struct { - bool ready; - bool crc; - uint8_t rssi; -} RxFlags; - -/** - * F_CPU dependent delay of 5 milliseconds. - * _delay_ms(5); - * - * @param ms - */ -void _rfmDelay5(void); - -/** - * Turns the radio on by pulling its reset pin high. - * PORTB |= (1 << PB0); - */ -void _rfmOn(void); - -/** - * Selects the radio to talk to via SPI. - * PORTB &= ~(1 << PB1); - */ -void _rfmSel(void); - -/** - * Deselects the radio to talk to via SPI. - * PORTB |= (1 << PB1); - */ -void _rfmDes(void); - -/** - * SPI transmits/receives given data/returns it. - * - * @param data - * @return data - */ -uint8_t _rfmTx(uint8_t data); - -/** - * 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 lora); - -/** - * Reads interrupt flags. Should be called when any interrupt occurs - * on DIO0 or DIO4. - */ -void rfmIrq(void); - -/** - * Sets the "Timeout" interrupt flag, allowing to "unlock" a possibly hanging - * wait for either "PayloadReady" or "Timeout" by the radio in FSK mode, - * ignored in LoRa mode. - */ -void rfmTimeout(void); - -/** - * Shuts down the radio. - */ -void rfmSleep(void); - -/** - * Wakes up the radio. - */ -void rfmWake(void); - -/** - * Sets the node address. - * - * @param address - */ -void rfmSetNodeAddress(uint8_t address); - -/** - * Sets the output power to -2 to +13 dBm. - * Values outside that range are ignored. - * - * @param dBm ouput power - */ -void rfmSetOutputPower(int8_t dBm); - -/** - * Returns the current output power setting in dBm. - * - * @return ouput power - */ -int8_t rfmGetOutputPower(void); - -/** - * 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 and puts the radio in standby mode if a "PayloadReady" - * interrupt arrived. - * For FSK mode. - * - * @return flags - */ -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 - * @return payload bytes actually received - */ -size_t rfmReadPayload(uint8_t *payload, size_t size); - -/** - * 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 - * @param timeout enable timeout - * @return payload bytes actually received - */ -size_t rfmReceivePayload(uint8_t *payload, size_t size, bool timeout); - -/** - * 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 - * @param node address - * @return payload bytes actually sent - */ -size_t rfmTransmitPayload(uint8_t *payload, size_t size, uint8_t node); - -/** - * 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 */ diff --git a/librfm95.c b/librfm95.c new file mode 100644 index 0000000..062d89f --- /dev/null +++ b/librfm95.c @@ -0,0 +1,477 @@ +/* + * File: librfm95.c + * Author: torsten.roemer@luniks.net + * + * Created on 28. Januar 2025, 19:57 + */ + +#include "librfm95.h" +#include "utils.h" + +/* 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. + * + * @param reg + * @param value + */ +static void regWrite(uint8_t reg, uint8_t value) { + _rfmSel(); + _rfmTx(reg | 0x80); + _rfmTx(value); + _rfmDes(); +} + +/** + * Reads and returns the value of the given register. + * + * @param reg + * @return value + */ +static uint8_t regRead(uint8_t reg) { + _rfmSel(); + _rfmTx(reg & 0x7f); + uint8_t value = _rfmTx(0x00); + _rfmDes(); + + return value; +} + +/** + * Sets the module to the given operating mode. + */ +static void setMode(uint8_t mode) { + regWrite(RFM_OP_MODE, (regRead(RFM_OP_MODE) & ~RFM_MASK_MODE) | (mode & RFM_MASK_MODE)); +} + +/** + * Enables or disables timeouts in FSK mode. + * + * @param enable + */ +static void timeoutEnableFSK(bool enable) { + // get "Timeout" on DIO4 + regWrite(RFM_DIO_MAP2, (regRead(RFM_DIO_MAP2) | 0x80) & ~0x40); + rxTimeout = false; + if (enable) { + // TODO calculate - seems to be about 50, 75, 100ms + regWrite(RFM_FSK_RX_TO_RSSI, 0x1f); + regWrite(RFM_FSK_RX_TO_PREA, 0x2f); + regWrite(RFM_FSK_RX_TO_SYNC, 0x3f); + } else { + regWrite(RFM_FSK_RX_TO_RSSI, 0x00); + regWrite(RFM_FSK_RX_TO_PREA, 0x00); + regWrite(RFM_FSK_RX_TO_SYNC, 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(RFM_FSK_FDEV_MSB, 0x00); + regWrite(RFM_FSK_FDEV_LSB, 0xa4); + + // AgcAutoOn, receiver trigger event PreambleDetect + regWrite(RFM_FSK_RX_CONFIG, 0x0e); + + // no RSSI offset, 32 samples used + regWrite(RFM_FSK_RSSI_CONFIG, 0x04); + + // 10 dB threshold for interferer detection + regWrite(RFM_FSK_RSSI_COLLIS, 0x0a); + + // RSSI threshold (POR 0xff) + regWrite(RFM_FSK_RSSI_THRESH, 0x94); + + // channel filter bandwith 20.8 kHz (default 10.4 kHz) + regWrite(RFM_FSK_RX_BW, 0x14); + + // RX_BW during AFC + regWrite(RFM_FSK_AFC_BW, 0x14); + + // AFC auto on + // regWrite(AFC_FEI, 0x00); + + // PreambleDetectorOn, PreambleDetectorSize 2 bytes, + // PreambleDetectorTol 4 chips per bit + regWrite(RFM_FSK_PREA_DETECT, 0xaa); + + // Preamble size + 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(RFM_FSK_SYNC_CONFIG, 0x13); + + // just set all sync word values to some really creative value + 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(RFM_FSK_PCK_CONFIG1, 0x9c); + + // Packet mode, ..., PayloadLength(10:8) + regWrite(RFM_FSK_PCK_CONFIG2, 0x40); + + // PayloadLength(7:0) + regWrite(RFM_FSK_PAYLOAD_LEN, 0x40); + + // node and broadcast 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(RFM_FSK_FIFO_THRESH, 0x8f); + + // AutoImageCalOn disabled, TempThreshold 10°C, TempMonitorOff 0 + regWrite(RFM_FSK_IMAGE_CAL, 0x02); + + // printString("Radio init done\r\n"); + + return true; +} + +static bool initLoRa(void) { + // SPI interface address pointer in FIFO data buffer (POR 0x00) + regWrite(RFM_LORA_FIFO_ADDR_PTR, 0x00); + + // 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) { + 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(RFM_MODE_SLEEP); +} + +void rfmWake(void) { + setMode(RFM_MODE_STDBY); + // should better wait for ModeReady irq? + _rfmDelay5(); +} + +void rfmSetNodeAddress(uint8_t 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 - RFM_PA_OFF, RFM_PA_MIN), RFM_PA_MAX)) & 0x0f; + regWrite(RFM_PA_CONFIG, pa); +} + +int8_t rfmGetOutputPower(void) { + return (regRead(RFM_PA_CONFIG) & 0x0f) + RFM_PA_OFF; +} + +void rfmStartReceive(bool timeout) { + timeoutEnableFSK(timeout); + + // get "PayloadReady" on DIO0 + regWrite(RFM_DIO_MAP1, regRead(RFM_DIO_MAP1) & ~0xc0); + rxDone = false; + + setMode(RFM_MODE_RX); +} + +RxFlags rfmPayloadReady(void) { + RxFlags flags = {.ready = false, .rssi = 255, .crc = false}; + if (rxDone) { + flags.ready = true; + 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(RFM_FIFO); + len = min(len, size); + + // TODO assume and ignore address for now (already filtered anyway) + regRead(RFM_FIFO); + + _rfmSel(); + _rfmTx(RFM_FIFO); + for (size_t i = 0; i < len; i++) { + payload[i] = _rfmTx(RFM_FIFO); + } + _rfmDes(); + + return len; +} + +size_t rfmReceivePayload(uint8_t *payload, size_t size, bool enable) { + rfmStartReceive(enable); + + // wait until "PayloadReady" or (forced) "Timeout" + do {} while (!rxDone && !rxTimeout); + + if (rxDone) { + timeoutEnableFSK(false); + } + + setMode(RFM_MODE_STDBY); + + if (rxTimeout) { + // full power as last resort, indicate timeout + regWrite(RFM_PA_CONFIG, 0xff); + + return 0; + } + + return rfmReadPayload(payload, size); +} + +size_t rfmTransmitPayload(uint8_t *payload, size_t size, uint8_t node) { + // message + address byte + size_t len = min(size, RFM_FSK_MSG_SIZE) + 1; + + _rfmSel(); + _rfmTx(RFM_FIFO | 0x80); + _rfmTx(len); + _rfmTx(node); + for (size_t i = 0; i < size; i++) { + _rfmTx(payload[i]); + } + _rfmDes(); + + // get "PacketSent" on DIO0 (default) + regWrite(RFM_DIO_MAP1, regRead(RFM_DIO_MAP1) & ~0xc0); + txDone = false; + + setMode(RFM_MODE_TX); + + // wait until "PacketSent" + do {} while (!txDone); + + 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/librfm95.h b/librfm95.h new file mode 100644 index 0000000..07708ba --- /dev/null +++ b/librfm95.h @@ -0,0 +1,318 @@ +/* + * File: librfm95.h + * Author: torsten.roemer@luniks.net + * + * Created on 30. März 2025 + */ + +#ifndef LIBRFM_H +#define LIBRFM_H + +#include +#include +#include + +/* 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 + +/* 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 + +/* 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 + +/* 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 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 'PayloadReady'/'RxDone' event. + */ +typedef struct { + bool ready; + bool crc; + uint8_t rssi; +} RxFlags; + +/** + * F_CPU dependent delay of 5 milliseconds. + * _delay_ms(5); + * + * @param ms + */ +void _rfmDelay5(void); + +/** + * Turns the radio on by pulling its reset pin high. + * PORTB |= (1 << PB0); + */ +void _rfmOn(void); + +/** + * Selects the radio to talk to via SPI. + * PORTB &= ~(1 << PB1); + */ +void _rfmSel(void); + +/** + * Deselects the radio to talk to via SPI. + * PORTB |= (1 << PB1); + */ +void _rfmDes(void); + +/** + * SPI transmits/receives given data/returns it. + * + * @param data + * @return data + */ +uint8_t _rfmTx(uint8_t data); + +/** + * 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 lora); + +/** + * Reads interrupt flags. Should be called when any interrupt occurs + * on DIO0 or DIO4. + */ +void rfmIrq(void); + +/** + * Sets the "Timeout" interrupt flag, allowing to "unlock" a possibly hanging + * wait for either "PayloadReady" or "Timeout" by the radio in FSK mode, + * ignored in LoRa mode. + */ +void rfmTimeout(void); + +/** + * Shuts down the radio. + */ +void rfmSleep(void); + +/** + * Wakes up the radio. + */ +void rfmWake(void); + +/** + * Sets the node address. + * + * @param address + */ +void rfmSetNodeAddress(uint8_t address); + +/** + * Sets the output power to -2 to +13 dBm. + * Values outside that range are ignored. + * + * @param dBm ouput power + */ +void rfmSetOutputPower(int8_t dBm); + +/** + * Returns the current output power setting in dBm. + * + * @return ouput power + */ +int8_t rfmGetOutputPower(void); + +/** + * 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 and puts the radio in standby mode if a "PayloadReady" + * interrupt arrived. + * For FSK mode. + * + * @return flags + */ +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 + * @return payload bytes actually received + */ +size_t rfmReadPayload(uint8_t *payload, size_t size); + +/** + * 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 + * @param timeout enable timeout + * @return payload bytes actually received + */ +size_t rfmReceivePayload(uint8_t *payload, size_t size, bool timeout); + +/** + * 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 + * @param node address + * @return payload bytes actually sent + */ +size_t rfmTransmitPayload(uint8_t *payload, size_t size, uint8_t node); + +/** + * 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 */ diff --git a/nbproject/Makefile-Custom.mk b/nbproject/Makefile-Custom.mk index 0b4c468..bb68404 100644 --- a/nbproject/Makefile-Custom.mk +++ b/nbproject/Makefile-Custom.mk @@ -35,7 +35,7 @@ # Object Files OBJECTFILES= \ - ${OBJECTDIR}/_ext/acb12f8c/librfm.o + ${OBJECTDIR}/_ext/acb12f8c/librfm95.o # C Compiler Flags @@ -62,9 +62,9 @@ ${MKDIR} -p ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM} ${LINK.c} -o ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/librfm95 ${OBJECTFILES} ${LDLIBSOPTIONS} -${OBJECTDIR}/_ext/acb12f8c/librfm.o: /home/dode/dev/librfm95/librfm.c +${OBJECTDIR}/_ext/acb12f8c/librfm95.o: /home/dode/dev/librfm95/librfm95.c ${MKDIR} -p ${OBJECTDIR}/_ext/acb12f8c - $(COMPILE.c) -g -o ${OBJECTDIR}/_ext/acb12f8c/librfm.o /home/dode/dev/librfm95/librfm.c + $(COMPILE.c) -g -o ${OBJECTDIR}/_ext/acb12f8c/librfm95.o /home/dode/dev/librfm95/librfm95.c # Subprojects .build-subprojects: diff --git a/nbproject/configurations.xml b/nbproject/configurations.xml index 92d7847..862fe27 100644 --- a/nbproject/configurations.xml +++ b/nbproject/configurations.xml @@ -2,7 +2,7 @@ - librfm.c + librfm95.c - +