RFM12B radio module driver library

Dependents:   IoTGateway_Basic

RF12B.cpp

Committer:
SomeRandomBloke
Date:
2012-03-31
Revision:
0:e724d8251cdc

File content as of revision 0:e724d8251cdc:

/* 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;
}