RFM12B radio module driver library
Revision 0:e724d8251cdc, committed 2012-03-31
- Comitter:
- SomeRandomBloke
- Date:
- Sat Mar 31 07:11:16 2012 +0000
- Commit message:
- Updated and cleaned up
Changed in this revision
RF12B.cpp | Show annotated file Show diff for this revision Revisions of this file |
RF12B.h | Show annotated file Show diff for this revision Revisions of this file |
diff -r 000000000000 -r e724d8251cdc RF12B.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/RF12B.cpp Sat Mar 31 07:11:16 2012 +0000 @@ -0,0 +1,303 @@ +/* RF12B Library. Based on work done by JeeLabs.org ported to mbed by SK Pang. +http://jeelabs.net/projects/cafe/wiki/RF12 + +http://opensource.org/licenses/mit-license.php + +Jan 2012 skpang.co.uk + +Modified by Andrew Lindsay (andrew [at] thiseldo [dot] co [dot] uk) + +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 "RF12B.h" + +DigitalOut led4(LED4, "led4"); + +// 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 + +// bits in the node id configuration byte +#define NODE_BAND 0xC0 // frequency band +#define NODE_ACKANY 0x20 // ack on broadcast packets if set +#define NODE_ID 0x1F // id of this node, as A..Z or 1..31 + +// transceiver states, these determine what to do with each interrupt +enum { + TXCRC1, TXCRC2, TXTAIL, TXDONE, TXIDLE, + TXRECV, + TXPRE1, TXPRE2, TXPRE3, TXSYN1, TXSYN2, +}; + +RF12B::RF12B(PinName _SDI, + PinName _SDO, + PinName _SCK, + PinName _NCS, + PinName _NIRQ):spi(_SDI, _SDO, _SCK), + NCS(_NCS), NIRQ(_NIRQ), NIRQ_in(_NIRQ) { + + /* SPI frequency, 8 bit word length, polarity and phase */ + spi.format(8,0); + spi.frequency(2000000); + + /* Set ~CS high */ + NCS = 1; + + /* Setup interrupt to happen on falling edge of NIRQ */ + NIRQ.fall(this, &RF12B::rxISR); +} + + +/********************************************************************** + * PRIVATE FUNCTIONS + *********************************************************************/ + +/* Initialises the RF12B module */ +void RF12B::init(uint8_t id, uint8_t band, uint8_t g) { + + nodeid = id; + group = g; + rf12_grp = g; + + writeCmd(0x0000); // intitial SPI transfer added to avoid power-up problem + writeCmd(RF_SLEEP_MODE); // DC (disable clk pin), enable lbd + + // wait until RFM12B is out of power-up reset, this takes several *seconds* + writeCmd(RF_TXREG_WRITE); // in case we're still in OOK mode + + while (NIRQ == 0) writeCmd(0x0000); + + // TODO: Have band specific parameters to optimise settings + + writeCmd(0x80C7 | (band << 4)); // EL (ena TX), EF (ena RX FIFO), 12.0pF + writeCmd(0xA640); // 868MHz + writeCmd(0xC606); // approx 49.2 Kbps, i.e. 10000/29/(1+6) Kbps + writeCmd(0x94A2); // VDI,FAST,134kHz,0dBm,-91dBm + writeCmd(0xC2AC); // AL,!ml,DIG,DQD4 + if (group != 0) { + writeCmd(0xCA83); // FIFO8,2-SYNC,!ff,DR + writeCmd(0xCE00 | group); // SYNC=2DXX + } else { + writeCmd(0xCA8B); // FIFO8,1-SYNC,!ff,DR + writeCmd(0xCE2D); // SYNC=2D + } + + writeCmd(0xC483); // @PWR,NO RSTRIC,!st,!fi,OE,EN + writeCmd(0x9850); // !mp,90kHz,MAX OUT + writeCmd(0xCC77); // OB1, OB0, LPX, ddy, DDIT, BW0 + writeCmd(0xE000); // NOT USE + writeCmd(0xC800); // NOT USE + writeCmd(0xC049); // 1.66MHz,3.1V + + rxstate = TXIDLE; + +} + +int RF12B::available( void ) { + + return (rf12_recvDone() && (check_crc() == 0) && length() < 66) ? length() : 0; +} + +/* Write a command to the RF Module */ +int RF12B::writeCmd(int cmd) { + NCS = 0; + int recv = spi.write(cmd >>8); + recv = spi.write(cmd); + NCS = 1; + return recv; +} + +/* Sends a byte of data across RF */ +void RF12B::send(uint8_t data) { + while (NIRQ); + writeCmd(0xB800 + data); +} + + +/* Interrupt routine for data reception and Txing */ +void RF12B::rxISR() { + led4 = 1; + // a transfer of 2x 16 bits @ 2 MHz over SPI takes 2x 8 us inside this ISR + writeCmd(0x0000); + + if (rxstate == TXRECV) { + uint8_t in = rf12_xfer(RF_RX_FIFO_READ); + + if (rxfill == 0 && group != 0) + rf12_buf[rxfill++] = group; + + rf12_buf[rxfill++] = in; + rf12_crc = _crc16_update(rf12_crc, in); + + if (rxfill >= rf12_len + 5 || rxfill >= RF_MAX) + rf12_xfer(RF_IDLE_MODE); + } else { + uint8_t out; + + if (rxstate < 0) { + uint8_t pos = 3 + 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 = rf12_grp; + rxstate = - (2 + rf12_len); + break; + case TXCRC1: + out = rf12_crc; + break; + case TXCRC2: + out = rf12_crc >> 8; + break; + case TXDONE: + rf12_xfer(RF_IDLE_MODE); // fall through + default: + out = 0xAA; + } + } + rf12_xfer(RF_TXREG_WRITE + out); + } + led4 = 0; +} + + +void RF12B::rf12_sendStart (uint8_t hdr, const void* ptr, uint8_t len) { + rf12_len = len; + memcpy((void*) rf12_data, ptr, len); + rf12_sendStart(hdr); +} + +//void RF12B::rf12_sendStart2 (uint8_t hdr) { +void RF12B::rf12_sendStart (uint8_t hdr) { + rf12_hdr = hdr & RF12_HDR_DST ? hdr : + (hdr & ~RF12_HDR_MASK) + (nodeid & NODE_ID); + + /* + if (crypter != 0) + crypter(1); + */ + rf12_crc = ~0; + + rf12_crc = _crc16_update(rf12_crc, rf12_grp); + rxstate = TXPRE1; + + rf12_xfer(RF_XMITTER_ON); // bytes will be fed via interrupts +} + + +uint16_t RF12B::rf12_xfer (uint16_t cmd) { + NCS = 0; + uint16_t reply = rf12_byte(cmd >> 8) << 8; + reply |= rf12_byte(cmd); + NCS = 1; + return reply; +} + +void RF12B::rf12_recvStart (void) { + rxfill = rf12_len = 0; + rf12_crc = ~0; + + if (group != 0) + rf12_crc = _crc16_update(~0, group); + + rxstate = TXRECV; + rf12_xfer(RF_RECEIVER_ON); +} + +uint16_t RF12B::check_crc(void) { + return rf12_crc; +} + +uint8_t RF12B::length(void) { + return rf12_len; +} + +uint8_t* RF12B::get_data(void) { + return (uint8_t*)rf12_buf; +} + +uint8_t* RF12B::get_payload(void) { + return (uint8_t*)rf12_data; +} + +uint8_t RF12B::rf12_recvDone (void) { + + if (rxstate == TXRECV && (rxfill >= rf12_len + 5 || rxfill >= RF_MAX)) { + rxstate = TXIDLE; + + if (rf12_len > RF12_MAXDATA) + rf12_crc = 1; // force bad crc if packet length is invalid + if (!(rf12_hdr & RF12_HDR_DST) || (nodeid & NODE_ID) == 31 || + (rf12_hdr & RF12_HDR_MASK) == (nodeid & NODE_ID)) { + /* + printf("RX Len: %d ", rf12_len+6); + for (int i=0; i<rf12_len+6; i++) { + printf("%02X ",rf12_buf[i]); + } + printf(" crc:%x\n",rf12_crc); + */ + /* + if (rf12_crc == 0 && crypter != 0) + crypter(0); + else + rf12_seq = -1; + */ + return 1; // it's a broadcast packet or it's addressed to this node + + } + } + if (rxstate == TXIDLE) + rf12_recvStart(); + return 0; +} + +uint8_t RF12B::rf12_byte(uint8_t out) { +// unsigned char recv = spi.write(out); +// return recv; + return spi.write(out); +} + +uint16_t RF12B::_crc16_update(uint16_t crc, uint8_t data) { + int i; + + crc ^= data; + for (i = 0; i < 8; ++i) { + if (crc & 1) + crc = (crc >> 1) ^ 0xA001; + else + crc = (crc >> 1); + } + + return crc; +}
diff -r 000000000000 -r e724d8251cdc RF12B.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/RF12B.h Sat Mar 31 07:11:16 2012 +0000 @@ -0,0 +1,132 @@ +/* RF12B Library. Based on work done by JeeLabs.org ported to mbed by SK Pang. +http://jeelabs.net/projects/cafe/wiki/RF12 + +http://opensource.org/licenses/mit-license.php + +Jan 2012 skpang.co.uk + +Modified by Andrew Lindsay (andrew [at] thiseldo [dot] co [dot] uk) + +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. +*/ + +#ifndef _RF12B_H +#define _RF12B_H + +#include "mbed.h" + +#define RF12_433MHZ 1 +#define RF12_868MHZ 2 +#define RF12_915MHZ 3 + +#define rf12_grp rf12_buf[0] +#define rf12_hdr rf12_buf[1] +#define rf12_len rf12_buf[2] +#define rf12_data (rf12_buf + 3) + +#define RF12_HDR_CTL 0x80 +#define RF12_HDR_DST 0x40 +#define RF12_HDR_ACK 0x20 +#define RF12_HDR_MASK 0x1F + +#define RF12_MAXDATA 66 +// maximum transmit / receive buffer: 3 header + data + 2 crc bytes +#define RF_MAX (RF12_MAXDATA + 5) +//#define PACKET_LEN 16 + +// shorthand to simplify sending out the proper ACK when requested +#define RF12_WANTS_ACK ((rf12_hdr & RF12_HDR_ACK) && !(rf12_hdr & RF12_HDR_CTL)) +#define RF12_ACK_REPLY (rf12_hdr & RF12_HDR_DST ? RF12_HDR_CTL : \ + RF12_HDR_CTL | RF12_HDR_DST | (rf12_hdr & RF12_HDR_MASK)) + +/** RFM12B Radio module class + * Example usage: + * @code + * #include "RF12B.h" + * + * RF12B rfm12b(p11, p12, p13, p14, p18); + * + * int main() { + * rfm12b.init(2, 868, 5 ); //id = 2, band 866, group 5 + * rfm12b.rf12_recvStart(); + * while (true) { + * if ( rfm12b.available() > 0 ) { + * // Do something with received data + * } + * } + * + * } + * @endcode + */ +class RF12B { +public: + /* Constructor */ + RF12B(PinName SDI, + PinName SDO, + PinName SCK, + PinName NCS, + PinName NIRQ); + + /* Initialises the RF12B module */ + void init(uint8_t id, uint8_t band, uint8_t g); + + /* Returns the packet length if data is available in the receive buffer, 0 otherwise*/ + bool havePacket( void ); + int available(); + void rf12_sendStart (uint8_t hdr, const void* ptr, uint8_t len); + void rf12_sendStart (uint8_t hdr); + uint8_t rf12_recvDone (void); + void rf12_recvStart (void); + uint16_t check_crc(void); + uint8_t length(void); + uint8_t* get_data(void); + uint8_t* get_payload(void); + +protected: + + /* SPI module */ + SPI spi; + + /* Other digital pins */ + DigitalOut NCS; + InterruptIn NIRQ; + DigitalIn NIRQ_in; + volatile uint16_t rf12_crc; // running crc value + volatile unsigned char rf12_buf[RF_MAX]; // recv/xmit buf, including hdr & crc bytes + volatile uint8_t nodeid; // address of this node + volatile uint8_t group; // network group + volatile uint8_t rxfill; // number of data bytes in rf12_buf + volatile int8_t rxstate; // current transceiver state + + /* Write a command to the RF Module */ + int writeCmd(int cmd); + + /* Sends a byte of data across RF */ + void send(uint8_t data); + + /* Interrupt routine for data reception */ + void rxISR(); + + uint16_t _crc16_update(uint16_t crc, uint8_t data); + + uint16_t rf12_xfer (uint16_t cmd); + uint8_t rf12_byte(uint8_t out); +}; + +#endif /* _RF12B_H */ \ No newline at end of file