/*
* File: rfm69.c
* Author: torsten.roemer@luniks.net
*
* Created on 28. Januar 2025, 19:57
*/
#include "rfm69.h"
#include "types.h"
static volatile uint8_t irqFlags1 = 0;
static volatile uint8_t irqFlags2 = 0;
/**
* Selects the radio to talk to via SPI.
*/
static void spiSel(void) {
PORT_RFM &= ~(1 << PIN_RCS);
}
/**
* Deselects the radio to talk to via SPI.
*/
static void spiDes(void) {
PORT_RFM |= (1 << PIN_RCS);
}
/**
* Writes the given value to the given register.
*
* @param reg
* @param value
*/
static void regWrite(uint8_t reg, uint8_t value) {
spiSel();
transmit(reg | 0x80);
transmit(value);
spiDes();
}
/**
* Reads and returns the value of the given register.
*
* @param reg
* @return value
*/
static uint8_t regRead(uint8_t reg) {
spiSel();
transmit(reg & 0x7f);
uint8_t value = transmit(0x00);
spiDes();
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;
}
ISR(INT0_vect) {
irqFlags1 = regRead(IRQ_FLAGS1);
irqFlags2 = regRead(IRQ_FLAGS2);
// printString("irq\r\n");
}
void initRadio(uint64_t freq, uint8_t node) {
// wait a bit after power on
_delay_ms(10);
// pull reset LOW to turn on the module
PORT_RFM &= ~(1 << PIN_RRST);
_delay_ms(5);
uint8_t version = regRead(0x10);
printString("Version: ");
printHex(version);
// 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, 0x0f);
// 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");
}
void setOutputPower(uint8_t rssi) {
uint8_t pa = 0x40; // -18 dBm with PA1
if (rssi > 0 && rssi <= 40) pa += 16;
if (rssi > 40 && rssi <= 60) pa += 21;
if (rssi > 60 && rssi <= 90) pa += 26;
if (rssi > 90) pa += 31;
regWrite(PA_LEVEL, pa);
}
void timeoutRadio(void) {
irqFlags1 |= (1 << 2);
}
void sleepRadio(void) {
setMode(MODE_SLEEP);
}
void wakeRadio(void) {
setMode(MODE_STDBY);
// should better wait for ModeReady irq?
_delay_ms(5);
}
void startReceive(void) {
// get "PayloadReady" on DIO0
regWrite(DIO_MAP1, 0x40);
setMode(MODE_RX);
}
uint8_t readRssi(void) {
return regRead(RSSI_VALUE);
}
PayloadFlags payloadReady(void) {
PayloadFlags flags = {.ready = false, .crc = false};
if (irqFlags2 & (1 << 2)) {
flags.ready = true;
flags.crc = irqFlags2 & (1 << 1);
clearIrqFlags();
setMode(MODE_STDBY);
}
return flags;
}
size_t readPayload(uint8_t *payload, size_t size) {
size_t len = min(regRead(FIFO), FIFO_SIZE) - 1;
len = min(len, size);
// TODO assume and ignore address for now
regRead(FIFO);
spiSel();
transmit(FIFO);
for (size_t i = 0; i < len; i++) {
payload[i] = transmit(FIFO);
}
spiDes();
// FIXME this is not the actual length of the payload received
return len;
}
size_t receivePayload(uint8_t *payload, size_t size) {
startReceive();
// wait until "PayloadReady" or timeout
do {} while (!(irqFlags2 & (1 << 2)) && !(irqFlags1 & (1 << 2)));
bool timeout = irqFlags1 & (1 << 2);
clearIrqFlags();
setMode(MODE_STDBY);
if (timeout) {
return 0;
}
return readPayload(payload, size);
}
size_t transmitPayload(uint8_t *payload, size_t size, uint8_t node) {
// payload + address byte
size_t len = min(size, FIFO_SIZE) + 1;
spiSel();
transmit(FIFO | 0x80);
transmit(len);
transmit(node);
for (size_t i = 0; i < size; i++) {
transmit(payload[i]);
}
spiDes();
// get "PacketSent" on DIO0 (default)
regWrite(DIO_MAP1, 0x00);
setMode(MODE_TX);
loop_until_bit_is_set(irqFlags2, 3);
clearIrqFlags();
setMode(MODE_STDBY);
return len;
}