Jonathan Jones
/
Radios
Radio Structures in OOP
drivers/CC1101/CC1101.cpp
- Committer:
- jjones646
- Date:
- 2015-01-14
- Revision:
- 5:146523a0d1f4
- Parent:
- 3:dc7e9c6bc26c
- Child:
- 6:4a3dbfbc30f1
File content as of revision 5:146523a0d1f4:
#include "CC1101.h" // Default constructor CC1101::CC1101() : CommLink() { } // 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(); init(); // [] - 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) { // [X] - 1 - Perform a soft reset for the CC1101 transceiver // ================= strobe(CCXXX1_SRES); return 0; // success } uint32_t CC1101::selfTest(void) { // [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 the initial state of an unselected chip *_cs = 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(); } void CC1101::put_rf_settings() { write_reg(CCXXX1_IOCFG2, rfSettings.IOCFG2); write_reg(CCXXX1_IOCFG1, rfSettings.IOCFG1); write_reg(CCXXX1_IOCFG0, rfSettings.IOCFG0); write_reg(CCXXX1_FIFOTHR, rfSettings.FIFOTHR); // SYNC1 // SYNC0 write_reg(CCXXX1_PCKLEN, rfSettings.PCKLEN); write_reg(CCXXX1_PCKCTRL1, rfSettings.PCKCTRL1); write_reg(CCXXX1_PCKCTRL0, rfSettings.PCKCTRL0); write_reg(CCXXX1_ADDR, rfSettings.ADDR); write_reg(CCXXX1_CHANNR, rfSettings.CHANNR); write_reg(CCXXX1_FSCTRL1, rfSettings.FSCTRL1); write_reg(CCXXX1_FSCTRL0, rfSettings.FSCTRL0); write_reg(CCXXX1_FREQ2, rfSettings.FREQ2); write_reg(CCXXX1_FREQ1, rfSettings.FREQ1); write_reg(CCXXX1_FREQ0, rfSettings.FREQ0); write_reg(CCXXX1_MDMCFG4, rfSettings.MDMCFG4); write_reg(CCXXX1_MDMCFG3, rfSettings.MDMCFG3); write_reg(CCXXX1_MDMCFG2, rfSettings.MDMCFG2); write_reg(CCXXX1_MDMCFG1, rfSettings.MDMCFG1); write_reg(CCXXX1_MDMCFG0, rfSettings.MDMCFG0); write_reg(CCXXX1_DEVIATN, rfSettings.DEVIATN); write_reg(CCXXX1_MCSM2 , rfSettings.MCSM2); write_reg(CCXXX1_MCSM1 , rfSettings.MCSM1); write_reg(CCXXX1_MCSM0 , rfSettings.MCSM0 ); write_reg(CCXXX1_FOCCFG, rfSettings.FOCCFG); write_reg(CCXXX1_BSCFG, rfSettings.BSCFG); write_reg(CCXXX1_AGCCTRL2, rfSettings.AGCCTRL2); write_reg(CCXXX1_AGCCTRL1, rfSettings.AGCCTRL1); write_reg(CCXXX1_AGCCTRL0, rfSettings.AGCCTRL0); // WOREVT1 // WOREVT0 // WORCTRL write_reg(CCXXX1_FREND1, rfSettings.FREND1); write_reg(CCXXX1_FREND0, rfSettings.FREND0); //write_reg(CCXXX1_FSCAL3, rfSettings.FSCAL3); //write_reg(CCXXX1_FSCAL2, rfSettings.FSCAL2); //write_reg(CCXXX1_FSCAL1, rfSettings.FSCAL1); //write_reg(CCXXX1_FSCAL0, rfSettings.FSCAL0); // PCCTRL1 // PCCTRL0 // FSTEST // PTEST // AGCTEST //write_reg(CCXXX1_TEST2, rfSettings.TEST2); //write_reg(CCXXX1_TEST1, rfSettings.TEST1); //write_reg(CCXXX1_TEST0, rfSettings.TEST0); } // put_rf_settings 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 toggle_cs(); DigitalOut *SI = new DigitalOut(_mosi_pin); DigitalOut *SCK = new DigitalOut(_sck_pin); DigitalIn *SO = new DigitalIn(_miso_pin); // 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 toggle_cs(); toggle_cs(); // wait at least 40us wait_us(45); // pull CSn low & wait for the serial out line to go low *_cs = 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(); 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(_miso_pin); while(*SO2); // make sure chip is deselected before returning *_cs = 1; // reestablish the SPI bus for the final time after removing the DigitalIn object delete SO2; setup_spi(); #if DEBUG_MODE > 0 std::printf("==============done\r\n"); #endif } // 2nd ighest level of initilization routines behind CC1101::setup(); void CC1101::init(void) { power_on_reset(); // send all configuration values to the CC1101 registers #if DEBUG_MODE > 0 std::printf(" Writing configuration registers..."); #endif put_rf_settings(); #if DEBUG_MODE > 0 std::printf("done\r\n"); #endif uint8_t paTable[] = {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // 10dB write_reg(CCXXX1_PATABLE, paTable[0]); // 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 #if DEBUG_MODE > 0 std::printf(" Configuring frequency offset estimate..."); #endif //write_reg(CCXXX1_FSCTRL0, status(CCXXX1_FREQEST)); #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(); #if DEBUG_MODE > 0 std::printf("done\r\n"); #endif } // 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 } // Read Register uint8_t CC1101::read_reg(uint8_t addr) { _spi->frequency(8000000); toggle_cs(); _spi->write(addr | CCXXX1_READ_SINGLE); //tiny_delay(); 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) { _spi->frequency(4500000); 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) { _spi->frequency(8000000); toggle_cs(); _spi->write(addr); //tiny_delay(); _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) { _spi->frequency(4500000); toggle_cs(); _spi->write(addr | CCXXX1_WRITE_BURST); for (uint8_t i = 0; i < count; i++) { _spi->write(buffer[i]); } 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) { _spi->frequency(4500000); toggle_cs(); uint8_t x = _spi->write(addr); toggle_cs(); return x; } // strobe // Macro to calibrate the frequency synthesizer void CC1101::calibrate(void) { // 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 < 4) { 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(); } 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 //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) { _spi->frequency(800000); 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. }