Jamie Smith / CC1200

Dependents:   CC1200-MorseEncoder CC1200-Examples

Committer:
Jamie Smith
Date:
Tue Jun 30 02:26:28 2020 -0700
Revision:
0:0c3532738887
Child:
1:98af824b145e
Add initial version of driver

Who changed what in which revision?

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