For robots and stuff
Diff: Wireless/CC1101/CC1101.cpp
- Revision:
- 0:c5afea7b9057
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Wireless/CC1101/CC1101.cpp Sun Dec 28 06:27:18 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); +}