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 to setRXFilterBandwidth
  • Removed setIFMixCFG() (which takes a byte parameter) and replaced it with setIFCfg(), 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 to setIFCfg().

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

Revision:
1:98af824b145e
Parent:
0:0c3532738887
Child:
2:2a447e8e50b8
--- a/CC1200.cpp	Tue Jun 30 02:26:28 2020 -0700
+++ b/CC1200.cpp	Sun Aug 09 23:39:21 2020 -0700
@@ -1,3 +1,5 @@
+#pragma clang diagnostic push
+#pragma ide diagnostic ignored "readability-magic-numbers"
 //
 // Created by jamie on 3/27/2020.
 //
@@ -283,11 +285,11 @@
 	writeRegister(Register::RFEND_CFG0, rfendCfg0);
 }
 
-void CC1200::setOnTransmitState(CC1200::State state)
+void CC1200::setOnTransmitState(CC1200::State txState)
 {
 	uint8_t rfendCfg0 = readRegister(Register::RFEND_CFG0);
 	rfendCfg0 &= ~(0b11 << RFEND_CFG0_TXOFF_MODE);
-	rfendCfg0 |= getOffModeBits(state) << RFEND_CFG0_TXOFF_MODE;
+	rfendCfg0 |= getOffModeBits(txState) << RFEND_CFG0_TXOFF_MODE;
 	writeRegister(Register::RFEND_CFG0, rfendCfg0);
 }
 
@@ -569,7 +571,7 @@
 	return CC1200_OSC_FREQ / (static_cast<float>(adcDecimation) * static_cast<float>(cicDecimation) * 2);
 }
 
-void CC1200::setRXFilterBandwidth(float bandwidthHz, bool isCC1201)
+void CC1200::setRXFilterBandwidth(float bandwidthHz)
 {
 	// settings that the chip supports
 	const uint8_t possibleADCDecimations[] = {12, 24, 48}; // indexes in this array represent the register value
@@ -629,6 +631,20 @@
 
 	writeRegister(Register::MDMCFG1, mdmCfg1Value);
 
+	// also set MDMCFG0.DATA_FILTER_EN, which should be 0b11 iff bandwidth / symbol rate > 10
+	uint8_t mdmCfg0Val = readRegister(Register::MDMCFG0);
+
+	if(bandwidthHz / symbolRateSps > 10.0f)
+	{
+		mdmCfg0Val |= 0b11 << MDMCFG0_DATA_FILTER_EN;
+	}
+	else
+	{
+		mdmCfg0Val &= ~(0b11 << MDMCFG0_DATA_FILTER_EN);
+	}
+
+	writeRegister(Register::MDMCFG0, mdmCfg0Val);
+
 	// finally, we need to set RX_CONFIG_LIMITATION.  It's not exactly clear what this does, but its setting changes
 	// based on the filter BW.
 	uint8_t syncCfg0Value = readRegister(Register::SYNC_CFG0);
@@ -716,6 +732,62 @@
 	writeRegister(Register::PREAMBLE_CFG1, preambleCfg1);
 }
 
+void CC1200::setPARampRate(uint8_t firstRampLevel, uint8_t secondRampLevel, CC1200::RampTime rampTime)
+{
+	uint8_t paCfg0Val = 0;
+	paCfg0Val |= (firstRampLevel << PA_CFG0_FIRST_IPL);
+	paCfg0Val |= (secondRampLevel << PA_CFG0_SECOND_IPL);
+	paCfg0Val |= (static_cast<uint8_t>(rampTime) << PA_CFG0_RAMP_SHAPE);
+
+	writeRegister(Register::PA_CFG0, paCfg0Val);
+}
+
+void CC1200::setAGCReferenceLevel(uint8_t level)
+{
+	writeRegister(Register::AGC_REF, level);
+}
+
+void CC1200::setAGCSyncBehavior(CC1200::SyncBehavior behavior)
+{
+	uint8_t agcCfg3Val = readRegister(Register::AGC_CFG3);
+	agcCfg3Val &= ~(0b111 << AGC_CFG3_AGC_SYNC_BEHAVIOUR);
+	agcCfg3Val |= static_cast<uint8_t>(behavior) << AGC_CFG3_AGC_SYNC_BEHAVIOUR;
+	writeRegister(Register::AGC_CFG3, agcCfg3Val);
+}
+
+void CC1200::setAGCGainTable(CC1200::GainTable table, uint8_t minGainIndex, uint8_t maxGainIndex)
+{
+	uint8_t agcCfg3Val = readRegister(Register::AGC_CFG3);
+	uint8_t agcCfg2Val = readRegister(Register::AGC_CFG2);
+
+	agcCfg3Val &= ~(0b11111 << AGC_CFG3_AGC_MIN_GAIN);
+	agcCfg2Val &= ~(0b11 << AGC_CFG2_FE_PERFORMANCE_MODE);
+	agcCfg2Val &= ~(0b11111 << AGC_CFG2_AGC_MAX_GAIN);
+
+	agcCfg3Val |= maxGainIndex << AGC_CFG3_AGC_MIN_GAIN;
+	agcCfg2Val |= static_cast<uint8_t>(table) << AGC_CFG2_FE_PERFORMANCE_MODE;
+	agcCfg2Val |= maxGainIndex << AGC_CFG2_AGC_MAX_GAIN;
+
+	writeRegister(Register::AGC_CFG3, agcCfg3Val);
+	writeRegister(Register::AGC_CFG2, agcCfg2Val);
+}
+
+void CC1200::setAGCHysteresis(uint8_t hysteresisCfg)
+{
+	uint8_t agcCfg0Val = readRegister(Register::AGC_CFG0);
+	agcCfg0Val &= ~(0b11 << AGC_CFG0_AGC_HYST_LEVEL);
+	agcCfg0Val |= hysteresisCfg << AGC_CFG0_AGC_HYST_LEVEL;
+	writeRegister(Register::AGC_CFG0, agcCfg0Val);
+}
+
+void CC1200::setAGCSlewRate(uint8_t slewrateCfg)
+{
+	uint8_t agcCfg0Val = readRegister(Register::AGC_CFG0);
+	agcCfg0Val &= ~(0b11 << AGC_CFG0_AGC_SLEWRATE_LIMIT);
+	agcCfg0Val |= slewrateCfg << AGC_CFG0_AGC_SLEWRATE_LIMIT;
+	writeRegister(Register::AGC_CFG0, agcCfg0Val);
+}
+
 void CC1200::setIFMixCFG(uint8_t value)
 {
 	uint8_t ifMixCfg = readRegister(ExtRegister::IF_MIX_CFG);
@@ -846,7 +918,7 @@
 {
 	spi.select();
 	loadStatusByte(spi.write(CC1200_READ | CC1200_MEM_ACCESS));
-	uint8_t value = spi.write(CC1200_TX_FIFO | address);
+	uint8_t value = spi.write(CC1200_RX_FIFO | address);
 	spi.deselect();
 
 #if CC1200_REGISTER_LEVEL_DEBUG
@@ -857,25 +929,4 @@
 }
 
 
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+#pragma clang diagnostic pop
\ No newline at end of file