RFM12B radio module driver library

Dependents:   IoTGateway_Basic

Revision:
0:e724d8251cdc
--- /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;
+}