#include #if defined(LINUX_ARCH_RASPBERRYPI) #include #include #elif defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) #include #endif #include "cc1101.h" static const uint8_t cc1101_init[] = { // IDX NAME RESET COMMENT 0x2E, // 00 IOCFG2 29 Tri-State 0x2E, // 01 IOCFG1 Tri-State 0x2E, // 02 IOCFG0 3F GDO0 for input 0x47, // 03 FIFOTHR RX filter bandwidth = 325 kHz, FIFOTHR = 0x47 0xD3, // 04 SYNC1 0x91, // 05 SYNC0 0x3D, // 06 PKTLEN 0F 0x04, // 07 PKTCTRL1 0x32, // 08 PKTCTRL0 45 0x00, // 09 ADDR 0x00, // 0A CHANNR 0x06, // 0B FSCTRL1 0F 152kHz IF Frquency 0x00, // 0C FSCTRL0 0x10, // 0D FREQ2 1E Freq #12 Reg Pos 0C 0xB0, // 0E FREQ1 C4 Reg Pos 0D 0x71, // 0F FREQ0 EC Reg Pos 0E 0x57, // 10 MDMCFG4 8C bWidth 325kHz 0xC4, // 11 MDMCFG3 22 DataRate 0x30, // 12 MDMCFG2 02 Modulation: ASK 0x23, // 13 MDMCFG1 22 0xb9, // 14 MDMCFG0 F8 ChannelSpace: 350kHz 0x00, // 15 DEVIATN 47 0x07, // 16 MCSM2 07 0x00, // 17 MCSM1 30 Bit 3:2 RXOFF_MODE: Select what should happen when a packet has been received: 0 = IDLE 3 = Stay in RX #### 0x18, // 18 MCSM0 04 Calibration: RX/TX->IDLE 0x14, // 19 FOCCFG 36 0x6C, // 1A BSCFG 0x07, // 1B AGCCTRL2 03 42 dB instead of 33dB 0x00, // 1C AGCCTRL1 40 0x91, // 1D AGCCTRL0 91 8dB decision boundery 0x87, // 1E WOREVT1 0x6B, // 1F WOREVT0 0xF8, // 20 WORCTRL 0xB6, // 21 FREND1 B6 RX filter bandwidth > 101 kHz, FREND1 = 0xB6 0x11, // 22 FREND0 16 0x11 for no PA ramping 0xE9, // 23 FSCAL3 A9 E9 ?? 0x2A, // 24 FSCAL2 0A 0x00, // 25 FSCAL1 20 19 ?? 0x1F, // 26 FSCAL0 0D 0x41, // 27 RCCTRL1 0x00, // 28 RCCTRL0 }; static const uint8_t patable_power_315[8] = { 0x17, 0x1D, 0x26, 0x69, 0x51, 0x86, 0xCC, 0xC3 }; static const uint8_t patable_power_434[8] = { 0x6C, 0x1C, 0x06, 0x3A, 0x51, 0x85, 0xC8, 0xC0 }; static const uint8_t patable_power_868[8] = { 0x00, 0x17, 0x1D, 0x26, 0x50, 0x86, 0xCD, 0xC0 }; static const uint8_t patable_power_915[8] = { 0x0B, 0x1B, 0x6D, 0x67, 0x50, 0x85, 0xC9, 0xC1 }; // 5 dB default value for factory reset static const uint8_t patable_power_ook[8] = { 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; enum MarcState : uint8_t { STATE_SLEEP = 0x00, STATE_IDLE = 0x01, STATE_XOFF = 0x02, STATE_VCOON_MC = 0x03, STATE_REGON_MC = 0x04, STATE_MANCAL = 0x05, STATE_VCOON = 0x06, STATE_REGCON = 0x07, STATE_STARTCAL = 0x08, STATE_BWBOOST = 0x09, STATE_FS_LOCK = 0x0A, STATE_IFADCON = 0x0B, STATE_ENDCAL = 0x0C, STATE_RX = 0x0D, STATE_RX_END = 0x0E, STATE_TX_RST = 0x0F, STATE_TXRX_SWITCH = 0x10, STATE_RXFIFO_OVERFLOW = 0x11, STATE_FSTXON = 0x12, STATE_TX = 0x13, STATE_TX_END = 0x14, STATE_RXTX_SWITCH = 0x15, STATE_TXFIFO_UNDERFLOW = 0x16, STATE_MASK = 0x1F, }; CC1101::~CC1101() { if (_init_done) (void)idle(); } #if !defined(LINUX_ARCH_RASPBERRYPI) void CC1101::set_spi_pins(uint8_t mosi, uint8_t miso, uint8_t sck) { _mosi = mosi; _miso = miso; _sck = sck; } void CC1101::select() { digitalWrite(_ss, LOW); } void CC1101::deselect() { digitalWrite(_ss, HIGH); } bool CC1101::wait_MISO() { uint8_t miso_count = 255; while(digitalRead(_miso) == HIGH && miso_count > 0) --miso_count; return (miso_count > 0); } uint8_t CC1101::spi_putc(const uint8_t value) { #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) return SPI.transfer(value); #else SPDR = value; asm volatile("nop"); while(!(SPSR & _BV(SPIF))); return SPDR; #endif } #endif bool CC1101::spi_begin() { #if defined(LINUX_ARCH_RASPBERRYPI) //4MHz SPI speed return (wiringPiSPISetup(0, 4000000) >= 0); #elif defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) pinMode(_ss, OUTPUT); deselect(); SPI.setDataMode(SPI_MODE0); SPI.setBitOrder(MSBFIRST); SPI.begin(); SPI.setClockDivider(SPI_CLOCK_DIV4); #else pinMode(_ss, OUTPUT); deselect(); pinMode(_sck, OUTPUT); pinMode(_mosi, OUTPUT); pinMode(_miso, INPUT); // SPI init SPCR = _BV(SPE) | _BV(MSTR); // SPI speed = CLK/4 digitalWrite(_sck, HIGH); digitalWrite(_mosi, LOW); #endif return true; } void CC1101::spi_write_register(Register spi_instr, uint8_t value) { #if defined(LINUX_ARCH_RASPBERRYPI) uint8_t tbuf[2] = {0}; tbuf[0] = spi_instr | WRITE_SINGLE_BYTE; tbuf[1] = value; wiringPiSPIDataRW(0, tbuf, 2); #else select(); wait_MISO(); spi_putc(spi_instr | WRITE_SINGLE_BYTE); spi_putc(value); deselect(); #endif } uint8_t CC1101::spi_read_register(Register spi_instr) { #if defined(LINUX_ARCH_RASPBERRYPI) uint8_t rbuf[2] = {0}; rbuf[0] = spi_instr | READ_SINGLE_BYTE; wiringPiSPIDataRW(0, rbuf, 2); return rbuf[1]; #else select(); wait_MISO(); spi_putc(spi_instr | READ_SINGLE_BYTE); uint8_t ret = spi_putc(0x00); deselect(); return ret; #endif } uint8_t CC1101::spi_write_strobe(Strobe spi_instr) { #if defined(LINUX_ARCH_RASPBERRYPI) uint8_t tbuf[1] = {0}; tbuf[0] = spi_instr; wiringPiSPIDataRW(0, tbuf, 1); return tbuf[0]; #else select(); wait_MISO(); uint8_t ret = spi_putc(spi_instr); wait_MISO(); deselect(); return ret; #endif } void CC1101::spi_read_burst(SPI_RW spi_instr, uint8_t *pArr, uint8_t len) { #if defined(LINUX_ARCH_RASPBERRYPI) uint8_t rbuf[len + 1]; rbuf[0] = spi_instr | READ_BURST; wiringPiSPIDataRW(0, rbuf, len + 1); for (uint8_t i = 0; i < len ;i++ ) pArr[i] = rbuf[i + 1]; #else select(); wait_MISO(); spi_putc(spi_instr | READ_BURST); for (uint8_t i = 0; i < len ;i++ ) pArr[i] = spi_putc(0x00); deselect(); #endif } void CC1101::spi_write_burst(SPI_RW spi_instr, const uint8_t *pArr, uint8_t len) { #if defined(LINUX_ARCH_RASPBERRYPI) uint8_t tbuf[len + 1]; tbuf[0] = spi_instr | WRITE_BURST; for (uint8_t i = 0; i < len; i++) tbuf[i + 1] = pArr[i]; wiringPiSPIDataRW(0, tbuf, len + 1); #else select(); wait_MISO(); spi_putc(spi_instr | WRITE_BURST); for (uint8_t i = 0; i < len; i++) spi_putc(pArr[i]); deselect(); #endif } bool CC1101::setup() { #if defined(LINUX_ARCH_RASPBERRYPI) wiringPiSetup(); #endif pinMode(_gdo0, OUTPUT); digitalWrite(_gdo0, LOW); pinMode(_gdo2, INPUT); spi_begin(); reset(); //uint8_t partnum = spi_read_register(PARTNUM); uint8_t version = spi_read_register(VERSION); //checks if valid Chip ID is found. Usualy 0x03 or 0x14. if not -> abort if (version == 0x00 || version == 0xFF) return false; spi_write_burst(WRITE_BURST, cc1101_init, sizeof(cc1101_init)); if (!register_check()) return false; _init_done = true; return true; } bool CC1101::setISM(CC1101::ISM_FREQ ism_freq) { uint8_t freq2, freq1, freq0; const uint8_t *patable = patable_power_ook; switch(ism_freq) { case CC1101::FREQ_315MHZ: freq2 = 0x0C; freq1 = 0x1D; freq0 = 0x89; //patable = patable_power_315; break; case CC1101::FREQ_434MHZ: freq2 = 0x10; freq1 = 0xB0; freq0 = 0x71; //patable = patable_power_434; break; case CC1101::FREQ_868MHZ: freq2 = 0x21; freq1 = 0x65; freq0 = 0x6A; //patable = patable_power_868; break; case CC1101::FREQ_915MHZ: freq2 = 0x23; freq1 = 0x31; freq0 = 0x3B; //patable = patable_power_915; break; /* case CC1101::FREQ_2430MHZ: freq2 = 0x5D; freq1 = 0x76; freq0 = 0x27; //patable = patable_power_2430; break; */ default: return false; } setPatable(patable); // stores the new freq setting for defined ISM band spi_write_register(FREQ2, freq2); spi_write_register(FREQ1, freq1); spi_write_register(FREQ0, freq0); return true; } void CC1101::setPatable(const uint8_t patable[8]) { spi_write_burst(PATABLE_BURST, patable, 8); } bool CC1101::register_check() { return (spi_read_register(PKTCTRL0) == cc1101_init[PKTCTRL0] && spi_read_register(IOCFG2) == cc1101_init[IOCFG2]); } bool CC1101::transmit() { idle(); setGDO0(LOW); spi_write_strobe(STX); uint8_t maxloop = 255; while (--maxloop && (spi_read_register(MARCSTATE) & STATE_MASK) != STATE_TX) delay(1); return (maxloop != 0); } void CC1101::setGDO0(int value) { digitalWrite(_gdo0, value); } bool CC1101::receive() { idle(); spi_write_strobe(SRX); uint8_t maxloop = 255; while (--maxloop && (spi_read_register(MARCSTATE) & STATE_MASK) != STATE_RX) delay(1); return (maxloop != 0); } void CC1101::wakeup() { digitalWrite(_ss, LOW); delayMicroseconds(10); digitalWrite(_ss, HIGH); delayMicroseconds(10); idle(); } void CC1101::powerdown() { idle(); spi_write_strobe(SPWD); } void CC1101::idle() { spi_write_strobe(SIDLE); uint8_t maxloop = 0xff; while (--maxloop && (spi_read_register(MARCSTATE) & STATE_MASK) != STATE_IDLE) delay(1); delayMicroseconds(100); } void CC1101::reset(void) { digitalWrite(_ss, LOW); delayMicroseconds(10); digitalWrite(_ss, HIGH); delayMicroseconds(40); spi_write_strobe(SRES); delay(1); }