/* * spreadspace avr utils * * * Copyright (C) 2013-2015 Christian Pointner * * This file is part of spreadspace avr utils. * * spreadspace avr utils is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * spreadspace avr utils is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with spreadspace avr utils. If not, see . */ #include #include "cc1101.h" #include "cc1101_defines.h" static cc1101_driver_conf_t drv = { .spi_cs_enable = NULL, .spi_cs_disable = NULL, .spi_read_miso = NULL, .spi_write_byte = NULL, .spi_read_byte = NULL, .spi_transfer_byte = NULL, .freq_corr = 1.0 }; /* * internal functions */ static inline void cc1101_spi_wait_rdy(void) { while(drv.spi_read_miso()); // wait for CHP_RDY to go low, TODO: timeout... } static uint8_t cc1101_spi_strobe_command(uint8_t cmd) { if(cmd < CC1101_CMD_MIN || cmd > CC1101_CMD_MAX) return -1; drv.spi_cs_enable(); cc1101_spi_wait_rdy(); uint8_t status = drv.spi_transfer_byte(CC1101_HEADER_COMMAND | cmd); drv.spi_cs_disable(); return status; } static uint8_t cc1101_spi_read_register(uint8_t addr) { if(addr > CC1101_ADDR_MAX) return 0xFF; if(addr > CC1101_REG_RW_MAX) addr |= CC1101_HEADER_READONLY; else addr |= CC1101_HEADER_READ; drv.spi_cs_enable(); cc1101_spi_wait_rdy(); drv.spi_write_byte(addr); uint8_t data = drv.spi_read_byte(); drv.spi_cs_disable(); return data; } static void cc1101_spi_write_register(uint8_t addr, uint8_t data) { if(addr > CC1101_REG_RW_MAX) return; drv.spi_cs_enable(); cc1101_spi_wait_rdy(); drv.spi_write_byte(CC1101_HEADER_WRITE | addr); drv.spi_write_byte(data); drv.spi_cs_disable(); } /* * EXTERNAL INTERFACE */ void cc1101_init(cc1101_driver_conf_t conf) { drv = conf; cc1101_soft_reset(); } void cc1101_reg_init(void) { // TODO: init register depending on mode beacon vs. data } void cc1101_soft_reset(void) { cc1101_spi_strobe_command(CC1101_CMD_SRES); } void cc1101_powerdown(void) { cc1101_spi_strobe_command(CC1101_CMD_SPWD); } void cc1101_idle(void) { cc1101_spi_strobe_command(CC1101_CMD_SIDLE); } void cc1101_osc_off(void) { cc1101_spi_strobe_command(CC1101_CMD_SXOFF); } void cc1101_calibrate(void) { cc1101_spi_strobe_command(CC1101_CMD_SCAL); } void cc1101_rx(void) { cc1101_spi_strobe_command(CC1101_CMD_SRX); } char* cc1101_state_to_string(cc1101_state_t state) { switch(state) { case sleep: return "sleep"; case idle: return "idle"; case xoff: return "xoff"; case mancal: return "mancal"; case fs_wakeup: return "fs_wakeup"; case calibrate: return "calibrate"; case settling: return "settling"; case rx: return "rx"; case txrx_settling: return "txrx_settling"; case rxfifo_overflow: return "rxfifo_overflow"; case fstxon: return "fstxon"; case tx: return "tx"; case rxtx_settling: return "rxtx_settling"; case txfifo_underflow: return "txfifo_underflow"; default: return "unknown"; } } static cc1101_state_t cc1101_marcstate_to_state(uint8_t marcstate) { switch(marcstate) { case CC1101_MARCSTATE_SLEEP: return sleep; case CC1101_MARCSTATE_IDLE: return idle; case CC1101_MARCSTATE_XOFF: return xoff; case CC1101_MARCSTATE_VCOON_MC: case CC1101_MARCSTATE_REGON_MC: case CC1101_MARCSTATE_MANCAL: return mancal; case CC1101_MARCSTATE_VCOON: case CC1101_MARCSTATE_REGON: return fs_wakeup; case CC1101_MARCSTATE_STARTCAL: return calibrate; case CC1101_MARCSTATE_BWBOOST: case CC1101_MARCSTATE_FS_LOCK: case CC1101_MARCSTATE_IFADCON: return settling; case CC1101_MARCSTATE_ENDCAL: return calibrate; case CC1101_MARCSTATE_RX: case CC1101_MARCSTATE_RX_END: case CC1101_MARCSTATE_RX_RST: return rx; case CC1101_MARCSTATE_TXRX_SWITCH: return txrx_settling; case CC1101_MARCSTATE_RXFIFO_OVERFLOW: return rxfifo_overflow; case CC1101_MARCSTATE_FSTXON: return fstxon; case CC1101_MARCSTATE_TX: case CC1101_MARCSTATE_TX_END: return tx; case CC1101_MARCSTATE_RXTX_SWITCH: return rxtx_settling; case CC1101_MARCSTATE_TXFIFO_UNDERFLOW: return txfifo_underflow; default: return unknown; } } cc1101_state_t cc1101_get_state(void) { return cc1101_marcstate_to_state(cc1101_spi_read_register(CC1101_REG_RO_MARCSTATE)); } void cc1101_set_freq_hz(uint32_t hz) { uint32_t freq = (uint32_t)((float)hz / drv.freq_corr); if(freq <= 0x3FFFFF) // TODO: this is only allowed in idle mode cc1101_spi_write_register(CC1101_REG_RW_FREQ0, freq & 0xFF); cc1101_spi_write_register(CC1101_REG_RW_FREQ1, (freq >> 8) & 0xFF); cc1101_spi_write_register(CC1101_REG_RW_FREQ2, (freq >> 16) & 0x3F); } uint32_t cc1101_get_freq_hz(void) { uint32_t freq = 0; freq = cc1101_spi_read_register(CC1101_REG_RW_FREQ2) & 0x3F; freq = freq << 8; freq |= cc1101_spi_read_register(CC1101_REG_RW_FREQ1); freq = freq << 8; freq |= cc1101_spi_read_register(CC1101_REG_RW_FREQ0); return (uint32_t)((float)freq * drv.freq_corr); } static char* cc1101_config_reg_to_string(uint8_t addr) { switch(addr) { case CC1101_REG_RW_IOCFG2: return "IOCFG2"; case CC1101_REG_RW_IOCFG1: return "IOCFG1"; case CC1101_REG_RW_IOCFG0: return "IOCFG0"; case CC1101_REG_RW_FIFOTHR: return "FIFOTHR"; case CC1101_REG_RW_SYNC1: return "SYNC1"; case CC1101_REG_RW_SYNC0: return "SYNC0"; case CC1101_REG_RW_PKTLEN: return "PKTLEN"; case CC1101_REG_RW_PKTCTRL1: return "PKTCTRL1"; case CC1101_REG_RW_PKTCTRL0: return "PKTCTRL0"; case CC1101_REG_RW_ADDR: return "ADDR"; case CC1101_REG_RW_CHANNR: return "CHANNR"; case CC1101_REG_RW_FSCTRL1: return "FSCTRL1"; case CC1101_REG_RW_FSCTRL0: return "FSCTRL0"; case CC1101_REG_RW_FREQ2: return "FREQ2"; case CC1101_REG_RW_FREQ1: return "FREQ1"; case CC1101_REG_RW_FREQ0: return "FREQ0"; case CC1101_REG_RW_MDMCFG4: return "MDMCFG4"; case CC1101_REG_RW_MDMCFG3: return "MDMCFG3"; case CC1101_REG_RW_MDMCFG2: return "MDMCFG2"; case CC1101_REG_RW_MDMCFG1: return "MDMCFG1"; case CC1101_REG_RW_MDMCFG0: return "MDMCFG0"; case CC1101_REG_RW_DEVIATN: return "DEVIATN"; case CC1101_REG_RW_MCSM2: return "MCSM2"; case CC1101_REG_RW_MCSM1: return "MCSM1"; case CC1101_REG_RW_MCSM0: return "MCSM0"; case CC1101_REG_RW_FOCCFG: return "FOCCFG"; case CC1101_REG_RW_BSCFG: return "BSCFG"; case CC1101_REG_RW_AGCCTRL2: return "AGCCTRL2"; case CC1101_REG_RW_AGCCTRL1: return "AGCCTRL1"; case CC1101_REG_RW_AGCCTRL0: return "AGCCTRL0"; case CC1101_REG_RW_WOREVT1: return "WOREVT1"; case CC1101_REG_RW_WOREVT0: return "WOREVT0"; case CC1101_REG_RW_WORCTRL: return "WORCTRL"; case CC1101_REG_RW_FREND1: return "FREND1"; case CC1101_REG_RW_FREND0: return "FREND0"; case CC1101_REG_RW_FSCAL3: return "FSCAL3"; case CC1101_REG_RW_FSCAL2: return "FSCAL2"; case CC1101_REG_RW_FSCAL1: return "FSCAL1"; case CC1101_REG_RW_FSCAL0: return "FSCAL0"; case CC1101_REG_RW_RCCTRL1: return "RCCTRL1"; case CC1101_REG_RW_RCCTRL0: return "RCCTRL0"; case CC1101_REG_RW_FSTEST: return "FSTEST"; case CC1101_REG_RW_PTEST: return "PTEST"; case CC1101_REG_RW_AGCTEST: return "AGCTEST"; case CC1101_REG_RW_TEST2: return "TEST2"; case CC1101_REG_RW_TEST1: return "TEST1"; case CC1101_REG_RW_TEST0: return "TEST0"; default: return "unknown"; } } static char* cc1101_status_reg_to_string(uint8_t addr) { switch(addr) { case CC1101_REG_RO_PARTNUM: return "PARTNUM"; case CC1101_REG_RO_VERSION: return "VERSION"; case CC1101_REG_RO_FREQUEST: return "FREQUEST"; case CC1101_REG_RO_LQI: return "LQI"; case CC1101_REG_RO_RSSI: return "RSSI"; case CC1101_REG_RO_MARCSTATE: return "MARCSTATE"; case CC1101_REG_RO_WORTIME1: return "WORTIME1"; case CC1101_REG_RO_WORTIME0: return "WORTIME0"; case CC1101_REG_RO_PKTSTATUS: return "PKTSTATUS"; case CC1101_REG_RO_VCO_VC_DAC: return "VCO_VC_DAC"; case CC1101_REG_RO_TXBYTES: return "TXBYTES"; case CC1101_REG_RO_RXBYTES: return "RXBYTES"; case CC1101_REG_RO_RCCTRL1_STATUS: return "RCCTRL1_STATUS"; case CC1101_REG_RO_RCCTRL0_STATUS: return "RCCTRL0_STATUS"; default: return "unknown"; } } void cc1101_dump_register(void) { printf("CC1101: register dump\r\n\r\n"); int i; printf(" config (read/write) register:\r\n"); for(i=0; i<=CC1101_REG_RW_MAX; ++i) { uint8_t data = cc1101_spi_read_register(i); // TODO: use burst mode... printf(" 0x%02X (%s): 0x%02X\r\n", i, cc1101_config_reg_to_string(i), data); } printf("\r\n"); printf(" status (read-only) register:\r\n"); for(i=CC1101_REG_RO_MIN; i<=CC1101_REG_RO_MAX; ++i) { uint8_t data = cc1101_spi_read_register(i); printf(" 0x%02X (%s): 0x%02X\r\n", i, cc1101_status_reg_to_string(i), data); } printf("\r\n"); }