Jonathan Jones
/
Radios
Radio Structures in OOP
Diff: drivers/CC1101/CC1101.cpp
- 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