Pages

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