Skip to content

Instantly share code, notes, and snippets.

@fragsalat
Created April 24, 2016 20:44
Show Gist options
  • Save fragsalat/9557cbf2cae5c6bd44e183605e27a8a5 to your computer and use it in GitHub Desktop.
Save fragsalat/9557cbf2cae5c6bd44e183605e27a8a5 to your computer and use it in GitHub Desktop.
// **********************************************************************************
// Driver definition for HopeRF RFM69W/RFM69HW/RFM69CW/RFM69HCW, Semtech SX1231/1231H
// **********************************************************************************
// Copyright Felix Rusu (2014), felix@lowpowerlab.com
// http://lowpowerlab.com/
// **********************************************************************************
// License
// **********************************************************************************
// This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU General
// Public License as published by the Free Software
// Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will
// be useful, but WITHOUT ANY WARRANTY; without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General
// Public License along with this program.
// If not, see <http://www.gnu.org/licenses/>.
//
// Licence can be viewed at
// http://www.gnu.org/licenses/gpl-3.0.txt
//
// Please maintain this license information along with authorship
// and copyright notices in any redistribution of this code
// **********************************************************************************
#include "RFM69OOK.h"
#include "RFM69OOKregisters.h"
#include <wiringPiSPI.h>
#include <wiringPi.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#define SPI_SPEED 2000000
#define SPI_DEVICE 0
volatile byte RFM69OOK::_mode; // current transceiver state
volatile int RFM69OOK::RSSI; // most accurate RSSI during reception (closest to the reception)
RFM69OOK* RFM69OOK::selfPointer;
RFM69OOK::RFM69OOK() {
_slaveSelectPin = RF69OOK_SPI_CS;
_interruptPin = RF69OOK_IRQ_PIN;
_interruptNum = RF69OOK_IRQ_NUM;
_mode = RF69OOK_MODE_STANDBY;
_powerLevel = 31;
_isRFM69HW = false;
};
void RFM69OOK::initialize() {
const byte CONFIG[][2] =
{
/* 0x01 */ { REG_OPMODE, RF_OPMODE_SEQUENCER_OFF | RF_OPMODE_LISTEN_OFF | RF_OPMODE_STANDBY },
/* 0x02 */ { REG_DATAMODUL, RF_DATAMODUL_DATAMODE_CONTINUOUSNOBSYNC | RF_DATAMODUL_MODULATIONTYPE_OOK | RF_DATAMODUL_MODULATIONSHAPING_00 }, // no shaping
/* 0x03 */ { REG_BITRATEMSB, 0x03}, // bitrate: 32768 Hz
/* 0x04 */ { REG_BITRATELSB, 0xD1},
/* 0x19 */ { REG_RXBW, RF_RXBW_DCCFREQ_010 | RF_RXBW_MANT_24 | RF_RXBW_EXP_4}, // BW: 10.4 kHz
/* 0x1B */ { REG_OOKPEAK, RF_OOKPEAK_THRESHTYPE_PEAK | RF_OOKPEAK_PEAKTHRESHSTEP_000 | RF_OOKPEAK_PEAKTHRESHDEC_000 },
/* 0x1D */ { REG_OOKFIX, 6 }, // Fixed threshold value (in dB) in the OOK demodulator
/* 0x29 */ { REG_RSSITHRESH, 140 }, // RSSI threshold in dBm = -(REG_RSSITHRESH / 2)
/* 0x6F */ { REG_TESTDAGC, RF_DAGC_IMPROVED_LOWBETA0 }, // run DAGC continuously in RX mode, recommended default for AfcLowBetaOn=0
{255, 0}
};
pinMode(_slaveSelectPin, OUTPUT);
// Initialize SPI device 0
if (wiringPiSPISetup(SPI_DEVICE, SPI_SPEED) < 0) {
fprintf(stderr, "Unable to open SPI device\n\r");
exit(1);
}
if (wiringPiSetup() < 0) {
fprintf(stderr, "Unable to open SPI device\n\r");
exit(1);
}
for (byte i = 0; CONFIG[i][0] != 255; i++)
writeReg(CONFIG[i][0], CONFIG[i][1]);
setHighPower(_isRFM69HW); // called regardless if it's a RFM69W or RFM69HW
setMode(RF69OOK_MODE_STANDBY);
while ((readReg(REG_IRQFLAGS1) & RF_IRQFLAGS1_MODEREADY) == 0x00); // Wait for ModeReady
selfPointer = this;
}
// Poll for OOK signal
bool RFM69OOK::poll() {
return digitalRead(_interruptPin);
}
// Send a 1 or 0 signal in OOK mode
void RFM69OOK::send(bool signal) {
digitalWrite(_interruptPin, signal);
}
// Turn the radio into transmission mode
void RFM69OOK::transmitBegin() {
setMode(RF69OOK_MODE_TX);
// detachInterrupt(_interruptNum); // not needed in TX mode
pinMode(_interruptPin, OUTPUT);
}
// Turn the radio back to standby
void RFM69OOK::transmitEnd() {
pinMode(_interruptPin, INPUT);
setMode(RF69OOK_MODE_STANDBY);
}
// Turn the radio into OOK listening mode
void RFM69OOK::receiveBegin() {
pinMode(_interruptPin, INPUT);
wiringPiISR(_interruptPin, INT_EDGE_BOTH, &RFM69OOK::isr0); // generate interrupts in RX mode
setMode(RF69OOK_MODE_RX);
}
// Turn the radio back to standby
void RFM69OOK::receiveEnd() {
setMode(RF69OOK_MODE_STANDBY);
// detachInterrupt(_interruptNum); // make sure there're no surprises
}
// Handle pin change interrupts in OOK mode
void RFM69OOK::interruptHandler() {
printf("interrupt\n");
if (userInterrupt != NULL) (*userInterrupt)();
}
// Set a user interrupt for all transfer methods in receive mode
// call with NULL to disable the user interrupt handler
void RFM69OOK::attachUserInterrupt(void (*function)()) {
userInterrupt = function;
}
// return the frequency (in Hz)
uint32_t RFM69OOK::getFrequency() {
return RF69OOK_FSTEP * (((uint32_t)readReg(REG_FRFMSB)<<16) + ((uint16_t)readReg(REG_FRFMID)<<8) + readReg(REG_FRFLSB));
}
// Set literal frequency using floating point MHz value
void RFM69OOK::setFrequencyMHz(float f) {
setFrequency(f * 1000000);
}
// set the frequency (in Hz)
void RFM69OOK::setFrequency(uint32_t freqHz) {
// TODO: p38 hopping sequence may need to be followed in some cases
freqHz /= RF69OOK_FSTEP; // divide down by FSTEP to get FRF
writeReg(REG_FRFMSB, freqHz >> 16);
writeReg(REG_FRFMID, freqHz >> 8);
writeReg(REG_FRFLSB, freqHz);
}
// set OOK bandwidth
void RFM69OOK::setBandwidth(uint8_t bw) {
writeReg(REG_RXBW, readReg(REG_RXBW) & 0xE0 | bw);
}
// set RSSI threshold
void RFM69OOK::setRSSIThreshold(int8_t rssi) {
writeReg(REG_RSSITHRESH, -(rssi << 1));
}
// set OOK fixed threshold
void RFM69OOK::setFixedThreshold(uint8_t threshold) {
writeReg(REG_OOKFIX, threshold);
}
// set sensitivity boost in REG_TESTLNA
// see: http://www.sevenwatt.com/main/rfm69-ook-dagc-sensitivity-boost-and-modulation-index
void RFM69OOK::setSensitivityBoost(uint8_t value) {
writeReg(REG_TESTLNA, value);
}
void RFM69OOK::setMode(byte newMode) {
if (newMode == _mode) return;
switch (newMode) {
case RF69OOK_MODE_TX:
writeReg(REG_OPMODE, (readReg(REG_OPMODE) & 0xE3) | RF_OPMODE_TRANSMITTER);
if (_isRFM69HW) setHighPowerRegs(true);
break;
case RF69OOK_MODE_RX:
writeReg(REG_OPMODE, (readReg(REG_OPMODE) & 0xE3) | RF_OPMODE_RECEIVER);
if (_isRFM69HW) setHighPowerRegs(false);
break;
case RF69OOK_MODE_SYNTH:
writeReg(REG_OPMODE, (readReg(REG_OPMODE) & 0xE3) | RF_OPMODE_SYNTHESIZER);
break;
case RF69OOK_MODE_STANDBY:
writeReg(REG_OPMODE, (readReg(REG_OPMODE) & 0xE3) | RF_OPMODE_STANDBY);
break;
case RF69OOK_MODE_SLEEP:
writeReg(REG_OPMODE, (readReg(REG_OPMODE) & 0xE3) | RF_OPMODE_SLEEP);
break;
default: return;
}
// waiting for mode ready is necessary when going from sleep because the FIFO may not be immediately available from previous mode
while (_mode == RF69OOK_MODE_SLEEP && (readReg(REG_IRQFLAGS1) & RF_IRQFLAGS1_MODEREADY) == 0x00); // Wait for ModeReady
_mode = newMode;
}
void RFM69OOK::sleep() {
setMode(RF69OOK_MODE_SLEEP);
}
// set output power: 0=min, 31=max
// this results in a "weaker" transmitted signal, and directly results in a lower RSSI at the receiver
void RFM69OOK::setPowerLevel(byte powerLevel) {
_powerLevel = powerLevel;
writeReg(REG_PALEVEL, (readReg(REG_PALEVEL) & 0xE0) | (_powerLevel > 31 ? 31 : _powerLevel));
}
void RFM69OOK::isr0() { selfPointer->interruptHandler(); }
int RFM69OOK::readRSSI(bool forceTrigger) {
int rssi = 0;
if (forceTrigger)
{
// RSSI trigger not needed if DAGC is in continuous mode
writeReg(REG_RSSICONFIG, RF_RSSI_START);
while ((readReg(REG_RSSICONFIG) & RF_RSSI_DONE) == 0x00); // Wait for RSSI_Ready
}
rssi = -readReg(REG_RSSIVALUE);
rssi >>= 1;
return rssi;
}
byte RFM69OOK::readReg(byte addr) {
unsigned char data[2];
data[0] = addr & 0x7F;
data[1] = 0;
digitalWrite(_slaveSelectPin, LOW);
wiringPiSPIDataRW(SPI_DEVICE, data, 2);
delay(5);
digitalWrite(_slaveSelectPin, HIGH);
return data[1];
}
void RFM69OOK::writeReg(byte addr, byte value) {
unsigned char data[2];
data[0] = addr | 0x80;
data[1] = value;
digitalWrite(_slaveSelectPin, LOW);
wiringPiSPIDataRW(SPI_DEVICE, data, 2);
delay(5);
digitalWrite(_slaveSelectPin, HIGH);
}
void RFM69OOK::setHighPower(bool onOff) {
_isRFM69HW = onOff;
writeReg(REG_OCP, _isRFM69HW ? RF_OCP_OFF : RF_OCP_ON);
if (_isRFM69HW) // turning ON
writeReg(REG_PALEVEL, (readReg(REG_PALEVEL) & 0x1F) | RF_PALEVEL_PA1_ON | RF_PALEVEL_PA2_ON); // enable P1 & P2 amplifier stages
else
writeReg(REG_PALEVEL, RF_PALEVEL_PA0_ON | RF_PALEVEL_PA1_OFF | RF_PALEVEL_PA2_OFF | _powerLevel); // enable P0 only
}
void RFM69OOK::setHighPowerRegs(bool onOff) {
writeReg(REG_TESTPA1, onOff ? 0x5D : 0x55);
writeReg(REG_TESTPA2, onOff ? 0x7C : 0x70);
}
void RFM69OOK::setCS(byte newSPISlaveSelect) {
_slaveSelectPin = newSPISlaveSelect;
pinMode(_slaveSelectPin, OUTPUT);
}
// for debugging
void RFM69OOK::readAllRegs() {
byte regVal;
for (byte regAddr = 1; regAddr <= 0x4F; regAddr++) {
regVal = readReg(regAddr);
printf("%02X - %02X - %b\n", regAddr, regVal, regVal);
}
}
byte RFM69OOK::readTemperature(byte calFactor) { // returns centigrade
setMode(RF69OOK_MODE_STANDBY);
writeReg(REG_TEMP1, RF_TEMP1_MEAS_START);
while ((readReg(REG_TEMP1) & RF_TEMP1_MEAS_RUNNING));
return ~readReg(REG_TEMP2) + COURSE_TEMP_COEF + calFactor; // 'complement' corrects the slope, rising temp = rising val
} // COURSE_TEMP_COEF puts reading in the ballpark, user can add additional correction
void RFM69OOK::rcCalibration() {
writeReg(REG_OSC1, RF_OSC1_RCCAL_START);
while ((readReg(REG_OSC1) & RF_OSC1_RCCAL_DONE) == 0x00);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment