Table of Contents

Programming CC1200

The CC1200 wireless transceiver is connected via the “Serial Peripheral Interface (SPI)” to the Beaglebone Black. Therefore, controlling the CC1200 means sending and receiving data from the SPI. The communication over the SPI is already implemented and the code can be accessed by including the file SPIv1.h. The include-file can be found in the directory /usr/local/include and the corresponding library libspi.so can be found in directory /usr/local/lib.

Controlling the SPI

int spi_init(void)
Before accessing the SPI, the function spi_init must be called. spi_init initializes the SPI. On success, the function will return true.

void spi_shutdown(void)
The function spi_shutdown does the opposite of function spi_init and disables the SPI. Therefore, for every call of function spi_init a corresponding call of function spi_shutdown must be called.

Writing and Reading CC1200 Registers

The functions cc1200_reg_write and cc1200_reg_read are provided to access the registers of the CC1200. You can see the registers in SmartRF Studio in the register view (look at Expert Mode Window). Each register has an address. The register names and its addresses can be found at page 11 to page 13 in the User’s Guide of the CC1200. The description and the structure of each register can be found at page 80ff.

It should be pointed out, that there are two types of registers: (basic) registers and extended registers. The basic registers start at address 0x00 and the extended registers start at address 0x2F. So every time you like to access an extended register, The value 0x2F00 needs ta add to the extended register address. E.g. the address of the extended register FREQ1 (see table 5) in the “User’s Guide” is 0x2F0D.

Writing or reading to/from the address '0x3F' will access the internal FIFO's of the CC1200. Writing will push new data to the transmission FIFO and reading will pop data drop the receive FIFO (a more extended explanation will be given later).

int cc1200_reg_write(int adr, int val)
With the function cc1200_reg_write a single register of the CC1200 will be written. The parameter adr must contain the register address and parameter val must contain the register value. On return, the function will provide the written value.

int cc1200_reg_read(int adr, int *val)
Function cc1200_reg_read will read the content of a single register. Parameter adr must contain the register address. The content of the register will be stored in a variable given by parameter val. In addition the return value will provide also the register value.

Controlling CC1200

Switching on the CC1200 will bring the transmitter into idle state. In idle state, only writing and reading the registers are allowed. Bringing the CC1200 in another state (e.g. receiving) a so called command strobe must be given. To issue a command strobe the function cc1200_cmd will be used.

CC1200 Commands

For possible commands #define statements are provided:

/* command strobes */
#define SRES     0x30 /**< CC1200 in den Ausgangszustand setzen (Chip reset). */
#define SFSTXON  0x31 /**< Schalte den Frequenzsynthesizer ein und kalibriere ihn. */
#define SXOFF    0x32 /**< Gehe in den XOFF Zustand. */
#define SCAL     0x33 /**< Kalibriere Frequenzsynthesizer und schalte ihn aus. */
#define SRX      0x34 /**< Kalibriere Chip und schalte in den Empfangsmodus. */
#define STX      0x35 /**< Schalte in den Sendemodus. */
#define SIDLE    0x36 /**< Gehe in den Ruhezustand. */
#define SAFC     0x37 /**< Führe eine automatische Frequenz-Kompensation (AFC) aus. */
#define SWOR     0x38 /**< Starte die automatische RX polling Sequenz. */
#define SPWD     0x39 /**< Gehe in des SLEEP Mode. */
#define SFRX     0x3A /**< Lösche den RX FIFO. */
#define SFTX     0x3B /**< Lösche den TX FIFO. */
#define SWORRST  0x3C /**< Setze die eWOR Zeit. */
#define SNOP     0x3D  /**< Kein Kommando. Wird benutzt um den Zustand des CC1200 zu ermitteln */

If the CC1200 received a command, he will automatically enter the state or perform the command. Afterwards, he will mostly come back to idle state.

To test, if the CC1200 has entered a new state, the function get_status_cc1200 will be used. get_status_cc1200 will return the last available status of CC1200. A status update is given, by performing a command or reading/writing a register. If CC1200 changes its state between such action, the state will actual. To be sure, to get the actual state, perform a SNOP command before reading the state. The SNOP command does nothing in the chip, but it will update the status. This ensures to get the correct state. If should also be considered, that the CC1200 is very much slower compared to the program executing the get_status_cc1200 command. So, it needs some time until CC1200 has updated its status.

CC1200 status information

For possible status informations an enumeration type hs been defined:

typedef enum {  
        IDLE          = 0, /**< Ruhe-Zustand */
        RX            = 1, /**< im Empfangsmode */
        TX            = 2, /**< im Sendemode */
        FSTXON        = 3, /**< Schneller Sendemode ist bereit */
        CALLIBRATE    = 4, /**< Frequenz Synthesizer wird kalibriert */
        SETTLING      = 5, /**< PLL rastet ein */
        RX_FIFO_ERROR = 6, /**< RX FIFO Über- bzw. Unterlauf */
        TX_FIFO_ERROR = 7, /**< TX FIFO Über- bzw. Unterlauf */
        UNKNOWN       = -1      /**< unbekannter Status */
} CC1200_STATES;

int cc1200_cmd(int cmd)
With function cc1200_cmd you execute a CC1200 command. Parameter cmd determines the command. Possible commands are listed above.

int get_status_cc1200(void)
Function get_status_cc1200 returns the last available status of CC1200. To be sure to obtain the actual status, execute cc1200_cmd(SNOP) before function get_status_cc1200.

char *get_status_cc1200_str(void)
Funktion get_status_cc1200_str does the same as function get_status_cc1200 but returns the status information as a string.

Integrating SmartRF Studio Export Files

As described in Section 8_smartrf), you are using 4C to select a predefined CC1200 register setting and modify this setting to your needs. Afterwards the register settings will be exported. Now, you have to apply theses register setting to the CC1200. This is necessary, because the CC1200 will load default values to its registers after witching on the chip or preforming a reset. This registers have to be reprogrammed to obtain the desired behavior. Reprogramming is done by the use of function cc1200_reg_write.

In the following, we will look at an example:

Open 4C and switch to RX Synchronous Serial Mode of the mode menu. This will load the predefined register setting “Symbolrate 38.4kbps, 2-GFSK, RX BW 100kHz, ETSI Standard (868MHz)” to the CC1200. Now, open the Export Register to C in file menu and export the register values.

The beginning of the file should look like this:

/***************************************************************/
/* C-file generated by CC1200 Control Center                   */
/*                                                             */
/*  Generated at x/x/2021  12:26.17                             */
/***************************************************************/
 
#define  IOCFG3             0x00 
#define  IOCFG2             0x01 
#define  IOCFG1             0x02 
#define  IOCFG0             0x03 
#define  SYNC3              0x04 
#define  SYNC2              0x05 
#define  SYNC1              0x06 
#define  SYNC0              0x07 
#define  SYNC_CFG1          0x08 
#define  SYNC_CFG0          0x09 
#define  DEVIATION_M        0x0A 
#define  MODCFG_DEV_E       0x0B 
#define  DCFILT_CFG         0x0C 
#define  PREAMBLE_CFG1      0X0D 
#define  PREAMBLE_CFG0      0x0E 
#define  IQIC               0x0F 
#define  CHAN_BW            0x10 
#define  MDMCFG1            0x11 
#define  MDMCFG0            0x12 
#define  SYMBOL_RATE2       0x13 
#define  SYMBOL_RATE1       0x14 
#define  SYMBOL_RATE0       0x15 
#define  AGC_REF            0x16 
#define  AGC_CS_THR         0x17 
#define  AGC_GAIN_ADJUST    0x18 
#define  AGC_CFG3           0x19 
#define  AGC_CFG2           0x1A 
#define  AGC_CFG1           0x1B 
#define  AGC_CFG0           0x1C 
#define  FIFO_CFG           0X1D 
#define  DEV_ADDR           0x1E 
#define  SETTLING_CFG       0x1F 
#define  FS_CFG             0x20 
#define  WOR_CFG1           0x21 
#define  WOR_CFG0           0x22 
#define  WOR_EVENT0_MSB     0x23 
#define  WOR_EVENT0_LSB     0x24 
#define  RXDCM_TIME         0x25 
#define  PKT_CFG2           0x26

Transfer this to the BeagleBone Black and include it into your C source code file. Following start programming the CC1200 registers:

cc1200_reg_write(RegSettings[0].adr, RegSettings[0].val); // GPIO3 IO Pin Configuration
cc1200_reg_write(RegSettings[1].adr, RegSettings[2].val); // GPIO2 IO Pin Configuration
 
// ...
 
// Programm the RF frequency
cc1200_reg_write(ExtRegSettings[12].adr, ExtRegSettings[12].val); //FREQ2
cc1200_reg_write(ExtRegSettings[13].adr, ExtRegSettings[13].val); //FREQ1
cc1200_reg_write(ExtRegSettings[14].adr, ExtRegSettings[14].val); //FREQ0
 
// ...
 
// you can program the above using a loop

The above example is not complete, because all registers have to be reprogrammed. It should be pointed out, the reprogramming must be done in idle mode of the CC1200.

Summarize

Your program controlling the CC1200 should probably habe the following structure:

/****************************************************************************/
/*                                                                          */
/* Structure of programs controlling CC1200                                 */
/*                                                                          */
/****************************************************************************/
 
#include <stdio.h>
#include <SPIv1.h> // necessary, otherwise CC1200 prototype are not available
#include "smartrf_CC1200.h" // import register settings
#include "smartrf_adr_CC1200.h" // import register addresses
 
int main (void) {
 
 int adr;
 int val;
 
  // first initialize
  if(spi_init()){
    printf("ERROR: Initialization failed\n");
    return -1;
 
  // do some register reading or writing, 
  // performance commands and get status information
 
  // reset CC1200
  cc1200_cmd(SRES);
 
  // CC1200 is now in idle mode, registers have their default values
  // Reprogram the registers
  cc1200_reg_write(IOCFG2_ADR, SMARTRF_SETTING_IOCFG2);
  cc1200_reg_write(IOCFG0_ADR, SMARTRF_SETTING_IOCFG0);
 
  // ... reprogram the remaining registers 
 
  // Programm the RF frequency
  cc1200_reg_write(FREQ2_ADR, SMARTRF_SETTING_FREQ2);
  cc1200_reg_write(FREQ1_ADR, SMARTRF_SETTING_FREQ1);
  cc1200_reg_write(FREQ0_ADR, SMARTRF_SETTING_FREQ0);
 
  // ... reprogram the remaining registers
 
  cc1200_reg_write(SERIAL_STATUS_ADR, SMARTRF_SETTING_SERIAL_STATUS);  
 
 
  // get status information
  cc1200_cmd(SNOP);
  printf("INFO: Status:%s\n", get_status_cc1200_str());
 
  adr = 0x01;
  // register read
  cc1200_reg_read(adr, &val);
  printf("INFO:read Adr:0x%x Val:0x%x\n", adr, val);
 
  // read extended register
  adr = EXT_ADR | 0x0A;
  cc1200_reg_read(adr, &val);
 
  // shutdown SPI
  spi_shutdown();
 
  return 0;
}

This structure has been used by testing the SPI in the page SPI Library. Feel free to use this template in the directory ~/Beaglebone/SPI_test and modify it to your needs.