Radio Structures in OOP

Dependencies:   mbed mbed-rtos

Revision:
3:dc7e9c6bc26c
Parent:
2:7d523bdd2f50
Child:
5:146523a0d1f4
--- a/drivers/CC1101/CC1101.cpp	Sun Dec 28 06:05:17 2014 +0000
+++ b/drivers/CC1101/CC1101.cpp	Sat Jan 03 04:35:32 2015 +0000
@@ -3,82 +3,641 @@
 
 // Default constructor
 CC1101::CC1101() :
-CommLink()
+    CommLink()
 {
-    // [] - 1 - Initialize the CC1101 radio transceiver for operation
-    
-    // [] - 2 - Test the interrupt pin for proper operation
-    
-    // [] - 3 - Check the CC1101's version register to ensure the chip is what we believe it to be
-    
-    // [] - 4 - Set the base class's `_isInit` variable to true if all checks are passed
 }
 
+// Main constructor
+CC1101::CC1101(PinName mosi, PinName miso, PinName sck, PinName cs, PinName int_pin) :
+    CommLink(mosi, miso, sck, cs, int_pin)
+{
+    // [X] - 1 - Setup the chip
+    setup();
+    
+    // [X] - 2 - Call the base class method for beginning full class operation with threads
+    // =================
+    CommLink::ready();
+}
 
 // Deconstructor
 CC1101::~CC1101()
 {
+    if (_spi)
+        delete _spi;
+    if (_cs)
+        delete _cs;
+    if (_int_in)
+        delete _int_in;
+}
 
+
+void CC1101::setup(void)
+{
+    // [X] - 1 - Initialize the CC1101 radio transceiver for operation
+    // =================
+    set_init_vars();
+    assign_if_freq(CCXXX1_IF_FREQUENCY);
+    freq(CCXXX1_BASE_FREQUENCY);
+    assign_channel_spacing(200000);    // 200kHz
+    datarate(250000);  // 250 kBaud
+    set_rf_settings();
+    
+    // [] - 2 - Test the interrupt pin for proper operation
+    // =================
+
+    // [] - 3 - Check the CC1101's version register to ensure the chip is what we believe it to be
+    // =================
+    
+}
+
+
+int32_t CC1101::powerUp(void)
+{
+#if CCXXX1_DEBUG_MODE > 0
+    std::printf("[CC1101 RADIO TRANSCEIVER INITILIZATION]\r\n");
+#endif
+
+    // power_on_reset();
+
+    // now send the assigned rfSettings struct to the CC1101 for full inililization
+    // init();
+
+    // scan();
+    
+    return 0;
 }
 
 
 bool CC1101::isConnected(void)
 {
     // [] - 1 - Perform a check to ensure the CC1101 can provide communication with a secondary base station link
+    // =================
     // Note: This does not necessarily mean the link must be reliable, this only needs to determine: `Can the perheripial provide communication?`
-    
+
     // [] - 2 - Return true/false for indicating a connected communication link
-    
+    // =================
+
     return true;
 }
 
 
 uint32_t CC1101::reset(void)
 {
-    // [] - 1 - Perform a soft reset for the CC1101 transceiver
-    
-    // [] - 2 - Return any error codes if necessary
-    
-    return 1;
+    // [X] - 1 - Perform a soft reset for the CC1101 transceiver
+    // =================
+    strobe(CCXXX1_SRES);
+    return 0;   // success
 }
 
 
 uint32_t CC1101::selfTest(void)
 {
-    // [] - 1 - Perform a self test for the CC1101 radio transceiver
+    // [X] - 1 - Get the chip's version number and fail if different from what was expected.
+    _chip_version = status(CCXXX1_VERSION);
+
+    if (_chip_version != CCXXX1_EXPECTED_VERSION_NUMBER) {
+
+        // send message over serial port
+        std::printf(
+            "[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"
+            , CCXXX1_VERSION, CCXXX1_EXPECTED_VERSION_NUMBER, _chip_version);
+
+        return 1;
+    } 
+    return 0;   // success
+}
+
+void CC1101::set_init_vars(void)
+{
+    // define an initial state of an unselected chip
+    *_cs = 1;
     
-    // [] - 2 - Return any error codes if necessary
-    
-    return 1;
+    _channel = 1;
+    _address = 0x00;
+
+    // frequency that radio links with another CC1101 over the air
+    _base_freq = CCXXX1_BASE_FREQUENCY;
+
+    // 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 = false;
+
+    // enable CRC calculation in TX and CRC checking in RX
+    _pck_control.crc_en = true;
+
+    // enable automatically flushing the RX buffer on a bad CRC (only works if 1 packet is in the RX buffer)
+    _pck_control.autoflush_en = true;
+
+    // enable appending 2 status bytes to the end of every packet that includes the CRC and
+    _pck_control.status_field_en = true;
+
+    // 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 = false;
+    _modem.manchester_encode_en = false;
+    _modem.fec_en = false;
+
+    // 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;
+
+    // the values assigned here are used for the frequency synthesizer control
+    // assign_if_freq(CCXXX1_IF_FREQUENCY);
+
+    // set all the configuration values into the rfSettings struct
+    // set_rf_settings();
+}
+
+
+// returns the current mode that the CC1101 is operating in
+uint8_t CC1101::mode(void)
+{
+    return status(CCXXX1_MARCSTATE);
+}
+
+
+int32_t CC1101::sendData(uint8_t *buf, uint8_t size)
+{
+    // [X] - 1 - Move all values down by 1 to make room for the packet's size value.
+    // =================
+    for (uint8_t i = size; i > 0; i--)
+        buf[i] = buf[i-1];
+
+
+    // [X] - 2 - Place the packet's size as the array's first value.
+    // =================
+    buf[0] = size;
+
+
+#if CCXXX1_DEBUG_MODE > 0
+    std::printf("\r\n[PACKET TRANSMITTED]\r\n  Bytes: %u\r\n", size);
+#endif
+
+
+    // [X] - 3 - Send the data to the CC1101. Increment the size value by 1 before doing so to account for the buffer's inserted value
+    // =================
+    write_reg(CCXXX1_TXFIFO, buf, ++size);
+
+
+#if CCXXX1_DEBUG_MODE > 1
+    Timer t1;
+#endif
+
+
+    // [X] - 4 - Enter the TX state.
+    // =================
+    strobe(CCXXX1_STX);
+
+
+#if CCXXX1_DEBUG_MODE > 1
+    /*  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.
+    */
+    t1.start();
+    float ti = t1.read();
+#endif
+
+
+    // [X] - 5 - Wait until radio enters back to the RX state.
+    // =================
+    // *Note: Takes very few cycles, so might as well wait before returning to elimate querky errors.
+    while(mode() != 0x0D);
+
+#if CCXXX1_DEBUG_MODE > 1
+    t1.stop();
+    std::printf("  Time:  %02.4f ms\r\n", (t1.read() - ti)*1000);
+#endif
+
+    // [] - 6 - Return any error codes if necessary.
+    // =================
+
+    return 0;   // success
+}
+
+
+int32_t CC1101::getData(uint8_t *buf, uint8_t *length)
+{
+    // [X] - 1 - Update the frequency offset estimate.
+    // =================
+    write_reg(CCXXX1_FSCTRL0, status(CCXXX1_FREQEST));
+
+
+    // [X] - 2 - Get the packet's size.
+    // =================
+    uint8_t rx_size;
+    read_reg(CCXXX1_RXFIFO, &rx_size, 1);
+
+
+    // [X] - 3 - Check if there are indeed bytes to be read.
+    // =================
+    if (rx_size & CCXXX1_RXFIFO_MASK) {
+
+        // [X] - 3.1 - Check if the received data will fit in the provided buffer - fill the buffer if so.
+        // =================
+        if (rx_size <= *length) {
+
+            // [X] - 3.1a - Read in received data from the CC1101 and put the contents in the provided buffer.
+            *length = rx_size;  // set the correct length
+            read_reg(CCXXX1_RXFIFO, buf, *length);
+
+            /*  The RSSI value takes a bit to be determined by the CC1101, so having 2 separate SPI reads gives
+                the CC1101 enough time to determine the correct value.
+            */
+
+            // [X] - 3.1b - Read the 2 appended status bytes.
+            // status[0] = RSSI. status[1] = LQI.
+            uint8_t status_bytes[2];
+            read_reg(CCXXX1_RXFIFO, status_bytes, 2);
+
+            // Update the RSSI reading.
+            rssi(status_bytes[0]);
+            _lqi = status_bytes[1] & CCXXX1_RXFIFO_MASK; // MSB of LQI is the CRC_OK bit - The interrupt is only triggered if CRC is OK, so no need to check again.
+
+#if CCXXX1_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
+
+            // [X] - 3.2c - Go back to the receiving state since CC1101 is configured for transitioning to IDLE on receiving a packet.
+            rx_mode();
+            return 0;   // success
+        } else {
+            *length = rx_size;
+            flush_rx();
+            return -2;  // passed buffer size is not large enough
+        }
+    }
+
+    flush_rx();
+    return -1;   // there is no new data to read
 }
 
 
-uint32_t CC1101::sendPacket(RTP_t* p)
+// Read Register
+uint8_t CC1101::read_reg(uint8_t addr)
+{
+    toggle_cs();
+    _spi->write(addr | CCXXX1_READ_SINGLE);
+    uint8_t x = _spi->write(0);
+    toggle_cs();
+
+#if CCXXX1_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_reg
+void CC1101::read_reg(uint8_t addr, uint8_t *buffer, uint8_t count)
+{
+    toggle_cs();
+    _spi->write(addr | CCXXX1_READ_BURST);
+    tiny_delay();
+    for (uint8_t i = 0; i < count; i++) {
+        buffer[i] = _spi->write(0);
+        tiny_delay();
+    }
+    toggle_cs();
+
+#if CCXXX1_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_reg
+
+
+
+// Write Register
+void CC1101::write_reg(uint8_t addr, uint8_t value)
+{
+    toggle_cs();
+    _spi->write(addr);
+    _spi->write(value);
+    toggle_cs();
+
+#if CCXXX1_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_reg
+void CC1101::write_reg(uint8_t addr, uint8_t *buffer, uint8_t count)
+{
+    toggle_cs();
+    _spi->write(addr | CCXXX1_WRITE_BURST);
+    tiny_delay();
+    for (uint8_t i = 0; i < count; i++) {
+        _spi->write(buffer[i]);
+        tiny_delay();
+    }
+    toggle_cs();
+
+#if CCXXX1_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_reg
+
+
+// Strobe
+uint8_t CC1101::strobe(uint8_t addr)
+{
+    toggle_cs();
+    uint8_t x = _spi->write(addr);
+    toggle_cs();
+    return x;
+}   // strobe
+
+
+// Macro to calibrate the frequency synthesizer
+void CC1101::calibrate(void)
 {
-    // [] - 1 - Read a packet from the txQueue
-    
-    // [] - 2 - Send the packet to the CC1101 in proper format
-    
-    // [] - 3 - Set the CC1101 to begin transmitting the data
-    
-    // [] - 4 - Return any error codes if necessary
-    
-    return 1;
+    // Send the calibration strobe
+    strobe(CCXXX1_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 );
+}
+
+
+void CC1101::tiny_delay(void)
+{
+    int i = 0;
+    while(i < 5) {
+        i++;
+    }
+}
+
+void CC1101::address(uint8_t addr)
+{
+    _address = addr;
+    // NOW, WRITE THE ADDRESS TO THE CC1101
+}
+
+uint8_t CC1101::lqi(void)
+{
+    return 0x3F - (_lqi & 0x3F);
+}
+
+// returns the CC1101's VERSION register that specifices what exact chip version is being used
+uint8_t CC1101::version(void)
+{
+    return _chip_version;
+}
+
+void CC1101::channel(uint16_t chan)
+{
+    if ( chan != _channel ) {
+        _channel = chan;
+        write_reg(CCXXX1_CHANNR, _channel);
+/*
+#if CCXXX1_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
+*/
+    }
+}
+
+// RSSI
+int16_t CC1101::rssi(void)
+{
+    return _rssi;
+}
+void CC1101::rssi(uint8_t rssi_val)
+{
+    int8_t temp;
+
+    if (rssi_val & 0x80) {
+        temp = (rssi_val - 256)>>1;
+    } else {
+        temp = rssi_val>>1; // divide by 2
+    }
+    _rssi = temp - 74;
+}   // rssi
+
+
+void CC1101::flush_rx(void)
+{
+    // Make sure that the radio is in IDLE state before flushing the FIFO
+    idle();
+
+    // Flush RX FIFO
+    strobe(CCXXX1_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(CCXXX1_SFTX);
+
+    // Enter back into a RX state
+    rx_mode();
 }
 
-// * The receivePacket method must account for being called from an interrupt trigger *
-uint32_t CC1101::receivePacket(void)
+
+void CC1101::rx_mode(void)
+{
+    //strobe(CCXXX1_SIDLE);
+    strobe(CCXXX1_SRX);
+    //while(mode() != 0x0D);
+}
+
+
+void CC1101::tx_mode(void)
+{
+    //strobe(CCXXX1_SIDLE);
+    strobe(CCXXX1_STX);
+    // while(mode() != 0x13);
+}
+
+void CC1101::idle(void)
+{
+    // Send the IDLE strobe
+    strobe(CCXXX1_SIDLE);
+    // Wait before returning
+    // === THIS LIKELY ISN'T NEEDED ===
+    while( mode() != 0x01);
+}
+
+
+// Status byte & status of a status register
+uint8_t CC1101::status(void)
+{
+    return strobe(CCXXX1_SNOP);
+}
+
+uint8_t CC1101::status(uint8_t addr)
 {
-    // [] - 1 - Read in received data from the CC1101
-    
-    // [] - 2 - Decipher the RTP packet and place it in the passed RTP_t* p pointer's data structure
-    
-    // [] - 3 - Add the packet to the base class (CommLink) rxQueue
-    
-    // [] - 4 - Ensure that the base class is informed of this newly received packet
-    
-    // [] - 3 - Return any error codes if necessary
-    
-    return 1;
+    toggle_cs();
+    _spi->write(addr | CCXXX1_READ_BURST);
+    tiny_delay();
+    uint8_t x = _spi->write(0);
+    toggle_cs();
+    return x;
+}   // status
+
+
+// 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.
+     */
+
+    // this is split into 3 bytes that are written to 3 different registers on the CC1101
+    uint32_t reg_freq = _base_freq / (CCXXX1_CRYSTAL_FREQUENCY>>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
+}
+
+
+void CC1101::datarate(uint32_t rate)
+{
+    // update the baud rate class member
+    _datarate = 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 = ((_datarate)/(CCXXX1_CRYSTAL_FREQUENCY>>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
+    rfSettings.FSCTRL1 = (freq/(CCXXX1_CRYSTAL_FREQUENCY>>10)) & 0x1F;
+    rfSettings.FSCTRL0 = 0x00;  // set the initial freq calibration to 0
+}
+// ===============
+void CC1101::assign_channel_spacing(uint32_t spacing)
+{
+    // 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 = (spacing/(CCXXX1_CRYSTAL_FREQUENCY>>shift_val)) - 256;
+}
+void CC1101::set_rf_settings(void)
+{
+    // 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 = _address;
+
+    // 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();
+
+    // disable GDO0
+    rfSettings.IOCFG0 = 0x2E;
+
+    // setup for SPI
+    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 = false;
+    bool RX_TIME_QUAL = false;
+    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 = false;
+    bool XOSC_FORCE_ON = false;
+
+    rfSettings.MCSM0 = ((FS_AUTOCAL & 0x03)<<4) | ((PO_TIMEOUT & 0x03)<<2) | (PIN_CTRL_EN<<1) | (XOSC_FORCE_ON);
+
+    bool FOC_BS_CS_GATE = false;
+    uint8_t FOC_PRE_K = 0x03;
+    bool FOC_POST_K = true;
+    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.
+}
\ No newline at end of file