Skip to content
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
executable file 676 lines (546 sloc) 19.1 KB
/* Copyright (c) 2013 Owen McAree
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "rfm22b.h"
// Set the frequency of the carrier wave
// This function calculates the values of the registers 0x75-0x77 to achieve the
// desired carrier wave frequency (without any hopping set)
// Frequency should be passed in integer Hertz
void RFM22B::setCarrierFrequency(unsigned int frequency) {
// Don't set a frequency outside the range specified in the datasheet
if (frequency < 240E6 || frequency > 960E6) {
printf("Cannot set carrier frequency to %fMHz, out of range!",frequency/1E6f);
return;
}
// The following determines the register values, see Section 3.5.1 of the datasheet
// Are we in the 'High Band'? (i.e. is hbsel == 1)
uint8_t hbsel = (frequency >= 480E6);
// What is the integer part of the frequency
uint8_t fb = frequency/10E6/(hbsel+1) - 24;
// Calculate register 0x75 from hbsel and fb. sbsel (bit 6) is always set
uint8_t fbs = (1<<6) | (hbsel<<5) | fb;
// Calculate the fractional part of the frequency
uint16_t fc = (frequency/(10E6f*(hbsel+1)) - fb - 24) * 64000;
// Split the fractional part in to most and least significant bits
// (Registers 0x76 and 0x77 respectively)
uint8_t ncf1 = (fc >> 8);
uint8_t ncf0 = fc & 0xff;
// Write the registers to the device
this->setRegister(FREQUENCY_BAND_SELECT, fbs);
this->setRegister(NOMINAL_CARRIER_FREQUENCY_1, ncf1);
this->setRegister(NOMINAL_CARRIER_FREQUENCY_0, ncf0);
}
// Get the frequency of the carrier wave in integer Hertz
// Without any frequency hopping
unsigned int RFM22B::getCarrierFrequency() {
// Read the register values
uint8_t fbs = this->getRegister(FREQUENCY_BAND_SELECT);
uint8_t ncf1 = this->getRegister(NOMINAL_CARRIER_FREQUENCY_1);
uint8_t ncf0 = this->getRegister(NOMINAL_CARRIER_FREQUENCY_0);
// The following calculations ceom from Section 3.5.1 of the datasheet
// Determine the integer part
uint8_t fb = fbs & 0x1F;
// Are we in the 'High Band'?
uint8_t hbsel = (fbs >> 5) & 1;
// Determine the fractional part
uint16_t fc = (ncf1 << 8) | ncf0;
// Return the frequency
return 10E6*(hbsel+1)*(fb+24+fc/64000.0);
}
// Get and set the frequency hopping step size
// Values are in Hertz (to stay SI) but floored to the nearest 10kHz
void RFM22B::setFrequencyHoppingStepSize(unsigned int step) {
if (step > 2550000) {
step = 2550000;
}
this->setRegister(FREQUENCY_HOPPING_STEP_SIZE, step/10000);
}
unsigned int RFM22B::getFrequencyHoppingStepSize() {
return this->getRegister(FREQUENCY_HOPPING_STEP_SIZE)*10000;
}
// Get and set the frequency hopping channel
void RFM22B::setChannel(uint8_t channel) {
this->setRegister(FREQUENCY_HOPPING_CHANNEL_SELECT, channel);
}
uint8_t RFM22B::getChannel() {
return this->getRegister(FREQUENCY_HOPPING_CHANNEL_SELECT);
}
// Set or get the frequency deviation (in Hz, but floored to the nearest 625Hz)
void RFM22B::setFrequencyDeviation(unsigned int deviation) {
if (deviation > 320000) {
deviation = 320000;
}
this->setRegister(FREQUENCY_DEVIATION, deviation/625);
}
unsigned int RFM22B::getFrequencyDeviation() {
return this->getRegister(FREQUENCY_DEVIATION)*625;
}
// Set or get the data rate (bps)
void RFM22B::setDataRate(unsigned int rate) {
// Get the Modulation Mode Control 1 register (for scaling bit)
uint8_t mmc1 = this->getRegister(MODULATION_MODE_CONTROL_1);
uint16_t txdr;
// Set the scale bit (5th bit of 0x70) high if data rate is below 30kbps
// and calculate the value for txdr registers (0x6E and 0x6F)
if (rate < 30000) {
mmc1 |= (1<<5);
txdr = rate * ((1 << (16 + 5)) / 1E6);
} else {
mmc1 &= ~(1<<5);
txdr = rate * ((1 << (16)) / 1E6);
}
// Set the data rate bytes
this->set16BitRegister(TX_DATA_RATE_1, txdr);
// Set the scaling byte
this->setRegister(MODULATION_MODE_CONTROL_1, mmc1);
}
unsigned int RFM22B::getDataRate() {
// Get the data rate scaling value (5th bit of 0x70)
uint8_t txdtrtscale = (this->getRegister(MODULATION_MODE_CONTROL_1) >> 5) & 1;
// Get the data rate registers
uint16_t txdr = this->get16BitRegister(TX_DATA_RATE_1);
// Return the data rate (in bps, hence extra 1E3)
return ((unsigned int) txdr * 1E6) / (1 << (16 + 5 * txdtrtscale));
}
// Set or get the modulation type
void RFM22B::setModulationType(RFM22B_Modulation_Type modulation) {
// Get the Modulation Mode Control 2 register
uint8_t mmc2 = this->getRegister(MODULATION_MODE_CONTROL_2);
// Clear the modtyp bits
mmc2 &= ~0x03;
// Set the desired modulation
mmc2 |= modulation;
// Set the register
this->setRegister(MODULATION_MODE_CONTROL_2, mmc2);
}
RFM22B::RFM22B_Modulation_Type RFM22B::getModulationType() {
// Get the Modulation Mode Control 2 register
uint8_t mmc2 = this->getRegister(MODULATION_MODE_CONTROL_2);
// Determine modtyp bits
uint8_t modtyp = mmc2 & 0x03;
// Ugly way to return correct enum
switch (modtyp) {
case 1:
return OOK;
case 2:
return FSK;
case 3:
return GFSK;
case 0:
default:
return UNMODULATED_CARRIER;
}
}
void RFM22B::setModulationDataSource(RFM22B_Modulation_Data_Source source) {
// Get the Modulation Mode Control 2 register
uint8_t mmc2 = this->getRegister(MODULATION_MODE_CONTROL_2);
// Clear the dtmod bits
mmc2 &= ~(0x03<<4);
// Set the desired data source
mmc2 |= source << 4;
// Set the register
this->setRegister(MODULATION_MODE_CONTROL_2, mmc2);
}
RFM22B::RFM22B_Modulation_Data_Source RFM22B::getModulationDataSource() {
// Get the Modulation Mode Control 2 register
uint8_t mmc2 = this->getRegister(MODULATION_MODE_CONTROL_2);
// Determine modtyp bits
uint8_t dtmod = (mmc2 >> 4) & 0x03;
// Ugly way to return correct enum
switch (dtmod) {
case 1:
return DIRECT_SDI;
case 2:
return FIFO;
case 3:
return PN9;
case 0:
default:
return DIRECT_GPIO;
}
}
void RFM22B::setDataClockConfiguration(RFM22B_Data_Clock_Configuration clock) {
// Get the Modulation Mode Control 2 register
uint8_t mmc2 = this->getRegister(MODULATION_MODE_CONTROL_2);
// Clear the trclk bits
mmc2 &= ~(0x03<<6);
// Set the desired data source
mmc2 |= clock << 6;
// Set the register
this->setRegister(MODULATION_MODE_CONTROL_2, mmc2);
}
RFM22B::RFM22B_Data_Clock_Configuration RFM22B::getDataClockConfiguration() {
// Get the Modulation Mode Control 2 register
uint8_t mmc2 = this->getRegister(MODULATION_MODE_CONTROL_2);
// Determine modtyp bits
uint8_t dtmod = (mmc2 >> 6) & 0x03;
// Ugly way to return correct enum
switch (dtmod) {
case 1:
return GPIO;
case 2:
return SDO;
case 3:
return NIRQ;
case 0:
default:
return NONE;
}
}
// Set or get the transmission power
void RFM22B::setTransmissionPower(uint8_t power) {
// Saturate to maximum power
if (power > 20) {
power = 20;
}
// Get the TX power register
uint8_t txp = this->getRegister(TX_POWER);
// Clear txpow bits
txp &= ~(0x07);
// Calculate txpow bits (See Section 5.7.1 of datasheet)
uint8_t txpow = (power + 1) / 3;
// Set txpow bits
txp |= txpow;
// Set the register
this->setRegister(TX_POWER, txp);
}
uint8_t RFM22B::getTransmissionPower() {
// Get the TX power register
uint8_t txp = this->getRegister(TX_POWER);
// Get the txpow bits
uint8_t txpow = txp & 0x07;
// Calculate power (see Section 5.7.1 of datasheet)
if (txpow == 0) {
return 1;
} else {
return txpow * 3 - 1;
}
}
// Set or get the GPIO configuration
void RFM22B::setGPIOFunction(RFM22B_GPIO gpio, RFM22B_GPIO_Function func) {
// Get the GPIO register
uint8_t gpioX = this->getRegister(gpio);
// Clear gpioX bits
gpioX &= ~((1<<5)-1);
// Set the gpioX bits
gpioX |= func;
// Set the register
this->setRegister(gpio, gpioX);
}
uint8_t RFM22B::getGPIOFunction(RFM22B_GPIO gpio) {
// Get the GPIO register
uint8_t gpioX = this->getRegister(gpio);
// Return the gpioX bits
// This should probably return an enum, but this needs a lot of cases
return gpioX & ((1<<5)-1);
}
// Enable or disable interrupts
void RFM22B::setInterruptEnable(RFM22B_Interrupt interrupt, bool enable) {
// Get the (16 bit) register value
uint16_t intEnable = this->get16BitRegister(INTERRUPT_ENABLE_1);
// Either enable or disable the interrupt
if (enable) {
intEnable |= interrupt;
} else {
intEnable &= ~interrupt;
}
// Set the (16 bit) register value
this->set16BitRegister(INTERRUPT_ENABLE_1, intEnable);
}
// Get the status of an interrupt
bool RFM22B::getInterruptStatus(RFM22B_Interrupt interrupt) {
// Get the (16 bit) register value
uint16_t intStatus = this->get16BitRegister(INTERRUPT_STATUS_1);
// Determine if interrupt bit is set and return
if ((intStatus & interrupt) > 0) {
return true;
} else {
return false;
}
}
// Set the operating mode
// This function does not toggle individual pins as with other functions
// It expects a bitwise-ORed combination of the modes you want set
void RFM22B::setOperatingMode(uint16_t mode) {
this->set16BitRegister(OPERATING_MODE_AND_FUNCTION_CONTROL_1, mode);
}
// Get operating mode (bitwise-ORed)
uint16_t RFM22B::getOperatingMode() {
return this->get16BitRegister(OPERATING_MODE_AND_FUNCTION_CONTROL_1);
}
// Manuall enter RX or TX mode
void RFM22B::enableRXMode() {
this->setOperatingMode(READY_MODE | RX_MODE);
}
void RFM22B::enableTXMode() {
this->setOperatingMode(READY_MODE | TX_MODE);
}
// Reset the device
void RFM22B::reset() {
this->setOperatingMode(READY_MODE | RESET);
}
// Set or get the trasmit header
void RFM22B::setTransmitHeader(uint32_t header) {
this->set32BitRegister(TRANSMIT_HEADER_3, header);
}
uint32_t RFM22B::getTransmitHeader() {
return this->get32BitRegister(TRANSMIT_HEADER_3);
}
// Set or get the check header
void RFM22B::setCheckHeader(uint32_t header) {
this->set32BitRegister(CHECK_HEADER_3, header);
}
uint32_t RFM22B::getCheckHeader() {
return this->get32BitRegister(CHECK_HEADER_3);
}
// Set or get the CRC mode
void RFM22B::setCRCMode(RFM22B::RFM22B_CRC_Mode mode) {
uint8_t dac = this->getRegister(DATA_ACCESS_CONTROL);
dac &= ~0x24;
switch (mode) {
case CRC_DISABLED:
break;
case CRC_DATA_ONLY:
dac |= 0x24;
break;
case CRC_NORMAL:
default:
dac |= 0x04;
break;
}
this->setRegister(DATA_ACCESS_CONTROL, dac);
}
RFM22B::RFM22B_CRC_Mode RFM22B::getCRCMode() {
uint8_t dac = this->getRegister(DATA_ACCESS_CONTROL);
if (! (dac & 0x04)) {
return CRC_DISABLED;
}
if (dac & 0x20) {
return CRC_DATA_ONLY;
}
return CRC_NORMAL;
}
// Set or get the CRC polynomial
void RFM22B::setCRCPolynomial(RFM22B::RFM22B_CRC_Polynomial poly) {
uint8_t dac = this->getRegister(DATA_ACCESS_CONTROL);
dac &= ~0x03;
dac |= poly;
this->setRegister(DATA_ACCESS_CONTROL, dac);
}
RFM22B::RFM22B_CRC_Polynomial RFM22B::getCRCPolynomial() {
uint8_t dac = this->getRegister(DATA_ACCESS_CONTROL);
switch (dac & 0x03) {
case 0:
return CCITT;
case 1:
return CRC16;
case 2:
return IEC16;
case 3:
return BIACHEVA;
}
return CRC16;
}
// Get and set all the FIFO threshold
void RFM22B::setTXFIFOAlmostFullThreshold(uint8_t thresh) {
this->setFIFOThreshold(TX_FIFO_CONTROL_1, thresh);
}
void RFM22B::setTXFIFOAlmostEmptyThreshold(uint8_t thresh) {
this->setFIFOThreshold(TX_FIFO_CONTROL_2, thresh);
}
void RFM22B::setRXFIFOAlmostFullThreshold(uint8_t thresh) {
this->setFIFOThreshold(RX_FIFO_CONTROL, thresh);
}
uint8_t RFM22B::getTXFIFOAlmostFullThreshold() {
return this->getRegister(TX_FIFO_CONTROL_1);
}
uint8_t RFM22B::getTXFIFOAlmostEmptyThreshold() {
return this->getRegister(TX_FIFO_CONTROL_2);
}
uint8_t RFM22B::getRXFIFOAlmostFullThreshold() {
return this->getRegister(RX_FIFO_CONTROL);
}
void RFM22B::setFIFOThreshold(RFM22B_Register reg, uint8_t thresh) {
thresh &= ((1 << 6) - 1);
this->setRegister(reg, thresh);
}
// Get RSSI value
uint8_t RFM22B::getRSSI() {
return this->getRegister(RECEIVED_SIGNAL_STRENGTH_INDICATOR);
}
// Get input power (in dBm)
// Coefficients approximated from the graph in Section 8.10 of the datasheet
int8_t RFM22B::getInputPower() {
return 0.56*this->getRSSI()-128.8;
}
// Get length of last received packet
uint8_t RFM22B::getReceivedPacketLength() {
return this->getRegister(RECEIVED_PACKET_LENGTH);
}
// Set length of packet to be transmitted
void RFM22B::setTransmitPacketLength(uint8_t length) {
return this->setRegister(TRANSMIT_PACKET_LENGTH, length);
}
void RFM22B::clearRXFIFO() {
//Toggle ffclrrx bit high and low to clear RX FIFO
this->setRegister(OPERATING_MODE_AND_FUNCTION_CONTROL_2, 2);
this->setRegister(OPERATING_MODE_AND_FUNCTION_CONTROL_2, 0);
}
void RFM22B::clearTXFIFO() {
//Toggle ffclrtx bit high and low to clear TX FIFO
this->setRegister(OPERATING_MODE_AND_FUNCTION_CONTROL_2, 1);
this->setRegister(OPERATING_MODE_AND_FUNCTION_CONTROL_2, 0);
}
// Send data
void RFM22B::send(uint8_t *data, int length) {
// Clear TX FIFO
this->clearTXFIFO();
// Initialise rx and tx arrays
uint8_t tx[MAX_PACKET_LENGTH+1] = { 0 };
uint8_t rx[MAX_PACKET_LENGTH+1] = { 0 };
// Set FIFO register address (with write flag)
tx[0] = FIFO_ACCESS | (1<<7);
// Truncate data if its too long
if (length > MAX_PACKET_LENGTH) {
length = MAX_PACKET_LENGTH;
}
// Copy data from input array to tx array
for (int i = 1; i <= length; i++) {
tx[i] = data[i-1];
}
// Set the packet length
this->setTransmitPacketLength(length);
// Make the transfer
this->transfer(tx,rx,length+1);
// Enter TX mode
this->enableTXMode();
// Loop until packet has been sent (device has left TX mode)
while (((this->getRegister(OPERATING_MODE_AND_FUNCTION_CONTROL_1)>>3) & 1)) {}
return;
};
// Receive data (blocking with timeout). Returns number of bytes received
int RFM22B::receive(uint8_t *data, int length, int timeout) {
// Make sure RX FIFO is empty, ready for new data
this->clearRXFIFO();
// Enter RX mode
this->enableRXMode();
// Initialise rx and tx arrays
uint8_t tx[MAX_PACKET_LENGTH+1] = { 0 };
uint8_t rx[MAX_PACKET_LENGTH+1] = { 0 };
// Set FIFO register address
tx[0] = FIFO_ACCESS;
// Timing for the interrupt loop timeout
struct timeval start, end;
gettimeofday(&start, NULL);
long elapsed = 0;
// Loop endlessly on interrupt or timeout
// Don't use interrupt registers here as these don't seem to behave consistently
// Watch the operating mode register for the device leaving RX mode. This is indicitive
// of a valid packet being received
while (((this->getRegister(OPERATING_MODE_AND_FUNCTION_CONTROL_1)>>2) & 1) && elapsed < timeout) {
// Determine elapsed time
gettimeofday(&end, NULL);
elapsed = (end.tv_usec - start.tv_usec)/1000 + (end.tv_sec - start.tv_sec)*1000;
}
// If timeout occured, return -1
if (elapsed >= timeout) {
return -1;
}
// Get length of packet received
uint8_t rxLength = this->getReceivedPacketLength();
if (rxLength > length) {
rxLength = length;
}
// Make the transfer
this->transfer(tx,rx,rxLength+1);
// Copy the data to the output array
for (int i = 1; i <= rxLength; i++) {
data[i-1] = rx[i];
}
return rxLength;
};
// Helper function to read a single byte from the device
uint8_t RFM22B::getRegister(uint8_t reg) {
// rx and tx arrays must be the same length
// Must be 2 elements as the device only responds whilst it is being sent
// data. tx[0] should be set to the requested register value and tx[1] left
// clear. Once complete, rx[0] will be left clear (no data was returned whilst
// the requested register was being sent), and rx[1] will contain the value
uint8_t tx[] = {0x00, 0x00};
uint8_t rx[] = {0x00, 0x00};
tx[0] = reg;
this->transfer(tx,rx,2);
return rx[1];
}
// Similar to function above, but for readying 2 consequtive registers as one
uint16_t RFM22B::get16BitRegister(uint8_t reg) {
uint8_t tx[] = {0x00, 0x00, 0x00};
uint8_t rx[] = {0x00, 0x00, 0x00};
tx[0] = reg;
this->transfer(tx,rx,3);
return (rx[1] << 8) | rx[2];
}
// Similar to function above, but for readying 4 consequtive registers as one
uint32_t RFM22B::get32BitRegister(uint8_t reg) {
uint8_t tx[] = {0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t rx[] = {0x00, 0x00, 0x00, 0x00, 0x00};
tx[0] = reg;
this->transfer(tx,rx,5);
return (rx[1] << 24) | (rx[2] << 16) | (rx[3] << 8) | rx[4];
}
// Helper function to write a single byte to a register
void RFM22B::setRegister(uint8_t reg, uint8_t value) {
// tx and rx arrays required even though we aren't receiving anything
uint8_t tx[] = {0x00, 0x00};
uint8_t rx[] = {0x00, 0x00};
// tx[0] is the requested register with the final bit set high to indicate
// a write operation (see Section 3.1 of the datasheet)
tx[0] = reg | (1<<7);
// tx[1] is the value to be set
tx[1] = value;
this->transfer(tx,rx,2);
}
// As above, but for 2 consequitive registers
void RFM22B::set16BitRegister(uint8_t reg, uint16_t value) {
// tx and rx arrays required even though we aren't receiving anything
uint8_t tx[] = {0x00, 0x00, 0x00};
uint8_t rx[] = {0x00, 0x00, 0x00};
// tx[0] is the requested register with the final bit set high to indicate
// a write operation (see Section 3.1 of the datasheet)
tx[0] = reg | (1<<7);
// tx[1-2] is the value to be set
tx[1] = (value >> 8);
tx[2] = (value) & 0xFF;
this->transfer(tx,rx,3);
}
// As above, but for 4 consequitive registers
void RFM22B::set32BitRegister(uint8_t reg, uint32_t value) {
// tx and rx arrays required even though we aren't receiving anything
uint8_t tx[] = {0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t rx[] = {0x00, 0x00, 0x00, 0x00, 0x00};
// tx[0] is the requested register with the final bit set high to indicate
// a write operation (see Section 3.1 of the datasheet)
tx[0] = reg | (1<<7);
// tx[1-4] is the value to be set
tx[1] = (value >> 24);
tx[2] = (value >> 16) & 0xFF;
tx[3] = (value >> 8) & 0xFF;
tx[4] = (value) & 0xFF;
this->transfer(tx,rx,5);
}