diff --git a/Makefile b/Makefile index 3e66925..aa7eb6b 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ -# Makefile to build librfm +# Makefile to build librfm69 # # Simplified version from: https://github.com/hexagon5un/AVR-Programming -MAIN = librfm.c +MAIN = librfm69.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: librfm69.h utils.h Makefile all: $(TARGET).a diff --git a/README.md b/README.md index 6358c89..15f1ba5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# librfm +# librfm69 ## About @@ -11,8 +11,8 @@ ## Usage -1. Include `librfm.h` and `librfm.a` in the project -2. Implement the `_rfm*` functions in `librfm.h` in the application +1. Include `librfm69.h` and `librfm69.a` in the project +2. Implement the `_rfm*` functions in `librfm69.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()` diff --git a/librfm.c b/librfm.c deleted file mode 100644 index 991aae1..0000000 --- a/librfm.c +++ /dev/null @@ -1,309 +0,0 @@ -/* - * File: librfm.c - * Author: torsten.roemer@luniks.net - * - * Created on 28. Januar 2025, 19:57 - */ - -#include "librfm.h" -#include "utils.h" - -static volatile uint8_t irqFlags1 = 0; -static volatile uint8_t irqFlags2 = 0; - -/** - * 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(OP_MODE, (regRead(OP_MODE) & ~MASK_MODE) | (mode & MASK_MODE)); -} - -/** - * Clears the IRQ flags read from the module. - */ -static void clearIrqFlags(void) { - irqFlags1 = 0; - irqFlags2 = 0; -} - -/** - * Enables or disables timeouts. - * - * @param enable - */ -static void timeoutEnable(bool enable) { - if (enable) { - // get "Timeout" on DIO4 (default) - regWrite(DIO_MAP2, regRead(DIO_MAP2) & ~0xc0); - // both sum up to about 100 ms - regWrite(RX_TO_RSSI, 0x1f); - regWrite(RX_TO_PRDY, 0x1f); - } else { - regWrite(RX_TO_RSSI, 0x00); - regWrite(RX_TO_PRDY, 0x00); - } -} - -bool rfmInit(uint64_t freq, uint8_t node) { - // wait a bit after power on - _rfmDelay5(); - _rfmDelay5(); - - // pull reset LOW to turn on the module - _rfmOn(); - - _rfmDelay5(); - - uint8_t version = regRead(0x10); - // printString("Version: "); - // printHex(version); - if (version == 0x00) { - return false; - } - - // packet mode, FSK modulation, no shaping (default) - regWrite(DATA_MOD, 0x00); - - // bit rate 9.6 kBit/s - // regWrite(BITRATE_MSB, 0x0d); - // regWrite(BITRATE_LSB, 0x05); - - // frequency deviation (default 5 kHz) - increasing to 10 kHz - // completely removes susceptibility to temperature changes - // RX_BW must be increased accordingly - regWrite(FDEV_MSB, 0x00); - regWrite(FDEV_LSB, 0xa4); - - // RC calibration, automatically done at device power-up - // regWrite(OSC1, 0x80); - // do { } while (!(regRead(OSC1) & 0x40)); - - // PA level (default +13 dBm with PA0, yields very weak output power, why?) - // regWrite(PA_LEVEL, 0x9f); - // +13 dBm on PA1, yields the expected output power - regWrite(PA_LEVEL, 0x5f); - // +17 dBm - doesn't seem to work just like that? - // regWrite(PA_LEVEL, 0x7f); - - // LNA 200 Ohm, gain AGC (default) - regWrite(LNA, 0x88); - // LNA 50 Ohm, gain AGC - // regWrite(LNA, 0x08); - - // LNA high sensitivity mode - // regWrite(TEST_LNA, 0x2d); - - // freq of DC offset canceller and channel filter bandwith (default 10.4 kHz) - // increasing to 20.8 kHz in connection with setting FDEV_*SB to 10 kHz - // completely removes susceptibility to temperature changes - regWrite(RX_BW, 0x54); - - // RX_BW during AFC (default 0x8b) - regWrite(AFC_BW, 0x54); - - // AFC auto on - // regWrite(AFC_FEI, 0x04); - - // RSSI threshold (default, POR 0xff) - regWrite(RSSI_THRESH, 0xe4); - - // Preamble size - regWrite(PREAMB_MSB, 0x00); - regWrite(PREAMB_LSB, 0x03); - - // turn off CLKOUT (not used) - regWrite(DIO_MAP2, 0x07); - - // set the carrier frequency - uint32_t frf = freq * 100000000000ULL / F_STEP; - regWrite(FRF_MSB, frf >> 16); - regWrite(FRF_MID, frf >> 8); - regWrite(FRF_LSB, frf >> 0); - - // enable sync word generation and detection, FIFO fill on sync address, - // 4 bytes sync word, tolerate 3 bit errors - regWrite(SYNC_CONF, 0x9b); - - // 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); - - // variable payload length, crc on, no address matching - // regWrite(PCK_CFG1, 0x90); - // match broadcast or node address - // regWrite(PCK_CFG1, 0x94); - // + CrcAutoClearOff - regWrite(PCK_CFG1, 0x9c); - - // disable automatic RX restart - regWrite(PCK_CFG2, 0x00); - - // node and broadcast address - regWrite(NODE_ADDR, node); - regWrite(CAST_ADDR, CAST_ADDRESS); - - // set TX start condition to "at least one byte in FIFO" - regWrite(FIFO_THRESH, 0x8f); - - // Fading Margin Improvement, improved margin, use if AfcLowBetaOn=0 - regWrite(TEST_DAGC, 0x30); - - // printString("Radio init done\r\n"); - - return true; -} - -void rfmIrq(void) { - irqFlags1 = regRead(IRQ_FLAGS1); - irqFlags2 = regRead(IRQ_FLAGS2); -} - -void rfmSleep(void) { - setMode(MODE_SLEEP); -} - -void rfmWake(void) { - setMode(MODE_STDBY); - // should better wait for ModeReady irq? - _rfmDelay5(); -} - -void rfmSetNodeAddress(uint8_t address) { - regWrite(NODE_ADDR, address); -} - -void rfmSetOutputPower(int8_t dBm) { - uint8_t pa = 0x40; // -18 dBm with PA1 - // adjust power from -2 to +13 dBm - pa |= (min(max(dBm + PA_OFF, PA_MIN), PA_MAX)) & 0x1f; - regWrite(PA_LEVEL, pa); -} - -int8_t rfmGetOutputPower(void) { - return (regRead(PA_LEVEL) & 0x1f) - PA_OFF; -} - -void rfmStartReceive(void) { - // get "PayloadReady" on DIO0 - regWrite(DIO_MAP1, (regRead(DIO_MAP1) & ~0x80) | 0x40); - - setMode(MODE_RX); -} - -PayloadFlags rfmPayloadReady(void) { - PayloadFlags flags = {.ready = false, .rssi = 255, .crc = false}; - if (irqFlags2 & (1 << 2)) { - clearIrqFlags(); - - flags.ready = true; - flags.rssi = regRead(RSSI_VALUE); - flags.crc = regRead(IRQ_FLAGS2) & (1 << 1); - setMode(MODE_STDBY); - } - - return flags; -} - -size_t rfmReadPayload(uint8_t *payload, size_t size) { - size_t len = regRead(FIFO); - len = min(len, size); - - // TODO assume and ignore address for now (already filtered anyway) - regRead(FIFO); - - _rfmSel(); - _rfmTx(FIFO); - for (size_t i = 0; i < len; i++) { - payload[i] = _rfmTx(FIFO); - } - _rfmDes(); - - return len; -} - -size_t rfmReceivePayload(uint8_t *payload, size_t size, bool timeout) { - timeoutEnable(timeout); - rfmStartReceive(); - - // wait until "PayloadReady" or "Timeout" - do {} while (!(irqFlags2 & (1 << 2)) && !(irqFlags1 & (1 << 2))); - bool ready = irqFlags2 & (1 << 2); - bool timedout = irqFlags1 & (1 << 2); - clearIrqFlags(); - - if (ready) { - timeoutEnable(false); - } - - setMode(MODE_STDBY); - - if (timedout) { - // full power as last resort, indicate timeout - regWrite(PA_LEVEL, 0x5f); - - 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, MESSAGE_SIZE) + 1; - - _rfmSel(); - _rfmTx(FIFO | 0x80); - _rfmTx(len); - _rfmTx(node); - for (size_t i = 0; i < size; i++) { - _rfmTx(payload[i]); - } - _rfmDes(); - - // get "PacketSent" on DIO0 (default) - regWrite(DIO_MAP1, regRead(DIO_MAP1) & ~0xc0); - - setMode(MODE_TX); - - // wait until "PacketSent" - do {} while (!(irqFlags2 & (1 << 3))); - clearIrqFlags(); - - setMode(MODE_STDBY); - - return len; -} diff --git a/librfm.h b/librfm.h deleted file mode 100644 index 4db5714..0000000 --- a/librfm.h +++ /dev/null @@ -1,217 +0,0 @@ -/* - * File: librfm.h - * Author: torsten.roemer@luniks.net - * - * Created on 23. März 2025, 23:04 - */ - -#ifndef LIBRFM_H -#define LIBRFM_H - -#include -#include -#include - -#define FIFO 0x00 -#define OP_MODE 0x01 -#define DATA_MOD 0x02 -#define BITRATE_MSB 0x03 -#define BITRATE_LSB 0x04 -#define FDEV_MSB 0x05 -#define FDEV_LSB 0x06 -#define FRF_MSB 0x07 -#define FRF_MID 0x08 -#define FRF_LSB 0x09 -#define OSC1 0x0a -#define PA_LEVEL 0x11 -#define LNA 0x18 -#define RX_BW 0x19 -#define AFC_FEI 0x1e -#define AFC_BW 0x20 -#define RSSI_CONFIG 0x23 -#define RSSI_VALUE 0x24 -#define DIO_MAP1 0x25 -#define DIO_MAP2 0x26 -#define IRQ_FLAGS1 0x27 -#define RSSI_THRESH 0x29 -#define RX_TO_RSSI 0x2a -#define RX_TO_PRDY 0x2b -#define PREAMB_MSB 0x2c -#define PREAMB_LSB 0x2d -#define IRQ_FLAGS2 0x28 -#define SYNC_CONF 0x2e -#define SYNC_VAL1 0x2f -#define SYNC_VAL2 0x30 -#define SYNC_VAL3 0x31 -#define SYNC_VAL4 0x32 -#define SYNC_VAL5 0x33 -#define SYNC_VAL6 0x34 -#define SYNC_VAL7 0x35 -#define SYNC_VAL8 0x36 -#define PCK_CFG1 0x37 -#define PAYLOAD_LEN 0x38 -#define NODE_ADDR 0x39 -#define CAST_ADDR 0x3a -#define AUTO_MODES 0x3b -#define FIFO_THRESH 0x3c -#define PCK_CFG2 0x3d -#define TEST_LNA 0x58 -#define TEST_PA1 0x5a -#define TEST_PA2 0x5c -#define TEST_DAGC 0x6f -#define TEST_AFC 0x71 - -#define MASK_MODE 0x1c - -#define MODE_SLEEP 0x00 -#define MODE_STDBY 0x04 -#define MODE_FS 0x08 -#define MODE_TX 0x0c -#define MODE_RX 0x10 - -#define DBM_MIN -2 -#define DBM_MAX 13 -#define PA_MIN 16 -#define PA_MAX 31 -#define PA_OFF 18 - -#define MESSAGE_SIZE 63 -#define F_STEP 6103515625ULL -#define CAST_ADDRESS 0x84 - -/** - * Flags for "payload ready" event. - */ -typedef struct { - bool ready; - bool crc; - uint8_t rssi; -} PayloadFlags; - -/** - * F_CPU dependent delay of 5 milliseconds. - * _delay_ms(5); - * - * @param ms - */ -void _rfmDelay5(void); - -/** - * Turns the radio on by pulling its reset pin LOW. - * 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 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); - -/** - * Reads interrupt flags. Should be called when any interrupt occurs - * on DIO0 or DIO4. - */ -void rfmIrq(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. - */ -void rfmStartReceive(void); - -/** - * Returns true if a "PayloadReady" interrupt arrived and clears the - * interrupt state. - * - * @return true if "PayloadReady" - */ -PayloadFlags 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. - * - * @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, and returns the length of the payload, or 0 if a timeout - * occurred. - * - * @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 64 bytes of the given payload with the given node address. - * - * @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); - -#endif /* LIBRFM_H */ - diff --git a/librfm69.c b/librfm69.c new file mode 100644 index 0000000..75e1af1 --- /dev/null +++ b/librfm69.c @@ -0,0 +1,309 @@ +/* + * File: librfm69.c + * Author: torsten.roemer@luniks.net + * + * Created on 28. Januar 2025, 19:57 + */ + +#include "librfm69.h" +#include "utils.h" + +static volatile uint8_t irqFlags1 = 0; +static volatile uint8_t irqFlags2 = 0; + +/** + * 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(OP_MODE, (regRead(OP_MODE) & ~MASK_MODE) | (mode & MASK_MODE)); +} + +/** + * Clears the IRQ flags read from the module. + */ +static void clearIrqFlags(void) { + irqFlags1 = 0; + irqFlags2 = 0; +} + +/** + * Enables or disables timeouts. + * + * @param enable + */ +static void timeoutEnable(bool enable) { + if (enable) { + // get "Timeout" on DIO4 (default) + regWrite(DIO_MAP2, regRead(DIO_MAP2) & ~0xc0); + // both sum up to about 100 ms + regWrite(RX_TO_RSSI, 0x1f); + regWrite(RX_TO_PRDY, 0x1f); + } else { + regWrite(RX_TO_RSSI, 0x00); + regWrite(RX_TO_PRDY, 0x00); + } +} + +bool rfmInit(uint64_t freq, uint8_t node) { + // wait a bit after power on + _rfmDelay5(); + _rfmDelay5(); + + // pull reset LOW to turn on the module + _rfmOn(); + + _rfmDelay5(); + + uint8_t version = regRead(0x10); + // printString("Version: "); + // printHex(version); + if (version == 0x00) { + return false; + } + + // packet mode, FSK modulation, no shaping (default) + regWrite(DATA_MOD, 0x00); + + // bit rate 9.6 kBit/s + // regWrite(BITRATE_MSB, 0x0d); + // regWrite(BITRATE_LSB, 0x05); + + // frequency deviation (default 5 kHz) - increasing to 10 kHz + // completely removes susceptibility to temperature changes + // RX_BW must be increased accordingly + regWrite(FDEV_MSB, 0x00); + regWrite(FDEV_LSB, 0xa4); + + // RC calibration, automatically done at device power-up + // regWrite(OSC1, 0x80); + // do { } while (!(regRead(OSC1) & 0x40)); + + // PA level (default +13 dBm with PA0, yields very weak output power, why?) + // regWrite(PA_LEVEL, 0x9f); + // +13 dBm on PA1, yields the expected output power + regWrite(PA_LEVEL, 0x5f); + // +17 dBm - doesn't seem to work just like that? + // regWrite(PA_LEVEL, 0x7f); + + // LNA 200 Ohm, gain AGC (default) + regWrite(LNA, 0x88); + // LNA 50 Ohm, gain AGC + // regWrite(LNA, 0x08); + + // LNA high sensitivity mode + // regWrite(TEST_LNA, 0x2d); + + // freq of DC offset canceller and channel filter bandwith (default 10.4 kHz) + // increasing to 20.8 kHz in connection with setting FDEV_*SB to 10 kHz + // completely removes susceptibility to temperature changes + regWrite(RX_BW, 0x54); + + // RX_BW during AFC (default 0x8b) + regWrite(AFC_BW, 0x54); + + // AFC auto on + // regWrite(AFC_FEI, 0x04); + + // RSSI threshold (default, POR 0xff) + regWrite(RSSI_THRESH, 0xe4); + + // Preamble size + regWrite(PREAMB_MSB, 0x00); + regWrite(PREAMB_LSB, 0x03); + + // turn off CLKOUT (not used) + regWrite(DIO_MAP2, 0x07); + + // set the carrier frequency + uint32_t frf = freq * 100000000000ULL / F_STEP; + regWrite(FRF_MSB, frf >> 16); + regWrite(FRF_MID, frf >> 8); + regWrite(FRF_LSB, frf >> 0); + + // enable sync word generation and detection, FIFO fill on sync address, + // 4 bytes sync word, tolerate 3 bit errors + regWrite(SYNC_CONF, 0x9b); + + // 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); + + // variable payload length, crc on, no address matching + // regWrite(PCK_CFG1, 0x90); + // match broadcast or node address + // regWrite(PCK_CFG1, 0x94); + // + CrcAutoClearOff + regWrite(PCK_CFG1, 0x9c); + + // disable automatic RX restart + regWrite(PCK_CFG2, 0x00); + + // node and broadcast address + regWrite(NODE_ADDR, node); + regWrite(CAST_ADDR, CAST_ADDRESS); + + // set TX start condition to "at least one byte in FIFO" + regWrite(FIFO_THRESH, 0x8f); + + // Fading Margin Improvement, improved margin, use if AfcLowBetaOn=0 + regWrite(TEST_DAGC, 0x30); + + // printString("Radio init done\r\n"); + + return true; +} + +void rfmIrq(void) { + irqFlags1 = regRead(IRQ_FLAGS1); + irqFlags2 = regRead(IRQ_FLAGS2); +} + +void rfmSleep(void) { + setMode(MODE_SLEEP); +} + +void rfmWake(void) { + setMode(MODE_STDBY); + // should better wait for ModeReady irq? + _rfmDelay5(); +} + +void rfmSetNodeAddress(uint8_t address) { + regWrite(NODE_ADDR, address); +} + +void rfmSetOutputPower(int8_t dBm) { + uint8_t pa = 0x40; // -18 dBm with PA1 + // adjust power from -2 to +13 dBm + pa |= (min(max(dBm + PA_OFF, PA_MIN), PA_MAX)) & 0x1f; + regWrite(PA_LEVEL, pa); +} + +int8_t rfmGetOutputPower(void) { + return (regRead(PA_LEVEL) & 0x1f) - PA_OFF; +} + +void rfmStartReceive(void) { + // get "PayloadReady" on DIO0 + regWrite(DIO_MAP1, (regRead(DIO_MAP1) & ~0x80) | 0x40); + + setMode(MODE_RX); +} + +PayloadFlags rfmPayloadReady(void) { + PayloadFlags flags = {.ready = false, .rssi = 255, .crc = false}; + if (irqFlags2 & (1 << 2)) { + clearIrqFlags(); + + flags.ready = true; + flags.rssi = regRead(RSSI_VALUE); + flags.crc = regRead(IRQ_FLAGS2) & (1 << 1); + setMode(MODE_STDBY); + } + + return flags; +} + +size_t rfmReadPayload(uint8_t *payload, size_t size) { + size_t len = regRead(FIFO); + len = min(len, size); + + // TODO assume and ignore address for now (already filtered anyway) + regRead(FIFO); + + _rfmSel(); + _rfmTx(FIFO); + for (size_t i = 0; i < len; i++) { + payload[i] = _rfmTx(FIFO); + } + _rfmDes(); + + return len; +} + +size_t rfmReceivePayload(uint8_t *payload, size_t size, bool timeout) { + timeoutEnable(timeout); + rfmStartReceive(); + + // wait until "PayloadReady" or "Timeout" + do {} while (!(irqFlags2 & (1 << 2)) && !(irqFlags1 & (1 << 2))); + bool ready = irqFlags2 & (1 << 2); + bool timedout = irqFlags1 & (1 << 2); + clearIrqFlags(); + + if (ready) { + timeoutEnable(false); + } + + setMode(MODE_STDBY); + + if (timedout) { + // full power as last resort, indicate timeout + regWrite(PA_LEVEL, 0x5f); + + 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, MESSAGE_SIZE) + 1; + + _rfmSel(); + _rfmTx(FIFO | 0x80); + _rfmTx(len); + _rfmTx(node); + for (size_t i = 0; i < size; i++) { + _rfmTx(payload[i]); + } + _rfmDes(); + + // get "PacketSent" on DIO0 (default) + regWrite(DIO_MAP1, regRead(DIO_MAP1) & ~0xc0); + + setMode(MODE_TX); + + // wait until "PacketSent" + do {} while (!(irqFlags2 & (1 << 3))); + clearIrqFlags(); + + setMode(MODE_STDBY); + + return len; +} diff --git a/librfm69.h b/librfm69.h new file mode 100644 index 0000000..373ebb6 --- /dev/null +++ b/librfm69.h @@ -0,0 +1,217 @@ +/* + * File: librfm69.h + * Author: torsten.roemer@luniks.net + * + * Created on 23. März 2025, 23:04 + */ + +#ifndef LIBRFM_H +#define LIBRFM_H + +#include +#include +#include + +#define FIFO 0x00 +#define OP_MODE 0x01 +#define DATA_MOD 0x02 +#define BITRATE_MSB 0x03 +#define BITRATE_LSB 0x04 +#define FDEV_MSB 0x05 +#define FDEV_LSB 0x06 +#define FRF_MSB 0x07 +#define FRF_MID 0x08 +#define FRF_LSB 0x09 +#define OSC1 0x0a +#define PA_LEVEL 0x11 +#define LNA 0x18 +#define RX_BW 0x19 +#define AFC_FEI 0x1e +#define AFC_BW 0x20 +#define RSSI_CONFIG 0x23 +#define RSSI_VALUE 0x24 +#define DIO_MAP1 0x25 +#define DIO_MAP2 0x26 +#define IRQ_FLAGS1 0x27 +#define RSSI_THRESH 0x29 +#define RX_TO_RSSI 0x2a +#define RX_TO_PRDY 0x2b +#define PREAMB_MSB 0x2c +#define PREAMB_LSB 0x2d +#define IRQ_FLAGS2 0x28 +#define SYNC_CONF 0x2e +#define SYNC_VAL1 0x2f +#define SYNC_VAL2 0x30 +#define SYNC_VAL3 0x31 +#define SYNC_VAL4 0x32 +#define SYNC_VAL5 0x33 +#define SYNC_VAL6 0x34 +#define SYNC_VAL7 0x35 +#define SYNC_VAL8 0x36 +#define PCK_CFG1 0x37 +#define PAYLOAD_LEN 0x38 +#define NODE_ADDR 0x39 +#define CAST_ADDR 0x3a +#define AUTO_MODES 0x3b +#define FIFO_THRESH 0x3c +#define PCK_CFG2 0x3d +#define TEST_LNA 0x58 +#define TEST_PA1 0x5a +#define TEST_PA2 0x5c +#define TEST_DAGC 0x6f +#define TEST_AFC 0x71 + +#define MASK_MODE 0x1c + +#define MODE_SLEEP 0x00 +#define MODE_STDBY 0x04 +#define MODE_FS 0x08 +#define MODE_TX 0x0c +#define MODE_RX 0x10 + +#define DBM_MIN -2 +#define DBM_MAX 13 +#define PA_MIN 16 +#define PA_MAX 31 +#define PA_OFF 18 + +#define MESSAGE_SIZE 63 +#define F_STEP 6103515625ULL +#define CAST_ADDRESS 0x84 + +/** + * Flags for "payload ready" event. + */ +typedef struct { + bool ready; + bool crc; + uint8_t rssi; +} PayloadFlags; + +/** + * F_CPU dependent delay of 5 milliseconds. + * _delay_ms(5); + * + * @param ms + */ +void _rfmDelay5(void); + +/** + * Turns the radio on by pulling its reset pin LOW. + * 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 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); + +/** + * Reads interrupt flags. Should be called when any interrupt occurs + * on DIO0 or DIO4. + */ +void rfmIrq(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. + */ +void rfmStartReceive(void); + +/** + * Returns true if a "PayloadReady" interrupt arrived and clears the + * interrupt state. + * + * @return true if "PayloadReady" + */ +PayloadFlags 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. + * + * @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, and returns the length of the payload, or 0 if a timeout + * occurred. + * + * @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 64 bytes of the given payload with the given node address. + * + * @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); + +#endif /* LIBRFM_H */ + diff --git a/nbproject/Makefile-Custom.mk b/nbproject/Makefile-Custom.mk index a9a3378..04fb513 100644 --- a/nbproject/Makefile-Custom.mk +++ b/nbproject/Makefile-Custom.mk @@ -64,7 +64,7 @@ ${OBJECTDIR}/_ext/5ac08470/librfm.o: /home/dode/dev/librfm/librfm.c ${MKDIR} -p ${OBJECTDIR}/_ext/5ac08470 - $(COMPILE.c) -g -o ${OBJECTDIR}/_ext/5ac08470/librfm.o /home/dode/dev/librfm/librfm.c + $(COMPILE.c) -g -std=c99 -o ${OBJECTDIR}/_ext/5ac08470/librfm.o /home/dode/dev/librfm/librfm.c # Subprojects .build-subprojects: diff --git a/nbproject/configurations.xml b/nbproject/configurations.xml index 18bdf72..42f3da5 100644 --- a/nbproject/configurations.xml +++ b/nbproject/configurations.xml @@ -1,22 +1,22 @@ - - librfm.c + + librfm69.c - /home/dode/dev/librfm/Makefile - /home/dode/dev/librfm/nbproject/private/launcher.properties + /home/dode/dev/librfm69/Makefile + /home/dode/dev/librfm69/nbproject/private/launcher.properties ^(nbproject)$ - /home/dode/dev/librfm + /home/dode/dev/librfm69 - /home/dode/dev/librfm/Makefile + /home/dode/dev/librfm69/Makefile @@ -26,7 +26,7 @@ - + diff --git a/nbproject/project.xml b/nbproject/project.xml index 969067c..fb11d69 100644 --- a/nbproject/project.xml +++ b/nbproject/project.xml @@ -3,14 +3,14 @@ org.netbeans.modules.cnd.makeproject - librfm + librfm69 c h UTF-8 - /home/dode/dev/librfm + /home/dode/dev/librfm69