diff --git a/.gitignore b/.gitignore index cd531cf..03515f6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,54 +1,3 @@ -# ---> C -# Prerequisites -*.d - -# Object files -*.o -*.ko -*.obj *.elf - -# Linker output -*.ilk -*.map -*.exp - -# Precompiled Headers -*.gch -*.pch - -# Libraries -*.lib -*.a -*.la -*.lo - -# Shared objects (inc. Windows DLLs) -*.dll -*.so -*.so.* -*.dylib - -# Executables -*.exe -*.out -*.app -*.i*86 -*.x86_64 +*.bin *.hex - -# Debug files -*.dSYM/ -*.su -*.idb -*.pdb - -# Kernel Module Compile Results -*.mod* -*.cmd -.tmp_versions/ -modules.order -Module.symvers -Mkfile.old -dkms.conf - diff --git a/main b/main new file mode 100755 index 0000000..74535a3 Binary files /dev/null and b/main differ diff --git a/makefile b/makefile new file mode 100644 index 0000000..19bc1a5 --- /dev/null +++ b/makefile @@ -0,0 +1,5 @@ +all: build run +build: + g++ -Wall -pthread -o main test/${MODE}/main.cpp src/*.cpp -lpigpio -lrt +run: + sudo ./main \ No newline at end of file diff --git a/src/driver.cpp b/src/driver.cpp new file mode 100644 index 0000000..c6aafef --- /dev/null +++ b/src/driver.cpp @@ -0,0 +1,49 @@ +#include "driver.h" +#include + +namespace driver +{ + namespace { + Mrf24j *mrf; + int spi; + } + + void callback(int gpio, int level, uint32_t tick) { + mrf->interrupt_handler(); + mrf->check_flags(); + } + + void init(Mrf24j *mrf) { + + gpioInitialise(); + driver::mrf = mrf; + spi = spiOpen(0, 1000000, 0); + gpioSetMode(RESET, PI_OUTPUT); + gpioWrite(RESET, 1); + gpioSetMode(SPI_CS, PI_OUTPUT); + cs_high(); + gpioSetAlertFunc(INT, callback); + } + + uint8_t transfer(uint8_t data) { + char txbuff[1], rxbuff[1]; + txbuff[0] = data; + spiXfer(spi, txbuff, rxbuff, 1); + return rxbuff[0]; + } + void reset() { + gpioWrite(RESET , 0); + time_sleep(0.01); + gpioWrite(RESET, 1); + time_sleep(0.02); + } + + void wake() { + } + + // INTERRUPT { + // mrf->interrupt_handler(); // mrf24 object interrupt routine + // mrf->check_flags(); + // } +} + diff --git a/src/driver.h b/src/driver.h new file mode 100644 index 0000000..eadaf96 --- /dev/null +++ b/src/driver.h @@ -0,0 +1,20 @@ +#pragma once + +#include "mrf24j.h" +#include + +#define RESET 24 //24 +#define SPI_CS 8 //8 +#define INT 25 //25 + +namespace driver +{ + void init(Mrf24j *mrf); + uint8_t transfer(uint8_t data); + + inline void cs_low() {gpioWrite(SPI_CS, 0);} + + inline void cs_high() {gpioWrite(SPI_CS, 1);} + void wake(); + void reset(); +} // namespace spi diff --git a/src/mrf24j.cpp b/src/mrf24j.cpp new file mode 100644 index 0000000..f6709b7 --- /dev/null +++ b/src/mrf24j.cpp @@ -0,0 +1,351 @@ +/** + * mrf24j.cpp, Karl Palsson, 2011, karlp@tweak.net.au + * modified bsd license / apache license + */ + +#include "mrf24j.h" +#include "driver.h" +#include +#include + + +// aMaxPHYPacketSize = 127, from the 802.15.4-2006 standard. +static uint8_t rx_buf[127]; + +// essential for obtaining the data frame only +// bytes_MHR = 2 Frame control + 1 sequence number + 2 panid + 2 shortAddr Destination + 2 shortAddr Source +static int bytes_MHR = 9; +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 + +static int ignoreBytes = 0; // bytes to ignore, some modules behaviour. + +static bool bufPHY = false; // flag to buffer all bytes in PHY Payload, or not + +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. + */ +Mrf24j::Mrf24j() { + driver::init(this); +} + +void Mrf24j::reset(void) { + driver::reset(); +} +void Mrf24j::handlers(void (*rx_handler)(void), void (*tx_handler)(void)) { + this->rx_handler = rx_handler; + this->tx_handler = tx_handler; + +} + +byte Mrf24j::read_short(byte address) { + driver::cs_low(); + // 0 top for short addressing, 0 bottom for read + driver::transfer(address<<1 & 0b01111110); + byte ret = driver::transfer(0x00); + driver::cs_high(); + return ret; +} + +byte Mrf24j::read_long(word address) { + driver::cs_low(); + byte ahigh = address >> 3; + byte alow = address << 5; + driver::transfer(0x80 | ahigh); // high bit for long + driver::transfer(alow); + byte ret = driver::transfer(0); + driver::cs_high(); + return ret; +} + + +void Mrf24j::write_short(byte address, byte data) { + driver::cs_low(); + // 0 for top short address, 1 bottom for write + driver::transfer((address<<1 & 0b01111110) | 0x01); + driver::transfer(data); + driver::cs_high(); +} + +void Mrf24j::write_long(word address, byte data) { + driver::cs_low(); + byte ahigh = address >> 3; + byte alow = address << 5; + driver::transfer(0x80 | ahigh); // high bit for long + driver::transfer(alow | 0x10); // last bit for write + driver::transfer(data); + driver::cs_high(); +} + +word Mrf24j::get_pan(void) { + byte panh = read_short(MRF_PANIDH); + return panh << 8 | read_short(MRF_PANIDL); +} + +void Mrf24j::set_pan(word panid) { + write_short(MRF_PANIDH, panid >> 8); + write_short(MRF_PANIDL, panid & 0xff); +} + +void Mrf24j::address16_write(word address16) { + write_short(MRF_SADRH, address16 >> 8); + write_short(MRF_SADRL, address16 & 0xff); +} + +word Mrf24j::address16_read(void) { + byte a16h = read_short(MRF_SADRH); + return a16h << 8 | read_short(MRF_SADRL); +} + + +void Mrf24j::send_str(word dest16, char * data) { + byte len = strlen(data); // get the length of the char* array + send(dest16, data, len); +} + +/** + * Simple send 16, with acks, not much of anything.. assumes src16 and local pan only. + * @param data + */ +void Mrf24j::send(word dest16, char * data, int len) { + int i = 0; + 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); + + // 0 | pan compression | ack | no security | no data pending | data frame[3 bits] + write_long(i++, 0b01100001); // first byte of Frame Control + // 16 bit source, 802.15.4 (2003), 16 bit dest, + write_long(i++, 0b10001000); // second byte of frame control + write_long(i++, 1); // sequence number 1 + + word panid = get_pan(); + + 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 + + word src16 = address16_read(); + write_long(i++, src16 & 0xff); // src16 low + write_long(i++, src16 >> 8); // src16 high + + // All testing seems to indicate that the next two bytes are ignored. + //2 bytes on FCS appended by TXMAC + i+=ignoreBytes; + for (int q = 0; q < len; q++) { + write_long(i++, data[q]); + } + // ack on, and go! + write_short(MRF_TXNCON, (1<> 6; + tx_info.channel_busy = (tmp & (1 << CCAFAIL)); + } +} + + +/** + * Call this function periodically, it will invoke your nominated handlers + */ +void Mrf24j::check_flags(){ + // TODO - we could check whether the flags are > 1 here, indicating data was lost? + if (flag_got_rx) { + flag_got_rx = 0; + this->rx_handler(); + } + if (flag_got_tx) { + flag_got_tx = 0; + this->tx_handler(); + } +} + +/** + * Set RX mode to promiscuous, or normal + */ +void Mrf24j::set_promiscuous(boolean enabled) { + if (enabled) { + write_short(MRF_RXMCR, 0x01); + } else { + write_short(MRF_RXMCR, 0x00); + } +} + +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; +} + +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. + } +} + +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 +} + +void Mrf24j::enable_wake() { + write_short(MRF_RXFLUSH, 0x60); // Enable WAKE pin and set polarity to active-high + write_short(MRF_WAKECON, 0x80); // Enable Immediate Wake-up mode +} + +void Mrf24j::sleep() { + write_short(MRF_SOFTRST, 0x04); // Perform a Power Management Reset + write_short(MRF_SLPACK, 0x80); // Put MRF24J40 to Sleep immediately +} + +void Mrf24j::wake() { + driver::wake(); + write_short(MRF_RFCTL, 0x04); // RF State Machine reset + write_short(MRF_RFCTL, 0x00); + gpioSleep(0, 0 ,2000); // Delay 2 ms to allow 20 MHz main oscillator time to stabilize before transmitting or receiving. +} + +void Mrf24j::turbo() { + write_short(MRF_BBREG0, 0x01); + write_short(MRF_BBREG3, (1<<5)|(1<<4)); + write_short(MRF_BBREG4, (1<<6)); + write_short(MRF_SOFTRST, (1<<1)); +} + +void Mrf24j::set_power(byte level) { + write_short(MRF_RFCON3, level); +} \ No newline at end of file diff --git a/src/mrf24j.h b/src/mrf24j.h new file mode 100644 index 0000000..2c8f71e --- /dev/null +++ b/src/mrf24j.h @@ -0,0 +1,250 @@ +/* + * File: mrf24j.h + * copyright Karl Palsson, karlp@tweak.net.au, 2011 + * modified BSD License / apache license + */ +typedef unsigned char uint8_t; + +typedef unsigned char byte; +typedef unsigned int word; +typedef bool boolean; + +#ifndef LIB_MRF24J_H +#define LIB_MRF24J_H + +#define MRF_RXMCR 0x00 +#define MRF_PANIDL 0x01 +#define MRF_PANIDH 0x02 +#define MRF_SADRL 0x03 +#define MRF_SADRH 0x04 +#define MRF_EADR0 0x05 +#define MRF_EADR1 0x06 +#define MRF_EADR2 0x07 +#define MRF_EADR3 0x08 +#define MRF_EADR4 0x09 +#define MRF_EADR5 0x0A +#define MRF_EADR6 0x0B +#define MRF_EADR7 0x0C +#define MRF_RXFLUSH 0x0D +//#define MRF_Reserved 0x0E +//#define MRF_Reserved 0x0F +#define MRF_ORDER 0x10 +#define MRF_TXMCR 0x11 +#define MRF_ACKTMOUT 0x12 +#define MRF_ESLOTG1 0x13 +#define MRF_SYMTICKL 0x14 +#define MRF_SYMTICKH 0x15 +#define MRF_PACON0 0x16 +#define MRF_PACON1 0x17 +#define MRF_PACON2 0x18 +//#define MRF_Reserved 0x19 +#define MRF_TXBCON0 0x1A + +// TXNCON: TRANSMIT NORMAL FIFO CONTROL REGISTER (ADDRESS: 0x1B) +#define MRF_TXNCON 0x1B +#define MRF_TXNTRIG 0 +#define MRF_TXNSECEN 1 +#define MRF_TXNACKREQ 2 +#define MRF_INDIRECT 3 +#define MRF_FPSTAT 4 + +#define MRF_TXG1CON 0x1C +#define MRF_TXG2CON 0x1D +#define MRF_ESLOTG23 0x1E +#define MRF_ESLOTG45 0x1F +#define MRF_ESLOTG67 0x20 +#define MRF_TXPEND 0x21 +#define MRF_WAKECON 0x22 +#define MRF_FRMOFFSET 0x23 +// TXSTAT: TX MAC STATUS REGISTER (ADDRESS: 0x24) +#define MRF_TXSTAT 0x24 +#define TXNRETRY1 7 +#define TXNRETRY0 6 +#define CCAFAIL 5 +#define TXG2FNT 4 +#define TXG1FNT 3 +#define TXG2STAT 2 +#define TXG1STAT 1 +#define TXNSTAT 0 + +#define MRF_TXBCON1 0x25 +#define MRF_GATECLK 0x26 +#define MRF_TXTIME 0x27 +#define MRF_HSYMTMRL 0x28 +#define MRF_HSYMTMRH 0x29 +#define MRF_SOFTRST 0x2A +//#define MRF_Reserved 0x2B +#define MRF_SECCON0 0x2C +#define MRF_SECCON1 0x2D +#define MRF_TXSTBL 0x2E +//#define MRF_Reserved 0x2F +#define MRF_RXSR 0x30 +#define MRF_INTSTAT 0x31 +#define MRF_INTCON 0x32 +#define MRF_GPIO 0x33 +#define MRF_TRISGPIO 0x34 +#define MRF_SLPACK 0x35 +#define MRF_RFCTL 0x36 +#define MRF_SECCR2 0x37 +#define MRF_BBREG0 0x38 +#define MRF_BBREG1 0x39 +#define MRF_BBREG2 0x3A +#define MRF_BBREG3 0x3B +#define MRF_BBREG4 0x3C +//#define MRF_Reserved 0x3D +#define MRF_BBREG6 0x3E +#define MRF_CCAEDTH 0x3F + +#define MRF_RFCON0 0x200 +#define MRF_RFCON1 0x201 +#define MRF_RFCON2 0x202 +#define MRF_RFCON3 0x203 +#define MRF_RFCON5 0x205 +#define MRF_RFCON6 0x206 +#define MRF_RFCON7 0x207 +#define MRF_RFCON8 0x208 +#define MRF_SLPCAL0 0x209 +#define MRF_SLPCAL1 0x20A +#define MRF_SLPCAL2 0x20B +#define MRF_RSSI 0x210 +#define MRF_SLPCON0 0x211 +#define MRF_SLPCON1 0x220 +#define MRF_WAKETIMEL 0x222 +#define MRF_WAKETIMEH 0x223 +#define MRF_REMCNTL 0x224 +#define MRF_REMCNTH 0x225 +#define MRF_MAINCNT0 0x226 +#define MRF_MAINCNT1 0x227 +#define MRF_MAINCNT2 0x228 +#define MRF_MAINCNT3 0x229 +#define MRF_TESTMODE 0x22F +#define MRF_ASSOEADR1 0x231 +#define MRF_ASSOEADR2 0x232 +#define MRF_ASSOEADR3 0x233 +#define MRF_ASSOEADR4 0x234 +#define MRF_ASSOEADR5 0x235 +#define MRF_ASSOEADR6 0x236 +#define MRF_ASSOEADR7 0x237 +#define MRF_ASSOSADR0 0x238 +#define MRF_ASSOSADR1 0x239 +#define MRF_UPNONCE0 0x240 +#define MRF_UPNONCE1 0x241 +#define MRF_UPNONCE2 0x242 +#define MRF_UPNONCE3 0x243 +#define MRF_UPNONCE4 0x244 +#define MRF_UPNONCE5 0x245 +#define MRF_UPNONCE6 0x246 +#define MRF_UPNONCE7 0x247 +#define MRF_UPNONCE8 0x248 +#define MRF_UPNONCE9 0x249 +#define MRF_UPNONCE10 0x24A +#define MRF_UPNONCE11 0x24B +#define MRF_UPNONCE12 0x24C + +#define MRF_I_RXIF 0b00001000 +#define MRF_I_TXNIF 0b00000001 + +#define MRF_S_WAKEPOL (1<<6) +#define MRF_S_PAKEPAD (1<<5) +#define MRF_S_IMMWAKE (1<<8) +#define MRF_S_SLPACK (1<<7) +#define MRF_S_RFRST (1<<2) + + + + +typedef struct _rx_info_t{ + uint8_t frame_length; + uint8_t rx_data[116]; //max data length = (127 aMaxPHYPacketSize - 2 Frame control - 1 sequence number - 2 panid - 2 shortAddr Destination - 2 shortAddr Source - 2 FCS) + uint8_t lqi; + uint8_t rssi; +} rx_info_t; + +/** + * Based on the TXSTAT register, but "better" + */ +typedef struct _tx_info_t{ + uint8_t tx_ok:1; + uint8_t retries:2; + uint8_t channel_busy:1; +} tx_info_t; + +class Mrf24j +{ + public: + Mrf24j(); + void reset(void); + void init(void); + void handlers(void (*rx_handler)(void), void (*tx_handler)(void)); + + byte read_short(byte address); + byte read_long(word address); + + void write_short(byte address, byte data); + void write_long(word address, byte data); + + word get_pan(void); + void set_pan(word panid); + + void address16_write(word address16); + word address16_read(void); + + void set_interrupts(void); + + void set_promiscuous(boolean enabled); + + /** + * Set the channel, using 802.15.4 channel numbers (11..26) + */ + void set_channel(byte channel); + + void rx_enable(void); + void rx_disable(void); + + /** If you want to throw away rx data */ + void rx_flush(void); + + rx_info_t * get_rxinfo(void); + + tx_info_t * get_txinfo(void); + + uint8_t * get_rxbuf(void); + + int rx_datalength(void); + + void set_ignoreBytes(int ib); + + /** + * Set bufPHY flag to buffer all bytes in PHY Payload, or not + */ + void set_bufferPHY(boolean bp); + + boolean get_bufferPHY(void); + + /** + * Set PA/LNA external control + */ + void set_palna(boolean enabled); + + void send(word dest16, char * data, int len); + void send_str(word dest16, char * data); + + void interrupt_handler(void); + + void check_flags(); + + void enable_wake(); + void sleep(); + void wake(); + void turbo(); + void set_power(byte level); + + private: + int _pin_reset; + int _pin_cs; + int _pin_int; + void (*rx_handler)(void); + void (*tx_handler)(void); +}; + +#endif /* LIB_MRF24J_H */ diff --git a/test/rx/main.cpp b/test/rx/main.cpp new file mode 100644 index 0000000..726b319 --- /dev/null +++ b/test/rx/main.cpp @@ -0,0 +1,67 @@ +/** + * Example code for using a microchip mrf24j40 module to send and receive + * packets using plain 802.15.4 + * Requirements: 3 pins for spi, 3 pins for reset, chip select and interrupt + * notifications + * This example file is considered to be in the public domain + * Originally written by Karl Palsson, karlp@tweak.net.au, March 2011 + */ +#define F_CPU 8000000UL +#include "../../src/mrf24j.h" +#include +#include + +Mrf24j mrf; + +void handle_rx(); +void handle_tx(); + +int main() { + mrf.reset(); + mrf.init(); + + mrf.set_pan(0xcafe); + mrf.set_bufferPHY(true); + // This is _our_ address + mrf.address16_write(0x4202); + mrf.handlers(&handle_rx, &handle_tx); + printf("Pan id: %x\n\r", mrf.get_pan()); + //mrf.turbo(); + + printf("Started"); + while(1) { + printf("Hello\n\r"); + time_sleep(2); + + } +} + + + +void handle_rx() { + printf("------------------------------------------------------\n\r"); + printf("received a packet %i bytes long\n\r", mrf.get_rxinfo()->frame_length); + + if(mrf.get_bufferPHY()){ + printf("Packet data (PHY Payload): "); + for (int i = 0; i < mrf.get_rxinfo()->frame_length; i++) { + printf("%x", mrf.get_rxbuf()[i]); + } + } + + printf("\r\nASCII data (relevant data): "); + for (int i = 0; i < mrf.rx_datalength(); i++) { + putchar(mrf.get_rxinfo()->rx_data[i]); + } + + printf("LQI/RSSI="); + printf("%i/%i\n\r", mrf.get_rxinfo()->lqi, mrf.get_rxinfo()->rssi); +} + +void handle_tx() { + if (mrf.get_txinfo()->tx_ok) { + printf("TX went ok, got ack\n\r"); + } else { + printf("TX failed after %i retries\n\n\r", mrf.get_txinfo()->retries); + } +} diff --git a/test/tx/main.cpp b/test/tx/main.cpp new file mode 100644 index 0000000..0bf1f35 --- /dev/null +++ b/test/tx/main.cpp @@ -0,0 +1,83 @@ +/** + * Example code for using a microchip mrf24j40 module to send and receive + * packets using plain 802.15.4 + * Requirements: 3 pins for spi, 3 pins for reset, chip select and interrupt + * notifications + * This example file is considered to be in the public domain + * Originally written by Karl Palsson, karlp@tweak.net.au, March 2011 + */ + +#define F_CPU 8000000UL +#include "../../src/mrf24j.h" +#include "../../src/driver.h" +#include +#include + + +Mrf24j mrf; +int i = 0; + +void handle_rx(); +void handle_tx(); + +int main() { + + mrf.reset(); + mrf.init(); + time_sleep(1); + + mrf.set_pan(0xcafe); + // This is _our_ address + mrf.address16_write(0x4201); + mrf.handlers(&handle_rx, &handle_tx); + printf("Pan id: %x\n\r", mrf.get_pan()); + //mrf.enable_wake(); + //mrf.set_bufferPHY(true); + //mrf.turbo(); + //mrf.set_power(0b1100000); + //mrf.sleep(); + + printf("Started\n\r"); + + char tmp[20]; + while(1) { + printf("txxxing... %i\n\r", i++); + //mrf.wake(); + + sprintf(tmp,"Received %i\n\r", i); + mrf.send_str(0x4202, tmp); + + time_sleep(2); + } +} + + + +void handle_rx() { + printf("received a packet %i bytes long\n\r", mrf.get_rxinfo()->frame_length); + + if(mrf.get_bufferPHY()){ + printf("Packet data (PHY Payload):\n\r"); + for (int i = 0; i < mrf.get_rxinfo()->frame_length; i++) { + printf("%i", mrf.get_rxbuf()[i]); + } + } + + // printf("\r\nASCII data (relevant data):\n\r"); + // for (int i = 0; i < mrf.rx_datalength(); i++) { + // usart::put(mrf.get_rxinfo()->rx_data[i]); + // } + + printf("\r\nLQI/RSSI="); + printf("%i/%i\n\r", mrf.get_rxinfo()->lqi, mrf.get_rxinfo()->rssi); +} + +void handle_tx() { + if (mrf.get_txinfo()->tx_ok) { + printf("TX went ok, got ack %i\n\r", i); + } else { + printf("TX failed after %i retries\n\n\r", mrf.get_txinfo()->retries); + } + printf("Sleeping\n\r"); + //mrf.sleep(); +}