M17 Project Wiki

CC1200 M17

CC1200 supports M17. Both receive and transmit modes have been tested using CC1200EMK-420-470 evaluation board.

Note - apply a solder blob to pins 1 and 2 to pull up the RESET_N pin.

The key is reading/writing to the CFM_TX_DATA_IN / CFM_TX_DATA_OUT registers at a rate of 48kHz.

Steps to follow to get it working

  1. Give it a few milliseconds to power up, then send a reset command - 0x30 (1-byte write)
  2. Wait 100ms - probably the delay can be a lot less than that
  3. Use the config below to set up CC1200
  4. Send either 0x34 or 0x35 command (1-byte) to enable RX or TX mode
  5. If necessary (eg. using a simple XO instead of proper TCXO, caesium reference or hydrogen maser) - apply frequency compensation by writing int16_t value to register 0x2F0A. See the code at the end of this subchapter.
  6. Disable auto address increment for baseband SPI data tranfer - write 0 to register 0x2F06
  7. Start sending baseband samples in int8_t format to the 0x2F7E register - don’t pull SPI_CS line high after the first baseband byte, do it after all bytes are sent. First, send a 3-byte “start”:
tx_data[0]=0x2F|0x40;
tx_data[1]=0x7E;
tx_data[2]=*((uint8_t*)&baseband);
SPI_CS_down();
SPI_Send(tx_data, 3); //3-byte SPI transfer
//repeat the following lines:
while(something)
{
   tx_data[0]=*((uint8_t*)&baseband); //set accordingly
   SPI_Send(tx_data, 1); //1-byte transfer - continue until there are no baseband samples left
}
SPI_CS_up();
//TX done

The 0x40 sets burst write mode. Along with (6) we can actually keep reading/writing to the same register without the need of transferring address byte every time.

The receive mode works in the same way, but instead, the address of the register is 0x2F7D and we have to read it. The range is full int8_t. Just as for TX, don’t re-assert SPI_CS until the RX is complete.

tx_data[0]=0x2F|0xC0; //set burst read mode
tx_data[1]=0x7D;
tx_data[2]=0;

Samples for transmission have to be limited to -64..+64 range. Use the code below as a reference. The input is a 48kHz, mono, S16_LE .raw audio file, eg. generated with m17-cxx-demod tool.

#include <stdio.h>
#include <stdint.h>

FILE *fin, *fout;
int16_t r;
int8_t val;

int main(void)
{
	fin = fopen("M17_test_baseband.raw", "rb");
	fout = fopen("M17_output.txt", "wb");
	
	fprintf(fout, "int8_t baseband[24000]=\n{\n");
	
	for(uint16_t i=0; i<24000*2; i+=2)
	{
		fread(&r, 2, 1, fin);
		val=r/512.0;
		fprintf(fout, "\t0x%02X,\n", *(uint8_t*)&val);
	}
	
	fprintf(fout, "}");
	
	fclose(fin);
	fclose(fout);
	
	return 0;
}

Usually, there’s some frequency offset at the XO/TCXO. To compensate for that, use the following code:

int16_t offset=210;  //set accordingly
tx_data[0]=0x2F|0x40; //0x40 stands for burst mode
tx_data[1]=0x0A; //0x2F0A is where the freq offset register is
tx_data[2]=*((uint8_t*)&offset+1);
tx_data[3]=*((uint8_t*)&offset);

SPI_CS_down();
SPI_Send(tx_data, 4);
SPI_CS_up();

Sample M17 config for 435MHz TX/RX

const uint8_t cc1200_rx_settings[50*3] =
{
	0x00, 0x01, 0x08,
	0x00, 0x03, 0x09,
	0x00, 0x08, 0x1F,
	0x00, 0x0A, 0x9F, //RX bw
	0x00, 0x0B, 0x00,
	0x00, 0x0C, 0x5D,
	0x00, 0x0D, 0x00,
	0x00, 0x0E, 0x8A,
	0x00, 0x0F, 0xCB,
	0x00, 0x10, 0xAC,
	0x00, 0x11, 0x00,
	0x00, 0x12, 0x45,
	0x00, 0x13, 0x3F, //symbol rate 2
	0x00, 0x14, 0x75, //symbol rate 1
	0x00, 0x15, 0x10, //symbol rate 0
	0x00, 0x16, 0x37,
	0x00, 0x17, 0xEC,
	0x00, 0x19, 0x11,
	0x00, 0x1B, 0x51,
	0x00, 0x1C, 0x87,
	0x00, 0x1D, 0x00,
	0x00, 0x20, 0x14,
	0x00, 0x26, 0x03,
	0x00, 0x27, 0x00,
	0x00, 0x28, 0x20,
	0x00, 0x2B, 0x3F,
	0x00, 0x2E, 0xFF,
	0x2F, 0x00, 0x1C,
	0x2F, 0x01, 0x02, //AFC, 0x22 - on, 0x02 - off
	0x2F, 0x05, 0x0D,
	0x2F, 0x0C, 0x57, //freq round((float)435000000/5000000*(1<<16))
	0x2F, 0x0D, 0x00, //freq
	0x2F, 0x0E, 0x00, //freq
	0x2F, 0x10, 0xEE,
	0x2F, 0x11, 0x10,
	0x2F, 0x12, 0x07,
	0x2F, 0x13, 0xAF,
	0x2F, 0x16, 0x40,
	0x2F, 0x17, 0x0E,
	0x2F, 0x19, 0x03,
	0x2F, 0x1B, 0x33,
	0x2F, 0x1D, 0x17,
	0x2F, 0x1F, 0x00,
	0x2F, 0x20, 0x6E,
	0x2F, 0x21, 0x1C,
	0x2F, 0x22, 0xAC,
	0x2F, 0x27, 0xB5,
	0x2F, 0x32, 0x0E,
	0x2F, 0x36, 0x03,
	0x2F, 0x91, 0x08,
};

const uint8_t cc1200_tx_settings[50*3] =
{
	0x00, 0x01, 0x08,
	0x00, 0x03, 0x09,
	0x00, 0x08, 0x1F,
	0x00, 0x0A, 0x06, //deviation
	0x00, 0x0B, 0x01, //deviation, LSB - exponent
	0x00, 0x0C, 0x5D,
	0x00, 0x0D, 0x00,
	0x00, 0x0E, 0x8A,
	0x00, 0x0F, 0xCB,
	0x00, 0x10, 0xAC,
	0x00, 0x11, 0x00,
	0x00, 0x12, 0x45,
	0x00, 0x13, 0x83, //symbol rate 2 - 24kSa/s
	0x00, 0x14, 0xA9, //symbol rate 1
	0x00, 0x15, 0x2A, //symbol rate 0
	0x00, 0x16, 0x37,
	0x00, 0x17, 0xEC,
	0x00, 0x19, 0x11,
	0x00, 0x1B, 0x51,
	0x00, 0x1C, 0x87,
	0x00, 0x1D, 0x00,
	0x00, 0x20, 0x14,
	0x00, 0x26, 0x03,
	0x00, 0x27, 0x00,
	0x00, 0x28, 0x20,
	0x00, 0x2B, 0x09, //power (0x01..0x3F)
	0x00, 0x2E, 0xFF,
	0x2F, 0x00, 0x1C,
	0x2F, 0x01, 0x22,
	0x2F, 0x05, 0x09, //16x upsampler, CFM enable
	0x2F, 0x0C, 0x57, //freq 435M = 0x570000
	0x2F, 0x0D, 0x00, //freq
	0x2F, 0x0E, 0x00, //freq
	0x2F, 0x10, 0xEE,
	0x2F, 0x11, 0x10,
	0x2F, 0x12, 0x07,
	0x2F, 0x13, 0xAF,
	0x2F, 0x16, 0x40,
	0x2F, 0x17, 0x0E,
	0x2F, 0x19, 0x03,
	0x2F, 0x1B, 0x33,
	0x2F, 0x1D, 0x17,
	0x2F, 0x1F, 0x00,
	0x2F, 0x20, 0x6E,
	0x2F, 0x21, 0x1C,
	0x2F, 0x22, 0xAC,
	0x2F, 0x27, 0xB5,
	0x2F, 0x32, 0x0E,
	0x2F, 0x36, 0x03,
	0x2F, 0x91, 0x08,
};

Settings for SmartRF Studio 7. Note - the symbol rate is set to 3ksps to obtain 48kHz sample rate (it’s oversampled by 16 internally). The deviation set in SmartRF Studio is too low for TX, it corresponds to 0x9F value in the DEVIATION_M register, which should hold a value of 0xC8 (as shown in the code above). For RX, a value of 0x9F is fine.

RF Studio

Results

CC1200 transmission at 435 MHz decoded with SDR++:

SDR++

M17 RF signal received by CC1200 (CFM_RX_DATA_OUT register output fed to DAC):

M17 signal transmitted by CC1200: