Saturday, February 23, 2013
RFM12B testing notes
Now I am reading my old blog post to refresh my memory on how to play with the clock. The so called crux of the matter is the following 14 statements.
SetupRfm12b()
01 putWord(0x80D7); //enable tx, rx fifo, 433MHz, 12.0pF
02 # putWord(0x8239); //enable xmitt, syn, xtal, disable clk
03 putWord(0xA640); //operation frequency ???
04 putWord(0xC647); //data rate 4.8kbps
05 putWord(0x94A0); //VDI,FAST,134kHz,0dBm,-103dBm
06 putWord(0xC2AC); //clk rec auto, digi fltr,DQD4
07 putWord(0xCA81); //fifo8,sync 1, sync, no fifo fill
08 putWord(0xCED4); //sync pattern d4
09 putWord(0xC483); //offset VDI hi, no restrict, afc, afc out
10 putWord(0x9850); //90kHz, max o/p pwr (odBm)
11 putWord(0xCC17); //10MHz, 620uA, disable pll dit, 256kbps
12 putWord(0xE000); //wake up timer not used
13 putWord(0xC800); //low duty cycle not used
14 putWord(0xC040); //1.66MHz,2.2V low battery
Last time I used Arduino C++ bit banging to do SCI PutWord. This time of course I will try RPi WiringPi SCI.
First thing I think I should do is to write a Python function to send a 2 byte word to RFM12B.
Wireless Transceiver 433MHz RFM12B Experiments 1 - 2009nov06 pm12:30
http://blog.yahoo.com/_ZGD2MIDSBMSKHSUJW23LXRS2EQ/articles/8583/category/My+Arduino+RFM12B
RFM12B Preliminary Test 2009/07
http://space.taobao.com/12d9ec1e98a6515fa89aa39751189943/show_blog-24340125.htm
Like what I did to test the RF module some two months ago, I usually did a 2 step preliminary self diagnostic test to make sure that the module is more or less working.
1. Use a scope to display pin 8 of the module. This pin should display the defualt external mcu use frequency of 1.000 MHz.
2. Use an Arduino program to change the ext mcu frequency to 2.000 MHz. This makes sure the following.
a. the module can be initialized.
b. the module can accept commands from Arduino.
c. the module can decode and exectue commands.
So I did the test and it took a few minutes for me to debug the 2MHz test program (I forgot to bypass the default disable ext clock init command.
So far so good. I guess I will repeat the other test functions and add them into the function library.
// *****************************************************************************
// RFM12B01.pde v0.01a - Example for RFM12B library functions 2009jul07hkt1529
// Created by TL Fong, 2009jul04
// Released into the public domain for learning. No commercial.
// *****************************************************************************
#include
#define SCK_PIN 13
#define SDO_PIN 11
#define SDI_PIN 12
#define CS_PIN 10
#define NIRQ_PIN 2
void setup()
{
}
void loop()
{
RFM12B01 rfm1(SCK_PIN, SDO_PIN, SDI_PIN, CS_PIN, NIRQ_PIN);
rfm1.testSetupClock2Mhz();
while(true)
{
}
}
// *****************************************************************************
// RFM12B01.h v0.02a - RFM12B library functions 2009jul07hkt523
// Created by TL Fong, 2009jul04
// Released into the public domain for learning. No commercial.
// *****************************************************************************
#ifndef RFM12B01_h
#define RFM12B01_h
#include "WProgram.h"
# define wkupOn 0x820E // turn on wake up timer (also xtl, lvd)
# define wkupOff 0x820C // turn off wake up timer (xtl, lvd still on)
# define wkup2ms 0XE002 // wake up every 2mS
# define config01 0x80D7
# define power01 0x8239
# define freq01 0xA640
# define drate01 0xC647
# define rxctrl01 0x94A0
# define fifors01 0xCA81
# define syncpt01 0xCED4
# define afc01 0xC483
# define txctrl01 0x9850
# define pll01 0xCC77
# define wkup01 0xE000
# define ldc01 0xC800
# define lbdclk01 0xC400
# define lvdOn 0x820C // turn on low voltage detection (also xtal, mcu clk)
# define lvd27 0xC064 // set low voltage threshold 2.7V (2.2 + 0.1 * 4) 2MHz,
# define status 0x0000 // read status to execute command and clear interrupt
# define clkMhz01 0xC004 // 01MHz, 2.7V (2.2 + 0.1 * 4)
# define clkMhz02 0xC064 // 02MHz, 2.7V (2.2 + 0.1 * 4)
# define clkMhz05 0xC0C4 // 05MHz, 2.7V (2.2 + 0.1 * 4)
# define clkMhz10 0xC0E4 // 10MHz, 2.7V (2.2 + 0.1 * 4)
class RFM12B01
{
public:
RFM12B01(int sckPin, int sdoPin, int sdiPin, int csPin, int nirqPin);
void putWord(unsigned int xWord);
void setupArduinoPins();
void setupRfm12b();
void sendChar(unsigned char data);
void sendFrame();
void testSetupClock2Mhz();
private:
int _sckPin;
int _sdoPin;
int _sdiPin;
int _csPin;
int _nirqPin;
};
#endif
// *****************************************************************************
// RFM12B01.cpp RFM12B01 Library v0.01a 2009jul07hkt1528 TL Fong
// Hope RFM12B v2.0, Seeeduino v1.1, Arduino 15
// Released into the public domain for learning. No commercial.
// *****************************************************************************
#include "WProgram.h"
#include "RFM12B01.h"
RFM12B01::RFM12B01(int sckPin, int sdoPin, int sdiPin, int csPin, int nirqPin)
{
_sckPin = sckPin;
_sdoPin = sdoPin;
_sdiPin = sdiPin;
_csPin = csPin;
_nirqPin = nirqPin;
setupArduinoPins();
setupRfm12b();
}
void RFM12B01::setupArduinoPins()
{
digitalWrite(_sckPin, LOW);
digitalWrite(_sdoPin, LOW);
digitalWrite(_csPin, LOW);
pinMode(_sckPin, OUTPUT);
pinMode(_sdoPin, OUTPUT);
pinMode(_csPin, OUTPUT);
pinMode(_sdiPin, INPUT);
pinMode(_nirqPin, INPUT);
}
void RFM12B01::putWord(unsigned int xWord)
{
int i;
digitalWrite(_csPin, LOW);
digitalWrite(_sckPin, LOW);
for(i = 0; i < 16; i++)
{
if (xWord & 0x8000)
digitalWrite(_sdoPin, HIGH); // if MSB (most significant bit) is 1 then prepare to output HIGH
else
digitalWrite(_sdoPin, LOW); // else output LOW
digitalWrite(_sckPin, HIGH); // clock high to output bit
digitalWrite(_sckPin, LOW); // clock low to get ready for next bit
xWord <<= 1; // shift next bit to MSB position
}
digitalWrite(_csPin, HIGH); // execute command
}
void RFM12B01::setupRfm12b()
{
putWord(0x80D7); //enable tx, rx fifo, 433MHz, 12.0pF
// putWord(0x8239); //enable xmitt, syn, xtal, disable clk
putWord(0xA640); //operation frequency ???
putWord(0xC647); //data rate 4.8kbps
putWord(0x94A0); //VDI,FAST,134kHz,0dBm,-103dBm
putWord(0xC2AC); //clk rec auto, digi fltr,DQD4
putWord(0xCA81); //fifo8,sync 1, sync, no fifo fill
putWord(0xCED4); //sync pattern d4
putWord(0xC483); //offset VDI hi, no restrict, afc, afc out
putWord(0x9850); //90kHz, max o/p pwr (odBm)
putWord(0xCC17); //10MHz, 620uA, disable pll dit, 256kbps
putWord(0xE000); //wake up timer not used
putWord(0xC800); //low duty cycle not used
putWord(0xC040); //1.66MHz,2.2V low battery
}
void RFM12B01::testSetupClock2Mhz()
{
putWord(0xC064);
}
.END
#define SCK_PIN 13
#define SDO_PIN 11
#define SDI_PIN 12
#define CS_PIN 10
#define NIRQ_PIN 2
void loop()
{
RFM12B01 rfm1(SCK_PIN, SDO_PIN, SDI_PIN, CS_PIN, NIRQ_PIN);
rfm1.testSetupClock2Mhz();
while(true)
{
}
}
# define wkupOn 0x820E // turn on wake up timer (also xtl, lvd)
# define wkupOff 0x820C // turn off wake up timer (xtl, lvd still on)
# define wkup2ms 0XE002 // wake up every 2mS
# define config01 0x80D7
# define power01 0x8239
# define freq01 0xA640
# define drate01 0xC647
# define rxctrl01 0x94A0
# define fifors01 0xCA81
# define syncpt01 0xCED4
# define afc01 0xC483
# define txctrl01 0x9850
# define pll01 0xCC77
# define wkup01 0xE000
# define ldc01 0xC800
# define lbdclk01 0xC400
# define lvdOn 0x820C // turn on low voltage detection (also xtal, mcu clk)
# define lvd27 0xC064 // set low voltage threshold 2.7V (2.2 + 0.1 * 4) 2MHz,
# define status 0x0000 // read status to execute command and clear interrupt
# define clkMhz01 0xC004 // 01MHz, 2.7V (2.2 + 0.1 * 4)
# define clkMhz02 0xC064 // 02MHz, 2.7V (2.2 + 0.1 * 4)
# define clkMhz05 0xC0C4 // 05MHz, 2.7V (2.2 + 0.1 * 4)
# define clkMhz10 0xC0E4 // 10MHz, 2.7V (2.2 + 0.1 * 4)
class RFM12B01
{
public:
RFM12B01(int sckPin, int sdoPin, int sdiPin, int csPin, int nirqPin);
void putWord(unsigned int xWord);
void setupArduinoPins();
void setupRfm12b();
void sendChar(unsigned char data);
void sendFrame();
void testSetupClock2Mhz();
private:
int _sckPin;
int _sdoPin;
int _sdiPin;
int _csPin;
int _nirqPin;
};
RFM12B01::RFM12B01(int sckPin, int sdoPin, int sdiPin, int csPin, int nirqPin)
{
_sckPin = sckPin;
_sdoPin = sdoPin;
_sdiPin = sdiPin;
_csPin = csPin;
_nirqPin = nirqPin;
setupArduinoPins();
setupRfm12b();
}
void RFM12B01::setupArduinoPins()
{
digitalWrite(_sckPin, LOW);
digitalWrite(_sdoPin, LOW);
digitalWrite(_csPin, LOW);
pinMode(_sckPin, OUTPUT);
pinMode(_sdoPin, OUTPUT);
pinMode(_csPin, OUTPUT);
pinMode(_sdiPin, INPUT);
pinMode(_nirqPin, INPUT);
}
void RFM12B01::putWord(unsigned int xWord)
{
int i;
digitalWrite(_csPin, LOW);
digitalWrite(_sckPin, LOW);
for(i = 0; i < 16; i++)
{
if (xWord & 0x8000)
digitalWrite(_sdoPin, HIGH); // if MSB (most significant bit) is 1 then prepare to output HIGH
else
digitalWrite(_sdoPin, LOW); // else output LOW
digitalWrite(_sckPin, HIGH); // clock high to output bit
digitalWrite(_sckPin, LOW); // clock low to get ready for next bit
xWord <<= 1; // shift next bit to MSB position
}
digitalWrite(_csPin, HIGH); // execute command
}
void RFM12B01::setupRfm12b()
{
putWord(0x80D7); //enable tx, rx fifo, 433MHz, 12.0pF
// putWord(0x8239); //enable xmitt, syn, xtal, disable clk
putWord(0xA640); //operation frequency ???
putWord(0xC647); //data rate 4.8kbps
putWord(0x94A0); //VDI,FAST,134kHz,0dBm,-103dBm
putWord(0xC2AC); //clk rec auto, digi fltr,DQD4
putWord(0xCA81); //fifo8,sync 1, sync, no fifo fill
putWord(0xCED4); //sync pattern d4
putWord(0xC483); //offset VDI hi, no restrict, afc, afc out
putWord(0x9850); //90kHz, max o/p pwr (odBm)
putWord(0xCC17); //10MHz, 620uA, disable pll dit, 256kbps
putWord(0xE000); //wake up timer not used
putWord(0xC800); //low duty cycle not used
putWord(0xC040); //1.66MHz,2.2V low battery
}
void RFM12B01::testSetupClock2Mhz()
{
putWord(0xC064);
}
.END
/// RF12 CTL bit mask.
//#define RF12_HDR_CTL 0x80
/// RF12 DST bit mask.
//#define RF12_HDR_DST 0x40
/// RF12 ACK bit mask.
//#define RF12_HDR_ACK 0x20
/// RF12 HDR bit mask.
//#define RF12_HDR_MASK 0x1F
/// RF12 SENDER extracted from last packet
//#define RF12_SOURCEID rf12_hdr & RF12_HDR_MASK
/// RF12 Maximum message size in bytes.
#define RF12_MAXDATA 128
/// Max transmit/receive buffer: 4 header + data + 2 crc bytes
#define RF_MAX (RF12_MAXDATA + 6)
//frequency bands
#define RF12_315MHZ 0
#define RF12_433MHZ 1
#define RF12_868MHZ 2
#define RF12_915MHZ 3
//Low batteery threshold (eg 2v25 = 2.25V)
#define RF12_2v25 0
#define RF12_2v55 3
#define RF12_2v65 4
#define RF12_2v75 5
#define RF12_3v05 8
#define RF12_3v15 9
#define RF12_3v25 10
#define RF12_HDR_IDMASK 0x7F
#define RF12_HDR_ACKCTLMASK 0x80
#define RF12_DESTID (rf12_hdr1 & RF12_HDR_IDMASK)
#define RF12_SOURCEID (rf12_hdr2 & RF12_HDR_IDMASK)
// shorthands to simplify sending out the proper ACK when requested
#define RF12_WANTS_ACK ((rf12_hdr2 & RF12_HDR_ACKCTLMASK) && !(rf12_hdr1 & RF12_HDR_ACKCTLMASK))
// options for RF12_sleep()
#define RF12_SLEEP 0
#define RF12_WAKEUP -1
/// Shorthand for RF12 group byte in rf12_buf.
#define rf12_grp rf12_buf[0]
/// pointer to 1st header byte in rf12_buf (CTL + DESTINATIONID)
#define rf12_hdr1 rf12_buf[1]
/// pointer to 2nd header byte in rf12_buf (ACK + SOURCEID)
#define rf12_hdr2 rf12_buf[2]
/// Shorthand for RF12 length byte in rf12_buf.
#define rf12_len rf12_buf[3]
/// Shorthand for first RF12 data byte in rf12_buf.
#define rf12_data (rf12_buf + 4)
// #define PINCHG_IRQ 1 // uncomment this to use pin-change interrupts
// ATmega168, ATmega328, etc.
#define RFM_IRQ 2
#define SS_DDR DDRB
#define SS_PORT PORTB
#define SS_BIT 2 // for PORTB: 2 = d.10, 1 = d.9, 0 = d.8
#define SPI_SS 10 // PB2, pin 16
#define SPI_MOSI 11 // PB3, pin 17
#define SPI_MISO 12 // PB4, pin 18
#define SPI_SCK 13 // PB5, pin 19
#endif
// RF12 command codes
#define RF_RECEIVER_ON 0x82DD
#define RF_XMITTER_ON 0x823D
#define RF_IDLE_MODE 0x820D
#define RF_SLEEP_MODE 0x8205
#define RF_WAKEUP_MODE 0x8207
#define RF_TXREG_WRITE 0xB800
#define RF_RX_FIFO_READ 0xB000
#define RF_WAKEUP_TIMER 0xE000
//RF12 status bits
#define RF_LBD_BIT 0x0400
#define RF_RSSI_BIT 0x0100
// transceiver states, these determine what to do with each interrupt
enum {
TXCRC1, TXCRC2, TXTAIL, TXDONE, TXIDLE,
TXRECV,
TXPRE1, TXPRE2, TXPRE3, TXSYN1, TXSYN2,
};
extern volatile uint8_t rf12_buf[RF_MAX]; // recv/xmit buf, including hdr & crc bytes
class RFM12B
{
static volatile uint8_t rxfill; // number of data bytes in rf12_buf
static volatile int8_t rxstate; // current transceiver state
static volatile uint16_t rf12_crc; // running crc value
static uint32_t seqNum; // encrypted send sequence number
static uint32_t cryptKey[4]; // encryption key to use
static long rf12_seq; // seq number of encrypted packet (or -1)
static uint8_t cs_pin; // chip select pin
void (*crypter)(bool); // does en-/decryption (null if disabled)
static uint8_t Byte(uint8_t out);
static uint16_t XFERSlow(uint16_t cmd);
static void XFER(uint16_t cmd);
static void InterruptHandler();
void SPIInit();
public:
//constructor
RFM12B():Data(rf12_data),DataLen(&rf12_buf[3]){}
static uint8_t networkID; // network group
static uint8_t nodeID; // address of this node
static const byte DATAMAXLEN;
volatile uint8_t* Data;
volatile uint8_t* DataLen;
//Defaults: Group: 0xAA=170, transmit power: 0(max), KBPS: 38.3Kbps (air transmission baud - has to be same on all radios in same group)
void Initialize(uint8_t nodeid, uint8_t freqBand, uint8_t groupid=0xAA, uint8_t txPower=0, uint8_t airKbps=0x08, uint8_t lowVoltageThreshold=RF12_2v75);
void SetCS(uint8_t pin);
void ReceiveStart();
bool ReceiveComplete();
bool CanSend();
uint16_t Control(uint16_t cmd);
void SendStart(uint8_t toNodeId, bool requestACK=false, bool sendACK=false);
void SendStart(uint8_t toNodeId, const void* sendBuf, uint8_t sendLen, bool requestACK=false, bool sendACK=false, uint8_t waitMode=SLEEP_MODE_STANDBY);
void SendACK(const void* sendBuf = "", uint8_t sendLen=0, uint8_t waitMode=SLEEP_MODE_IDLE);
void Send(uint8_t toNodeId, const void* sendBuf, uint8_t sendLen, bool requestACK = false, uint8_t waitMode=SLEEP_MODE_STANDBY);
void SendWait(uint8_t waitMode=0);
void OnOff(uint8_t value);
void Sleep(char n);
void Sleep();
void Wakeup();
volatile uint8_t * GetData();
uint8_t GetDataLen(); //how many bytes were received
uint8_t GetSender();
bool LowBattery();
bool ACKRequested();
bool ACKReceived(uint8_t fromNodeID=0);
static void CryptFunction(bool sending);
void Encrypt(const uint8_t* key);
bool CRCPass() { return rf12_crc == 0; }
};
#endif
// RFM12B driver definitions
// http://opensource.org/licenses/mit-license.php
// 2012-12-12 (C) felix@lowpowerlab.com
// Based on the RFM12 driver from jeelabs.com (2009-02-09 <jc@wippler.nl>)
#include "RFM12B.h"
uint8_t RFM12B::cs_pin; // CS pin for SPI
uint8_t RFM12B::nodeID; // address of this node
uint8_t RFM12B::networkID; // network group ID
long RFM12B::rf12_seq;
uint32_t RFM12B::seqNum;
uint32_t RFM12B::cryptKey[4];
volatile uint8_t RFM12B::rxfill; // number of data bytes in rf12_buf
volatile int8_t RFM12B::rxstate; // current transceiver state
volatile uint16_t RFM12B::rf12_crc; // running crc value
volatile uint8_t rf12_buf[RF_MAX]; // recv/xmit buf, including hdr & crc bytes
// function to set chip select
void RFM12B::SetCS(uint8_t arduinoPin)
{
if (arduinoPin==10) cs_pin = 2;
if (arduinoPin== 9) cs_pin = 1;
if (arduinoPin== 8) cs_pin = 0;
}
void RFM12B::SPIInit() {
bitSet(SS_PORT, cs_pin);
bitSet(SS_DDR, cs_pin);
digitalWrite(SPI_SS, 1);
pinMode(SPI_SS, OUTPUT);
pinMode(SPI_MOSI, OUTPUT);
pinMode(SPI_MISO, INPUT);
pinMode(SPI_SCK, OUTPUT);
#ifdef SPCR
SPCR = _BV(SPE) | _BV(MSTR);
#if F_CPU > 10000000
// use clk/2 (2x 1/4th) for sending (and clk/8 for recv, see XFERSlow)
SPSR |= _BV(SPI2X);
#endif
#else
// ATtiny
USICR = bit(USIWM0);
#endif
pinMode(RFM_IRQ, INPUT);
digitalWrite(RFM_IRQ, 1); // pull-up
}
uint8_t RFM12B::Byte(uint8_t out) {
#ifdef SPDR
SPDR = out;
// this loop spins 4 usec with a 2 MHz SPI clock
while (!(SPSR & _BV(SPIF)));
return SPDR;
#else
// ATtiny
USIDR = out;
byte v1 = bit(USIWM0) | bit(USITC);
byte v2 = bit(USIWM0) | bit(USITC) | bit(USICLK);
#if F_CPU <= 5000000
// only unroll if resulting clock stays under 2.5 MHz
USICR = v1; USICR = v2;
USICR = v1; USICR = v2;
USICR = v1; USICR = v2;
USICR = v1; USICR = v2;
USICR = v1; USICR = v2;
USICR = v1; USICR = v2;
USICR = v1; USICR = v2;
USICR = v1; USICR = v2;
#else
for (uint8_t i = 0; i < 8; ++i) {
USICR = v1;
USICR = v2;
}
#endif
return USIDR;
#endif
}
uint16_t RFM12B::XFERSlow(uint16_t cmd) {
// slow down to under 2.5 MHz
#if F_CPU > 10000000
bitSet(SPCR, SPR0);
#endif
bitClear(SS_PORT, cs_pin);
uint16_t reply = Byte(cmd >> 8) << 8;
reply |= Byte(cmd);
bitSet(SS_PORT, cs_pin);
#if F_CPU > 10000000
bitClear(SPCR, SPR0);
#endif
return reply;
}
void RFM12B::XFER(uint16_t cmd) {
#if OPTIMIZE_SPI
// writing can take place at full speed, even 8 MHz works
bitClear(SS_PORT, cs_pin);
Byte(cmd >> 8) << 8;
Byte(cmd);
bitSet(SS_PORT, cs_pin);
#else
XFERSlow(cmd);
#endif
}
// Call this once with params:
// - node ID (0-31)
// - frequency band (RF12_433MHZ, RF12_868MHZ, RF12_915MHZ)
// - networkid [optional - default = 170] (0-255 for RF12B, only 212 allowed for RF12)
// - txPower [optional - default = 0 (max)] (7 is min value)
// - AirKbps [optional - default = 38.31Kbps]
// - lowVoltageThreshold [optional - default = RF12_2v75]
void RFM12B::Initialize(uint8_t ID, uint8_t freqBand, uint8_t networkid, uint8_t txPower, uint8_t airKbps, uint8_t lowVoltageThreshold)
{
while(millis()<60);
cs_pin = SS_BIT;
nodeID = ID;
networkID = networkid;
SPIInit();
XFER(0x0000); // intitial SPI transfer added to avoid power-up problem
XFER(RF_SLEEP_MODE); // DC (disable clk pin), enable lbd
// wait until RFM12B is out of power-up reset, this takes several *seconds*
XFER(RF_TXREG_WRITE); // in case we're still in OOK mode
while (digitalRead(RFM_IRQ) == 0)
XFER(0x0000);
XFER(0x80C7 | (freqBand << 4)); // EL (ena TX), EF (ena RX FIFO), 12.0pF
XFER(0xA640); // Frequency is exactly 434/868/915MHz (whatever freqBand is)
XFER(0xC600 + airKbps); //Air transmission baud rate: 0x08= ~38.31Kbps
XFER(0x94A2); // VDI,FAST,134kHz,0dBm,-91dBm
XFER(0xC2AC); // AL,!ml,DIG,DQD4
if (networkID != 0) {
XFER(0xCA83); // FIFO8,2-SYNC,!ff,DR
XFER(0xCE00 | networkID); // SYNC=2DXX;
} else {
XFER(0xCA8B); // FIFO8,1-SYNC,!ff,DR
XFER(0xCE2D); // SYNC=2D;
}
XFER(0xC483); // @PWR,NO RSTRIC,!st,!fi,OE,EN
XFER(0x9850 | (txPower > 7 ? 7 : txPower)); // !mp,90kHz,MAX OUT //last byte=power level: 0=highest, 7=lowest
XFER(0xCC77); // OB1,OB0, LPX,!ddy,DDIT,BW0
XFER(0xE000); // NOT USE
XFER(0xC800); // NOT USE
XFER(0xC043); // Clock output (1.66MHz), Low Voltage threshold (2.55V)
rxstate = TXIDLE;
#if PINCHG_IRQ
#if RFM_IRQ < 8
if (nodeID != 0) {
bitClear(DDRD, RFM_IRQ); // input
bitSet(PORTD, RFM_IRQ); // pull-up
bitSet(PCMSK2, RFM_IRQ); // pin-change
bitSet(PCICR, PCIE2); // enable
} else
bitClear(PCMSK2, RFM_IRQ);
#elif RFM_IRQ < 14
if (nodeID != 0) {
bitClear(DDRB, RFM_IRQ - 8); // input
bitSet(PORTB, RFM_IRQ - 8); // pull-up
bitSet(PCMSK0, RFM_IRQ - 8); // pin-change
bitSet(PCICR, PCIE0); // enable
} else
bitClear(PCMSK0, RFM_IRQ - 8);
#else
if (nodeID != 0) {
bitClear(DDRC, RFM_IRQ - 14); // input
bitSet(PORTC, RFM_IRQ - 14); // pull-up
bitSet(PCMSK1, RFM_IRQ - 14); // pin-change
bitSet(PCICR, PCIE1); // enable
} else
bitClear(PCMSK1, RFM_IRQ - 14);
#endif
#else
if (nodeID != 0)
attachInterrupt(0, InterruptHandler, LOW);
else
detachInterrupt(0);
#endif
}
// access to the RFM12B internal registers with interrupts disabled
uint16_t RFM12B::Control(uint16_t cmd) {
#ifdef EIMSK
bitClear(EIMSK, INT0);
uint16_t r = XFERSlow(cmd);
bitSet(EIMSK, INT0);
#else
// ATtiny
bitClear(GIMSK, INT0);
uint16_t r = XFERSlow(cmd);
bitSet(GIMSK, INT0);
#endif
return r;
}
void RFM12B::InterruptHandler() {
// a transfer of 2x 16 bits @ 2 MHz over SPI takes 2x 8 us inside this ISR
// correction: now takes 2 + 8 µs, since sending can be done at 8 MHz
XFER(0x0000);
if (rxstate == TXRECV) {
uint8_t in = XFERSlow(RF_RX_FIFO_READ);
if (rxfill == 0 && networkID != 0)
rf12_buf[rxfill++] = networkID;
//Serial.print(out, HEX); Serial.print(' ');
rf12_buf[rxfill++] = in;
rf12_crc = _crc16_update(rf12_crc, in);
if (rxfill >= rf12_len + 6 || rxfill >= RF_MAX)
XFER(RF_IDLE_MODE);
} else {
uint8_t out;
if (rxstate < 0) {
uint8_t pos = 4 + rf12_len + rxstate++;
out = rf12_buf[pos];
rf12_crc = _crc16_update(rf12_crc, out);
} else
switch (rxstate++) {
case TXSYN1: out = 0x2D; break;
case TXSYN2: out = networkID; rxstate = -(3 + rf12_len); break;
case TXCRC1: out = rf12_crc; break;
case TXCRC2: out = rf12_crc >> 8; break;
case TXDONE: XFER(RF_IDLE_MODE); // fall through
default: out = 0xAA;
}
//Serial.print(out, HEX); Serial.print(' ');
XFER(RF_TXREG_WRITE + out);
}
}
#if PINCHG_IRQ
#if RFM_IRQ < 8
ISR(PCINT2_vect) {
while (!bitRead(PIND, RFM_IRQ))
InterruptHandler();
}
#elif RFM_IRQ < 14
ISR(PCINT0_vect) {
while (!bitRead(PINB, RFM_IRQ - 8))
InterruptHandler();
}
#else
ISR(PCINT1_vect) {
while (!bitRead(PINC, RFM_IRQ - 14))
InterruptHandler();
}
#endif
#endif
void RFM12B::ReceiveStart() {
rxfill = rf12_len = 0;
rf12_crc = ~0;
if (networkID != 0)
rf12_crc = _crc16_update(~0, networkID);
rxstate = TXRECV;
XFER(RF_RECEIVER_ON);
}
bool RFM12B::ReceiveComplete() {
if (rxstate == TXRECV && (rxfill >= rf12_len + 6 || rxfill >= RF_MAX)) {
rxstate = TXIDLE;
if (rf12_len > RF12_MAXDATA)
rf12_crc = 1; // force bad crc if packet length is invalid
if (RF12_DESTID == 0 || RF12_DESTID == nodeID) { //if (!(rf12_hdr & RF12_HDR_DST) || (nodeID & NODE_ID) == 31 || (rf12_hdr & RF12_HDR_MASK) == (nodeID & NODE_ID)) {
if (rf12_crc == 0 && crypter != 0)
crypter(false);
else
rf12_seq = -1;
return true; // it's a broadcast packet or it's addressed to this node
}
}
if (rxstate == TXIDLE)
ReceiveStart();
return false;
}
bool RFM12B::CanSend() {
// no need to test with interrupts disabled: state TXRECV is only reached
// outside of ISR and we don't care if rxfill jumps from 0 to 1 here
if (rxstate == TXRECV && rxfill == 0 && (Byte(0x00) & (RF_RSSI_BIT >> 8)) == 0) {
XFER(RF_IDLE_MODE); // stop receiver
//XXX just in case, don't know whether these RF12 reads are needed!
// rf12_XFER(0x0000); // status register
// rf12_XFER(RF_RX_FIFO_READ); // fifo read
rxstate = TXIDLE;
return true;
}
return false;
}
void RFM12B::SendStart(uint8_t toNodeID, bool requestACK, bool sendACK) {
rf12_hdr1 = toNodeID | (sendACK ? RF12_HDR_ACKCTLMASK : 0);
rf12_hdr2 = nodeID | (requestACK ? RF12_HDR_ACKCTLMASK : 0);
if (crypter != 0) crypter(true);
rf12_crc = ~0;
rf12_crc = _crc16_update(rf12_crc, networkID);
rxstate = TXPRE1;
XFER(RF_XMITTER_ON); // bytes will be fed via interrupts
}
void RFM12B::SendStart(uint8_t toNodeID, const void* sendBuf, uint8_t sendLen, bool requestACK, bool sendACK, uint8_t waitMode) {
rf12_len = sendLen;
memcpy((void*) rf12_data, sendBuf, sendLen);
SendStart(toNodeID, requestACK, sendACK);
SendWait(waitMode);
}
/// Should be called immediately after reception in case sender wants ACK
void RFM12B::SendACK(const void* sendBuf, uint8_t sendLen, uint8_t waitMode) {
while (!CanSend()) ReceiveComplete();
SendStart(RF12_SOURCEID, sendBuf, sendLen, false, true, waitMode);
}
void RFM12B::Send(uint8_t toNodeID, const void* sendBuf, uint8_t sendLen, bool requestACK, uint8_t waitMode)
{
while (!CanSend()) ReceiveComplete();
SendStart(toNodeID, sendBuf, sendLen, requestACK, false, waitMode);
}
void RFM12B::SendWait(uint8_t waitMode) {
// wait for packet to actually finish sending
// go into low power mode, as interrupts are going to come in very soon
while (rxstate != TXIDLE)
if (waitMode) {
// power down mode is only possible if the fuses are set to start
// up in 258 clock cycles, i.e. approx 4 us - else must use standby!
// modes 2 and higher may lose a few clock timer ticks
set_sleep_mode(waitMode == 3 ? SLEEP_MODE_PWR_DOWN :
#ifdef SLEEP_MODE_STANDBY
waitMode == 2 ? SLEEP_MODE_STANDBY :
#endif
SLEEP_MODE_IDLE);
sleep_mode();
}
}
void RFM12B::OnOff(uint8_t value) {
XFER(value ? RF_XMITTER_ON : RF_IDLE_MODE);
}
void RFM12B::Sleep(char n) {
if (n < 0)
Control(RF_IDLE_MODE);
else {
Control(RF_WAKEUP_TIMER | 0x0500 | n);
Control(RF_SLEEP_MODE);
if (n > 0)
Control(RF_WAKEUP_MODE);
}
rxstate = TXIDLE;
}
void RFM12B::Sleep() { Sleep(0); }
void RFM12B::Wakeup() { Sleep(-1); }
bool RFM12B::LowBattery() {
return (Control(0x0000) & RF_LBD_BIT) != 0;
}
uint8_t RFM12B::GetSender(){
return RF12_SOURCEID;
}
volatile uint8_t * RFM12B::GetData() { return rf12_data; }
uint8_t RFM12B::GetDataLen() { return *DataLen; }
bool RFM12B::ACKRequested() { return RF12_WANTS_ACK; }
/// Should be polled immediately after sending a packet with ACK request
bool RFM12B::ACKReceived(uint8_t fromNodeID) {
if (ReceiveComplete())
return CRCPass() &&
RF12_DESTID == nodeID &&
(RF12_SOURCEID == fromNodeID || fromNodeID == 0) &&
(rf12_hdr1 & RF12_HDR_ACKCTLMASK) &&
!(rf12_hdr2 & RF12_HDR_ACKCTLMASK);
return false;
}
// XXTEA by David Wheeler, adapted from http://en.wikipedia.org/wiki/XXTEA
#define DELTA 0x9E3779B9
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (cryptKey[(uint8_t)((p&3)^e)] ^ z)))
void RFM12B::CryptFunction(bool sending) {
uint32_t y, z, sum, *v = (uint32_t*) rf12_data;
uint8_t p, e, rounds = 6;
if (sending) {
// pad with 1..4-byte sequence number
*(uint32_t*)(rf12_data + rf12_len) = ++seqNum;
uint8_t pad = 3 - (rf12_len & 3);
rf12_len += pad;
rf12_data[rf12_len] &= 0x3F;
rf12_data[rf12_len] |= pad << 6;
++rf12_len;
// actual encoding
char n = rf12_len / 4;
if (n > 1) {
sum = 0;
z = v[n-1];
do {
sum += DELTA;
e = (sum >> 2) & 3;
for (p=0; p<n-1; p++)
y = v[p+1], z = v[p] += MX;
y = v[0];
z = v[n-1] += MX;
} while (--rounds);
}
} else if (rf12_crc == 0) {
// actual decoding
char n = rf12_len / 4;
if (n > 1) {
sum = rounds*DELTA;
y = v[0];
do {
e = (sum >> 2) & 3;
for (p=n-1; p>0; p--)
z = v[p-1], y = v[p] -= MX;
z = v[n-1];
y = v[0] -= MX;
} while ((sum -= DELTA) != 0);
}
// strip sequence number from the end again
if (n > 0) {
uint8_t pad = rf12_data[--rf12_len] >> 6;
rf12_seq = rf12_data[rf12_len] & 0x3F;
while (pad-- > 0)
rf12_seq = (rf12_seq << 8) | rf12_data[--rf12_len];
}
}
}
void RFM12B::Encrypt(const uint8_t* key) {
// by using a pointer to CryptFunction, we only link it in when actually used
if (key != 0) {
for (uint8_t i = 0; i < sizeof cryptKey; ++i)
((uint8_t*) cryptKey)[i] = key[i]; //eeprom_read_byte(key + i);
crypter = CryptFunction;
} else crypter = 0;
}
No comments:
Post a Comment