For robots and stuff
Wireless/CC1101/CC1101.cpp
- Committer:
- jjones646
- Date:
- 2014-12-28
- Revision:
- 0:c5afea7b9057
File content as of revision 0:c5afea7b9057:
#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); }