====== 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 [[network_protocol_programming_lab:8_smartrf#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 [[http://www.ti.com/lit/ug/swru346b/swru346b.pdf|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 [[network_protocol_programming_lab: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 #include // 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 [[network_protocol_programming_lab:4_prepareppl#SPI Library]]. Feel free to use this template in the directory ''~/Beaglebone/SPI_test'' and modify it to your needs.