Driver for TI's CC1200 radio ICs. Forget hardcoded register settings -- this driver calculates everything from scratch!
Dependents: CC1200-MorseEncoder CC1200-Examples
CC1200 Driver
by Jamie Smith / USC Rocket Propulsion Lab
After months of work, we are proud to present our driver for Texas Instruments' CC1200 digital radio IC! This driver has been written from scratch to be an easy and flexible way of using this radio transceiver. For our application, we needed to be able to tune each and every setting of the radio to try and eke that last bit of performance of our system - so using premade configurations alone wasn't going to cut it! Instead, this driver calculates each parameter of the radio using the equations and instructions given in the datasheet. So, you can tweak parameters to your heart's content, and you shouldn't have to do any math yourself!
Features
- Automatic calculation of correct register values for:
- RF frequency
- FSK deviation
- Symbol rate
- Output power
- RX filter bandwidth (this one's harder than it looks!)
- Easy handling of data packets
- GPIO configuration
- Preamble and sync word configuration
- RTOS compatible (always locks SPI bus during transactions)
- Two debug levels available
- RSSI and LQI support
Not Supported
- Transparent mode
- FM mode
- ASK parameter configuration
- Frequency offsets
Examples
- See the example project here for an example of how to use the driver.
- Another example (using a more exotic configuration) is the CC1200-MorseEncoder.
Changelog
Version 1.2 May 3 2021
- Added unfinished infinite length packet support via the readStream() and writeStream() functions. The API is complete and basic usage works but there's still a bug I haven't been able to track down yet where incorrect data is transmitted at the end of a stream. Use with caution!
- Added
preferHigherCICDec
parameter tosetRXFilterBandwidth
- Removed
setIFMixCFG()
(which takes a byte parameter) and replaced it withsetIFCfg()
, which takes documented enum class values. - Added
setAGCSettleWait()
, which per my testing is needed for correct 430MHz operation. - Added support for reading RSSI and LQI values, both from packet appended status bytes and from the registers.
- Update 430MHz black box registers based on SmartRF values
- Removed
setIQMismatchCompensationEnabled()
. This call has been replaced by the new 2nd parameter tosetIFCfg()
.
Version 1.1 Aug 28 2020
- Add fixed length packet support and other features needed for Morse support.
- Fix bug causing weird behavior with low sample rates (<1ksps).
NOTE: you must now call setPacketMode() when configuring the radio.
Version 1.0 Aug 10 2020
Initial Release
CC1200.cpp@2:2a447e8e50b8, 2020-08-10 (annotated)
- Committer:
- Jamie Smith
- Date:
- Mon Aug 10 01:30:40 2020 -0700
- Revision:
- 2:2a447e8e50b8
- Parent:
- 1:98af824b145e
- Child:
- 3:f464b14ce62f
Update to Mbed OS 6. Also fix CC1200::hasReceivedPacket, which has never actually worked until now (whoops)
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
Jamie Smith |
1:98af824b145e | 1 | #pragma clang diagnostic push |
Jamie Smith |
1:98af824b145e | 2 | #pragma ide diagnostic ignored "readability-magic-numbers" |
Jamie Smith |
0:0c3532738887 | 3 | // |
Jamie Smith |
0:0c3532738887 | 4 | // Created by jamie on 3/27/2020. |
Jamie Smith |
0:0c3532738887 | 5 | // |
Jamie Smith |
0:0c3532738887 | 6 | |
Jamie Smith |
0:0c3532738887 | 7 | #include "CC1200.h" |
Jamie Smith |
0:0c3532738887 | 8 | #include "CC1200Bits.h" |
Jamie Smith |
0:0c3532738887 | 9 | |
Jamie Smith |
0:0c3532738887 | 10 | #include <cinttypes> |
Jamie Smith |
0:0c3532738887 | 11 | #include <cmath> |
Jamie Smith |
0:0c3532738887 | 12 | |
Jamie Smith |
0:0c3532738887 | 13 | // change to 1 to print debug info |
Jamie Smith |
0:0c3532738887 | 14 | #define CC1200_DEBUG 1 |
Jamie Smith |
0:0c3532738887 | 15 | |
Jamie Smith |
0:0c3532738887 | 16 | // change to 1 to print register read/write level debug info |
Jamie Smith |
0:0c3532738887 | 17 | #define CC1200_REGISTER_LEVEL_DEBUG 0 |
Jamie Smith |
0:0c3532738887 | 18 | |
Jamie Smith |
0:0c3532738887 | 19 | // miscellaneous constants |
Jamie Smith |
0:0c3532738887 | 20 | #define CC1200_READ (1 << 7) // SPI initial byte flag indicating read |
Jamie Smith |
0:0c3532738887 | 21 | #define CC1200_WRITE 0 // SPI initial byte flag indicating write |
Jamie Smith |
0:0c3532738887 | 22 | #define CC1200_BURST (1 << 6) // SPI initial byte flag indicating burst access |
Jamie Smith |
0:0c3532738887 | 23 | |
Jamie Smith |
0:0c3532738887 | 24 | // SPI commands to access data buffers. Can be used with CC1200_BURST. |
Jamie Smith |
0:0c3532738887 | 25 | #define CC1200_ENQUEUE_TX_FIFO 0x3F |
Jamie Smith |
0:0c3532738887 | 26 | #define CC1200_DEQUEUE_RX_FIFO 0xBF |
Jamie Smith |
0:0c3532738887 | 27 | |
Jamie Smith |
0:0c3532738887 | 28 | // SPI command to access FIFO memory (or several other areas depending on mode) |
Jamie Smith |
0:0c3532738887 | 29 | #define CC1200_MEM_ACCESS 0x3E |
Jamie Smith |
0:0c3532738887 | 30 | |
Jamie Smith |
0:0c3532738887 | 31 | #define CC1200_RX_FIFO (1 << 7) // address flag to access RX FIFO |
Jamie Smith |
0:0c3532738887 | 32 | #define CC1200_TX_FIFO 0 // address flag to access TX FIFO |
Jamie Smith |
0:0c3532738887 | 33 | |
Jamie Smith |
0:0c3532738887 | 34 | |
Jamie Smith |
0:0c3532738887 | 35 | #define CC1200_PART_NUMBER ((uint8_t)0x20) // part number we expect the chip to read |
Jamie Smith |
0:0c3532738887 | 36 | #define CC1201_PART_NUMBER ((uint8_t)0x21) |
Jamie Smith |
0:0c3532738887 | 37 | #define CC1200_EXT_ADDR 0x2F // SPI initial byte address indicating extended register space |
Jamie Smith |
0:0c3532738887 | 38 | |
Jamie Smith |
0:0c3532738887 | 39 | #define SPI_MODE 0 |
Jamie Smith |
0:0c3532738887 | 40 | #define SPI_FREQ 5000000 // hz |
Jamie Smith |
0:0c3532738887 | 41 | // NOTE: the chip supports a higher frequency for most operations but reads to extended registers require a lower frequency |
Jamie Smith |
0:0c3532738887 | 42 | |
Jamie Smith |
0:0c3532738887 | 43 | // frequency of the chip's crystal oscillator |
Jamie Smith |
0:0c3532738887 | 44 | #define CC1200_OSC_FREQ 40000000 // hz |
Jamie Smith |
0:0c3532738887 | 45 | #define CC1200_OSC_FREQ_LOG2 25.253496f // log2 of above number |
Jamie Smith |
0:0c3532738887 | 46 | |
Jamie Smith |
0:0c3532738887 | 47 | // length of the TX and RX FIFOS |
Jamie Smith |
0:0c3532738887 | 48 | #define CC1200_FIFO_SIZE 128 |
Jamie Smith |
0:0c3532738887 | 49 | |
Jamie Smith |
0:0c3532738887 | 50 | // maximum length of the packets we can send, including the length byte which we add. |
Jamie Smith |
0:0c3532738887 | 51 | // Since the TX and RX FIFOs are 128 bytes, supporting packet lengths longer than 128 bytes |
Jamie Smith |
0:0c3532738887 | 52 | // requires streaming bytes in during the transmission, which would make things complicated. |
Jamie Smith |
0:0c3532738887 | 53 | #define MAX_PACKET_LENGTH 128 |
Jamie Smith |
0:0c3532738887 | 54 | |
Jamie Smith |
0:0c3532738887 | 55 | // utility function: compile-time power calculator. |
Jamie Smith |
0:0c3532738887 | 56 | // Works on all signed and unsigned integer types for T. |
Jamie Smith |
0:0c3532738887 | 57 | // from: http://prosepoetrycode.potterpcs.net/2015/07/a-simple-constexpr-power-function-c/ |
Jamie Smith |
0:0c3532738887 | 58 | template <typename T> |
Jamie Smith |
0:0c3532738887 | 59 | constexpr T constexpr_pow(T num, unsigned int pow) |
Jamie Smith |
0:0c3532738887 | 60 | { |
Jamie Smith |
0:0c3532738887 | 61 | return pow == 0 ? 1 : num * constexpr_pow(num, pow-1); |
Jamie Smith |
0:0c3532738887 | 62 | } |
Jamie Smith |
0:0c3532738887 | 63 | |
Jamie Smith |
0:0c3532738887 | 64 | // power of two constants |
Jamie Smith |
0:0c3532738887 | 65 | const float twoToThe16 = constexpr_pow(2.0f, 16); |
Jamie Smith |
0:0c3532738887 | 66 | const float twoToThe20 = constexpr_pow(2.0f, 20); |
Jamie Smith |
0:0c3532738887 | 67 | const float twoToThe21 = constexpr_pow(2.0f, 21); |
Jamie Smith |
0:0c3532738887 | 68 | const float twoToThe22 = constexpr_pow(2.0f, 22); |
Jamie Smith |
0:0c3532738887 | 69 | const float twoToThe38 = constexpr_pow(2.0f, 38); |
Jamie Smith |
0:0c3532738887 | 70 | const float twoToThe39 = constexpr_pow(2.0f, 39); |
Jamie Smith |
0:0c3532738887 | 71 | |
Jamie Smith |
0:0c3532738887 | 72 | // binary value size constants |
Jamie Smith |
0:0c3532738887 | 73 | const size_t maxValue3Bits = constexpr_pow(2, 3) - 1; |
Jamie Smith |
0:0c3532738887 | 74 | const size_t maxValue4Bits = constexpr_pow(2, 4) - 1; |
Jamie Smith |
0:0c3532738887 | 75 | const size_t maxValue8Bits = constexpr_pow(2, 8) - 1; |
Jamie Smith |
0:0c3532738887 | 76 | const size_t maxValue20Bits = constexpr_pow(2, 20) - 1; |
Jamie Smith |
0:0c3532738887 | 77 | const size_t maxValue24Bits = constexpr_pow(2, 24) - 1; |
Jamie Smith |
0:0c3532738887 | 78 | |
Jamie Smith |
0:0c3532738887 | 79 | CC1200::CC1200(PinName mosiPin, PinName misoPin, PinName sclkPin, PinName csPin, PinName rstPin, Stream * _debugStream, bool _isCC1201): |
Jamie Smith |
0:0c3532738887 | 80 | spi(mosiPin, misoPin, sclkPin, csPin, use_gpio_ssel), |
Jamie Smith |
0:0c3532738887 | 81 | rst(rstPin, 1), |
Jamie Smith |
0:0c3532738887 | 82 | debugStream(_debugStream), |
Jamie Smith |
0:0c3532738887 | 83 | isCC1201(_isCC1201) |
Jamie Smith |
0:0c3532738887 | 84 | { |
Jamie Smith |
0:0c3532738887 | 85 | spi.format(8, SPI_MODE); |
Jamie Smith |
0:0c3532738887 | 86 | spi.frequency(SPI_FREQ); |
Jamie Smith |
0:0c3532738887 | 87 | } |
Jamie Smith |
0:0c3532738887 | 88 | |
Jamie Smith |
0:0c3532738887 | 89 | bool CC1200::begin() |
Jamie Smith |
0:0c3532738887 | 90 | { |
Jamie Smith |
0:0c3532738887 | 91 | chipReady = false; |
Jamie Smith |
0:0c3532738887 | 92 | |
Jamie Smith |
0:0c3532738887 | 93 | // reset |
Jamie Smith |
0:0c3532738887 | 94 | rst.write(0); |
Jamie Smith |
0:0c3532738887 | 95 | wait_us(100); |
Jamie Smith |
0:0c3532738887 | 96 | rst.write(1); |
Jamie Smith |
0:0c3532738887 | 97 | |
Jamie Smith |
2:2a447e8e50b8 | 98 | const auto resetTimeout = 10ms; |
Jamie Smith |
0:0c3532738887 | 99 | Timer timeoutTimer; |
Jamie Smith |
0:0c3532738887 | 100 | timeoutTimer.start(); |
Jamie Smith |
0:0c3532738887 | 101 | |
Jamie Smith |
0:0c3532738887 | 102 | while(!chipReady) |
Jamie Smith |
0:0c3532738887 | 103 | { |
Jamie Smith |
0:0c3532738887 | 104 | // datasheet specifies 240us reset time |
Jamie Smith |
0:0c3532738887 | 105 | wait_us(250); |
Jamie Smith |
0:0c3532738887 | 106 | updateState(); |
Jamie Smith |
0:0c3532738887 | 107 | |
Jamie Smith |
2:2a447e8e50b8 | 108 | if(timeoutTimer.elapsed_time() > resetTimeout) |
Jamie Smith |
0:0c3532738887 | 109 | { |
Jamie Smith |
0:0c3532738887 | 110 | debugStream->printf("Timeout waiting for ready response from CC1200\n"); |
Jamie Smith |
0:0c3532738887 | 111 | return false; |
Jamie Smith |
0:0c3532738887 | 112 | } |
Jamie Smith |
0:0c3532738887 | 113 | } |
Jamie Smith |
0:0c3532738887 | 114 | |
Jamie Smith |
0:0c3532738887 | 115 | // read ID register |
Jamie Smith |
0:0c3532738887 | 116 | uint8_t partNumber = readRegister(ExtRegister::PARTNUMBER); |
Jamie Smith |
0:0c3532738887 | 117 | uint8_t partVersion = readRegister(ExtRegister::PARTVERSION); |
Jamie Smith |
0:0c3532738887 | 118 | |
Jamie Smith |
0:0c3532738887 | 119 | uint8_t expectedPartNumber = isCC1201 ? CC1201_PART_NUMBER : CC1200_PART_NUMBER; |
Jamie Smith |
0:0c3532738887 | 120 | if(partNumber != expectedPartNumber) |
Jamie Smith |
0:0c3532738887 | 121 | { |
Jamie Smith |
0:0c3532738887 | 122 | debugStream->printf("Read incorrect part number 0x%" PRIx8 " from CC1200, expected 0x%" PRIx8 "\n", partNumber, expectedPartNumber); |
Jamie Smith |
0:0c3532738887 | 123 | return false; |
Jamie Smith |
0:0c3532738887 | 124 | } |
Jamie Smith |
0:0c3532738887 | 125 | |
Jamie Smith |
0:0c3532738887 | 126 | #if CC1200_DEBUG |
Jamie Smith |
0:0c3532738887 | 127 | debugStream->printf("Detected CC1200, Part Number 0x%" PRIx8 ", Hardware Version %" PRIx8 "\n", partNumber, partVersion); |
Jamie Smith |
0:0c3532738887 | 128 | #endif |
Jamie Smith |
0:0c3532738887 | 129 | |
Jamie Smith |
0:0c3532738887 | 130 | |
Jamie Smith |
0:0c3532738887 | 131 | // Set packet format settings for this driver |
Jamie Smith |
0:0c3532738887 | 132 | // ------------------------------------------------------------------------ |
Jamie Smith |
0:0c3532738887 | 133 | |
Jamie Smith |
0:0c3532738887 | 134 | // enable CRC but disable status bytes |
Jamie Smith |
0:0c3532738887 | 135 | writeRegister(Register::PKT_CFG1, (0b01 << PKT_CFG1_CRC_CFG)); |
Jamie Smith |
0:0c3532738887 | 136 | |
Jamie Smith |
0:0c3532738887 | 137 | // configure packet length to adjustable |
Jamie Smith |
0:0c3532738887 | 138 | writeRegister(Register::PKT_CFG0, (0b01 << PKT_CFG0_LENGTH_CONFIG)); |
Jamie Smith |
0:0c3532738887 | 139 | |
Jamie Smith |
0:0c3532738887 | 140 | // set max packet length |
Jamie Smith |
0:0c3532738887 | 141 | writeRegister(Register::PKT_LEN, MAX_PACKET_LENGTH); |
Jamie Smith |
0:0c3532738887 | 142 | |
Jamie Smith |
0:0c3532738887 | 143 | |
Jamie Smith |
0:0c3532738887 | 144 | return true; |
Jamie Smith |
0:0c3532738887 | 145 | } |
Jamie Smith |
0:0c3532738887 | 146 | |
Jamie Smith |
0:0c3532738887 | 147 | size_t CC1200::getTXFIFOLen() |
Jamie Smith |
0:0c3532738887 | 148 | { |
Jamie Smith |
0:0c3532738887 | 149 | return readRegister(ExtRegister::NUM_TXBYTES); |
Jamie Smith |
0:0c3532738887 | 150 | } |
Jamie Smith |
0:0c3532738887 | 151 | |
Jamie Smith |
0:0c3532738887 | 152 | size_t CC1200::getRXFIFOLen() |
Jamie Smith |
0:0c3532738887 | 153 | { |
Jamie Smith |
0:0c3532738887 | 154 | return readRegister(ExtRegister::NUM_RXBYTES); |
Jamie Smith |
0:0c3532738887 | 155 | } |
Jamie Smith |
0:0c3532738887 | 156 | |
Jamie Smith |
0:0c3532738887 | 157 | bool CC1200::enqueuePacket(char const * data, size_t len) |
Jamie Smith |
0:0c3532738887 | 158 | { |
Jamie Smith |
0:0c3532738887 | 159 | uint8_t totalLength = len + 1; // add one byte for length byte |
Jamie Smith |
0:0c3532738887 | 160 | |
Jamie Smith |
0:0c3532738887 | 161 | if(totalLength > MAX_PACKET_LENGTH) |
Jamie Smith |
0:0c3532738887 | 162 | { |
Jamie Smith |
0:0c3532738887 | 163 | // packet too big |
Jamie Smith |
0:0c3532738887 | 164 | return false; |
Jamie Smith |
0:0c3532738887 | 165 | } |
Jamie Smith |
0:0c3532738887 | 166 | |
Jamie Smith |
0:0c3532738887 | 167 | uint8_t txFreeBytes = CC1200_FIFO_SIZE - getTXFIFOLen(); |
Jamie Smith |
0:0c3532738887 | 168 | if(totalLength > txFreeBytes) |
Jamie Smith |
0:0c3532738887 | 169 | { |
Jamie Smith |
0:0c3532738887 | 170 | // packet doesn't fit in TX FIFO |
Jamie Smith |
0:0c3532738887 | 171 | return false; |
Jamie Smith |
0:0c3532738887 | 172 | } |
Jamie Smith |
0:0c3532738887 | 173 | |
Jamie Smith |
0:0c3532738887 | 174 | // burst write to TX FIFO |
Jamie Smith |
0:0c3532738887 | 175 | spi.select(); |
Jamie Smith |
0:0c3532738887 | 176 | loadStatusByte(spi.write(CC1200_ENQUEUE_TX_FIFO | CC1200_BURST)); |
Jamie Smith |
0:0c3532738887 | 177 | spi.write(len); |
Jamie Smith |
0:0c3532738887 | 178 | for(size_t byteIndex = 0; byteIndex < len; ++byteIndex) |
Jamie Smith |
0:0c3532738887 | 179 | { |
Jamie Smith |
0:0c3532738887 | 180 | spi.write(data[byteIndex]); |
Jamie Smith |
0:0c3532738887 | 181 | } |
Jamie Smith |
0:0c3532738887 | 182 | spi.deselect(); |
Jamie Smith |
0:0c3532738887 | 183 | |
Jamie Smith |
0:0c3532738887 | 184 | #if CC1200_REGISTER_LEVEL_DEBUG |
Jamie Smith |
0:0c3532738887 | 185 | debugStream->printf("Wrote packet of data length %zu: %" PRIx8, len, static_cast<uint8_t>(len)); |
Jamie Smith |
0:0c3532738887 | 186 | for(size_t byteIndex = 0; byteIndex < len; ++byteIndex) |
Jamie Smith |
0:0c3532738887 | 187 | { |
Jamie Smith |
0:0c3532738887 | 188 | debugStream->printf(" %" PRIx8, data[byteIndex]); |
Jamie Smith |
0:0c3532738887 | 189 | } |
Jamie Smith |
0:0c3532738887 | 190 | debugStream->printf("\n"); |
Jamie Smith |
0:0c3532738887 | 191 | #endif |
Jamie Smith |
0:0c3532738887 | 192 | return true; |
Jamie Smith |
0:0c3532738887 | 193 | } |
Jamie Smith |
0:0c3532738887 | 194 | |
Jamie Smith |
0:0c3532738887 | 195 | bool CC1200::hasReceivedPacket() |
Jamie Smith |
0:0c3532738887 | 196 | { |
Jamie Smith |
0:0c3532738887 | 197 | size_t bytesReceived = getRXFIFOLen(); |
Jamie Smith |
0:0c3532738887 | 198 | |
Jamie Smith |
0:0c3532738887 | 199 | if(bytesReceived < 1) |
Jamie Smith |
0:0c3532738887 | 200 | { |
Jamie Smith |
0:0c3532738887 | 201 | // no bytes at all, can't check the length |
Jamie Smith |
0:0c3532738887 | 202 | return false; |
Jamie Smith |
0:0c3532738887 | 203 | } |
Jamie Smith |
0:0c3532738887 | 204 | |
Jamie Smith |
2:2a447e8e50b8 | 205 | // get value of first byte of the packet, which is the length. |
Jamie Smith |
2:2a447e8e50b8 | 206 | |
Jamie Smith |
2:2a447e8e50b8 | 207 | // The datasheet is wrong about this! It says that the first byte in the RX FIFO can |
Jamie Smith |
2:2a447e8e50b8 | 208 | // be found by accessing address RXFIRST via direct fifo access. |
Jamie Smith |
2:2a447e8e50b8 | 209 | // However, in my own testing, RXFIRST points to the second entry in the FIFO, and |
Jamie Smith |
2:2a447e8e50b8 | 210 | // the first entry must be accessed through RXFIFO_PRE_BUF. |
Jamie Smith |
2:2a447e8e50b8 | 211 | uint8_t packetLen = readRegister(ExtRegister::RXFIFO_PRE_BUF); |
Jamie Smith |
0:0c3532738887 | 212 | |
Jamie Smith |
0:0c3532738887 | 213 | // if we have received a full packet's worth of bytes, then we have received a full packet. |
Jamie Smith |
0:0c3532738887 | 214 | return bytesReceived >= static_cast<size_t>(packetLen + 1); // Add one because length field does not include itself |
Jamie Smith |
0:0c3532738887 | 215 | } |
Jamie Smith |
0:0c3532738887 | 216 | |
Jamie Smith |
0:0c3532738887 | 217 | size_t CC1200::receivePacket(char *buffer, size_t bufferLen) |
Jamie Smith |
0:0c3532738887 | 218 | { |
Jamie Smith |
0:0c3532738887 | 219 | // burst read from RX FIFO |
Jamie Smith |
0:0c3532738887 | 220 | spi.select(); |
Jamie Smith |
0:0c3532738887 | 221 | loadStatusByte(spi.write(CC1200_DEQUEUE_RX_FIFO | CC1200_BURST)); |
Jamie Smith |
0:0c3532738887 | 222 | |
Jamie Smith |
0:0c3532738887 | 223 | // first read length byte |
Jamie Smith |
0:0c3532738887 | 224 | uint8_t dataLen = spi.write(0); |
Jamie Smith |
0:0c3532738887 | 225 | |
Jamie Smith |
0:0c3532738887 | 226 | for(size_t byteIndex = 0; byteIndex < dataLen; ++byteIndex) |
Jamie Smith |
0:0c3532738887 | 227 | { |
Jamie Smith |
0:0c3532738887 | 228 | uint8_t currByte = spi.write(0); |
Jamie Smith |
0:0c3532738887 | 229 | if(byteIndex < bufferLen) |
Jamie Smith |
0:0c3532738887 | 230 | { |
Jamie Smith |
0:0c3532738887 | 231 | buffer[byteIndex] = currByte; |
Jamie Smith |
0:0c3532738887 | 232 | } |
Jamie Smith |
0:0c3532738887 | 233 | } |
Jamie Smith |
0:0c3532738887 | 234 | spi.deselect(); |
Jamie Smith |
0:0c3532738887 | 235 | |
Jamie Smith |
0:0c3532738887 | 236 | #if CC1200_REGISTER_LEVEL_DEBUG |
Jamie Smith |
0:0c3532738887 | 237 | debugStream->printf("Read packet of data length %" PRIu8 ": %" PRIx8, dataLen, static_cast<uint8_t>(dataLen)); |
Jamie Smith |
0:0c3532738887 | 238 | for(size_t byteIndex = 0; byteIndex < dataLen; ++byteIndex) |
Jamie Smith |
0:0c3532738887 | 239 | { |
Jamie Smith |
0:0c3532738887 | 240 | debugStream->printf(" %" PRIx8, buffer[byteIndex]); |
Jamie Smith |
0:0c3532738887 | 241 | } |
Jamie Smith |
0:0c3532738887 | 242 | debugStream->printf("\n"); |
Jamie Smith |
0:0c3532738887 | 243 | #endif |
Jamie Smith |
0:0c3532738887 | 244 | |
Jamie Smith |
0:0c3532738887 | 245 | return dataLen; |
Jamie Smith |
0:0c3532738887 | 246 | } |
Jamie Smith |
0:0c3532738887 | 247 | |
Jamie Smith |
0:0c3532738887 | 248 | // helper function: convert a state to the bits for RXOFF_MODE and TXOFF_MODE |
Jamie Smith |
0:0c3532738887 | 249 | inline uint8_t getOffModeBits(CC1200::State state) |
Jamie Smith |
0:0c3532738887 | 250 | { |
Jamie Smith |
0:0c3532738887 | 251 | uint8_t offBits = 0b0; |
Jamie Smith |
0:0c3532738887 | 252 | if(state == CC1200::State::IDLE) |
Jamie Smith |
0:0c3532738887 | 253 | { |
Jamie Smith |
0:0c3532738887 | 254 | offBits = 0b0; |
Jamie Smith |
0:0c3532738887 | 255 | } |
Jamie Smith |
0:0c3532738887 | 256 | else if(state == CC1200::State::FAST_ON) |
Jamie Smith |
0:0c3532738887 | 257 | { |
Jamie Smith |
0:0c3532738887 | 258 | offBits = 0b1; |
Jamie Smith |
0:0c3532738887 | 259 | } |
Jamie Smith |
0:0c3532738887 | 260 | else if(state == CC1200::State::TX) |
Jamie Smith |
0:0c3532738887 | 261 | { |
Jamie Smith |
0:0c3532738887 | 262 | offBits = 0b10; |
Jamie Smith |
0:0c3532738887 | 263 | } |
Jamie Smith |
0:0c3532738887 | 264 | else if(state == CC1200::State::RX) |
Jamie Smith |
0:0c3532738887 | 265 | { |
Jamie Smith |
0:0c3532738887 | 266 | offBits = 0b11; |
Jamie Smith |
0:0c3532738887 | 267 | } |
Jamie Smith |
0:0c3532738887 | 268 | return offBits; |
Jamie Smith |
0:0c3532738887 | 269 | } |
Jamie Smith |
0:0c3532738887 | 270 | |
Jamie Smith |
0:0c3532738887 | 271 | void CC1200::setOnReceiveState(CC1200::State goodPacket, CC1200::State badPacket) |
Jamie Smith |
0:0c3532738887 | 272 | { |
Jamie Smith |
0:0c3532738887 | 273 | // configure good packet action via RXOFF_MODE |
Jamie Smith |
0:0c3532738887 | 274 | uint8_t rfendCfg1 = readRegister(Register::RFEND_CFG1); |
Jamie Smith |
0:0c3532738887 | 275 | rfendCfg1 &= ~(0b11 << RFEND_CFG1_RXOFF_MODE); |
Jamie Smith |
0:0c3532738887 | 276 | rfendCfg1 |= getOffModeBits(goodPacket) << RFEND_CFG1_RXOFF_MODE; |
Jamie Smith |
0:0c3532738887 | 277 | writeRegister(Register::RFEND_CFG1, rfendCfg1); |
Jamie Smith |
0:0c3532738887 | 278 | |
Jamie Smith |
0:0c3532738887 | 279 | // configure bad packet action via TERM_ON_BAD_PACKET_EN |
Jamie Smith |
0:0c3532738887 | 280 | uint8_t rfendCfg0 = readRegister(Register::RFEND_CFG0); |
Jamie Smith |
0:0c3532738887 | 281 | if(badPacket == State::RX) |
Jamie Smith |
0:0c3532738887 | 282 | { |
Jamie Smith |
0:0c3532738887 | 283 | rfendCfg0 &= ~(1 << RFEND_CFG0_TERM_ON_BAD_PACKET_EN); |
Jamie Smith |
0:0c3532738887 | 284 | } |
Jamie Smith |
0:0c3532738887 | 285 | else |
Jamie Smith |
0:0c3532738887 | 286 | { |
Jamie Smith |
0:0c3532738887 | 287 | rfendCfg0 |= 1 << RFEND_CFG1_RXOFF_MODE; |
Jamie Smith |
0:0c3532738887 | 288 | } |
Jamie Smith |
0:0c3532738887 | 289 | writeRegister(Register::RFEND_CFG0, rfendCfg0); |
Jamie Smith |
0:0c3532738887 | 290 | } |
Jamie Smith |
0:0c3532738887 | 291 | |
Jamie Smith |
1:98af824b145e | 292 | void CC1200::setOnTransmitState(CC1200::State txState) |
Jamie Smith |
0:0c3532738887 | 293 | { |
Jamie Smith |
0:0c3532738887 | 294 | uint8_t rfendCfg0 = readRegister(Register::RFEND_CFG0); |
Jamie Smith |
0:0c3532738887 | 295 | rfendCfg0 &= ~(0b11 << RFEND_CFG0_TXOFF_MODE); |
Jamie Smith |
1:98af824b145e | 296 | rfendCfg0 |= getOffModeBits(txState) << RFEND_CFG0_TXOFF_MODE; |
Jamie Smith |
0:0c3532738887 | 297 | writeRegister(Register::RFEND_CFG0, rfendCfg0); |
Jamie Smith |
0:0c3532738887 | 298 | } |
Jamie Smith |
0:0c3532738887 | 299 | |
Jamie Smith |
0:0c3532738887 | 300 | void CC1200::setFSCalMode(FSCalMode mode) |
Jamie Smith |
0:0c3532738887 | 301 | { |
Jamie Smith |
0:0c3532738887 | 302 | uint8_t settlingCfg = readRegister(Register::SETTLING_CFG); |
Jamie Smith |
0:0c3532738887 | 303 | settlingCfg &= ~(0b11 << SETTLING_CFG_FS_AUTOCAL); |
Jamie Smith |
0:0c3532738887 | 304 | settlingCfg |= static_cast<uint8_t>(mode) << SETTLING_CFG_FS_AUTOCAL; |
Jamie Smith |
0:0c3532738887 | 305 | writeRegister(Register::SETTLING_CFG, settlingCfg); |
Jamie Smith |
0:0c3532738887 | 306 | } |
Jamie Smith |
0:0c3532738887 | 307 | |
Jamie Smith |
0:0c3532738887 | 308 | void CC1200::configureGPIO(uint8_t gpioNumber, CC1200::GPIOMode mode, bool outputInvert) |
Jamie Smith |
0:0c3532738887 | 309 | { |
Jamie Smith |
0:0c3532738887 | 310 | // gpio 3 is the first register, then it goes down to 0 |
Jamie Smith |
0:0c3532738887 | 311 | Register gpioReg = static_cast<Register>(static_cast<uint8_t>(Register::IOCFG3) + (3 - gpioNumber)); |
Jamie Smith |
0:0c3532738887 | 312 | |
Jamie Smith |
0:0c3532738887 | 313 | uint8_t gpioCfgVal = static_cast<uint8_t>(mode); |
Jamie Smith |
0:0c3532738887 | 314 | if(outputInvert) |
Jamie Smith |
0:0c3532738887 | 315 | { |
Jamie Smith |
0:0c3532738887 | 316 | gpioCfgVal |= (1 << GPIO_INV); |
Jamie Smith |
0:0c3532738887 | 317 | } |
Jamie Smith |
0:0c3532738887 | 318 | writeRegister(gpioReg, gpioCfgVal); |
Jamie Smith |
0:0c3532738887 | 319 | } |
Jamie Smith |
0:0c3532738887 | 320 | |
Jamie Smith |
0:0c3532738887 | 321 | void CC1200::configureFIFOMode() |
Jamie Smith |
0:0c3532738887 | 322 | { |
Jamie Smith |
0:0c3532738887 | 323 | // configure packet format |
Jamie Smith |
0:0c3532738887 | 324 | uint8_t pktCfg2 = readRegister(Register::PKT_CFG2); |
Jamie Smith |
0:0c3532738887 | 325 | pktCfg2 &= ~(0b11 << PKT_CFG2_PKT_FORMAT); |
Jamie Smith |
0:0c3532738887 | 326 | writeRegister(Register::PKT_CFG2, pktCfg2); |
Jamie Smith |
0:0c3532738887 | 327 | |
Jamie Smith |
0:0c3532738887 | 328 | // enable fifo |
Jamie Smith |
0:0c3532738887 | 329 | uint8_t mdmCfg1 = readRegister(Register::MDMCFG1); |
Jamie Smith |
0:0c3532738887 | 330 | mdmCfg1 |= 1 << MDMCFG1_FIFO_EN; |
Jamie Smith |
0:0c3532738887 | 331 | writeRegister(Register::MDMCFG1, mdmCfg1); |
Jamie Smith |
0:0c3532738887 | 332 | |
Jamie Smith |
0:0c3532738887 | 333 | // make sure transparent mode is disabled |
Jamie Smith |
0:0c3532738887 | 334 | uint8_t mdmCfg0 = readRegister(Register::MDMCFG0); |
Jamie Smith |
0:0c3532738887 | 335 | mdmCfg0 &= ~(1 << MDMCFG0_TRANSPARENT_MODE_EN); |
Jamie Smith |
0:0c3532738887 | 336 | writeRegister(Register::MDMCFG0, mdmCfg0); |
Jamie Smith |
0:0c3532738887 | 337 | } |
Jamie Smith |
0:0c3532738887 | 338 | |
Jamie Smith |
0:0c3532738887 | 339 | void CC1200::setModulationFormat(CC1200::ModFormat format) |
Jamie Smith |
0:0c3532738887 | 340 | { |
Jamie Smith |
0:0c3532738887 | 341 | uint8_t modcfgDevE = readRegister(Register::MODCFG_DEV_E); |
Jamie Smith |
0:0c3532738887 | 342 | modcfgDevE &= ~(0b111 << MODCFG_DEV_E_MOD_FORMAT); |
Jamie Smith |
0:0c3532738887 | 343 | modcfgDevE |= static_cast<uint8_t>(format) << MODCFG_DEV_E_DEV_E; |
Jamie Smith |
0:0c3532738887 | 344 | writeRegister(Register::MODCFG_DEV_E, modcfgDevE); |
Jamie Smith |
0:0c3532738887 | 345 | } |
Jamie Smith |
0:0c3532738887 | 346 | |
Jamie Smith |
0:0c3532738887 | 347 | void CC1200::setFSKDeviation(float deviation) |
Jamie Smith |
0:0c3532738887 | 348 | { |
Jamie Smith |
0:0c3532738887 | 349 | // Deviation is set as two values, an exponent register from 0-3 and a mantissa register from 0-256. |
Jamie Smith |
0:0c3532738887 | 350 | // See user guide page 81 for the original equation, this function was worked out from that |
Jamie Smith |
0:0c3532738887 | 351 | |
Jamie Smith |
0:0c3532738887 | 352 | // First assume mantissa is zero and calculate the needed exponent |
Jamie Smith |
0:0c3532738887 | 353 | float exactExponent = std::log2(deviation) - CC1200_OSC_FREQ_LOG2 + 14; |
Jamie Smith |
0:0c3532738887 | 354 | uint8_t actualExponent = static_cast<uint8_t>(std::min(static_cast<float>(maxValue3Bits), exactExponent)); |
Jamie Smith |
0:0c3532738887 | 355 | // note: 14 comes from log2(2^22) - log2(256) |
Jamie Smith |
0:0c3532738887 | 356 | |
Jamie Smith |
0:0c3532738887 | 357 | float exactMantissa; |
Jamie Smith |
0:0c3532738887 | 358 | if(actualExponent >= 1) |
Jamie Smith |
0:0c3532738887 | 359 | { |
Jamie Smith |
0:0c3532738887 | 360 | exactMantissa = (std::pow(2.0f, static_cast<float>(22 - actualExponent)) * deviation) |
Jamie Smith |
0:0c3532738887 | 361 | / CC1200_OSC_FREQ - 256; |
Jamie Smith |
0:0c3532738887 | 362 | } |
Jamie Smith |
0:0c3532738887 | 363 | else |
Jamie Smith |
0:0c3532738887 | 364 | { |
Jamie Smith |
0:0c3532738887 | 365 | // use alternate high-resolution formula for case where exponent = 0 |
Jamie Smith |
0:0c3532738887 | 366 | exactMantissa = deviation * twoToThe21 / CC1200_OSC_FREQ; |
Jamie Smith |
0:0c3532738887 | 367 | } |
Jamie Smith |
0:0c3532738887 | 368 | |
Jamie Smith |
0:0c3532738887 | 369 | // now calculate closest mantissa |
Jamie Smith |
0:0c3532738887 | 370 | uint8_t actualMantissa = static_cast<uint8_t>(std::min(static_cast<float>(maxValue8Bits), exactMantissa)); |
Jamie Smith |
0:0c3532738887 | 371 | |
Jamie Smith |
0:0c3532738887 | 372 | // set exponent and mantissa |
Jamie Smith |
0:0c3532738887 | 373 | writeRegister(Register::DEVIATION_M, actualMantissa); |
Jamie Smith |
0:0c3532738887 | 374 | |
Jamie Smith |
0:0c3532738887 | 375 | uint8_t modcfgDevE = readRegister(Register::MODCFG_DEV_E); |
Jamie Smith |
0:0c3532738887 | 376 | modcfgDevE &= ~(0b111 << MODCFG_DEV_E_DEV_E); |
Jamie Smith |
0:0c3532738887 | 377 | modcfgDevE |= actualExponent << MODCFG_DEV_E_DEV_E; |
Jamie Smith |
0:0c3532738887 | 378 | writeRegister(Register::MODCFG_DEV_E, modcfgDevE); |
Jamie Smith |
0:0c3532738887 | 379 | |
Jamie Smith |
0:0c3532738887 | 380 | #if CC1200_DEBUG |
Jamie Smith |
0:0c3532738887 | 381 | debugStream->printf("Setting FSK deviation, requested +-%.00f Hz, setting DEV_E = 0x%" PRIx8 " DEV_M = 0x%" PRIx8 "\n", |
Jamie Smith |
0:0c3532738887 | 382 | deviation, actualExponent, actualMantissa); |
Jamie Smith |
0:0c3532738887 | 383 | |
Jamie Smith |
0:0c3532738887 | 384 | float actualDeviation; |
Jamie Smith |
0:0c3532738887 | 385 | if(actualExponent == 0) |
Jamie Smith |
0:0c3532738887 | 386 | { |
Jamie Smith |
0:0c3532738887 | 387 | actualDeviation = CC1200_OSC_FREQ * actualMantissa / twoToThe21; |
Jamie Smith |
0:0c3532738887 | 388 | } |
Jamie Smith |
0:0c3532738887 | 389 | else |
Jamie Smith |
0:0c3532738887 | 390 | { |
Jamie Smith |
0:0c3532738887 | 391 | actualDeviation = (CC1200_OSC_FREQ / twoToThe22) * (256.0f + static_cast<float>(actualMantissa)) * pow(2.0f, static_cast<float>(actualExponent)); |
Jamie Smith |
0:0c3532738887 | 392 | } |
Jamie Smith |
0:0c3532738887 | 393 | // sanity check: calculate actual deviation |
Jamie Smith |
0:0c3532738887 | 394 | debugStream->printf("This yields an actual deviation of +-%.00f Hz\n", actualDeviation); |
Jamie Smith |
0:0c3532738887 | 395 | #endif |
Jamie Smith |
0:0c3532738887 | 396 | } |
Jamie Smith |
0:0c3532738887 | 397 | |
Jamie Smith |
0:0c3532738887 | 398 | void CC1200::setSymbolRate(float symbolRateHz) |
Jamie Smith |
0:0c3532738887 | 399 | { |
Jamie Smith |
0:0c3532738887 | 400 | // Datasheet says that the cc1200 works in ksps, but testing with SmartRF studio |
Jamie Smith |
0:0c3532738887 | 401 | // shows that it actually is sps. |
Jamie Smith |
0:0c3532738887 | 402 | |
Jamie Smith |
0:0c3532738887 | 403 | symbolRateSps = symbolRateHz; |
Jamie Smith |
0:0c3532738887 | 404 | |
Jamie Smith |
0:0c3532738887 | 405 | // Note: these equations are given on page 29 of the user guide |
Jamie Smith |
0:0c3532738887 | 406 | |
Jamie Smith |
0:0c3532738887 | 407 | float exactExponent = std::log2(symbolRateSps) - CC1200_OSC_FREQ_LOG2 + 19; |
Jamie Smith |
0:0c3532738887 | 408 | // note: 19 comes from log2(2^39) - 20 |
Jamie Smith |
0:0c3532738887 | 409 | uint8_t actualExponent = static_cast<uint8_t>(std::min(static_cast<float>(maxValue4Bits), exactExponent)); |
Jamie Smith |
0:0c3532738887 | 410 | // note: 15 comes from the largest number storable in 4 bits |
Jamie Smith |
0:0c3532738887 | 411 | |
Jamie Smith |
0:0c3532738887 | 412 | float exactMantissa; |
Jamie Smith |
0:0c3532738887 | 413 | if(actualExponent >= 1) |
Jamie Smith |
0:0c3532738887 | 414 | { |
Jamie Smith |
0:0c3532738887 | 415 | exactMantissa = (std::pow(2.0f, static_cast<float>(39 - actualExponent)) * symbolRateSps) |
Jamie Smith |
0:0c3532738887 | 416 | / CC1200_OSC_FREQ - twoToThe20; |
Jamie Smith |
0:0c3532738887 | 417 | } |
Jamie Smith |
0:0c3532738887 | 418 | else |
Jamie Smith |
0:0c3532738887 | 419 | { |
Jamie Smith |
0:0c3532738887 | 420 | // use alternate high-resolution formula for case where exponent = 0 |
Jamie Smith |
0:0c3532738887 | 421 | exactMantissa = symbolRateSps * twoToThe38 / CC1200_OSC_FREQ; |
Jamie Smith |
0:0c3532738887 | 422 | } |
Jamie Smith |
0:0c3532738887 | 423 | |
Jamie Smith |
0:0c3532738887 | 424 | // mantissa is a 20 bit number, so restrict it to that domain |
Jamie Smith |
0:0c3532738887 | 425 | uint32_t actualMantissa = static_cast<uint32_t>(std::min(static_cast<float>(maxValue20Bits), exactMantissa)); |
Jamie Smith |
0:0c3532738887 | 426 | |
Jamie Smith |
0:0c3532738887 | 427 | // program symbol rate registers |
Jamie Smith |
0:0c3532738887 | 428 | std::array<uint8_t, 3> symbolRateRegisters ={ |
Jamie Smith |
0:0c3532738887 | 429 | static_cast<uint8_t>((actualExponent << SYMBOL_RATE2_SRATE_E) | (static_cast<uint8_t>((actualMantissa >> 16) & 0xFF) << SYMBOL_RATE2_SRATE_M_19_16)), |
Jamie Smith |
0:0c3532738887 | 430 | static_cast<uint8_t>((actualMantissa >> 8) & 0xFF), |
Jamie Smith |
0:0c3532738887 | 431 | static_cast<uint8_t>((actualMantissa & 0xFF)) |
Jamie Smith |
0:0c3532738887 | 432 | }; |
Jamie Smith |
0:0c3532738887 | 433 | writeRegisters(Register::SYMBOL_RATE2, symbolRateRegisters); |
Jamie Smith |
0:0c3532738887 | 434 | |
Jamie Smith |
0:0c3532738887 | 435 | // Calculate upsampler value according to the forumula in its register description |
Jamie Smith |
0:0c3532738887 | 436 | float upsamplingFactor = CC1200_OSC_FREQ / (64 * symbolRateSps); |
Jamie Smith |
0:0c3532738887 | 437 | uint8_t upsamplerPVal = std::floor(std::log2(upsamplingFactor)); |
Jamie Smith |
0:0c3532738887 | 438 | |
Jamie Smith |
0:0c3532738887 | 439 | uint8_t mdmcfg2 = readRegister(ExtRegister::MDMCFG2); |
Jamie Smith |
0:0c3532738887 | 440 | mdmcfg2 &= ~(0b111 << MDMCFG2_UPSAMPLER_P); |
Jamie Smith |
0:0c3532738887 | 441 | mdmcfg2 |= (upsamplerPVal << MDMCFG2_UPSAMPLER_P); |
Jamie Smith |
0:0c3532738887 | 442 | |
Jamie Smith |
0:0c3532738887 | 443 | writeRegister(ExtRegister::MDMCFG2, mdmcfg2); |
Jamie Smith |
0:0c3532738887 | 444 | |
Jamie Smith |
0:0c3532738887 | 445 | #if CC1200_DEBUG |
Jamie Smith |
0:0c3532738887 | 446 | debugStream->printf("Setting symbol rate, requested %.00f Hz, setting SRATE_E = 0x%" PRIx8 " SRATE_M = 0x%" PRIx32 "\n", |
Jamie Smith |
0:0c3532738887 | 447 | symbolRateHz, actualExponent, actualMantissa); |
Jamie Smith |
0:0c3532738887 | 448 | |
Jamie Smith |
0:0c3532738887 | 449 | // sanity check: calculate actual symbol rate |
Jamie Smith |
0:0c3532738887 | 450 | float actualSymbolRateKsps; |
Jamie Smith |
0:0c3532738887 | 451 | if(actualExponent == 0) |
Jamie Smith |
0:0c3532738887 | 452 | { |
Jamie Smith |
0:0c3532738887 | 453 | actualSymbolRateKsps = (static_cast<float>(actualMantissa) * CC1200_OSC_FREQ) / twoToThe38; |
Jamie Smith |
0:0c3532738887 | 454 | } |
Jamie Smith |
0:0c3532738887 | 455 | else |
Jamie Smith |
0:0c3532738887 | 456 | { |
Jamie Smith |
0:0c3532738887 | 457 | |
Jamie Smith |
0:0c3532738887 | 458 | actualSymbolRateKsps = ((static_cast<float>(actualMantissa) + twoToThe20) * |
Jamie Smith |
0:0c3532738887 | 459 | pow(2.0f,static_cast<float>(actualExponent)) * CC1200_OSC_FREQ) |
Jamie Smith |
0:0c3532738887 | 460 | / twoToThe39; |
Jamie Smith |
0:0c3532738887 | 461 | } |
Jamie Smith |
0:0c3532738887 | 462 | |
Jamie Smith |
0:0c3532738887 | 463 | debugStream->printf("This yields an actual symbol rate of %.00f Hz\n", actualSymbolRateKsps * 1000.0f); |
Jamie Smith |
0:0c3532738887 | 464 | |
Jamie Smith |
0:0c3532738887 | 465 | uint8_t actualUpsampling = static_cast<uint8_t>(pow(2.0f, static_cast<float>(upsamplerPVal))); |
Jamie Smith |
0:0c3532738887 | 466 | debugStream->printf("Also setting upsampling factor to %" PRIu8 " via UPSAMPLER_P = %" PRIx8 "\n", actualUpsampling, upsamplerPVal); |
Jamie Smith |
0:0c3532738887 | 467 | #endif |
Jamie Smith |
0:0c3532738887 | 468 | } |
Jamie Smith |
0:0c3532738887 | 469 | |
Jamie Smith |
0:0c3532738887 | 470 | void CC1200::setOutputPower(float outPower) |
Jamie Smith |
0:0c3532738887 | 471 | { |
Jamie Smith |
0:0c3532738887 | 472 | const float minOutputPower = -16.0f; |
Jamie Smith |
0:0c3532738887 | 473 | |
Jamie Smith |
0:0c3532738887 | 474 | // note: datasheet says this is 14.5, but that must be a mistake: 14.5 would produce a number |
Jamie Smith |
0:0c3532738887 | 475 | // too large to fit into 6 bits. |
Jamie Smith |
0:0c3532738887 | 476 | const float maxOutputPower = 14.0f; |
Jamie Smith |
0:0c3532738887 | 477 | |
Jamie Smith |
0:0c3532738887 | 478 | // clamp output power into correct range |
Jamie Smith |
0:0c3532738887 | 479 | outPower = std::min(outPower, maxOutputPower); |
Jamie Smith |
0:0c3532738887 | 480 | outPower = std::max(outPower, minOutputPower); |
Jamie Smith |
0:0c3532738887 | 481 | |
Jamie Smith |
0:0c3532738887 | 482 | // this equation derived from user guide section 7.1 |
Jamie Smith |
0:0c3532738887 | 483 | float exactPowerRamp = (2 * outPower) + 35; |
Jamie Smith |
0:0c3532738887 | 484 | uint8_t actualPowerRamp = static_cast<uint8_t>(exactPowerRamp); // round to nearest |
Jamie Smith |
0:0c3532738887 | 485 | |
Jamie Smith |
0:0c3532738887 | 486 | uint8_t paCfg1 = readRegister(Register::PA_CFG1); |
Jamie Smith |
0:0c3532738887 | 487 | paCfg1 &= ~(0b111111 << PA_CFG1_PA_POWER_RAMP); |
Jamie Smith |
0:0c3532738887 | 488 | paCfg1 |= actualPowerRamp << PA_CFG1_PA_POWER_RAMP; |
Jamie Smith |
0:0c3532738887 | 489 | writeRegister(Register::PA_CFG1, paCfg1); |
Jamie Smith |
0:0c3532738887 | 490 | } |
Jamie Smith |
0:0c3532738887 | 491 | |
Jamie Smith |
0:0c3532738887 | 492 | void CC1200::setRadioFrequency(CC1200::Band band, float frequencyHz) |
Jamie Smith |
0:0c3532738887 | 493 | { |
Jamie Smith |
0:0c3532738887 | 494 | // Frequency synthesizer configuration. This is completely opaque and it is unknown what these bits do -- |
Jamie Smith |
0:0c3532738887 | 495 | // they are only generated by SmartRF studio. |
Jamie Smith |
0:0c3532738887 | 496 | // I manually deduplicated them since only a few change based on frequency. |
Jamie Smith |
0:0c3532738887 | 497 | writeRegister(ExtRegister::IF_ADC1,0xEE); |
Jamie Smith |
0:0c3532738887 | 498 | writeRegister(ExtRegister::IF_ADC0,0x10); |
Jamie Smith |
0:0c3532738887 | 499 | writeRegister(ExtRegister::FS_DIG1,0x04); |
Jamie Smith |
0:0c3532738887 | 500 | writeRegister(ExtRegister::FS_CAL1,0x40); |
Jamie Smith |
0:0c3532738887 | 501 | writeRegister(ExtRegister::FS_CAL0,0x0E); |
Jamie Smith |
0:0c3532738887 | 502 | writeRegister(ExtRegister::FS_DIVTWO,0x03); |
Jamie Smith |
0:0c3532738887 | 503 | writeRegister(ExtRegister::FS_DSM0,0x33); |
Jamie Smith |
0:0c3532738887 | 504 | writeRegister(ExtRegister::FS_DVC1,0xF7); |
Jamie Smith |
0:0c3532738887 | 505 | writeRegister(ExtRegister::FS_PFD,0x00); |
Jamie Smith |
0:0c3532738887 | 506 | writeRegister(ExtRegister::FS_PRE,0x6E); |
Jamie Smith |
0:0c3532738887 | 507 | writeRegister(ExtRegister::FS_REG_DIV_CML,0x1C); |
Jamie Smith |
0:0c3532738887 | 508 | writeRegister(ExtRegister::FS_SPARE,0xAC); |
Jamie Smith |
0:0c3532738887 | 509 | writeRegister(ExtRegister::FS_VCO0,0xB5); |
Jamie Smith |
0:0c3532738887 | 510 | writeRegister(ExtRegister::XOSC5,0x0E); |
Jamie Smith |
0:0c3532738887 | 511 | writeRegister(ExtRegister::XOSC1,0x03); |
Jamie Smith |
0:0c3532738887 | 512 | if(band == Band::BAND_820_960MHz) |
Jamie Smith |
0:0c3532738887 | 513 | { |
Jamie Smith |
0:0c3532738887 | 514 | writeRegister(ExtRegister::FS_DIG0,0x55); |
Jamie Smith |
0:0c3532738887 | 515 | writeRegister(ExtRegister::FS_DVC0,0x17); |
Jamie Smith |
0:0c3532738887 | 516 | writeRegister(ExtRegister::IFAMP,0x09); |
Jamie Smith |
0:0c3532738887 | 517 | } |
Jamie Smith |
0:0c3532738887 | 518 | else if(band == Band::BAND_410_480MHz || band == Band::BAND_164_192MHz) |
Jamie Smith |
0:0c3532738887 | 519 | { |
Jamie Smith |
0:0c3532738887 | 520 | writeRegister(ExtRegister::FS_DIG0,0x50); |
Jamie Smith |
0:0c3532738887 | 521 | writeRegister(ExtRegister::FS_DVC0,0x0F); |
Jamie Smith |
0:0c3532738887 | 522 | writeRegister(ExtRegister::IFAMP,0x0D); |
Jamie Smith |
0:0c3532738887 | 523 | } |
Jamie Smith |
0:0c3532738887 | 524 | else |
Jamie Smith |
0:0c3532738887 | 525 | { |
Jamie Smith |
0:0c3532738887 | 526 | // TI doesn't make settings public for the other radio bands. |
Jamie Smith |
0:0c3532738887 | 527 | // Let's take a guess and use the 480-164MHz values. |
Jamie Smith |
0:0c3532738887 | 528 | writeRegister(ExtRegister::FS_DIG0,0x50); |
Jamie Smith |
0:0c3532738887 | 529 | writeRegister(ExtRegister::FS_DVC0,0x0F); |
Jamie Smith |
0:0c3532738887 | 530 | writeRegister(ExtRegister::IFAMP,0x0D); |
Jamie Smith |
0:0c3532738887 | 531 | } |
Jamie Smith |
0:0c3532738887 | 532 | |
Jamie Smith |
0:0c3532738887 | 533 | // convert band to LO Divider value. |
Jamie Smith |
0:0c3532738887 | 534 | // Most of the bands just multiply the register value by 2, but nooo, not BAND_136_160MHz. |
Jamie Smith |
0:0c3532738887 | 535 | uint8_t loDividerValue; |
Jamie Smith |
0:0c3532738887 | 536 | if(band == Band::BAND_136_160MHz) |
Jamie Smith |
0:0c3532738887 | 537 | { |
Jamie Smith |
0:0c3532738887 | 538 | loDividerValue = 24; |
Jamie Smith |
0:0c3532738887 | 539 | } |
Jamie Smith |
0:0c3532738887 | 540 | else |
Jamie Smith |
0:0c3532738887 | 541 | { |
Jamie Smith |
0:0c3532738887 | 542 | loDividerValue = static_cast<uint8_t>(band) * 2; |
Jamie Smith |
0:0c3532738887 | 543 | } |
Jamie Smith |
0:0c3532738887 | 544 | |
Jamie Smith |
0:0c3532738887 | 545 | // program band (also enable FS out of lock detector, which is useful for testing) |
Jamie Smith |
0:0c3532738887 | 546 | writeRegister(Register::FS_CFG, (1 << FS_CFG_FS_LOCK_EN) | (static_cast<uint8_t>(band) << FS_CFG_FSD_BANDSELECT)); |
Jamie Smith |
0:0c3532738887 | 547 | |
Jamie Smith |
0:0c3532738887 | 548 | // equation derived from user guide section 9.12 |
Jamie Smith |
0:0c3532738887 | 549 | float exactFreqValue = (twoToThe16 * frequencyHz * static_cast<float>(loDividerValue)) / CC1200_OSC_FREQ; |
Jamie Smith |
0:0c3532738887 | 550 | uint32_t actualFreqValue = static_cast<uint32_t>(std::min(static_cast<float>(maxValue24Bits), exactFreqValue)); |
Jamie Smith |
0:0c3532738887 | 551 | |
Jamie Smith |
0:0c3532738887 | 552 | // program frequency registers |
Jamie Smith |
0:0c3532738887 | 553 | std::array<uint8_t, 3> freqRegisters ={ |
Jamie Smith |
0:0c3532738887 | 554 | static_cast<uint8_t>((actualFreqValue >> 16) & 0xFF), |
Jamie Smith |
0:0c3532738887 | 555 | static_cast<uint8_t>((actualFreqValue >> 8) & 0xFF), |
Jamie Smith |
0:0c3532738887 | 556 | static_cast<uint8_t>((actualFreqValue & 0xFF)) |
Jamie Smith |
0:0c3532738887 | 557 | }; |
Jamie Smith |
0:0c3532738887 | 558 | writeRegisters(ExtRegister::FREQ2, freqRegisters); |
Jamie Smith |
0:0c3532738887 | 559 | |
Jamie Smith |
0:0c3532738887 | 560 | #if CC1200_DEBUG |
Jamie Smith |
0:0c3532738887 | 561 | debugStream->printf("Setting radio frequency, requested %.00f Hz, setting FREQ = 0x%" PRIx32 "\n", |
Jamie Smith |
0:0c3532738887 | 562 | frequencyHz, actualFreqValue); |
Jamie Smith |
0:0c3532738887 | 563 | |
Jamie Smith |
0:0c3532738887 | 564 | // sanity check: calculate actual frequency |
Jamie Smith |
0:0c3532738887 | 565 | float actualFrequency = (static_cast<float>(actualFreqValue) * CC1200_OSC_FREQ) / (twoToThe16 * static_cast<float>(loDividerValue)); |
Jamie Smith |
0:0c3532738887 | 566 | |
Jamie Smith |
0:0c3532738887 | 567 | debugStream->printf("This yields an actual frequency of %.00f Hz\n", actualFrequency); |
Jamie Smith |
0:0c3532738887 | 568 | #endif |
Jamie Smith |
0:0c3532738887 | 569 | } |
Jamie Smith |
0:0c3532738887 | 570 | |
Jamie Smith |
0:0c3532738887 | 571 | // helper function for setRXFilterBandwidth: |
Jamie Smith |
0:0c3532738887 | 572 | // calculate actual receive bandwidth from the given decimations. |
Jamie Smith |
0:0c3532738887 | 573 | float calcReceiveBandwidth(uint8_t adcDecimation, uint8_t cicDecimation) |
Jamie Smith |
0:0c3532738887 | 574 | { |
Jamie Smith |
0:0c3532738887 | 575 | return CC1200_OSC_FREQ / (static_cast<float>(adcDecimation) * static_cast<float>(cicDecimation) * 2); |
Jamie Smith |
0:0c3532738887 | 576 | } |
Jamie Smith |
0:0c3532738887 | 577 | |
Jamie Smith |
1:98af824b145e | 578 | void CC1200::setRXFilterBandwidth(float bandwidthHz) |
Jamie Smith |
0:0c3532738887 | 579 | { |
Jamie Smith |
0:0c3532738887 | 580 | // settings that the chip supports |
Jamie Smith |
0:0c3532738887 | 581 | const uint8_t possibleADCDecimations[] = {12, 24, 48}; // indexes in this array represent the register value |
Jamie Smith |
0:0c3532738887 | 582 | const size_t numADCDecimations = sizeof(possibleADCDecimations) / sizeof(uint8_t); |
Jamie Smith |
0:0c3532738887 | 583 | const uint8_t minBBDecimation = 1; |
Jamie Smith |
0:0c3532738887 | 584 | |
Jamie Smith |
0:0c3532738887 | 585 | // maximum supported BB decimation based on ADC decimation varies based on chip |
Jamie Smith |
0:0c3532738887 | 586 | const uint8_t maxBBDecimations1201[] = {33, 16, 8}; |
Jamie Smith |
0:0c3532738887 | 587 | const uint8_t maxBBDecimations1200[] = {44, 44, 44}; |
Jamie Smith |
0:0c3532738887 | 588 | uint8_t const * maxBBDecimations = isCC1201 ? maxBBDecimations1201 : maxBBDecimations1200; |
Jamie Smith |
0:0c3532738887 | 589 | |
Jamie Smith |
0:0c3532738887 | 590 | // the datasheet suggests to use the highest possible ADC decimation factor that will work for the requested frequency. |
Jamie Smith |
0:0c3532738887 | 591 | // So, we compute the closest we can get with each ADC decimation, and if there's a tie, we choose the one with the higher ADC bandwidth |
Jamie Smith |
0:0c3532738887 | 592 | |
Jamie Smith |
0:0c3532738887 | 593 | uint8_t actualBBDecimations[numADCDecimations]; |
Jamie Smith |
0:0c3532738887 | 594 | |
Jamie Smith |
0:0c3532738887 | 595 | for(size_t adcDecimationIndex = 0; adcDecimationIndex < numADCDecimations; ++adcDecimationIndex) |
Jamie Smith |
0:0c3532738887 | 596 | { |
Jamie Smith |
0:0c3532738887 | 597 | uint8_t adcDecimation = possibleADCDecimations[adcDecimationIndex]; |
Jamie Smith |
0:0c3532738887 | 598 | |
Jamie Smith |
0:0c3532738887 | 599 | // calculate BB decimation closest to the requested frequency |
Jamie Smith |
0:0c3532738887 | 600 | // derived from formula in section 6.1 |
Jamie Smith |
0:0c3532738887 | 601 | float exactBBDecimation = CC1200_OSC_FREQ / (2 * bandwidthHz * static_cast<float>(adcDecimation)); |
Jamie Smith |
0:0c3532738887 | 602 | exactBBDecimation = std::max(exactBBDecimation, static_cast<float>(minBBDecimation)); |
Jamie Smith |
0:0c3532738887 | 603 | exactBBDecimation = std::min(exactBBDecimation, static_cast<float>(maxBBDecimations[adcDecimationIndex])); |
Jamie Smith |
0:0c3532738887 | 604 | |
Jamie Smith |
0:0c3532738887 | 605 | actualBBDecimations[adcDecimationIndex] = static_cast<uint8_t>(exactBBDecimation); |
Jamie Smith |
0:0c3532738887 | 606 | } |
Jamie Smith |
0:0c3532738887 | 607 | |
Jamie Smith |
0:0c3532738887 | 608 | // now, choose the best of the ones we calculated |
Jamie Smith |
0:0c3532738887 | 609 | uint8_t bestDecimationIndex = 0; |
Jamie Smith |
0:0c3532738887 | 610 | float bestDecimationError = std::abs(bandwidthHz - calcReceiveBandwidth(possibleADCDecimations[0], actualBBDecimations[0])); |
Jamie Smith |
0:0c3532738887 | 611 | |
Jamie Smith |
0:0c3532738887 | 612 | for(size_t adcDecimationIndex = 1; adcDecimationIndex < numADCDecimations; ++adcDecimationIndex) |
Jamie Smith |
0:0c3532738887 | 613 | { |
Jamie Smith |
0:0c3532738887 | 614 | float thisDecimationError = std::abs(bandwidthHz - |
Jamie Smith |
0:0c3532738887 | 615 | calcReceiveBandwidth(possibleADCDecimations[adcDecimationIndex], actualBBDecimations[adcDecimationIndex])); |
Jamie Smith |
0:0c3532738887 | 616 | if(thisDecimationError <= bestDecimationError) |
Jamie Smith |
0:0c3532738887 | 617 | { |
Jamie Smith |
0:0c3532738887 | 618 | bestDecimationError = thisDecimationError; |
Jamie Smith |
0:0c3532738887 | 619 | bestDecimationIndex = adcDecimationIndex; |
Jamie Smith |
0:0c3532738887 | 620 | } |
Jamie Smith |
0:0c3532738887 | 621 | } |
Jamie Smith |
0:0c3532738887 | 622 | |
Jamie Smith |
0:0c3532738887 | 623 | // now use the best value! |
Jamie Smith |
0:0c3532738887 | 624 | uint8_t chanBwValue = (bestDecimationIndex << CHAN_BW_ADC_CIC_DECFACT) | (actualBBDecimations[bestDecimationIndex] << CHAN_BW_BB_CIC_DECFACT); |
Jamie Smith |
0:0c3532738887 | 625 | writeRegister(Register::CHAN_BW, chanBwValue); |
Jamie Smith |
0:0c3532738887 | 626 | |
Jamie Smith |
0:0c3532738887 | 627 | // also set DVGA_GAIN, which depends on bandwidth |
Jamie Smith |
0:0c3532738887 | 628 | uint8_t mdmCfg1Value = readRegister(Register::MDMCFG1); |
Jamie Smith |
0:0c3532738887 | 629 | |
Jamie Smith |
0:0c3532738887 | 630 | mdmCfg1Value &= ~(0b11 << MDMCFG1_DVGA_GAIN); |
Jamie Smith |
0:0c3532738887 | 631 | if(bandwidthHz >= 100000) |
Jamie Smith |
0:0c3532738887 | 632 | { |
Jamie Smith |
0:0c3532738887 | 633 | mdmCfg1Value |= 1 << MDMCFG1_DVGA_GAIN; |
Jamie Smith |
0:0c3532738887 | 634 | } |
Jamie Smith |
0:0c3532738887 | 635 | |
Jamie Smith |
0:0c3532738887 | 636 | writeRegister(Register::MDMCFG1, mdmCfg1Value); |
Jamie Smith |
0:0c3532738887 | 637 | |
Jamie Smith |
1:98af824b145e | 638 | // also set MDMCFG0.DATA_FILTER_EN, which should be 0b11 iff bandwidth / symbol rate > 10 |
Jamie Smith |
1:98af824b145e | 639 | uint8_t mdmCfg0Val = readRegister(Register::MDMCFG0); |
Jamie Smith |
1:98af824b145e | 640 | |
Jamie Smith |
1:98af824b145e | 641 | if(bandwidthHz / symbolRateSps > 10.0f) |
Jamie Smith |
1:98af824b145e | 642 | { |
Jamie Smith |
1:98af824b145e | 643 | mdmCfg0Val |= 0b11 << MDMCFG0_DATA_FILTER_EN; |
Jamie Smith |
1:98af824b145e | 644 | } |
Jamie Smith |
1:98af824b145e | 645 | else |
Jamie Smith |
1:98af824b145e | 646 | { |
Jamie Smith |
1:98af824b145e | 647 | mdmCfg0Val &= ~(0b11 << MDMCFG0_DATA_FILTER_EN); |
Jamie Smith |
1:98af824b145e | 648 | } |
Jamie Smith |
1:98af824b145e | 649 | |
Jamie Smith |
1:98af824b145e | 650 | writeRegister(Register::MDMCFG0, mdmCfg0Val); |
Jamie Smith |
1:98af824b145e | 651 | |
Jamie Smith |
0:0c3532738887 | 652 | // finally, we need to set RX_CONFIG_LIMITATION. It's not exactly clear what this does, but its setting changes |
Jamie Smith |
0:0c3532738887 | 653 | // based on the filter BW. |
Jamie Smith |
0:0c3532738887 | 654 | uint8_t syncCfg0Value = readRegister(Register::SYNC_CFG0); |
Jamie Smith |
0:0c3532738887 | 655 | |
Jamie Smith |
0:0c3532738887 | 656 | if(symbolRateSps < bandwidthHz / 2 || bandwidthHz > 1500000) |
Jamie Smith |
0:0c3532738887 | 657 | { |
Jamie Smith |
0:0c3532738887 | 658 | // clear RX_CONFIG_LIMITATION |
Jamie Smith |
0:0c3532738887 | 659 | syncCfg0Value &= ~(1 << SYNC_CFG0_RX_CONFIG_LIMITATION); |
Jamie Smith |
0:0c3532738887 | 660 | } |
Jamie Smith |
0:0c3532738887 | 661 | else |
Jamie Smith |
0:0c3532738887 | 662 | { |
Jamie Smith |
0:0c3532738887 | 663 | // set RX_CONFIG_LIMITATION |
Jamie Smith |
0:0c3532738887 | 664 | syncCfg0Value |= (1 << SYNC_CFG0_RX_CONFIG_LIMITATION); |
Jamie Smith |
0:0c3532738887 | 665 | } |
Jamie Smith |
0:0c3532738887 | 666 | |
Jamie Smith |
0:0c3532738887 | 667 | writeRegister(Register::SYNC_CFG0, syncCfg0Value); |
Jamie Smith |
0:0c3532738887 | 668 | |
Jamie Smith |
0:0c3532738887 | 669 | adcCicDecimation = possibleADCDecimations[bestDecimationIndex]; |
Jamie Smith |
0:0c3532738887 | 670 | |
Jamie Smith |
0:0c3532738887 | 671 | #if CC1200_DEBUG |
Jamie Smith |
0:0c3532738887 | 672 | debugStream->printf("Setting BB decimation to %" PRIu8 " and ADC decimation to %" PRIu8 "\n", |
Jamie Smith |
0:0c3532738887 | 673 | actualBBDecimations[bestDecimationIndex], possibleADCDecimations[bestDecimationIndex]); |
Jamie Smith |
0:0c3532738887 | 674 | debugStream->printf("This yields an actual RX filter BW of %.00f\n", |
Jamie Smith |
0:0c3532738887 | 675 | calcReceiveBandwidth(possibleADCDecimations[bestDecimationIndex], actualBBDecimations[bestDecimationIndex])); |
Jamie Smith |
0:0c3532738887 | 676 | #endif |
Jamie Smith |
0:0c3532738887 | 677 | } |
Jamie Smith |
0:0c3532738887 | 678 | |
Jamie Smith |
0:0c3532738887 | 679 | void CC1200::configureDCFilter(bool enableAutoFilter, uint8_t settlingCfg, uint8_t cutoffCfg) |
Jamie Smith |
0:0c3532738887 | 680 | { |
Jamie Smith |
0:0c3532738887 | 681 | uint8_t dcfiltCfg = 0; |
Jamie Smith |
0:0c3532738887 | 682 | |
Jamie Smith |
0:0c3532738887 | 683 | if(!enableAutoFilter) |
Jamie Smith |
0:0c3532738887 | 684 | { |
Jamie Smith |
0:0c3532738887 | 685 | // set "freeze coeff" bit |
Jamie Smith |
0:0c3532738887 | 686 | dcfiltCfg |= (1 << DCFILT_CFG_DCFILT_FREEZE_COEFF); |
Jamie Smith |
0:0c3532738887 | 687 | } |
Jamie Smith |
0:0c3532738887 | 688 | |
Jamie Smith |
0:0c3532738887 | 689 | dcfiltCfg |= settlingCfg << DCFILT_CFG_DCFILT_BW_SETTLE; |
Jamie Smith |
0:0c3532738887 | 690 | dcfiltCfg |= cutoffCfg << DCFILT_CFG_DCFILT_BW; |
Jamie Smith |
0:0c3532738887 | 691 | |
Jamie Smith |
0:0c3532738887 | 692 | writeRegister(Register::DCFILT_CFG, dcfiltCfg); |
Jamie Smith |
0:0c3532738887 | 693 | } |
Jamie Smith |
0:0c3532738887 | 694 | |
Jamie Smith |
0:0c3532738887 | 695 | void CC1200::setIQMismatchCompensationEnabled(bool enabled) |
Jamie Smith |
0:0c3532738887 | 696 | { |
Jamie Smith |
0:0c3532738887 | 697 | uint8_t iqicValue = readRegister(Register::IQIC); |
Jamie Smith |
0:0c3532738887 | 698 | |
Jamie Smith |
0:0c3532738887 | 699 | if(enabled) |
Jamie Smith |
0:0c3532738887 | 700 | { |
Jamie Smith |
0:0c3532738887 | 701 | iqicValue |= (1 << IQIC_IQIC_EN); |
Jamie Smith |
0:0c3532738887 | 702 | } |
Jamie Smith |
0:0c3532738887 | 703 | else |
Jamie Smith |
0:0c3532738887 | 704 | { |
Jamie Smith |
0:0c3532738887 | 705 | iqicValue &= ~(1 << IQIC_IQIC_EN); |
Jamie Smith |
0:0c3532738887 | 706 | } |
Jamie Smith |
0:0c3532738887 | 707 | |
Jamie Smith |
0:0c3532738887 | 708 | writeRegister(Register::IQIC, iqicValue); |
Jamie Smith |
0:0c3532738887 | 709 | } |
Jamie Smith |
0:0c3532738887 | 710 | |
Jamie Smith |
0:0c3532738887 | 711 | void CC1200::configureSyncWord(uint32_t syncWord, CC1200::SyncMode mode, uint8_t syncThreshold) |
Jamie Smith |
0:0c3532738887 | 712 | { |
Jamie Smith |
0:0c3532738887 | 713 | // program sync word registers |
Jamie Smith |
0:0c3532738887 | 714 | std::array<uint8_t, 4> syncWordRegisters ={ |
Jamie Smith |
0:0c3532738887 | 715 | static_cast<uint8_t>((syncWord >> 24) & 0xFF), |
Jamie Smith |
0:0c3532738887 | 716 | static_cast<uint8_t>((syncWord >> 16) & 0xFF), |
Jamie Smith |
0:0c3532738887 | 717 | static_cast<uint8_t>((syncWord >> 8) & 0xFF), |
Jamie Smith |
0:0c3532738887 | 718 | static_cast<uint8_t>(syncWord & 0xFF) |
Jamie Smith |
0:0c3532738887 | 719 | }; |
Jamie Smith |
0:0c3532738887 | 720 | writeRegisters(Register::SYNC3, syncWordRegisters); |
Jamie Smith |
0:0c3532738887 | 721 | |
Jamie Smith |
0:0c3532738887 | 722 | // program sync word cfg |
Jamie Smith |
0:0c3532738887 | 723 | writeRegister(Register::SYNC_CFG1, (static_cast<uint8_t>(mode) << SYNC_CFG1_SYNC_MODE) | (syncThreshold << SYNC_CFG1_SYNC_THR)); |
Jamie Smith |
0:0c3532738887 | 724 | } |
Jamie Smith |
0:0c3532738887 | 725 | |
Jamie Smith |
0:0c3532738887 | 726 | bool CC1200::isFSLocked() |
Jamie Smith |
0:0c3532738887 | 727 | { |
Jamie Smith |
0:0c3532738887 | 728 | return readRegister(ExtRegister::FSCAL_CTRL) & (1 << FSCAL_CTRL_LOCK); |
Jamie Smith |
0:0c3532738887 | 729 | } |
Jamie Smith |
0:0c3532738887 | 730 | |
Jamie Smith |
0:0c3532738887 | 731 | void CC1200::configurePreamble(uint8_t preambleLengthCfg, uint8_t preambleFormatCfg) |
Jamie Smith |
0:0c3532738887 | 732 | { |
Jamie Smith |
0:0c3532738887 | 733 | uint8_t preambleCfg1 = 0; |
Jamie Smith |
0:0c3532738887 | 734 | preambleCfg1 |= preambleLengthCfg << PREAMBLE_CFG1_NUM_PREAMBLE; |
Jamie Smith |
0:0c3532738887 | 735 | preambleCfg1 |= preambleFormatCfg << PREAMBLE_CFG1_PREAMBLE_WORD; |
Jamie Smith |
0:0c3532738887 | 736 | writeRegister(Register::PREAMBLE_CFG1, preambleCfg1); |
Jamie Smith |
0:0c3532738887 | 737 | } |
Jamie Smith |
0:0c3532738887 | 738 | |
Jamie Smith |
1:98af824b145e | 739 | void CC1200::setPARampRate(uint8_t firstRampLevel, uint8_t secondRampLevel, CC1200::RampTime rampTime) |
Jamie Smith |
1:98af824b145e | 740 | { |
Jamie Smith |
1:98af824b145e | 741 | uint8_t paCfg0Val = 0; |
Jamie Smith |
1:98af824b145e | 742 | paCfg0Val |= (firstRampLevel << PA_CFG0_FIRST_IPL); |
Jamie Smith |
1:98af824b145e | 743 | paCfg0Val |= (secondRampLevel << PA_CFG0_SECOND_IPL); |
Jamie Smith |
1:98af824b145e | 744 | paCfg0Val |= (static_cast<uint8_t>(rampTime) << PA_CFG0_RAMP_SHAPE); |
Jamie Smith |
1:98af824b145e | 745 | |
Jamie Smith |
1:98af824b145e | 746 | writeRegister(Register::PA_CFG0, paCfg0Val); |
Jamie Smith |
1:98af824b145e | 747 | } |
Jamie Smith |
1:98af824b145e | 748 | |
Jamie Smith |
1:98af824b145e | 749 | void CC1200::setAGCReferenceLevel(uint8_t level) |
Jamie Smith |
1:98af824b145e | 750 | { |
Jamie Smith |
1:98af824b145e | 751 | writeRegister(Register::AGC_REF, level); |
Jamie Smith |
1:98af824b145e | 752 | } |
Jamie Smith |
1:98af824b145e | 753 | |
Jamie Smith |
1:98af824b145e | 754 | void CC1200::setAGCSyncBehavior(CC1200::SyncBehavior behavior) |
Jamie Smith |
1:98af824b145e | 755 | { |
Jamie Smith |
1:98af824b145e | 756 | uint8_t agcCfg3Val = readRegister(Register::AGC_CFG3); |
Jamie Smith |
1:98af824b145e | 757 | agcCfg3Val &= ~(0b111 << AGC_CFG3_AGC_SYNC_BEHAVIOUR); |
Jamie Smith |
1:98af824b145e | 758 | agcCfg3Val |= static_cast<uint8_t>(behavior) << AGC_CFG3_AGC_SYNC_BEHAVIOUR; |
Jamie Smith |
1:98af824b145e | 759 | writeRegister(Register::AGC_CFG3, agcCfg3Val); |
Jamie Smith |
1:98af824b145e | 760 | } |
Jamie Smith |
1:98af824b145e | 761 | |
Jamie Smith |
1:98af824b145e | 762 | void CC1200::setAGCGainTable(CC1200::GainTable table, uint8_t minGainIndex, uint8_t maxGainIndex) |
Jamie Smith |
1:98af824b145e | 763 | { |
Jamie Smith |
1:98af824b145e | 764 | uint8_t agcCfg3Val = readRegister(Register::AGC_CFG3); |
Jamie Smith |
1:98af824b145e | 765 | uint8_t agcCfg2Val = readRegister(Register::AGC_CFG2); |
Jamie Smith |
1:98af824b145e | 766 | |
Jamie Smith |
1:98af824b145e | 767 | agcCfg3Val &= ~(0b11111 << AGC_CFG3_AGC_MIN_GAIN); |
Jamie Smith |
1:98af824b145e | 768 | agcCfg2Val &= ~(0b11 << AGC_CFG2_FE_PERFORMANCE_MODE); |
Jamie Smith |
1:98af824b145e | 769 | agcCfg2Val &= ~(0b11111 << AGC_CFG2_AGC_MAX_GAIN); |
Jamie Smith |
1:98af824b145e | 770 | |
Jamie Smith |
1:98af824b145e | 771 | agcCfg3Val |= maxGainIndex << AGC_CFG3_AGC_MIN_GAIN; |
Jamie Smith |
1:98af824b145e | 772 | agcCfg2Val |= static_cast<uint8_t>(table) << AGC_CFG2_FE_PERFORMANCE_MODE; |
Jamie Smith |
1:98af824b145e | 773 | agcCfg2Val |= maxGainIndex << AGC_CFG2_AGC_MAX_GAIN; |
Jamie Smith |
1:98af824b145e | 774 | |
Jamie Smith |
1:98af824b145e | 775 | writeRegister(Register::AGC_CFG3, agcCfg3Val); |
Jamie Smith |
1:98af824b145e | 776 | writeRegister(Register::AGC_CFG2, agcCfg2Val); |
Jamie Smith |
1:98af824b145e | 777 | } |
Jamie Smith |
1:98af824b145e | 778 | |
Jamie Smith |
1:98af824b145e | 779 | void CC1200::setAGCHysteresis(uint8_t hysteresisCfg) |
Jamie Smith |
1:98af824b145e | 780 | { |
Jamie Smith |
1:98af824b145e | 781 | uint8_t agcCfg0Val = readRegister(Register::AGC_CFG0); |
Jamie Smith |
1:98af824b145e | 782 | agcCfg0Val &= ~(0b11 << AGC_CFG0_AGC_HYST_LEVEL); |
Jamie Smith |
1:98af824b145e | 783 | agcCfg0Val |= hysteresisCfg << AGC_CFG0_AGC_HYST_LEVEL; |
Jamie Smith |
1:98af824b145e | 784 | writeRegister(Register::AGC_CFG0, agcCfg0Val); |
Jamie Smith |
1:98af824b145e | 785 | } |
Jamie Smith |
1:98af824b145e | 786 | |
Jamie Smith |
1:98af824b145e | 787 | void CC1200::setAGCSlewRate(uint8_t slewrateCfg) |
Jamie Smith |
1:98af824b145e | 788 | { |
Jamie Smith |
1:98af824b145e | 789 | uint8_t agcCfg0Val = readRegister(Register::AGC_CFG0); |
Jamie Smith |
1:98af824b145e | 790 | agcCfg0Val &= ~(0b11 << AGC_CFG0_AGC_SLEWRATE_LIMIT); |
Jamie Smith |
1:98af824b145e | 791 | agcCfg0Val |= slewrateCfg << AGC_CFG0_AGC_SLEWRATE_LIMIT; |
Jamie Smith |
1:98af824b145e | 792 | writeRegister(Register::AGC_CFG0, agcCfg0Val); |
Jamie Smith |
1:98af824b145e | 793 | } |
Jamie Smith |
1:98af824b145e | 794 | |
Jamie Smith |
0:0c3532738887 | 795 | void CC1200::setIFMixCFG(uint8_t value) |
Jamie Smith |
0:0c3532738887 | 796 | { |
Jamie Smith |
0:0c3532738887 | 797 | uint8_t ifMixCfg = readRegister(ExtRegister::IF_MIX_CFG); |
Jamie Smith |
0:0c3532738887 | 798 | ifMixCfg &= ~(0b111 << IF_MIX_CFG_CMIX_CFG); |
Jamie Smith |
0:0c3532738887 | 799 | ifMixCfg |= (value << IF_MIX_CFG_CMIX_CFG); |
Jamie Smith |
0:0c3532738887 | 800 | writeRegister(ExtRegister::IF_MIX_CFG, ifMixCfg); |
Jamie Smith |
0:0c3532738887 | 801 | } |
Jamie Smith |
0:0c3532738887 | 802 | |
Jamie Smith |
0:0c3532738887 | 803 | uint8_t CC1200::readRegister(CC1200::Register reg) |
Jamie Smith |
0:0c3532738887 | 804 | { |
Jamie Smith |
0:0c3532738887 | 805 | spi.select(); |
Jamie Smith |
0:0c3532738887 | 806 | loadStatusByte(spi.write(CC1200_READ | static_cast<uint8_t>(reg))); |
Jamie Smith |
0:0c3532738887 | 807 | uint8_t regValue = spi.write(0); |
Jamie Smith |
0:0c3532738887 | 808 | spi.deselect(); |
Jamie Smith |
0:0c3532738887 | 809 | |
Jamie Smith |
0:0c3532738887 | 810 | #if CC1200_REGISTER_LEVEL_DEBUG |
Jamie Smith |
0:0c3532738887 | 811 | debugStream->printf("Read register 0x%" PRIx8 " -> 0x%" PRIx8 "\n", static_cast<uint8_t>(reg), regValue); |
Jamie Smith |
0:0c3532738887 | 812 | #endif |
Jamie Smith |
0:0c3532738887 | 813 | |
Jamie Smith |
0:0c3532738887 | 814 | return regValue; |
Jamie Smith |
0:0c3532738887 | 815 | } |
Jamie Smith |
0:0c3532738887 | 816 | |
Jamie Smith |
0:0c3532738887 | 817 | void CC1200::writeRegister(Register reg, uint8_t value) |
Jamie Smith |
0:0c3532738887 | 818 | { |
Jamie Smith |
0:0c3532738887 | 819 | spi.select(); |
Jamie Smith |
0:0c3532738887 | 820 | loadStatusByte(spi.write(CC1200_WRITE | static_cast<uint8_t>(reg))); |
Jamie Smith |
0:0c3532738887 | 821 | spi.write(value); |
Jamie Smith |
0:0c3532738887 | 822 | spi.deselect(); |
Jamie Smith |
0:0c3532738887 | 823 | |
Jamie Smith |
0:0c3532738887 | 824 | #if CC1200_REGISTER_LEVEL_DEBUG |
Jamie Smith |
0:0c3532738887 | 825 | debugStream->printf("Wrote register 0x%" PRIx8 " <- 0x%" PRIx8 "\n", static_cast<uint8_t>(reg), value); |
Jamie Smith |
0:0c3532738887 | 826 | #endif |
Jamie Smith |
0:0c3532738887 | 827 | } |
Jamie Smith |
0:0c3532738887 | 828 | |
Jamie Smith |
0:0c3532738887 | 829 | void CC1200::writeRegisters(CC1200::Register startReg, uint8_t const *values, size_t numRegisters) |
Jamie Smith |
0:0c3532738887 | 830 | { |
Jamie Smith |
0:0c3532738887 | 831 | spi.select(); |
Jamie Smith |
0:0c3532738887 | 832 | loadStatusByte(spi.write(CC1200_WRITE | CC1200_BURST | static_cast<uint8_t>(startReg))); |
Jamie Smith |
0:0c3532738887 | 833 | |
Jamie Smith |
0:0c3532738887 | 834 | for(size_t byteIndex = 0; byteIndex < numRegisters; ++byteIndex) |
Jamie Smith |
0:0c3532738887 | 835 | { |
Jamie Smith |
0:0c3532738887 | 836 | spi.write(values[byteIndex]); |
Jamie Smith |
0:0c3532738887 | 837 | } |
Jamie Smith |
0:0c3532738887 | 838 | spi.deselect(); |
Jamie Smith |
0:0c3532738887 | 839 | |
Jamie Smith |
0:0c3532738887 | 840 | #if CC1200_REGISTER_LEVEL_DEBUG |
Jamie Smith |
0:0c3532738887 | 841 | for(size_t byteIndex = 0; byteIndex < numRegisters; ++byteIndex) |
Jamie Smith |
0:0c3532738887 | 842 | { |
Jamie Smith |
0:0c3532738887 | 843 | debugStream->printf("Wrote register 0x%" PRIx8 " <- 0x%" PRIx8 "\n", |
Jamie Smith |
0:0c3532738887 | 844 | static_cast<uint8_t>(static_cast<uint8_t>(startReg) + byteIndex), |
Jamie Smith |
0:0c3532738887 | 845 | values[byteIndex]); |
Jamie Smith |
0:0c3532738887 | 846 | } |
Jamie Smith |
0:0c3532738887 | 847 | #endif |
Jamie Smith |
0:0c3532738887 | 848 | } |
Jamie Smith |
0:0c3532738887 | 849 | |
Jamie Smith |
0:0c3532738887 | 850 | void CC1200::loadStatusByte(uint8_t status) |
Jamie Smith |
0:0c3532738887 | 851 | { |
Jamie Smith |
0:0c3532738887 | 852 | chipReady = !(status >> 7); |
Jamie Smith |
0:0c3532738887 | 853 | state = static_cast<State>((status >> 4) & 0x7); |
Jamie Smith |
0:0c3532738887 | 854 | |
Jamie Smith |
0:0c3532738887 | 855 | #if CC1200_REGISTER_LEVEL_DEBUG |
Jamie Smith |
0:0c3532738887 | 856 | debugStream->printf("Updated status, state = 0x%" PRIx8 " ready = %s\n", static_cast<uint8_t>(state), chipReady ? "true" : "false"); |
Jamie Smith |
0:0c3532738887 | 857 | #endif |
Jamie Smith |
0:0c3532738887 | 858 | } |
Jamie Smith |
0:0c3532738887 | 859 | |
Jamie Smith |
0:0c3532738887 | 860 | uint8_t CC1200::readRegister(CC1200::ExtRegister reg) |
Jamie Smith |
0:0c3532738887 | 861 | { |
Jamie Smith |
0:0c3532738887 | 862 | spi.select(); |
Jamie Smith |
0:0c3532738887 | 863 | loadStatusByte(spi.write(CC1200_READ | CC1200_EXT_ADDR)); |
Jamie Smith |
0:0c3532738887 | 864 | spi.write(static_cast<uint8_t>(reg)); |
Jamie Smith |
0:0c3532738887 | 865 | uint8_t regValue = spi.write(0); |
Jamie Smith |
0:0c3532738887 | 866 | spi.deselect(); |
Jamie Smith |
0:0c3532738887 | 867 | |
Jamie Smith |
0:0c3532738887 | 868 | #if CC1200_REGISTER_LEVEL_DEBUG |
Jamie Smith |
0:0c3532738887 | 869 | debugStream->printf("Read ext register 0x%" PRIx8 " -> 0x%" PRIx8 "\n", static_cast<uint8_t>(reg), regValue); |
Jamie Smith |
0:0c3532738887 | 870 | #endif |
Jamie Smith |
0:0c3532738887 | 871 | |
Jamie Smith |
0:0c3532738887 | 872 | return regValue; |
Jamie Smith |
0:0c3532738887 | 873 | } |
Jamie Smith |
0:0c3532738887 | 874 | |
Jamie Smith |
0:0c3532738887 | 875 | void CC1200::writeRegister(CC1200::ExtRegister reg, uint8_t value) |
Jamie Smith |
0:0c3532738887 | 876 | { |
Jamie Smith |
0:0c3532738887 | 877 | spi.select(); |
Jamie Smith |
0:0c3532738887 | 878 | loadStatusByte(spi.write(CC1200_WRITE | CC1200_EXT_ADDR)); |
Jamie Smith |
0:0c3532738887 | 879 | spi.write(static_cast<uint8_t>(reg)); |
Jamie Smith |
0:0c3532738887 | 880 | spi.write(value); |
Jamie Smith |
0:0c3532738887 | 881 | spi.deselect(); |
Jamie Smith |
0:0c3532738887 | 882 | |
Jamie Smith |
0:0c3532738887 | 883 | #if CC1200_REGISTER_LEVEL_DEBUG |
Jamie Smith |
0:0c3532738887 | 884 | debugStream->printf("Wrote ext register 0x%" PRIx8 " <- 0x%" PRIx8 "\n", static_cast<uint8_t>(reg), value); |
Jamie Smith |
0:0c3532738887 | 885 | #endif |
Jamie Smith |
0:0c3532738887 | 886 | } |
Jamie Smith |
0:0c3532738887 | 887 | |
Jamie Smith |
0:0c3532738887 | 888 | void CC1200::writeRegisters(CC1200::ExtRegister startReg, uint8_t const *values, size_t numRegisters) |
Jamie Smith |
0:0c3532738887 | 889 | { |
Jamie Smith |
0:0c3532738887 | 890 | spi.select(); |
Jamie Smith |
0:0c3532738887 | 891 | loadStatusByte(spi.write(CC1200_WRITE | CC1200_BURST | CC1200_EXT_ADDR)); |
Jamie Smith |
0:0c3532738887 | 892 | spi.write(static_cast<uint8_t>(startReg)); |
Jamie Smith |
0:0c3532738887 | 893 | |
Jamie Smith |
0:0c3532738887 | 894 | for(size_t byteIndex = 0; byteIndex < numRegisters; ++byteIndex) |
Jamie Smith |
0:0c3532738887 | 895 | { |
Jamie Smith |
0:0c3532738887 | 896 | spi.write(values[byteIndex]); |
Jamie Smith |
0:0c3532738887 | 897 | } |
Jamie Smith |
0:0c3532738887 | 898 | spi.deselect(); |
Jamie Smith |
0:0c3532738887 | 899 | |
Jamie Smith |
0:0c3532738887 | 900 | #if CC1200_REGISTER_LEVEL_DEBUG |
Jamie Smith |
0:0c3532738887 | 901 | for(size_t byteIndex = 0; byteIndex < numRegisters; ++byteIndex) |
Jamie Smith |
0:0c3532738887 | 902 | { |
Jamie Smith |
0:0c3532738887 | 903 | debugStream->printf("Wrote extended register 0x%" PRIx8 " <- 0x%" PRIx8 "\n", |
Jamie Smith |
0:0c3532738887 | 904 | static_cast<uint8_t>(static_cast<uint8_t>(startReg) + byteIndex), |
Jamie Smith |
0:0c3532738887 | 905 | values[byteIndex]); |
Jamie Smith |
0:0c3532738887 | 906 | } |
Jamie Smith |
0:0c3532738887 | 907 | #endif |
Jamie Smith |
0:0c3532738887 | 908 | } |
Jamie Smith |
0:0c3532738887 | 909 | |
Jamie Smith |
0:0c3532738887 | 910 | void CC1200::sendCommand(CC1200::Command command) |
Jamie Smith |
0:0c3532738887 | 911 | { |
Jamie Smith |
0:0c3532738887 | 912 | spi.select(); |
Jamie Smith |
0:0c3532738887 | 913 | loadStatusByte(spi.write(static_cast<uint8_t>(command))); |
Jamie Smith |
0:0c3532738887 | 914 | spi.deselect(); |
Jamie Smith |
0:0c3532738887 | 915 | |
Jamie Smith |
0:0c3532738887 | 916 | #if CC1200_REGISTER_LEVEL_DEBUG |
Jamie Smith |
0:0c3532738887 | 917 | debugStream->printf("Sent SPI command 0x%" PRIx8 "\n", static_cast<uint8_t>(command)); |
Jamie Smith |
0:0c3532738887 | 918 | #endif |
Jamie Smith |
0:0c3532738887 | 919 | } |
Jamie Smith |
0:0c3532738887 | 920 | |
Jamie Smith |
0:0c3532738887 | 921 | uint8_t CC1200::readRXFIFOByte(uint8_t address) |
Jamie Smith |
0:0c3532738887 | 922 | { |
Jamie Smith |
0:0c3532738887 | 923 | spi.select(); |
Jamie Smith |
0:0c3532738887 | 924 | loadStatusByte(spi.write(CC1200_READ | CC1200_MEM_ACCESS)); |
Jamie Smith |
2:2a447e8e50b8 | 925 | spi.write(CC1200_RX_FIFO | address); |
Jamie Smith |
2:2a447e8e50b8 | 926 | int value = spi.write(0); |
Jamie Smith |
0:0c3532738887 | 927 | spi.deselect(); |
Jamie Smith |
0:0c3532738887 | 928 | |
Jamie Smith |
0:0c3532738887 | 929 | #if CC1200_REGISTER_LEVEL_DEBUG |
Jamie Smith |
2:2a447e8e50b8 | 930 | debugStream->printf("Read RX FIFO[0x%" PRIx8 "]: 0x%x 0x%x -> 0x%" PRIx8 "\n", static_cast<uint8_t>(address), CC1200_READ | CC1200_MEM_ACCESS, CC1200_RX_FIFO | address, value); |
Jamie Smith |
0:0c3532738887 | 931 | #endif |
Jamie Smith |
0:0c3532738887 | 932 | |
Jamie Smith |
0:0c3532738887 | 933 | return value; |
Jamie Smith |
0:0c3532738887 | 934 | } |
Jamie Smith |
0:0c3532738887 | 935 | |
Jamie Smith |
0:0c3532738887 | 936 | |
Jamie Smith |
1:98af824b145e | 937 | #pragma clang diagnostic pop |