2011-03-13 17:33:58 +00:00
|
|
|
|
/**
|
|
|
|
|
* mrf24j.cpp, Karl Palsson, 2011, karlp@tweak.net.au
|
2011-12-17 06:31:12 +00:00
|
|
|
|
* modified bsd license / apache license
|
2011-03-13 17:33:58 +00:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "mrf24j.h"
|
|
|
|
|
|
2011-12-17 06:31:12 +00:00
|
|
|
|
// aMaxPHYPacketSize = 127, from the 802.15.4-2006 standard.
|
|
|
|
|
static uint8_t rx_buf[127];
|
2011-03-13 17:33:58 +00:00
|
|
|
|
|
2011-12-17 06:31:12 +00:00
|
|
|
|
// essential for obtaining the data frame only
|
2011-12-17 19:23:12 +00:00
|
|
|
|
// bytes_MHR = 2 Frame control + 1 sequence number + 2 panid + 2 shortAddr Destination + 2 shortAddr Source
|
|
|
|
|
static int bytes_MHR = 9;
|
2011-12-17 06:31:12 +00:00
|
|
|
|
static int bytes_FCS = 2; // FCS length = 2
|
|
|
|
|
static int bytes_nodata = bytes_MHR + bytes_FCS; // no_data bytes in PHY payload, header length + FCS
|
|
|
|
|
|
2011-12-17 19:23:12 +00:00
|
|
|
|
static int ignoreBytes = 0; // bytes to ignore, some modules behaviour.
|
|
|
|
|
|
|
|
|
|
static boolean bufPHY = false; // flag to buffer all bytes in PHY Payload, or not
|
|
|
|
|
|
2011-12-17 06:31:12 +00:00
|
|
|
|
volatile uint8_t flag_got_rx;
|
|
|
|
|
volatile uint8_t flag_got_tx;
|
|
|
|
|
|
|
|
|
|
static rx_info_t rx_info;
|
|
|
|
|
static tx_info_t tx_info;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Constructor MRF24J Object.
|
|
|
|
|
* @param pin_reset, @param pin_chip_select, @param pin_interrupt
|
|
|
|
|
*/
|
2011-03-13 17:33:58 +00:00
|
|
|
|
Mrf24j::Mrf24j(int pin_reset, int pin_chip_select, int pin_interrupt) {
|
|
|
|
|
_pin_reset = pin_reset;
|
|
|
|
|
_pin_cs = pin_chip_select;
|
|
|
|
|
_pin_int = pin_interrupt;
|
|
|
|
|
|
|
|
|
|
pinMode(_pin_reset, OUTPUT);
|
|
|
|
|
pinMode(_pin_cs, OUTPUT);
|
|
|
|
|
pinMode(_pin_int, INPUT);
|
|
|
|
|
|
2011-12-17 06:31:12 +00:00
|
|
|
|
SPI.setBitOrder(MSBFIRST) ;
|
|
|
|
|
SPI.setDataMode(SPI_MODE0);
|
2011-03-13 17:33:58 +00:00
|
|
|
|
SPI.begin();
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-13 18:35:18 +00:00
|
|
|
|
void Mrf24j::reset(void) {
|
2011-03-13 17:33:58 +00:00
|
|
|
|
digitalWrite(_pin_reset, LOW);
|
|
|
|
|
delay(10); // just my gut
|
|
|
|
|
digitalWrite(_pin_reset, HIGH);
|
|
|
|
|
delay(20); // from manual
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-13 18:35:18 +00:00
|
|
|
|
byte Mrf24j::read_short(byte address) {
|
2011-03-13 17:33:58 +00:00
|
|
|
|
digitalWrite(_pin_cs, LOW);
|
|
|
|
|
// 0 top for short addressing, 0 bottom for read
|
|
|
|
|
SPI.transfer(address<<1 & 0b01111110);
|
2011-12-17 06:31:12 +00:00
|
|
|
|
byte ret = SPI.transfer(0x00);
|
2011-03-13 17:33:58 +00:00
|
|
|
|
digitalWrite(_pin_cs, HIGH);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-13 18:35:18 +00:00
|
|
|
|
byte Mrf24j::read_long(word address) {
|
2011-03-13 17:33:58 +00:00
|
|
|
|
digitalWrite(_pin_cs, LOW);
|
|
|
|
|
byte ahigh = address >> 3;
|
|
|
|
|
byte alow = address << 5;
|
|
|
|
|
SPI.transfer(0x80 | ahigh); // high bit for long
|
|
|
|
|
SPI.transfer(alow);
|
|
|
|
|
byte ret = SPI.transfer(0);
|
|
|
|
|
digitalWrite(_pin_cs, HIGH);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2011-03-13 18:35:18 +00:00
|
|
|
|
void Mrf24j::write_short(byte address, byte data) {
|
2011-03-13 17:33:58 +00:00
|
|
|
|
digitalWrite(_pin_cs, LOW);
|
2011-12-17 06:31:12 +00:00
|
|
|
|
// 0 for top short address, 1 bottom for write
|
2011-03-13 17:33:58 +00:00
|
|
|
|
SPI.transfer((address<<1 & 0b01111110) | 0x01);
|
|
|
|
|
SPI.transfer(data);
|
|
|
|
|
digitalWrite(_pin_cs, HIGH);
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-13 18:35:18 +00:00
|
|
|
|
void Mrf24j::write_long(word address, byte data) {
|
2011-03-13 17:33:58 +00:00
|
|
|
|
digitalWrite(_pin_cs, LOW);
|
|
|
|
|
byte ahigh = address >> 3;
|
|
|
|
|
byte alow = address << 5;
|
|
|
|
|
SPI.transfer(0x80 | ahigh); // high bit for long
|
|
|
|
|
SPI.transfer(alow | 0x10); // last bit for write
|
|
|
|
|
SPI.transfer(data);
|
|
|
|
|
digitalWrite(_pin_cs, HIGH);
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-13 18:35:18 +00:00
|
|
|
|
word Mrf24j::get_pan(void) {
|
|
|
|
|
byte panh = read_short(MRF_PANIDH);
|
|
|
|
|
return panh << 8 | read_short(MRF_PANIDL);
|
2011-03-13 17:33:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-13 18:35:18 +00:00
|
|
|
|
void Mrf24j::set_pan(word panid) {
|
|
|
|
|
write_short(MRF_PANIDH, panid >> 8);
|
|
|
|
|
write_short(MRF_PANIDL, panid & 0xff);
|
2011-03-13 17:33:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-13 18:35:18 +00:00
|
|
|
|
void Mrf24j::address16_write(word address16) {
|
|
|
|
|
write_short(MRF_SADRH, address16 >> 8);
|
|
|
|
|
write_short(MRF_SADRL, address16 & 0xff);
|
2011-03-13 17:33:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-13 18:35:18 +00:00
|
|
|
|
word Mrf24j::address16_read(void) {
|
|
|
|
|
byte a16h = read_short(MRF_SADRH);
|
|
|
|
|
return a16h << 8 | read_short(MRF_SADRL);
|
2011-03-13 17:33:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Simple send 16, with acks, not much of anything.. assumes src16 and local pan only.
|
|
|
|
|
* @param data
|
|
|
|
|
*/
|
2011-12-17 06:31:12 +00:00
|
|
|
|
void Mrf24j::send16(word dest16, char * data) {
|
|
|
|
|
byte len = strlen(data); // get the length of the char* array
|
2011-03-13 17:33:58 +00:00
|
|
|
|
int i = 0;
|
2011-12-17 19:23:12 +00:00
|
|
|
|
write_long(i++, bytes_MHR); // header length
|
|
|
|
|
// +ignoreBytes is because some module seems to ignore 2 bytes after the header?!.
|
|
|
|
|
// default: ignoreBytes = 0;
|
|
|
|
|
write_long(i++, bytes_MHR+ignoreBytes+len);
|
2011-03-13 17:33:58 +00:00
|
|
|
|
|
2011-12-17 06:31:12 +00:00
|
|
|
|
// 0 | pan compression | ack | no security | no data pending | data frame[3 bits]
|
2011-03-13 18:35:18 +00:00
|
|
|
|
write_long(i++, 0b01100001); // first byte of Frame Control
|
2011-12-17 06:31:12 +00:00
|
|
|
|
// 16 bit source, 802.15.4 (2003), 16 bit dest,
|
2011-03-13 18:35:18 +00:00
|
|
|
|
write_long(i++, 0b10001000); // second byte of frame control
|
|
|
|
|
write_long(i++, 1); // sequence number 1
|
2011-03-13 17:33:58 +00:00
|
|
|
|
|
2011-03-13 18:35:18 +00:00
|
|
|
|
word panid = get_pan();
|
2011-03-13 17:33:58 +00:00
|
|
|
|
|
2011-03-13 18:35:18 +00:00
|
|
|
|
write_long(i++, panid & 0xff); // dest panid
|
|
|
|
|
write_long(i++, panid >> 8);
|
|
|
|
|
write_long(i++, dest16 & 0xff); // dest16 low
|
|
|
|
|
write_long(i++, dest16 >> 8); // dest16 high
|
2011-03-13 17:33:58 +00:00
|
|
|
|
|
2011-03-13 18:35:18 +00:00
|
|
|
|
word src16 = address16_read();
|
|
|
|
|
write_long(i++, src16 & 0xff); // src16 low
|
|
|
|
|
write_long(i++, src16 >> 8); // src16 high
|
2011-03-13 17:33:58 +00:00
|
|
|
|
|
2011-12-17 19:23:12 +00:00
|
|
|
|
// All testing seems to indicate that the next two bytes are ignored.
|
|
|
|
|
//2 bytes on FCS appended by TXMAC
|
|
|
|
|
i+=ignoreBytes;
|
2011-03-13 17:33:58 +00:00
|
|
|
|
for (int q = 0; q < len; q++) {
|
2011-03-13 18:35:18 +00:00
|
|
|
|
write_long(i++, data[q]);
|
2011-03-13 17:33:58 +00:00
|
|
|
|
}
|
|
|
|
|
// ack on, and go!
|
2011-03-13 18:35:18 +00:00
|
|
|
|
write_short(MRF_TXNCON, (1<<MRF_TXNACKREQ | 1<<MRF_TXNTRIG));
|
2011-03-13 17:33:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-13 18:35:18 +00:00
|
|
|
|
void Mrf24j::set_interrupts(void) {
|
2011-03-13 17:33:58 +00:00
|
|
|
|
// interrupts for rx and tx normal complete
|
2011-03-13 18:35:18 +00:00
|
|
|
|
write_short(MRF_INTCON, 0b11110110);
|
2011-03-13 17:33:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-13 18:35:18 +00:00
|
|
|
|
/** use the 802.15.4 channel numbers..
|
|
|
|
|
*/
|
|
|
|
|
void Mrf24j::set_channel(byte channel) {
|
2011-03-13 19:23:55 +00:00
|
|
|
|
write_long(MRF_RFCON0, (((channel - 11) << 4) | 0x03));
|
2011-03-13 17:33:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-13 18:35:18 +00:00
|
|
|
|
void Mrf24j::init(void) {
|
2011-12-17 06:31:12 +00:00
|
|
|
|
/*
|
|
|
|
|
// Seems a bit ridiculous when I use reset pin anyway
|
2011-03-13 18:35:18 +00:00
|
|
|
|
write_short(MRF_SOFTRST, 0x7); // from manual
|
|
|
|
|
while (read_short(MRF_SOFTRST) & 0x7 != 0) {
|
2011-03-13 17:33:58 +00:00
|
|
|
|
; // wait for soft reset to finish
|
|
|
|
|
}
|
2011-12-17 06:31:12 +00:00
|
|
|
|
*/
|
2011-03-13 18:35:18 +00:00
|
|
|
|
write_short(MRF_PACON2, 0x98); // – Initialize FIFOEN = 1 and TXONTS = 0x6.
|
|
|
|
|
write_short(MRF_TXSTBL, 0x95); // – Initialize RFSTBL = 0x9.
|
2011-03-13 17:33:58 +00:00
|
|
|
|
|
2011-03-13 18:35:18 +00:00
|
|
|
|
write_long(MRF_RFCON0, 0x03); // – Initialize RFOPT = 0x03.
|
|
|
|
|
write_long(MRF_RFCON1, 0x01); // – Initialize VCOOPT = 0x02.
|
|
|
|
|
write_long(MRF_RFCON2, 0x80); // – Enable PLL (PLLEN = 1).
|
|
|
|
|
write_long(MRF_RFCON6, 0x90); // – Initialize TXFIL = 1 and 20MRECVR = 1.
|
|
|
|
|
write_long(MRF_RFCON7, 0x80); // – Initialize SLPCLKSEL = 0x2 (100 kHz Internal oscillator).
|
|
|
|
|
write_long(MRF_RFCON8, 0x10); // – Initialize RFVCO = 1.
|
|
|
|
|
write_long(MRF_SLPCON1, 0x21); // – Initialize CLKOUTEN = 1 and SLPCLKDIV = 0x01.
|
2011-03-13 17:33:58 +00:00
|
|
|
|
|
|
|
|
|
// Configuration for nonbeacon-enabled devices (see Section 3.8 “Beacon-Enabled and
|
|
|
|
|
// Nonbeacon-Enabled Networks”):
|
2011-03-13 18:35:18 +00:00
|
|
|
|
write_short(MRF_BBREG2, 0x80); // Set CCA mode to ED
|
|
|
|
|
write_short(MRF_CCAEDTH, 0x60); // – Set CCA ED threshold.
|
|
|
|
|
write_short(MRF_BBREG6, 0x40); // – Set appended RSSI value to RXFIFO.
|
|
|
|
|
set_interrupts();
|
|
|
|
|
set_channel(12);
|
2011-03-13 17:33:58 +00:00
|
|
|
|
// max power is by default.. just leave it...
|
2011-12-17 19:23:12 +00:00
|
|
|
|
// Set transmitter power - See “REGISTER 2-62: RF CONTROL 3 REGISTER (ADDRESS: 0x203)”.
|
2011-03-13 18:35:18 +00:00
|
|
|
|
write_short(MRF_RFCTL, 0x04); // – Reset RF state machine.
|
|
|
|
|
write_short(MRF_RFCTL, 0x00); // part 2
|
2011-03-13 17:33:58 +00:00
|
|
|
|
delay(1); // delay at least 192usec
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-17 06:31:12 +00:00
|
|
|
|
/**
|
|
|
|
|
* Call this from within an interrupt handler connected to the MRFs output
|
|
|
|
|
* interrupt pin. It handles reading in any data from the module, and letting it
|
|
|
|
|
* continue working.
|
|
|
|
|
* Only the most recent data is ever kept.
|
|
|
|
|
*/
|
|
|
|
|
void Mrf24j::interrupt_handler(void) {
|
|
|
|
|
uint8_t last_interrupt = read_short(MRF_INTSTAT);
|
|
|
|
|
if (last_interrupt & MRF_I_RXIF) {
|
|
|
|
|
flag_got_rx++;
|
|
|
|
|
// read out the packet data...
|
|
|
|
|
noInterrupts();
|
|
|
|
|
rx_disable();
|
2011-12-17 19:23:12 +00:00
|
|
|
|
// read start of rxfifo for, has 2 bytes more added by FCS. frame_length = m + n + 2
|
|
|
|
|
uint8_t frame_length = read_long(0x300);
|
2011-12-17 06:31:12 +00:00
|
|
|
|
|
|
|
|
|
// buffer all bytes in PHY Payload
|
2011-12-17 19:23:12 +00:00
|
|
|
|
if(bufPHY){
|
|
|
|
|
int rb_ptr = 0;
|
|
|
|
|
for (int i = 0; i < frame_length; i++) { // from 0x301 to (0x301 + frame_length -1)
|
|
|
|
|
rx_buf[rb_ptr++] = read_long(0x301 + i);
|
|
|
|
|
}
|
2011-12-17 06:31:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// buffer data bytes
|
|
|
|
|
int rd_ptr = 0;
|
2011-12-17 19:23:12 +00:00
|
|
|
|
// from (0x301 + bytes_MHR) to (0x301 + frame_length - bytes_nodata - 1)
|
|
|
|
|
for (int i = 0; i < rx_datalength(); i++) {
|
2011-12-17 06:31:12 +00:00
|
|
|
|
rx_info.rx_data[rd_ptr++] = read_long(0x301 + bytes_MHR + i);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rx_info.frame_length = frame_length;
|
2011-12-17 19:23:12 +00:00
|
|
|
|
// same as datasheet 0x301 + (m + n + 2) <-- frame_length
|
|
|
|
|
rx_info.lqi = read_long(0x301 + frame_length);
|
|
|
|
|
// same as datasheet 0x301 + (m + n + 3) <-- frame_length + 1
|
|
|
|
|
rx_info.rssi = read_long(0x301 + frame_length + 1);
|
2011-12-17 06:31:12 +00:00
|
|
|
|
|
|
|
|
|
rx_enable();
|
|
|
|
|
interrupts();
|
|
|
|
|
}
|
|
|
|
|
if (last_interrupt & MRF_I_TXNIF) {
|
|
|
|
|
flag_got_tx++;
|
|
|
|
|
uint8_t tmp = read_short(MRF_TXSTAT);
|
|
|
|
|
// 1 means it failed, we want 1 to mean it worked.
|
|
|
|
|
tx_info.tx_ok = !(tmp & ~(1 << TXNSTAT));
|
|
|
|
|
tx_info.retries = tmp >> 6;
|
|
|
|
|
tx_info.channel_busy = (tmp & (1 << CCAFAIL));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Call this function periodically, it will invoke your nominated handlers
|
|
|
|
|
*/
|
|
|
|
|
void Mrf24j::check_flags(void (*rx_handler)(void), void (*tx_handler)(void)){
|
|
|
|
|
// TODO - we could check whether the flags are > 1 here, indicating data was lost?
|
|
|
|
|
if (flag_got_rx) {
|
|
|
|
|
flag_got_rx = 0;
|
|
|
|
|
rx_handler();
|
|
|
|
|
}
|
|
|
|
|
if (flag_got_tx) {
|
|
|
|
|
flag_got_tx = 0;
|
|
|
|
|
tx_handler();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set RX mode to promiscuous, or normal
|
|
|
|
|
*/
|
2011-03-13 18:35:18 +00:00
|
|
|
|
void Mrf24j::set_promiscuous(boolean enabled) {
|
|
|
|
|
if (enabled) {
|
|
|
|
|
write_short(MRF_RXMCR, 0x01);
|
|
|
|
|
} else {
|
|
|
|
|
write_short(MRF_RXMCR, 0x00);
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-03-13 19:49:07 +00:00
|
|
|
|
|
2011-12-17 06:31:12 +00:00
|
|
|
|
rx_info_t * Mrf24j::get_rxinfo(void) {
|
|
|
|
|
return &rx_info;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tx_info_t * Mrf24j::get_txinfo(void) {
|
|
|
|
|
return &tx_info;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t * Mrf24j::get_rxbuf(void) {
|
|
|
|
|
return rx_buf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Mrf24j::rx_datalength(void) {
|
|
|
|
|
return rx_info.frame_length - bytes_nodata;
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-17 19:23:12 +00:00
|
|
|
|
void Mrf24j::set_ignoreBytes(int ib) {
|
|
|
|
|
// some modules behaviour
|
|
|
|
|
ignoreBytes = ib;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set bufPHY flag to buffer all bytes in PHY Payload, or not
|
|
|
|
|
*/
|
|
|
|
|
void Mrf24j::set_bufferPHY(boolean bp) {
|
|
|
|
|
bufPHY = bp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
boolean Mrf24j::get_bufferPHY(void) {
|
|
|
|
|
return bufPHY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set PA/LNA external control
|
|
|
|
|
*/
|
|
|
|
|
void Mrf24j::set_palna(boolean enabled) {
|
|
|
|
|
if (enabled) {
|
|
|
|
|
write_long(MRF_TESTMODE, 0x07); // Enable PA/LNA on MRF24J40MB module.
|
|
|
|
|
}else{
|
|
|
|
|
write_long(MRF_TESTMODE, 0x00); // Disable PA/LNA on MRF24J40MB module.
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-13 19:49:07 +00:00
|
|
|
|
void Mrf24j::rx_flush(void) {
|
|
|
|
|
write_short(MRF_RXFLUSH, 0x01);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Mrf24j::rx_disable(void) {
|
|
|
|
|
write_short(MRF_BBREG1, 0x04); // RXDECINV - disable receiver
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Mrf24j::rx_enable(void) {
|
|
|
|
|
write_short(MRF_BBREG1, 0x00); // RXDECINV - enable receiver
|
|
|
|
|
}
|