Chris Dick / nRF2401A

Dependents:   nRF2401A_Hello_World nRF2401A_Wireless_Accelerometer_joypad nRF2401A_Gameduino_Invaders

Files at this revision

API Documentation at this revision

Comitter:
TheChrisyd
Date:
Fri Oct 04 16:14:49 2013 +0000
Child:
1:8c57f88ff574
Commit message:
first commit for week 1 progress update

Changed in this revision

nRF2401A.cpp Show annotated file Show diff for this revision Revisions of this file
nRF2401A.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nRF2401A.cpp	Fri Oct 04 16:14:49 2013 +0000
@@ -0,0 +1,306 @@
+/* mbed nRF2401A  Library
+ *
+ * Copyright (c) 2011, Per Söderstam
+ *
+ * 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 "nRF2401A.h"
+
+
+nRF2401A::nRF2401A(PinName ce,
+                   PinName cs,
+                   PinName dr1,
+                   PinName clk1,
+                   PinName data)
+        : _ce(DigitalOut(ce)),
+        _cs(DigitalOut(cs)),
+        _dr1(DigitalIn(dr1)),
+        _clk1(DigitalOut(clk1)),
+        _data(DigitalInOut(data)),
+        _state(nRF2401A::UNDEF),
+        _rx_handler((nRF2401A_rx_handler_t) 0),
+        _rx_handler_arg((void *) 0),
+        _dr1_isr(InterruptIn(dr1)) {
+
+    // init member variables
+    _data.output();
+    // setup...
+    _ctrl_packet = (uint8_t *) &_ctrl_packet_buf;
+    _dr1_isr.rise(this, &nRF2401A::dataReadyHandler);
+    // ...tranciever in standby...
+    _ce = 0;
+    _cs = 0;
+    // ...and clear receive buffer
+    for (int i = 0; i < 16; i++)
+        _data_buf[i] = 0x0;
+    // ...set imutable control fields...
+    _ctrl_packet_buf.enable_dual_channel_mode = 0x0;    // single channel receive
+    _ctrl_packet_buf.communication_mode = 0x1;          // ShockBurst mode
+    _ctrl_packet_buf.xo_frequency = 0x3;                // 16 MHz crystal
+    _ctrl_packet_buf.rf_power = 0x3;                    // 0 dBm (1 mW) output power
+    // ...start in RX mode
+    _ctrl_packet_buf.txr_switch = nRF2401A::RX_MODE;
+    // assure minimum wake up time while assuming tranciever powers up with uP
+    wait_ms(Tpd2cfgm);
+
+    return;
+}
+
+void nRF2401A::printControlPacket(Serial& port)
+{
+    for(int i = 0; i < sizeof(_ctrl_packet_buf); i++)
+        port.printf("%02x ", _ctrl_packet[i]);
+    port.printf("\n\r");
+    return;
+}
+
+void nRF2401A::printDataPacket(Serial& port)
+{
+    for(int i = 0; i < sizeof(_data_buf); i++)
+        port.printf("%02x ", _data_buf[i]);
+    port.printf("\r");
+    return;
+}
+
+nRF2401A& nRF2401A::attachRXHandler(nRF2401A_rx_handler_t handler, void *arg) {
+
+    _rx_handler = handler;
+    _rx_handler_arg = arg;
+
+    return *this;
+}
+
+nRF2401A& nRF2401A::sendMsg(nRF2401A::address_t addr, uint8_t addr_len, uint8_t *msg_buf, uint8_t msg_len) {
+
+    // point to start of address byte in address
+    uint8_t *aligned_addr = &addr[sizeof(address_t) - (addr_len / 8)];
+    // wait for tx completion
+    int Toa = (_ctrl_packet_buf.rf_data_rate == nRF2401A::BIT_RATE_1MBITS ? 1 : 4) * (addr_len + msg_len + 1 + 16);
+
+    switch (_state) {
+        case nRF2401A::RX:
+            // switch to transmit
+            _ce = 0;
+            _cs = 0;
+            wait_us(Td);
+            // assert CS/CE and wait Tcs2data
+            _ce = 0;
+            _cs = 1;
+            wait_us(Tcs2data);
+            // push out the bits
+            _data = nRF2401A::TX_MODE;
+            wait_us(Ts);
+            _clk1 = 1;
+            wait_us(Th);
+            _clk1 = 0;
+            // wait Td
+            wait_us(Td);
+            // deassert CS/CE and done...
+            _cs = 0;
+            _ce = 0;
+
+            // zero control and data lines
+            _clk1 = 0;
+            _data = 0;
+            // wait Td
+            wait_us(Td);
+            // assert CE and wait Tcs2data
+            _ce = 1;
+            wait_us(Tce2data);
+            // push out the address bits
+            for (int i = 0; i < addr_len; i++) {
+                _data = ((0x80 >> (i % 8)) & aligned_addr[i / 8]) ? 0x1 : 0x0;
+                wait_us(Ts);
+                _clk1 = 1;
+                wait_us(Th);
+                _clk1 = 0;
+            }
+            // push out the message bits
+            for (int i = 0; i < msg_len; i++) {
+                _data = ((0x80 >> (i % 8)) & msg_buf[i / 8]) ? 0x1 : 0x0;
+                wait_us(Ts);
+                _clk1 = 1;
+                wait_us(Th);
+                _clk1 = 0;
+            }
+            // reset data
+            _data = 0;
+            // deassert CE will initiate transmission
+            _ce = 0;
+            wait_us(Tsby2txSB + Toa);
+
+            // switch back to receive
+            wait_us(Td);
+            // assert CS/CE and wait Tcs2data
+            _cs = 1;
+            wait_us(Tcs2data);
+            // push out the bits
+            _data = nRF2401A::RX_MODE;
+            wait_us(Ts);
+            _clk1 = 1;
+            wait_us(Th);
+            _clk1 = 0;
+            // wait Td
+            wait_us(Td);
+            _data = 0;
+            // deassert CS/CE and done...
+            _cs = 0;
+            // wait Td to avoid simultaineous control high
+            wait_us(Td);
+            _ce = 1;
+            // done
+            break;
+        case nRF2401A::STANDBY:
+        case nRF2401A::TX:
+        case nRF2401A::UNDEF:
+        default:
+            // can only send in RX mode
+            break;
+    }
+
+    return *this;
+}
+
+void nRF2401A::pushCtrl(uint8_t *buf, uint8_t n_bits, bool is_ctrl) {
+
+    DigitalOut  &ctrl_pin = is_ctrl ? _cs : _ce;
+
+    // set data to output
+    _data.output();
+    // zero control and data lines
+    _cs = 0;
+    _ce = 0;
+    _clk1 = 0;
+    _data = 0;
+    // wait Td
+    wait_us(Td);
+    // assert CS/CE and wait Tcs2data
+    ctrl_pin = 1;
+    wait_us(Tcs2data);
+    // push out the bits
+    for (int i = 0; i < n_bits; i++) {
+        _data = ((0x80 >> (i % 8)) & buf[i / 8]) ? 0x1 : 0x0;
+        wait_us(Ts);
+        _clk1 = 1;
+        wait_us(Th);
+        _clk1 = 0;
+    }
+    _data = 0;
+    // wait Td
+    wait_us(Td);
+    // deassert CS/CE and done...
+    ctrl_pin = 0;
+
+    return;
+}
+
+int nRF2401A::pull(uint8_t *buf) {
+    int n = 0;
+    
+    // read from data pin
+    _data.input();
+    // init signals, go to standby
+    _ce = 1;
+    _cs = 0;
+    _clk1 = 0;
+    // ensure time from DR
+    wait_us(Td);
+    
+    while (_dr1 == 1) {
+        _clk1 = 1;
+        wait_us(Thmin);
+        if(_data.read())
+            buf[n / 8] |= (0x80 >> (n % 8));
+        else
+            buf[n / 8] &= ~(0x80 >> (n % 8));
+        n++;
+        _clk1 = 0;
+        wait_us(Thmin);
+    }
+    // return to active
+    _ce = 1;
+    // reset data pin direction
+    _data.output();
+    
+    return n;
+}
+
+void nRF2401A::activate(bool active) {
+    switch (_state) {
+        case nRF2401A::RX:
+            if (!active) {
+                _state = nRF2401A::STANDBY;
+                _ce = 0;
+                _cs = 0;
+            }
+            break;
+        case nRF2401A::STANDBY:
+            if (active) {
+                _state = nRF2401A::RX;
+                _ce = 1;
+                _cs = 0;
+            }
+            break;
+        case nRF2401A::TX:
+        case nRF2401A::UNDEF:
+        default:
+            break;
+    }
+
+    return;
+}
+
+void nRF2401A::dataReadyHandler(void) {
+    switch (_state) {
+        case nRF2401A::RX:
+            pull(_data_buf);
+            if (_rx_handler != (nRF2401A_rx_handler_t) 0)
+                _rx_handler(_rx_handler_arg);
+            break;
+        default:
+            // todo: error msg
+            break;
+    }
+    return;
+}
+
+nRF2401A& nRF2401A::flushControlPacket() {
+    switch (_state) {
+        case nRF2401A::UNDEF:
+        case nRF2401A::RX:
+            pushCtrl(_ctrl_packet, 15 << 3 );
+            _state = nRF2401A::RX;
+            _ce = 1;
+            _cs = 0;
+            break;
+        case nRF2401A::STANDBY:
+            pushCtrl(_ctrl_packet, 15 << 3 );
+            _state = nRF2401A::STANDBY;
+            _ce = 0;
+            _cs = 0;
+            break;
+        case nRF2401A::TX:
+        default:
+            _ce = 0;
+            _cs = 0;
+    }
+
+    return *this;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nRF2401A.h	Fri Oct 04 16:14:49 2013 +0000
@@ -0,0 +1,340 @@
+/* mbed nRF2401A  Library
+ *
+ * Copyright (c) 2011, Per Söderstam
+ *
+ * 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 <mbed.h>
+#include <inttypes.h>
+#define   Ts         1     /**< Setup time from data to rising clock edge on write (accualy 500 ns). */
+#define   Th         1     /**< Hold time from rising clock to data toggle/falling clock (accualy 500 ns). */
+#define   Tcs2data   5     /**< Min delay from CS assert to data, in us. */
+#define   Tce2data   5     /**< Min delay from CE assert to data, in us. */
+#define   Td         1     /**< Minimum delay between edges (actually 50 ns). */
+#define   Tpd2cfgm   3     /**< Minimum delay from power up of tranciever to configuration. */
+#define   Tsby2txSB  195   /**< Minimum delay from tx initation to air, in us. */
+#define   Thmin      5     /**< */
+#define   Tclk2data  1     /**< */
+
+/** Servo control class, based on a PwmOut
+ *
+ * Example:
+ * @code
+ * #include "mbed.h"
+ * #include "nRF2401A.h"
+ *
+ * DigitalOut  myled(LED1);
+ * nRF2401A    rf1(p10, p11, p12, p13, p14);
+ * nRF2401A    rf2(p21, p22, p23, p24, p25);
+ *
+ * Serial pc(USBTX, USBRX);
+ *
+ * int main() {
+ *
+ *     wait(0.005);
+ *
+ *     pc.printf("Hello nRF2401A\n\r");
+ *   
+ *     rf1.setDataPayloadLength(4 << 3)
+ *        .setAddress(0x0, 0x0, 0xa6, 0xa6, 0xa6, 3 << 3)
+ *        .setCRCMode(nRF2401A::NO_CRC)
+ *        .setDataRate(nRF2401A::BIT_RATE_250KBITS)
+ *        .setChannel(0x02);
+ *        
+ *     rf1.printControlPacket(pc);
+ *     
+ *     rf2.setDataPayloadLength(4 << 3)
+ *        .setAddress(0x0, 0x0, 0x53, 0x53, 0x53, 3 << 3)
+ *        .setCRCMode(nRF2401A::NO_CRC)
+ *        .setDataRate(nRF2401A::BIT_RATE_250KBITS)
+ *        .setChannel(0x02);
+ *        
+ *     rf2.printControlPacket(pc);
+ *     
+ *     rf1.flushControlPacket();
+ *     rf2.flushControlPacket();
+ *     
+ *     nRF2401A::address_t rf2_addr = {0x0, 0x0, 0x53, 0x53, 0x53};
+ *     uint8_t msg[] = {0x01, 0x01, 0x01, 0x01};
+ *     uint32_t *msg32 = (uint32_t *) msg;
+ *         
+ *     while(1) {
+ *             
+ *         rf1.sendMsg(rf2_addr, 3 << 3, msg, 4 << 3);
+ *         *msg32 += 1;
+ *         
+ *         myled = 1;
+ *         wait(0.25);
+ *         
+ *         rf2.printDataPacket(pc);
+ *         
+ *         myled = 0;
+ *         wait(0.25);
+ *     }
+ * }
+ * @endcode
+ */
+
+/** ISR handler prototype for receiving messages.
+ * A function of this type is registered and called when the DR pin on the
+ * nRF tranciever signals the reception of a message. The void * argument
+ * is likewise supplied when registering and is returned at call time.
+ */
+typedef void (*nRF2401A_rx_handler_t)(void *);
+
+/**
+ *
+ *
+ */
+class nRF2401A
+{
+    public:
+        /** Class constructor.
+         * The constructor assigns the specified pinout, attatch the
+         * DR1 to a pin interrupt and sets up inmutable control packet
+         * fields.
+         * \param ce Chip Enable (CE) pin of the nRF2401A.
+         * \param c2 Chip Select (CS) pin of the nRF2401A.
+         * \param dr1 Data Ready 1 (DR1) pin of the nRF2401A.
+         * \param clk1 Clock 1 (CLK1) pin of the nRF2401A.
+         * \param data Data (DATA) pin of the nRF2401A.
+         */
+        nRF2401A(PinName ce,
+                 PinName cs,
+                 PinName dr1,
+                 PinName clk1,
+                 PinName data);
+                 
+        /** Class destructor.
+         * Pretty much useless in the embedded world...
+         */
+        virtual ~nRF2401A() { return; }
+        
+        /** Set the payload length, in bits.
+         * Set the control packet field for length, in number of bits, of the message payload.
+         * \param n Number of bits of the message payload.
+         * \return Reference to the invoked object (for chaining operations).
+         */
+        nRF2401A& setDataPayloadLength(uint8_t n) 
+        { 
+            _ctrl_packet_buf.channel_1_data_payload_len = n; 
+            return *this; 
+        }
+        
+        /** Set the address of channel 1.
+         * The channel address is a up to 40 bit number identifying the tranciever.
+         * \param addr4 Bits 39-32 of the address.
+         * \param addr4 Bits 31-24 of the address.
+         * \param addr4 Bits 23-16 of the address.
+         * \param addr4 Bits 15-8 of the address.
+         * \param addr4 Bits 7-0 of the address.
+         * \param n_bits Number of bits used in the address.
+         * \return Reference to the invoked object (for chaining operations).
+         */
+        nRF2401A& setAddress(uint8_t addr4, uint8_t addr3, uint8_t addr2, uint8_t addr1, uint8_t addr0, uint8_t n_bits)
+        {
+            _ctrl_packet_buf.channel_1_address[0] = addr4;
+            _ctrl_packet_buf.channel_1_address[1] = addr3;
+            _ctrl_packet_buf.channel_1_address[2] = addr2;
+            _ctrl_packet_buf.channel_1_address[3] = addr1;
+            _ctrl_packet_buf.channel_1_address[4] = addr0;
+            _ctrl_packet_buf.channel_address_len = n_bits;
+            
+            return *this;
+        }
+        
+        /** CRC settings.
+         * Type covering the allowed settings for use of CRC.
+         */
+        typedef enum 
+        {
+            NO_CRC = 0x0,   /**< Do not use CRC. */
+            CRC_8 = 0x1,    /**< Use a 8-bit CRC. */
+            CRC_16 = 0x3    /**< Use a 16-bit CRC. */
+        } CRC_T;
+        
+        /** Set CRC use.
+         * Set the CRC mode field of the control packet.
+         * \param mode The CRC mode of choise.
+         * \return Reference to the invoked object (for chaining operations).
+         */
+        nRF2401A& setCRCMode(CRC_T mode) 
+        { 
+            _ctrl_packet_buf.crc_config = mode; 
+            return *this; 
+        }
+        
+        /** Data rate settings.
+         * Type covering the allowed settings for the tranciever data rate.
+         */
+        typedef enum
+        {
+            BIT_RATE_250KBITS = 0x0,    /**< */
+            BIT_RATE_1MBITS = 0x1       /**< */
+        } DATA_RATE_T;
+        
+        /** Set tranciever data rate.
+         * Sets the data rate field to either 250 kbit/s or 1 Mbit/s data transfer rate.
+         * \param mode The data rate of choise.
+         * \return Reference to the invoked object (for chaining operations).
+         */
+        nRF2401A& setDataRate(DATA_RATE_T data_rate)
+        {
+            _ctrl_packet_buf.rf_data_rate = data_rate;
+            return *this;
+        }
+        
+        /** Set RF channel.
+         * Sets the control packet field for channel number. Channel numbers are from 0 to 127
+         * representing channel frequencies equal to (2400 + channel number) MHz.
+         * \param ch Channel number, from the range [0, 127].
+         * \return Reference to the invoked object (for chaining operations).
+         */
+        nRF2401A& setChannel(uint8_t ch)
+        {
+            _ctrl_packet_buf.rf_channel = ch;
+            return *this;
+        }
+        
+        /** Send the control packet to the nRF2401A.
+         * This function transfer the control packet image to the nRF2401A.
+         * \return Reference to the invoked object (for chaining operations).
+         */
+        nRF2401A& flushControlPacket();
+        
+        /**
+         *
+         */
+        void activate(bool active = true);
+        
+        /**
+         *
+         */
+        typedef uint8_t address_t[5];
+        
+        /** Send a message.
+         * This routine will transfer the data from the supplied buffer and send
+         * it to the specified address using the current control packet settings.
+         * \param addr The address to send to.
+         * \param addr_len Length of address, in bits.
+         * \param msg_buf Message body.
+         * \param msg_len Length of message,  in bits.
+         * \return Reference to the invoked object (for chaining operations).
+         */
+        nRF2401A& sendMsg(address_t addr, uint8_t addr_len, uint8_t *msg_buf, uint8_t msg_len);
+        
+        /** Register a receive action callback.
+         * Attach a callback that will be called when the tranciever intercept a
+         * message. This callback will be called in the context of an interrupt
+         * routine and should act accordingly.
+         * \param handler The callback, of type nRF2401_rx_handler_t.
+         * \param arg Pointer to data supplied to the handler at call time.
+         * \return Reference to the invoked object (for chaining operations).
+         */     
+        nRF2401A& attachRXHandler(nRF2401A_rx_handler_t handler, void *arg);
+        
+        void printControlPacket(Serial& port);
+        void printDataPacket(Serial& port);
+        
+    private:
+    
+        DigitalOut      _ce;    /**< Chip Enable pin. */
+        DigitalOut      _cs;    /**< Chip select pin. */
+        DigitalIn       _dr1;   /**< Data Ready 1 pin. */
+        DigitalOut      _clk1;  /**< Clock 1 pin. */
+        DigitalInOut    _data;  /**< Data pin. */
+        
+        /**
+         *
+         */
+        typedef enum 
+        { 
+            UNDEF,      /**< The start state. */
+            RX,         /**< The tranciever is in receive mode. */
+            TX,         /**< The tranciever is transmitting. */
+            STANDBY     /**< The tranciever goes into stanby mode. */
+        } STATE_T;
+        
+        STATE_T  _state;
+        
+        /** Contol packet data.
+         *
+         */
+        struct nRF2401A_ctrl_packet_t
+        {
+            uint8_t     channel_2_data_payload_len;     /**< */
+            uint8_t     channel_1_data_payload_len;     /**< */
+            uint8_t     channel_2_address[5];           /**< */
+            uint8_t     channel_1_address[5];           /**< */
+            
+            uint8_t     crc_config : 2;                 /**< */
+            uint8_t     channel_address_len : 6;        /**< */
+            
+            uint8_t     rf_power : 2;                   /**< */
+            uint8_t     xo_frequency : 3;               /**< */
+            uint8_t     rf_data_rate : 1;               /**< */
+            uint8_t     communication_mode : 1;         /**< */
+            uint8_t     enable_dual_channel_mode : 1;   /**< */
+            
+            uint8_t     txr_switch : 1;                 /**< */
+            uint8_t     rf_channel : 7;                 /**< */
+            
+        }                                       _ctrl_packet_buf;   /**< */
+        
+        uint8_t         *_ctrl_packet;  /**< */
+        
+        uint8_t         _data_buf[32];  /**< */        
+        
+        nRF2401A_rx_handler_t   _rx_handler;        /**< */
+        void                    *_rx_handler_arg;   /**< */
+        
+        /** Receive ISR.
+         * This handler is attached to the rising flank of the DR1 pin. It
+         * will thus be called when the nRF2401A receives a packet in ShockBurst
+         * mode (the mode used). It will in turn call the attached handler.
+         */
+        void dataReadyHandler(void);
+        
+        /**
+         *
+         */
+        InterruptIn     _dr1_isr;
+        
+        /*
+         *
+         */
+        typedef enum { RX_MODE = 0x1, TX_MODE = 0x0 } TXR_T;
+        
+        /** Write to the data bus.
+         * Write n_bits bits on the DATA line.
+         * \param buf Data buffer.
+         * \param n_bits Number of bits to transfer.
+         * \param is_ctrl True if the tranfered data is control word, false if data.
+         */
+        void pushCtrl(uint8_t *buf, uint8_t n_bits, bool is_ctrl = true);
+        
+        /** Read a message from the tranciever.
+         * Read until DR1 goes low.
+         * \param buf Data buffer.
+         * \return Number of bits read.
+         */
+        int pull(uint8_t *buf);
+};
\ No newline at end of file