For robots and stuff

Dependents:   Base Station

Revision:
1:05a48c038381
Parent:
0:c5afea7b9057
Child:
2:c42a035d71ed
diff -r c5afea7b9057 -r 05a48c038381 CC1101/CC1101.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CC1101/CC1101.cpp	Sun Dec 28 06:28:42 2014 +0000
@@ -0,0 +1,1016 @@
+#include "CC1101.h"
+
+#define DEBUG_MODE 0
+#define ACK_SHOW 1
+
+// 0db power
+#define RF_DB 10
+
+CC1101::CC1101() :
+    Radio()
+{  };
+
+CC1101::CC1101(PinName mosi, PinName miso, PinName clk, PinName csn, PinName tx_led, PinName rx_led, PinName interpt, unsigned int xosc) :
+    Radio(_tx_led, _rx_led),
+    _crystal_freq(xosc)
+{
+    setup_spi(mosi, miso, clk);
+    setup_pins(csn, interpt);
+    setup_chip();
+}
+
+// Deconstructor
+CC1101::~CC1101()
+{
+    if ( _spi ) {
+        delete _spi;
+    }
+    if ( _rx_int ) {
+        delete _rx_int;
+    }
+    if ( _csn ) {
+        delete _csn;
+    }
+}
+
+// Configuration method that is called from the constructors
+void CC1101::setup_spi(PinName mosi, PinName miso, PinName clk)
+{
+    if (mosi != NC & miso != NC & clk != NC) {
+        // Setup the spi for 8 bit data, high steady state clock, second edge capture
+        _so = miso;
+        _si = mosi;
+        _sck = clk;
+        _spi = new SPI(mosi, miso, clk);
+        _spi->format(8,0);
+        _spi->frequency(5000000);
+    }
+}
+
+
+// Configuration method that is called from the constructors
+void CC1101::setup_pins(PinName csn, PinName int_pin)
+{
+    if (csn != NC) {
+        _csn = new DigitalOut(csn);
+    }
+    if (int_pin != NC) {
+        InterruptIn *_rx_int = new InterruptIn(int_pin);
+        _rx_int->mode(PullDown);
+        _rx_int->rise(this, &CC1101::isr_receive);   // attach member function for interrupt trigger
+    }
+}
+
+/*
+// Configuration method that is called from the constructors
+void CC1101::setup_lights(void)
+{
+    for (int i=0; i<10; i++) {
+        _tx_led_thread.signal_set(SET_LED_TICK);
+        _rx_led_thread.signal_set(SET_LED_TICK);
+    }
+}
+*/
+
+
+// Configuration method that is called from the constructors
+void CC1101::setup_chip()
+{
+    // define an initial state of an unselected chip
+    *_csn = 1;
+
+    // frequency that radio links with another CC1101 over the air
+    _carrier_freq = _902MHZ_;
+
+    // turn off address packet filtering and assign 0 (broadcast address) to the address value
+    _pck_control.addr_check = ADDR_OFF;
+
+    // these values determine how the CC1101 will handel a packet
+    _pck_control.whitening_en = 0;
+
+    // enable CRC calculation in TX and CRC checking in RX
+    _pck_control.crc_en = 1;
+
+    // enable automatically flushing the RX buffer on a bad CRC (only works if 1 packet is in the RX buffer)
+    _pck_control.autoflush_en = 1;
+
+    // enable appending 2 status bytes to the end of every packet that includes the CRC and
+    _pck_control.status_field_en = 1;
+
+    // normal packet mode uses RX and TX buffers
+    _pck_control.format_type = FORMAT_DEFAULT;
+
+    // setup how the payload of the packet is transmitted - default to a fixed length of 61 bytes
+    _pck_control.length_type = PACKET_VARIABLE;
+
+    //_pck_control.length_type = PACKET_FIXED;
+    //_pck_control.size = 61;
+
+    // this is a preamble threshold for determining when a packet should be accepted
+    _pck_control.preamble_thresh = 2;
+
+    // these values determine how the frequency bands and channels are distributed as well as defining the modulation type
+    _modem.dc_filter_off_en = 0;
+    _modem.manchester_encode_en = 0;
+    _modem.fec_en = 0;
+
+    // bandwidth configurations
+    _modem.channel_bw = 2;
+    _modem.channel_bw_exp = 0;
+    _modem.channel_space_exp = 2;
+    _modem.data_rate_exp = 13;
+
+    _modem.mod_type = MOD_GFSK;
+    _modem.sync_mode = SYNC_HIGH_ALLOW_TWO;
+    _modem.preamble_bytes = PREAM_FOUR;
+
+    _send_count = 1;
+    _receive_count = 0;
+
+    // the values assigned here are used for the frequency synthesizer control
+    assign_if_freq(_316KHZ_);
+    assign_freq_offset(0);
+
+    // set all the configuration values into the rfSettings struct
+    set_rf_settings();
+
+    // get the chip's partnumber
+    _partnum = status(CCxxx0_PARTNUM);
+
+    // get the chip's version number and fail if different from what was expected
+    _chip_version = status(CCxxx0_VERSION);
+    uint8_t expected_version = 0x04;
+
+    if (_chip_version != expected_version) {
+
+        // write results to the log file before killing mbed from going any further
+#if RJ_BOOT_LOG
+        FILE *fp = fopen("/local/log.txt", "a");  // Open text file for tracking boot sequence results
+        if (fp == NULL) {
+            error("Could not get file pointer to log file\r\n");
+        }
+        fprintf(fp, "FATAL ERROR: CC1101 Version Error\n");
+        fclose(fp);
+#endif
+        // send message over serial port and kill mbed
+        error(
+            "[FATAL ERROR]\r\n"
+            "  Wrong version number returned from chip's 'VERSION' register (Addr: 0x%02X)\r\n"
+            "\r\n"
+            "  Expected: 0x%02X\r\n"
+            "  Found:    0x%02X\r\n"
+            "\r\n"
+            "  Troubleshooting Tips:\r\n"
+            "    - Check that the chip is fully connected with no soldering errors\r\n"
+            "    - Determine if chip is newer version & update firmware\r\n"
+            , CCxxx0_VERSION, expected_version, _chip_version);
+    }
+}
+
+
+void powerUp(void)
+{
+#if DEBUG_MODE > 0
+    std::printf("[CC1101 RADIO TRANSCEIVER INITILIZATION]\r\n");
+#endif
+    // execute a power-on-reset call to the CC1101 before writing all configuration values
+    power_on_reset();
+
+    // now send the assigned rfSettings struct to the CC1101 for full inililization
+    init();
+
+    scan();
+
+    //  start the regular class operations for the thread. This should always be the last call in the constructor!
+    _transmit_thread.signal_set(START_THREAD);
+    _receive_thread.signal_set(START_THREAD);
+
+
+#if DEBUG_MODE > 1
+    uint8_t start_address = 0x00;
+    uint16_t reg_count = 46;
+    uint8_t buf[reg_count];
+
+    read_reg(start_address ,buf, reg_count);
+    std::printf("\r\nDumping CC1101 register contents\r\n  ___________________\r\n | Address |  Value  |\r\n |===================|\r\n");
+    for (int i = 0; i<reg_count+1; i++) {
+        std::printf(" |   0x%02X  |   0x%02X  |\r\n", start_address + i, buf[start_address + i]);
+    }
+
+    std::printf(" |_________|_________|\r\n\r\nDumping CC1101 status register conetnts\r\n  ___________________\r\n | Address |  Value  |\r\n |===================|\r\n");
+    for (int i = 0; i<14; i++) {
+        std::printf(" |   0x%02X  |   0x%02X  |\r\n", CCxxx0_PARTNUM + i, status(CCxxx0_PARTNUM + i));
+    }
+    std::printf(" |_________|_________|\r\n\r\n");
+#endif
+
+#if DEBUG_MODE > 0
+    compute_freq();
+    std::printf("    CC1101 READY!\r\n      Chip Version: %-02u\r\n      Channel:      %-02u\r\n      Frequency:    %-3.2f MHz\r\n      Baud Rate:    %3u kHz\r\n", _chip_version, Radio::channel(), static_cast<float>(Radio::freq()/1000000), _baud_rate/1000 );
+#endif
+
+}
+
+// ===============
+void CC1101::set_rf_settings()
+{
+    // set rfSettings carrier frequency fields
+    freq(_carrier_freq);
+
+    // set the fields for packet controls
+    assign_packet_params();
+
+    // set the fields for the frequency limits of the modem
+    assign_modem_params();
+
+    // assign an address to the CC1101. This can be used to filter packets
+    rfSettings.ADDR = _addr;
+
+    // there can be 16 different channel numbers. The bandwidth and spacing are defined in other registers
+    rfSettings.CHANNR = _channel;
+
+    // compute the final adjusted frequency so that it will be correct after the constructor
+    compute_freq();
+
+    assign_baud_rate(250000);  // 250 kBaud
+
+    assign_channel_spacing(200000);    // 200kHz
+
+    // disable GDO0
+    rfSettings.IOCFG0 = 0x2E;
+
+    // setup for serial synchronous data output
+    rfSettings.IOCFG1 = 0x0C;
+
+    // setup for going HIGH when packet received and CRC is ok
+    rfSettings.IOCFG2 = 0x07;
+
+    rfSettings.DEVIATN = 0x62;
+
+    rfSettings.FREND1 = 0xB6;
+    rfSettings.FREND0 = 0x10;
+
+    bool RX_TIME_RSSI = 0;
+    bool RX_TIME_QUAL = 0;
+    uint8_t RX_TIME = 0x07;    // no timeout
+
+    rfSettings.MCSM2 = (RX_TIME_RSSI<<4) | (RX_TIME_QUAL<<3) | (RX_TIME & 0x07);
+
+    uint8_t CCA_MODE = 0x00;
+    uint8_t RXOFF_MODE = 0x00;  // go directly to IDLE when existing RX
+    //uint8_t RXOFF_MODE = 0x03;  // stay in RX when existing RX
+    uint8_t TXOFF_MODE = 0x03;  // go directly to RX when existing TX
+    // uint8_t TXOFF_MODE = 0x00;  // go directly to IDLE when existing TX
+
+    rfSettings.MCSM1 = ((CCA_MODE & 0x03)<<4) | ((RXOFF_MODE & 0x03)<<2) | (TXOFF_MODE & 0x03);
+
+    uint8_t FS_AUTOCAL = 0x01;  // calibrate when going from IDLE to RX or TX
+    uint8_t PO_TIMEOUT = 0x02;
+    bool PIN_CTRL_EN = 0;
+    bool XOSC_FORCE_ON = 0;
+
+    rfSettings.MCSM0 = ((FS_AUTOCAL & 0x03)<<4) | ((PO_TIMEOUT & 0x03)<<2) | (PIN_CTRL_EN<<1) | (XOSC_FORCE_ON);
+
+    bool FOC_BS_CS_GATE = 0;
+    uint8_t FOC_PRE_K = 0x03;
+    bool FOC_POST_K = 1;
+    uint8_t FOC_LIMIT = 0x01;
+
+    rfSettings.FOCCFG = 0x40 | (FOC_BS_CS_GATE<<5) | ((FOC_PRE_K & 0x03)<<3) | (FOC_POST_K<<2) | (FOC_LIMIT & 0x03);
+
+    rfSettings.BSCFG = 0x1C;
+
+    rfSettings.AGCCTRL2 = 0xC7;
+    rfSettings.AGCCTRL1 = 0x00;
+    rfSettings.AGCCTRL0 = 0xB0;
+
+    // rfSettings.FIFOTHR = 0x0F;   // RXFIFO and TXFIFO thresholds.
+
+    // 33 byte TX FIFO & 32 byte RX FIFO
+    rfSettings.FIFOTHR = 0x07;   // RXFIFO and TXFIFO thresholds.
+}
+
+#if RF_DB == 0
+// PATABLE (0 dBm output power)
+char paTable[] = {0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+#elif RF_DB == 10
+// PATABLE (10 dBm output power)
+char paTable[] = {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+#endif
+
+
+// ===============
+void CC1101::assign_baud_rate(uint32_t rate)
+{
+    // update the baud rate class member
+    _baud_rate = rate;
+
+    // have to be careful with bit shifting here since it requires a large amount of shifts
+    uint32_t shift_val = 28 - (_modem.data_rate_exp & 0x0F);
+
+    // compute the register value and assign it
+    rfSettings.MDMCFG3 = ((_baud_rate)/(_crystal_freq>>shift_val)) - 256;
+}
+
+
+// ===============
+void CC1101::assign_channel_spacing(uint32_t freq)
+{
+    // update the channel spacing frequency's class member
+    _channel_spacing = freq;
+
+    // have to be careful with bit shifting here since it requires a large amount of shifts
+    uint32_t shift_val = 18 - (_modem.channel_space_exp & 0x03);
+
+    // compute the register value and assign it
+    rfSettings.MDMCFG0 = (_channel_spacing/(_crystal_freq>>shift_val)) - 256;
+}
+
+
+// ===============
+void CC1101::assign_modem_params()
+{
+    rfSettings.MDMCFG4 = (_modem.channel_bw_exp & 0x03)<<6 | (_modem.channel_bw & 0x03)<<4 | (_modem.data_rate_exp & 0x0F);
+    rfSettings.MDMCFG2 = _modem.dc_filter_off_en<<7 | (_modem.mod_type & 0x07)<<4 | _modem.manchester_encode_en<<3 | (_modem.sync_mode & 0x07);
+    rfSettings.MDMCFG1 = _modem.fec_en<<7 | (_modem.preamble_bytes & 0x07)<<4 | (_modem.channel_space_exp & 0x03);
+}
+
+// ===============
+void CC1101::assign_packet_params()
+{
+    rfSettings.PCKCTRL0 = _pck_control.whitening_en<<6 | (_pck_control.format_type & 0x3)<<4 | _pck_control.crc_en<<2 | (_pck_control.length_type & 0x3);
+    rfSettings.PCKCTRL1 = (_pck_control.preamble_thresh & 0x07)<<5 | _pck_control.autoflush_en<<3 | _pck_control.status_field_en<<2 | (_pck_control.addr_check & 0x03);
+    rfSettings.PCKLEN = _pck_control.size;
+}
+
+
+void CC1101::assign_if_freq(uint32_t freq)
+{
+    // The desired IF frequency for RX. Subtracted from FS base frequency in RX.
+    // bits 7..5 are always 0
+    _if_freq = freq;
+    rfSettings.FSCTRL1 = (_if_freq/(_crystal_freq>>10)) & 0x1F;
+    rfSettings.FSCTRL0 = 0x00;  // set the initial freq calibration to 0
+}
+
+
+// computes the final adjusted operating frequency
+void CC1101::compute_freq()
+{
+    // there's no need to make this heavy computation numerous times when it rarely ever changes - that's why it has its own class method
+    uint32_t freq = (rfSettings.FREQ2<<16) | (rfSettings.FREQ1<<8) | (rfSettings.FREQ0);
+    uint32_t offset = (_channel & 0xFF)*((256 + rfSettings.MDMCFG0)<<(_modem.channel_space_exp & 0x03));
+    _freq = (_crystal_freq>>16)*(freq + offset);    // set the frequency from the base class
+}
+
+
+// SET FREQUENCY
+void CC1101::freq(uint32_t freq)
+{
+    /* calculate the value that is written to the register for settings the base frequency
+     * that the CC1101 should use for sending/receiving over the air. Default value is equivalent
+     * to 901.83 MHz.
+     */
+
+    // update the class's frequency value
+    _carrier_freq = freq;
+
+    // this is split into 3 bytes that are written to 3 different registers on the CC1101
+    uint32_t reg_freq = _carrier_freq / (_crystal_freq>>16);
+
+    rfSettings.FREQ2 = (reg_freq>>16) & 0xFF;   // high byte, bits 7..6 are always 0 for this register
+    rfSettings.FREQ1 = (reg_freq>>8) & 0xFF;    // middle byte
+    rfSettings.FREQ0 = reg_freq & 0xFF;         // low byte
+}
+
+
+// set the device's hardware address (8 bits)
+void CC1101::address(uint8_t addr)
+{
+    _addr = addr;
+}
+
+
+// returns the CC1101's VERSION register that specifices what exact chip version is being used
+uint8_t CC1101::version(void)
+{
+    return _chip_version;
+}
+
+
+// return's the part number for the CC1101 chip being used
+uint8_t CC1101::partnum(void)
+{
+    return _partnum;
+}
+
+
+// returns the current mode that the CC1101 is operating in
+uint8_t CC1101::mode(void)
+{
+    return status(CCxxx0_MARCSTATE);
+}
+
+
+// returns the LQI value from the most recently received packet
+uint8_t CC1101::lqi(void)
+{
+    return 0x3F - (_lqi & 0x3F);
+}
+
+
+// update the channel within the class and also update the CC1101's channel register
+void CC1101::channel(uint8_t chan)
+{
+    // only update channel numbers that are valid (8 bits)
+    if ( chan != _channel ) {
+        // channels start at 0 for the CC1101. this subtracts 1 before saving the channel number for the reason defined in the _channel member's getter method
+        _channel = chan;
+
+        // update the value with the CC1101's channel number register
+        write_reg(CCxxx0_CHANNR, _channel);
+
+        // recalculate the adjusted frequency value since the channel number has changed
+        compute_freq();
+
+#if DEBUG_MODE > 0
+        std::printf("\r\n[CHANNEL UPDATED]\r\n  Channel: %02u\r\n  Freq:    %3.2f MHz\r\n", _channel, static_cast<float>(_final_freq)/1000000);
+#endif
+    }
+}
+
+
+// returns the RSSI value from the most recently received packet
+uint8_t CC1101::rssi(void)
+{
+    return _rssi;
+}
+
+void CC1101::rssi(uint8_t rssi_reg)
+{
+    int8_t temp;
+
+    if (rssi_reg & 0x80) {
+        temp = (rssi_reg - 256)>>1;
+    } else {
+        temp = rssi_reg>>1; // divide by 2
+    }
+    _rssi = temp - 74;
+}
+
+
+// Assign the offset frequency to the class definition
+void CC1101::assign_freq_offset(uint8_t freq)
+{
+    _offset_freq = freq;
+}
+
+
+// Macro to calibrate the frequency synthesizer
+void CC1101::calibrate()
+{
+    // Send the calibration strobe
+    strobe(CCxxx0_SCAL);
+
+    // Wait for the radio to leave the calibration step
+    while( (mode() == 0x04) | (mode() == 0x05) );
+
+    // The radio is now is IDLE mode, so go to RX mode
+    rx_mode();
+
+    // Wait for the radio to enter back into RX mode
+    while( mode() != 0x0D );
+}
+
+
+// Macro to reset the CCxxx0 and wait for it to be ready
+void CC1101::reset(void)
+{
+    strobe(CCxxx0_SRES);
+}
+
+
+void CC1101::power_on_reset(void)
+{
+#if DEBUG_MODE > 0
+    std::printf("    Starting Power-on-Reset procedure...");
+#endif
+    delete _spi;
+
+    // make sure chip is not selected
+    *_csn = 1;
+
+    DigitalOut *SI = new DigitalOut(_si);
+    DigitalOut *SCK = new DigitalOut(_sck);
+    DigitalIn *SO = new DigitalIn(_so);
+
+    // bring SPI lines to a defined state. Reasons are outlined in CC1101 datasheet - section 11.3
+    *SI = 0;
+    *SCK = 1;
+
+    // toggle chip select and remain in high state afterwards
+    *_csn = 0;
+    *_csn = 1;
+
+    // wait at least 40us
+    wait_us(45);
+
+    // pull CSn low & wait for the serial out line to go low
+    *_csn = 0;
+
+    while(*SO);
+
+    // cleanup everything before the mbed's SPI library calls take back over
+    delete SI;
+    delete SO;
+    delete SCK;
+
+    // reestablish the SPI bus and call the reset strobe
+    setup_spi(_si, _so, _sck);
+    reset();
+
+    delete _spi;
+    // wait for the SO line to go low again. Once low, reset is complete and CC1101 is in IDLE state
+    DigitalIn *SO2 = new DigitalIn(_so);
+    while(*SO2);
+
+    // make sure chip is deselected before returning
+    *_csn = 1;
+
+    // reestablish the SPI bus for the final time after removing the DigitalIn object
+    delete SO2;
+    setup_spi(_si, _so, _sck);
+
+#if DEBUG_MODE > 0
+    std::printf("done\r\n");
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////////////
+uint8_t CC1101::status(void)
+{
+    return strobe(CCxxx0_SNOP);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////
+//  uint8_t status(uint8_t addr)
+//
+//  DESCRIPTION:
+//      This function reads a CCxxx0 status register.
+//
+//  ARGUMENTS:
+//      uint8_t addr
+//          Address of the CCxxx0 status register to be accessed.
+//
+//  RETURN VALUE:
+//      uint8_t
+//          Value of the accessed CCxxx0 status register.
+///////////////////////////////////////////////////////////////////////////////////////
+uint8_t CC1101::status(uint8_t addr)
+{
+    *_csn = 0;
+    _spi->write(addr | READ_BURST);
+    tiny_delay();
+    uint8_t x = _spi->write(0);
+    *_csn = 1;
+    return x;
+}// status
+
+
+///////////////////////////////////////////////////////////////////////////////////////
+//  uint8_t strobe(uint8_t strobe)
+//
+//  DESCRIPTION:
+//      Function for writing a strobe command to the CCxxx0
+//
+//  ARGUMENTS:
+//      uint8_t strobe
+//          strobe command
+///////////////////////////////////////////////////////////////////////////////////////
+uint8_t CC1101::strobe(uint8_t strobe)
+{
+    *_csn = 0;
+    uint8_t x = _spi->write(strobe);
+    *_csn = 1;
+    return x;
+}// strobe
+
+
+///////////////////////////////////////////////////////////////////////////////////////
+//  void put_rf_settings(rf_settings_t *pRfSettings)
+//
+//  DESCRIPTION:
+//      This function is used to configure the CCxxx0 based on a given rf setting
+//
+//  ARGUMENTS:
+//      rf_settings_t *pRfSettings
+//          Pointer to a struct containing rf register settings
+///////////////////////////////////////////////////////////////////////////////////////
+void CC1101::put_rf_settings()
+{
+    write_reg(CCxxx0_IOCFG2,   rfSettings.IOCFG2);
+    write_reg(CCxxx0_IOCFG1,   rfSettings.IOCFG1);
+    write_reg(CCxxx0_IOCFG0,   rfSettings.IOCFG0);
+    write_reg(CCxxx0_FIFOTHR,  rfSettings.FIFOTHR);
+    // SYNC1
+    // SYNC0
+    write_reg(CCxxx0_PCKLEN,   rfSettings.PCKLEN);
+    write_reg(CCxxx0_PCKCTRL1, rfSettings.PCKCTRL1);
+    write_reg(CCxxx0_PCKCTRL0, rfSettings.PCKCTRL0);
+    write_reg(CCxxx0_ADDR,     rfSettings.ADDR);
+    write_reg(CCxxx0_CHANNR,   rfSettings.CHANNR);
+    write_reg(CCxxx0_FSCTRL1,  rfSettings.FSCTRL1);
+    write_reg(CCxxx0_FSCTRL0,  rfSettings.FSCTRL0);
+    write_reg(CCxxx0_FREQ2,    rfSettings.FREQ2);
+    write_reg(CCxxx0_FREQ1,    rfSettings.FREQ1);
+    write_reg(CCxxx0_FREQ0,    rfSettings.FREQ0);
+    write_reg(CCxxx0_MDMCFG4,  rfSettings.MDMCFG4);
+    write_reg(CCxxx0_MDMCFG3,  rfSettings.MDMCFG3);
+    write_reg(CCxxx0_MDMCFG2,  rfSettings.MDMCFG2);
+    write_reg(CCxxx0_MDMCFG1,  rfSettings.MDMCFG1);
+    write_reg(CCxxx0_MDMCFG0,  rfSettings.MDMCFG0);
+    write_reg(CCxxx0_DEVIATN,  rfSettings.DEVIATN);
+    write_reg(CCxxx0_MCSM2 ,   rfSettings.MCSM2);
+    write_reg(CCxxx0_MCSM1 ,   rfSettings.MCSM1);
+    write_reg(CCxxx0_MCSM0 ,   rfSettings.MCSM0 );
+    write_reg(CCxxx0_FOCCFG,   rfSettings.FOCCFG);
+    write_reg(CCxxx0_BSCFG,    rfSettings.BSCFG);
+    write_reg(CCxxx0_AGCCTRL2, rfSettings.AGCCTRL2);
+    write_reg(CCxxx0_AGCCTRL1, rfSettings.AGCCTRL1);
+    write_reg(CCxxx0_AGCCTRL0, rfSettings.AGCCTRL0);
+    // WOREVT1
+    // WOREVT0
+    // WORCTRL
+    write_reg(CCxxx0_FREND1,   rfSettings.FREND1);
+    write_reg(CCxxx0_FREND0,   rfSettings.FREND0);
+    // FSCAL3
+    // FSCAL2
+    // FSCAL1
+    //write_reg(CCxxx0_FSCAL0,   rfSettings.FSCAL0);
+    // PCCTRL1
+    // PCCTRL0
+    // FSTEST
+    // PTEST
+    // AGCTEST
+    // TEST2
+    // TEST1
+    // TEST0
+}   // put_rf_settings
+
+
+// main ititilization
+void CC1101::init(void)
+{
+    // strobe(CCxxx0_SIDLE);
+    // send all configuration values to the CC1101 registers
+#if DEBUG_MODE > 0
+    std::printf("    Writing configuration registers...");
+#endif
+    put_rf_settings();
+    write_reg(CCxxx0_PATABLE, paTable[0]);
+#if DEBUG_MODE > 0
+    std::printf("done\r\n");
+#endif
+
+
+#if DEBUG_MODE > 0
+    std::printf("    Calibrating...");
+#endif
+    calibrate();
+//    while( (mode() == 0x04) | (mode() == 0x05) );   // wait until it leaves calibration
+    write_reg(CCxxx0_FSCTRL0,  status(CCxxx0_FREQEST));
+#if DEBUG_MODE > 0
+    std::printf("done\r\n");
+#endif
+
+
+    // flush TX and RX buffers before beginning
+#if DEBUG_MODE > 0
+    std::printf("    Clearing TX and RX buffers...");
+#endif
+    flush_rx();
+    flush_tx();
+#if DEBUG_MODE > 0
+    std::printf("done\r\n");
+#endif
+
+
+    // Enter RX mode
+#if DEBUG_MODE > 0
+    std::printf("    Entering RX mode...");
+#endif
+    rx_mode();
+    while( mode() != 0x0D );    // wait until it enters RX state
+#if DEBUG_MODE > 0
+    std::printf("done\r\n");
+#endif
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////
+//  uint8_t read_reg(uint8_t addr)
+//
+//  DESCRIPTION:
+//      This function gets the value of a single specified CCxxx0 register.
+//
+//  ARGUMENTS:
+//      uint8_t addr
+//          Address of the CCxxx0 register to be accessed.
+//
+//  RETURN VALUE:
+//      uint8_t
+//          Value of the accessed CCxxx0 register.
+///////////////////////////////////////////////////////////////////////////////////////
+uint8_t CC1101::read_reg(uint8_t addr)
+{
+    *_csn = 0;
+    _spi->write(addr | READ_SINGLE);
+    uint8_t x = _spi->write(0);
+    *_csn = 1;
+
+#if DEBUG_MODE > 1
+    std::printf("\r\n== Single Register Read ==\r\n    Address: 0x%02X\r\n    Value:   0x%02X\r\n", addr, x);
+#endif
+    return x;
+}   // read
+
+
+///////////////////////////////////////////////////////////////////////////////////////
+//  void read_reg(uint8_t addr, uint8_t *buffer, uint8_t count)
+//
+//  DESCRIPTION:
+//      This function reads multiple CCxxx0 register, using SPI burst access.
+//
+//  ARGUMENTS:
+//      uint8_t addr
+//          Address of the first CCxxx0 register to be accessed.
+//      uint8_t *buffer
+//          Pointer to a byte array which stores the values read from a
+//          corresponding range of CCxxx0 registers.
+//      uint8_t count
+//          Number of bytes to be read from the subsequent CCxxx0 registers.
+///////////////////////////////////////////////////////////////////////////////////////
+void CC1101::read_reg(uint8_t addr, uint8_t *buffer, uint8_t count)
+{
+    *_csn = 0;
+    _spi->write(addr | READ_BURST);
+    tiny_delay();
+    for (uint8_t i = 0; i < count; i++) {
+        buffer[i] = _spi->write(0);
+        tiny_delay();
+    }
+    *_csn = 1;
+
+#if DEBUG_MODE > 1
+    std::printf("\r\n== Burst Register Read ==\r\n    Address: 0x%02X\r\n    Bytes:   %u\r\n", addr, count);
+#endif
+}   // read
+
+
+///////////////////////////////////////////////////////////////////////////////////////
+//  void write_reg(uint8_t addr, uint8_t value)
+//
+//  DESCRIPTION:
+//      Function for writing to a single CCxxx0 register
+//
+//  ARGUMENTS:
+//      uint8_t addr
+//          Address of a specific CCxxx0 register to accessed.
+//      uint8_t value
+//          Value to be written to the specified CCxxx0 register.
+///////////////////////////////////////////////////////////////////////////////////////
+void CC1101::write_reg(uint8_t addr, uint8_t value)
+{
+    *_csn = 0;
+    _spi->write(addr);
+    _spi->write(value);
+    *_csn = 1;
+
+#if DEBUG_MODE > 1
+    std::printf("\r\n== Single Register Write ==\r\n    Address: 0x%02X\r\n    Value:   0x%02X\r\n", _addr, value);
+#endif
+}   // write
+
+
+///////////////////////////////////////////////////////////////////////////////////////
+//  void write_reg(uint8_t addr, uint8_t *buffer, uint8_t count)
+//
+//  DESCRIPTION:
+//      This function writes to multiple CCxxx0 register, using SPI burst access.
+//
+//  ARGUMENTS:
+//      uint8_t addr
+//          Address of the first CCxxx0 register to be accessed.
+//      uint8_t *buffer
+//          Array of bytes to be written into a corresponding range of
+//          CCxx00 registers, starting by the address specified in _addr_.
+//      uint8_t count
+//          Number of bytes to be written to the subsequent CCxxx0 registers.
+///////////////////////////////////////////////////////////////////////////////////////
+void CC1101::write_reg(uint8_t addr, uint8_t *buffer, uint8_t count)
+{
+    *_csn = 0;
+    _spi->write(addr | WRITE_BURST);
+    tiny_delay();
+    for (uint8_t i = 0; i < count; i++) {
+        _spi->write(buffer[i]);
+        tiny_delay();
+    }
+    *_csn = 1;
+
+#if DEBUG_MODE > 1
+    std::printf("\r\n== Burst Register Write ==\r\n    Address: 0x%02X\r\n    Bytes:   %u\r\n", addr, count);
+#endif
+}   // write
+
+
+void CC1101::tiny_delay(void)
+{
+    int i = 0;
+    while(i < 5) {
+        i++;
+    }
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////
+//  void put_pck(uint8_t *txBuffer, uint8_t size)
+//
+//  DESCRIPTION:
+//      This function can be used to transmit a packet with packet length up to 63 bytes.
+//
+//  ARGUMENTS:
+//      uint8_t *txBuffer
+//          Pointer to a buffer containing the data that are going to be transmitted
+//
+//      uint8_t size
+//          The size of the txBuffer
+//
+void CC1101::put_pck(uint8_t *txBuffer, uint8_t size)
+{
+    // Blink the TX LED
+    _tx_led_thread.signal_set(SET_LED_TICK);
+
+    // move all values down by 1 to make room for the packet size value
+    for (uint8_t i = size; i > 0; i--)
+        txBuffer[i] = txBuffer[i-1];
+
+    // place the packet's size as the array's first value
+    txBuffer[0] = size++;
+
+#if DEBUG_MODE > 0
+    std::printf("\r\n[PACKET TRANSMITTED]\r\n  Bytes: %u\r\n  ACK:   %sRequested\r\n", size, (ack==1 ? "":"Not ") );
+#endif
+
+    // send the data to the CC1101
+    write_reg(CCxxx0_TXFIFO, txBuffer, size);
+
+#if DEBUG_MODE > 1
+    Timer t1;
+#endif
+    // enter the TX state
+    strobe(CCxxx0_STX);
+
+    /*  For the debug mode, this will determine how long the CC1101 takes to transmit everything in the TX buffer
+        and enter or return to the RX state.
+    */
+#if DEBUG_MODE > 1
+    t1.start();
+    float ti = t1.read();
+#endif
+
+
+    // wait until radio enters back to the RX state. takes very few cycles, so might as well wait before returning to elimate querky errors
+    while(mode() != 0x0D);
+
+#if DEBUG_MODE > 1
+    t1.stop();
+    std::printf("  Time:  %02.4f ms\r\n", (t1.read() - ti)*1000);
+#endif
+
+}   // put_pck
+
+
+///////////////////////////////////////////////////////////////////////////////////////
+//  bool get_pck(uint8_t *rxBuffer, uint8_t *length)
+//
+//  DESCRIPTION:
+//      This function can be used to receive a packet of variable packet length (first byte in the packet
+//      must be the length byte). The packet length should not exceed the RX FIFO size.
+//
+//  ARGUMENTS:
+//      uint8_t *rxBuffer
+//          Pointer to the buffer where the incoming data should be stored
+//      uint8_t *length
+//          Pointer to a variable containing the size of the buffer where the incoming data should be
+//          stored. After this function returns, that variable holds the packet length.
+//
+//  RETURN VALUE:
+//      BOOL
+//          1:  CRC OK
+//          0:  CRC NOT OK (or no packet was put in the RX FIFO due to filtering)
+//
+bool CC1101::get_pck(uint8_t *rxBuffer, uint8_t *length)
+{
+    // Blink the RX LED
+    _rx_led_thread.signal_set(SET_LED_TICK);
+
+    // Update the frequency offset estimate
+    write_reg(CCxxx0_FSCTRL0, status(CCxxx0_FREQEST));
+
+    // Get the packet's size
+    uint8_t rx_size;
+    read_reg(CCxxx0_RXFIFO, &rx_size, 1);
+
+    if (rx_size & BYTES_IN_RXFIFO) {
+
+        // Read data from RX FIFO and store in rxBuffer
+        if (rx_size <= *length) {
+
+            *length = rx_size;
+            read_reg(CCxxx0_RXFIFO, rxBuffer, *length);
+
+            // Read the 2 appended status bytes (status[0] = RSSI, status[1] = LQI)
+            uint8_t status_bytes[2];
+            read_reg(CCxxx0_RXFIFO, status_bytes, 2);
+
+            // update the RSSI reading
+            rssi(status_bytes[RSSI]);
+            _lqi = status_bytes[LQI] & 0x7F; // MSB of LQI is the CRC_OK bit
+
+#if DEBUG_MODE > 0
+            std::printf("\r\n[PACKET RECEIVED]\r\n  Bytes: %u\r\n", *length);
+            std::printf("  RSSI: %ddBm\r\n", _rssi);
+            std::printf("  LQI:  %u\r\n", _lqi);
+#endif
+
+            if (_p_count%5 == 0) {
+                //calibrate the frequency synthesizer
+                calibrate();
+            }
+
+            // Go back to the receiving state since CC1101 is configured for transitioning to IDLE on receiving a packet
+            rx_mode();
+            return 1;
+
+        } else {
+            *length = size;
+        }
+    }
+
+    flush_rx();
+    return 0;
+
+}   // get_pck
+
+
+void CC1101::flush_rx(void)
+{
+    // Make sure that the radio is in IDLE state before flushing the FIFO
+    idle();
+
+    // Flush RX FIFO
+    strobe(CCxxx0_SFRX);
+
+    // Enter back into a RX state
+    rx_mode();
+}
+
+
+void CC1101::flush_tx(void)
+{
+    // Make sure that the radio is in IDLE state before flushing the FIFO
+    idle();
+
+    // Flush TX FIFO
+    strobe(CCxxx0_SFTX);
+
+    // Enter back into a RX state
+    rx_mode();
+}
+
+
+void CC1101::rx_mode(void)
+{
+    //strobe(CCxxx0_SIDLE);
+    strobe(CCxxx0_SRX);
+    //while(mode() != 0x0D);
+}
+
+
+void CC1101::tx_mode(void)
+{
+    //strobe(CCxxx0_SIDLE);
+    strobe(CCxxx0_STX);
+    // while(mode() != 0x13);
+}
+
+void CC1101::idle(void)
+{
+    // Send the IDLE strobe
+    strobe(CCxxx0_SIDLE);
+    // Wait before returning
+    // === THIS LIKELY ISN'T NEEDED ===
+    while( mode() != 0x01);
+}