Created
April 24, 2016 20:44
-
-
Save fragsalat/9557cbf2cae5c6bd44e183605e27a8a5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ********************************************************************************** | |
// 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