/* * Copyright 2012 Michael Ossmann * Copyright 2014 Jared Boone * * This file is part of HackRF. * * This program 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 2, or (at your option) * any later version. * * This program 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 this program; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, * Boston, MA 02110-1301, USA. */ /* * 'gcc -DTEST -DDEBUG -O2 -o test rffc5071.c' prints out what test * program would do if it had a real spi library */ /* * The actual part on Jawbreaker is the RFFC5072, not the RFFC5071, but the * RFFC5071 may be installed instead. The only difference between the parts is * that the RFFC5071 includes a second mixer. */ #include #include #include "rffc5071.h" #include "rffc5071_regs.def" // private register def macros #include "hackrf_core.h" /* Default register values. */ static const uint16_t rffc5071_regs_default[RFFC5071_NUM_REGS] = { 0xbefa, /* 00 */ 0x4064, /* 01 */ 0x9055, /* 02 */ 0x2d02, /* 03 */ 0xacbf, /* 04 */ 0xacbf, /* 05 */ 0x0028, /* 06 */ 0x0028, /* 07 */ 0xff00, /* 08 */ 0x8220, /* 09 */ 0x0202, /* 0A */ 0x4800, /* 0B */ 0x1a94, /* 0C */ 0xd89d, /* 0D */ 0x8900, /* 0E */ 0x1e84, /* 0F */ 0x89d8, /* 10 */ 0x9d00, /* 11 */ 0x2a20, /* 12 */ 0x0000, /* 13 */ 0x0000, /* 14 */ 0x0000, /* 15 */ 0x0000, /* 16 */ 0x4900, /* 17 */ 0x0281, /* 18 */ 0xf00f, /* 19 */ 0x0000, /* 1A */ 0x0000, /* 1B */ 0xc840, /* 1C */ 0x1000, /* 1D */ 0x0005, /* 1E */ }; /* Set up all registers according to defaults specified in docs. */ void rffc5071_init(rffc5071_driver_t* const drv) { memcpy(drv->regs, rffc5071_regs_default, sizeof(drv->regs)); drv->regs_dirty = 0x7fffffff; /* Write default register values to chip. */ rffc5071_regs_commit(drv); } /* * Set up pins for GPIO and SPI control, configure SSP peripheral for SPI, and * set our own default register configuration. */ void rffc5071_setup(rffc5071_driver_t* const drv) { gpio_set(drv->gpio_reset); gpio_output(drv->gpio_reset); rffc5071_init(drv); /* initial setup */ /* put zeros in freq contol registers */ set_RFFC5071_P2N(drv, 0); set_RFFC5071_P2LODIV(drv, 0); set_RFFC5071_P2PRESC(drv, 0); set_RFFC5071_P2VCOSEL(drv, 0); set_RFFC5071_P2N(drv, 0); set_RFFC5071_P2LODIV(drv, 0); set_RFFC5071_P2PRESC(drv, 0); set_RFFC5071_P2VCOSEL(drv, 0); set_RFFC5071_P2N(drv, 0); set_RFFC5071_P2LODIV(drv, 0); set_RFFC5071_P2PRESC(drv, 0); set_RFFC5071_P2VCOSEL(drv, 0); /* set ENBL and MODE to be configured via 3-wire interface, * not control pins. */ set_RFFC5071_SIPIN(drv, 1); /* GPOs are active at all times */ set_RFFC5071_GATE(drv, 1); rffc5071_regs_commit(drv); } static uint16_t rffc5071_spi_read(rffc5071_driver_t* const drv, uint8_t r) { (void)drv; uint16_t data[] = { 0x80 | (r & 0x7f), 0xffff }; spi_bus_transfer(drv->bus, data, 2); return data[1]; } static void rffc5071_spi_write(rffc5071_driver_t* const drv, uint8_t r, uint16_t v) { (void)drv; uint16_t data[] = { 0x00 | (r & 0x7f), v }; spi_bus_transfer(drv->bus, data, 2); } uint16_t rffc5071_reg_read(rffc5071_driver_t* const drv, uint8_t r) { /* Readback register is not cached. */ if (r == RFFC5071_READBACK_REG) return rffc5071_spi_read(drv, r); /* Discard uncommited write when reading. This shouldn't * happen, and probably has not been tested. */ if ((drv->regs_dirty >> r) & 0x1) { drv->regs[r] = rffc5071_spi_read(drv, r); }; return drv->regs[r]; } void rffc5071_reg_write(rffc5071_driver_t* const drv, uint8_t r, uint16_t v) { drv->regs[r] = v; rffc5071_spi_write(drv, r, v); RFFC5071_REG_SET_CLEAN(drv, r); } static inline void rffc5071_reg_commit(rffc5071_driver_t* const drv, uint8_t r) { rffc5071_reg_write(drv, r, drv->regs[r]); } void rffc5071_regs_commit(rffc5071_driver_t* const drv) { int r; for(r = 0; r < RFFC5071_NUM_REGS; r++) { if ((drv->regs_dirty >> r) & 0x1) { rffc5071_reg_commit(drv, r); } } } void rffc5071_tx(rffc5071_driver_t* const drv) { set_RFFC5071_ENBL(drv, 0); set_RFFC5071_FULLD(drv, 0); set_RFFC5071_MODE(drv, 1); /* mixer 2 used for both RX and TX */ rffc5071_regs_commit(drv); } void rffc5071_rx(rffc5071_driver_t* const drv) { set_RFFC5071_ENBL(drv, 0); set_RFFC5071_FULLD(drv, 0); set_RFFC5071_MODE(drv, 1); /* mixer 2 used for both RX and TX */ rffc5071_regs_commit(drv); } /* * This function turns on both mixer (full-duplex) on the RFFC5071, but our * current hardware designs do not support full-duplex operation. */ void rffc5071_rxtx(rffc5071_driver_t* const drv) { set_RFFC5071_ENBL(drv, 0); set_RFFC5071_FULLD(drv, 1); /* mixer 1 and mixer 2 (RXTX) */ rffc5071_regs_commit(drv); rffc5071_enable(drv); } void rffc5071_disable(rffc5071_driver_t* const drv) { set_RFFC5071_ENBL(drv, 0); rffc5071_regs_commit(drv); } void rffc5071_enable(rffc5071_driver_t* const drv) { set_RFFC5071_ENBL(drv, 1); rffc5071_regs_commit(drv); } #define LO_MAX 5400 #define REF_FREQ 40 #define FREQ_ONE_MHZ (1000*1000) /* configure frequency synthesizer in integer mode (lo in MHz) */ uint64_t rffc5071_config_synth_int(rffc5071_driver_t* const drv, uint16_t lo) { uint8_t lodiv; uint16_t fvco; uint8_t fbkdiv; uint16_t n; uint64_t tune_freq_hz; uint16_t p1nmsb; uint8_t p1nlsb; /* Calculate n_lo */ uint8_t n_lo = 0; uint16_t x = LO_MAX / lo; while ((x > 1) && (n_lo < 5)) { n_lo++; x >>= 1; } lodiv = 1 << n_lo; fvco = lodiv * lo; /* higher divider and charge pump current required above * 3.2GHz. Programming guide says these values (fbkdiv, n, * maybe pump?) can be changed back after enable in order to * improve phase noise, since the VCO will already be stable * and will be unaffected. */ if (fvco > 3200) { fbkdiv = 4; set_RFFC5071_PLLCPL(drv, 3); } else { fbkdiv = 2; set_RFFC5071_PLLCPL(drv, 2); } uint64_t tmp_n = ((uint64_t)fvco << 29ULL) / (fbkdiv*REF_FREQ) ; n = tmp_n >> 29ULL; p1nmsb = (tmp_n >> 13ULL) & 0xffff; p1nlsb = (tmp_n >> 5ULL) & 0xff; tune_freq_hz = (REF_FREQ * (tmp_n >> 5ULL) * fbkdiv * FREQ_ONE_MHZ) / (lodiv * (1 << 24ULL)); /* Path 2 */ set_RFFC5071_P2LODIV(drv, n_lo); set_RFFC5071_P2N(drv, n); set_RFFC5071_P2PRESC(drv, fbkdiv >> 1); set_RFFC5071_P2NMSB(drv, p1nmsb); set_RFFC5071_P2NLSB(drv, p1nlsb); rffc5071_regs_commit(drv); return tune_freq_hz; } /* !!!!!!!!!!! hz is currently ignored !!!!!!!!!!! */ uint64_t rffc5071_set_frequency(rffc5071_driver_t* const drv, uint16_t mhz) { uint32_t tune_freq; rffc5071_disable(drv); tune_freq = rffc5071_config_synth_int(drv, mhz); rffc5071_enable(drv); return tune_freq; } void rffc5071_set_gpo(rffc5071_driver_t* const drv, uint8_t gpo) { /* We set GPO for both paths just in case. */ set_RFFC5071_P1GPO(drv, gpo); set_RFFC5071_P2GPO(drv, gpo); rffc5071_regs_commit(drv); }