cariboulabs-cariboulite/software/libcariboulite/src/rffc507x/rffc507x.c

480 wiersze
14 KiB
C

/*
* This file was derived from code written by HackRF Team:
* 1. 2012 Michael Ossmann
* 2. 2014 Jared Boone <jared@sharebrained.com>
* and was modified by David Michaeli (cariboulabs.co@gmail.com) to
* adapt if for the CaribouLite project running on Linux OS (RPI).
*
* 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.
*/
#ifndef ZF_LOG_LEVEL
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
#endif
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
#define ZF_LOG_TAG "RFFC5072"
#include <stdint.h>
#include <string.h>
#include <math.h>
#include "zf_log/zf_log.h"
#include "rffc507x.h"
#include "rffc507x_regs.h" // private register def macros
#if (defined DEBUG)
#include <stdio.h>
#define LOG printf
#else
#define LOG(x,...)
#endif
#define LO_MAX 5400
#define LO_MAX_HZ (LO_MAX*1e6)
//===========================================================================
// Default register values
static uint16_t rffc507x_regs_default[RFFC507X_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, /* 15h / 21d <== SDI_CTRL - SDI Control */
0x0000, /* 16h / 22d <== GPO - General Purpose Outputs */
0x4900, /* 17 */
0x0281, /* 18 */
0xf00f, /* 19 */
0x0000, /* 1A */
0x0000, /* 1B */
0xc840, /* 1C */
0x1000, /* 1D */
0x0005, /* 1E */
};
//===========================================================================
static inline void rffc507x_reg_commit(rffc507x_st* dev, uint8_t r)
{
//printf("writing reg %d, value: %04X\n", r, dev->rffc507x_regs[r]);
rffc507x_reg_write(dev, r, dev->rffc507x_regs[r]);
}
//===========================================================================
int rffc507x_regs_commit(rffc507x_st* dev)
{
int r;
for (r = 0; r < RFFC507X_NUM_REGS; r++)
{
if ((dev->rffc507x_regs_dirty >> r) & 0x1)
{
rffc507x_reg_commit(dev, r);
}
}
return 0;
}
//===========================================================================
// Set up all registers according to defaults specified in docs.
int rffc507x_init( rffc507x_st* dev,
io_utils_spi_st* io_spi)
{
if (dev == NULL)
{
ZF_LOGE("input dev is NULL");
return -1;
}
ZF_LOGD("Initializing RFFC507x driver");
memcpy(dev->rffc507x_regs, rffc507x_regs_default, sizeof(dev->rffc507x_regs));
dev->rffc507x_regs_dirty = 0x7fffffff;
ZF_LOGD("Setting up device GPIOs");
dev->io_spi = io_spi;
// Configure GPIO pins
io_utils_setup_gpio(dev->reset_pin, io_utils_dir_output, io_utils_pull_up);
// set to known state
rffc507x_reset(dev);
dev->io_spi_handle = io_utils_spi_add_chip(dev->io_spi, dev->cs_pin, 5000000, 0, 0,
io_utils_spi_chip_type_rffc, NULL);
ZF_LOGD("Received spi handle %d", dev->io_spi_handle);
rffc507x_device_id_st did = {0};
rffc507x_device_status_st stat = {0};
rffc507x_readback_status(dev, &did, &stat);
rffc507x_print_dev_id(&did);
// initial setup
// SDI CTRL set of configurations
set_RFFC507X_SIPIN(dev, 1); // ENBL and MODE physical pins are ignores
// controlling their functionality from the 3-wire spi
// interface
set_RFFC507X_ENBL(dev, 0); // The device is disabled
// For the RFFC5072 mixer 2 and register bank PLL2 are normally used
set_RFFC507X_MODE(dev, 1);
// put zeros in freq contol registers
set_RFFC507X_P2N(dev, 0);
set_RFFC507X_P2LODIV(dev, 0);
set_RFFC507X_P2PRESC(dev, 0);
set_RFFC507X_P2VCOSEL(dev, 0);
/*set_RFFC507X_CTMIN(dev, 0);
set_RFFC507X_CTMAX(dev, 127);
set_RFFC507X_P2CTV(dev, 12);
set_RFFC507X_FULLD(dev, 0);*/
set_RFFC507X_P2MIXIDD(dev, 5);
// Others
set_RFFC507X_LDEN(dev, 1);
set_RFFC507X_LDLEV(dev, 1);
set_RFFC507X_BYPAS(dev, 0);
// Write default register values to chip.
int ret = rffc507x_regs_commit(dev);
if (ret < 0)
{
ZF_LOGE("Failed commiting varibles into rffc507X");
return -1;
}
dev->initialized = 1;
return 0;
}
//===========================================================================
void rffc507x_reset(rffc507x_st* dev)
{
io_utils_write_gpio(dev->reset_pin, 0);
io_utils_usleep(10000);
io_utils_write_gpio(dev->reset_pin, 1);
io_utils_usleep(20000);
}
//===========================================================================
int rffc507x_release(rffc507x_st* dev)
{
if (dev == NULL)
{
ZF_LOGE("Device pointer NULL - cannot release");
return -1;
}
if (!dev->initialized)
{
ZF_LOGE("Device not initialized - cannot release");
return 0;
}
dev->initialized = 0;
io_utils_setup_gpio(dev->reset_pin, io_utils_dir_input, io_utils_pull_up);
// Release the SPI device
io_utils_spi_remove_chip(dev->io_spi, dev->io_spi_handle);
ZF_LOGD("Device release completed");
return 0;
}
//===========================================================================
uint16_t rffc507x_reg_read(rffc507x_st* dev, uint8_t r)
{
uint8_t vout = r;
uint16_t vin = 0;
// Readback register is not cached.
if (r == RFFC507X_READBACK_REG)
{
io_utils_spi_transmit(dev->io_spi, dev->io_spi_handle, &vout, (uint8_t*)&vin, 2, io_utils_spi_read);
return vin;
}
// Discard uncommited write when reading. This shouldn't
// happen, and probably has not been tested.
//if ((dev->rffc507x_regs_dirty >> r) & 0x1)
//{
io_utils_spi_transmit(dev->io_spi, dev->io_spi_handle, &vout, (uint8_t*)&(dev->rffc507x_regs[r]),
2, io_utils_spi_read);
//}
return dev->rffc507x_regs[r];
}
//===========================================================================
void rffc507x_reg_write(rffc507x_st* dev, uint8_t r, uint16_t v)
{
dev->rffc507x_regs[r] = v;
uint8_t vout[3] = {0};
vout[0] = r;
*((uint16_t*)&vout[1]) = v;
io_utils_spi_transmit(dev->io_spi, dev->io_spi_handle, vout, NULL, 3, io_utils_spi_write);
RFFC507X_REG_SET_CLEAN(dev, r);
}
//===========================================================================
void rffc507x_disable(rffc507x_st* dev)
{
ZF_LOGD("rfc5071_disable");
set_RFFC507X_ENBL(dev, 0);
rffc507x_regs_commit(dev);
}
//===========================================================================
void rffc507x_enable(rffc507x_st* dev)
{
ZF_LOGD("rfc5071_enable");
set_RFFC507X_ENBL(dev, 1);
rffc507x_regs_commit(dev);
}
//===========================================================================
void rffc507x_calculate_freq_params(double ref_freq_hz, uint8_t lodiv, double fvco, uint8_t fbkdiv,
uint16_t *n, uint16_t *p1nmsb, uint8_t *p1nlsb, double* act_freq_hz)
{
double n_div = fvco / fbkdiv / ref_freq_hz;
*n = (uint16_t)(n_div);
double temp_p1nmsb = ( (double)(1<<16) ) * ( n_div - (double)(*n) );
*p1nmsb = (uint16_t)(temp_p1nmsb) & 0xFFFF;
*p1nlsb = (uint8_t)( round(( temp_p1nmsb - *p1nmsb ) * ((double)(1<<8)))) & 0xFF;
//*p1nlsb = (uint8_t)( (( temp_p1nmsb - *p1nmsb ) * ((double)(1<<8)))) & 0xFF;
uint32_t n_div24_bit = (uint32_t)(round(n_div * (1<<24))) & 0xFFFFFFFF;
//uint32_t n_div24_bit = (uint32_t)((n_div * (1<<24))) & 0xFFFFFFFF;
if (act_freq_hz) *act_freq_hz = (ref_freq_hz * n_div24_bit * fbkdiv) / ((double)(lodiv) * (double)(1<<24));
}
//===========================================================================
int rffc507x_setup_reference_freq(rffc507x_st* dev, double ref_freq_hz)
{
if (ref_freq_hz < 10e6 || ref_freq_hz > 104e6)
{
return -1;
}
dev->ref_freq_hz = ref_freq_hz;
return 0;
}
//===========================================================================
double rffc507x_set_frequency(rffc507x_st* dev, double lo_hz)
{
uint8_t lodiv;
double fvco;
uint8_t fbkdiv;
uint16_t n;
double tune_freq_hz;
uint16_t p1nmsb;
uint8_t p1nlsb;
rffc507x_disable(dev);
// Calculate n_lo
uint8_t n_lo = (uint8_t)log2(LO_MAX_HZ / lo_hz);
lodiv = 1 << n_lo; // lodiv = 2^(n_lo)
fvco = lodiv * lo_hz; // in Hz!
if (fvco > 3200000000.0f)
{
fbkdiv = 4;
set_RFFC507X_PLLCPL(dev, 3);
}
else
{
fbkdiv = 2;
set_RFFC507X_PLLCPL(dev, 2);
}
rffc507x_calculate_freq_params(dev->ref_freq_hz, lodiv, fvco, fbkdiv, &n, &p1nmsb, &p1nlsb, &tune_freq_hz);
//ZF_LOGD("----------------------------------------------------------");
//ZF_LOGD("LO_HZ=%.2f n_lo=%d lodiv=%d", lo_hz, n_lo, lodiv);
//ZF_LOGD("fvco=%.2f fbkdiv=%d n=%d", fvco, fbkdiv, n);
//ZF_LOGD("frac=%d, p1nmsb=%d, p1nlsb=%d, tune_freq_hz=%.2f", p1nmsb<<8 | p1nlsb, p1nmsb, p1nlsb, tune_freq_hz);
// Path 2
set_RFFC507X_P2LODIV(dev, n_lo);
set_RFFC507X_P2N(dev, n);
set_RFFC507X_P2PRESC(dev, fbkdiv >> 1);
//set_RFFC507X_P2VCOSEL(dev, 0);
//set_RFFC507X_AUTO(dev, 1);
set_RFFC507X_P2NMSB(dev, p1nmsb);
set_RFFC507X_P2NLSB(dev, p1nlsb);
rffc507x_regs_commit(dev);
/*if (fbkdiv == 4)
{
// For optimum VCO phase noise the prescaler divider should be set to divide by 2. If the VCO frequency is
// greater than 3.2GHz, it is necessary to set the ratio to 4 to allow the CT_cal algorithm to work.
// After the device is enabled, the divider values can be reprogrammed with the prescaler divider ratio of 2
// and the new n, nummsb, and numlsb values. Taking the previous example of an LO of 314.159265MHz:
fbkdiv = 2;
rffc507x_calculate_freq_params(dev->ref_freq_hz, lodiv, fvco, fbkdiv, &n, &p1nmsb, &p1nlsb, &tune_freq_hz);
//ZF_LOGD("LO_HZ=%.2f n_lo=%d lodiv=%d", lo_hz, n_lo, lodiv);
//ZF_LOGD("fvco=%.2f fbkdiv=%d n=%d", fvco, fbkdiv, n);
//ZF_LOGD("frac=%d, p1nmsb=%d, p1nlsb=%d, tune_freq_hz=%.2f", p1nmsb<<8 | p1nlsb, p1nmsb, p1nlsb, tune_freq_hz);
//ZF_LOGD("----------------------------------------------------------");
// Path 2
set_RFFC507X_P2LODIV(dev, n_lo);
set_RFFC507X_P2N(dev, n);
set_RFFC507X_P2PRESC(dev, fbkdiv >> 1);
set_RFFC507X_P2NMSB(dev, p1nmsb);
set_RFFC507X_P2NLSB(dev, p1nlsb);
rffc507x_regs_commit(dev);
}*/
rffc507x_enable(dev);
return tune_freq_hz;
}
//===========================================================================
void rffc507x_readback(rffc507x_st* dev, uint16_t *readback_buff, int buf_len)
{
if (buf_len > 16) buf_len = 16;
for (int i = 0; i < buf_len; i++)
{
set_RFFC507X_READSEL(dev, i);
rffc507x_regs_commit(dev);
readback_buff[i] = rffc507x_reg_read(dev, RFFC507X_READBACK_REG);
//printf ("READBACK #%d: %04X\n", i, readback_buff[i]);
}
}
//===========================================================================
void rffc507x_readback_status(rffc507x_st* dev, rffc507x_device_id_st* dev_id,
rffc507x_device_status_st* stat)
{
uint16_t *dev_id_int = (uint16_t *)dev_id;
uint16_t *stat_int = (uint16_t *)stat;
if (dev_id_int)
{
set_RFFC507X_READSEL(dev, 0);
rffc507x_regs_commit(dev);
*dev_id_int = rffc507x_reg_read(dev, RFFC507X_READBACK_REG);
//printf("%04X\n", *dev_id_int);
}
if (stat_int)
{
set_RFFC507X_READSEL(dev, 1);
rffc507x_regs_commit(dev);
*stat_int = rffc507x_reg_read(dev, RFFC507X_READBACK_REG);
//printf("%04X\n", *stat_int);
}
}
//===========================================================================
void rffc507x_print_dev_id(rffc507x_device_id_st* dev_id)
{
if (!dev_id) return;
uint16_t *temp = (uint16_t*)dev_id;
ZF_LOGD("RFFC507X DEVID: 0x%04X ID: 0x%04X, Rev: %d (%s)", *temp,
dev_id->device_id, dev_id->device_rev,
dev_id->device_rev==1?"RFFC507x":"RFFC507xA");
}
//===========================================================================
void rffc507x_print_stat(rffc507x_device_status_st* stat)
{
if (!stat) return;
uint16_t *temp = (uint16_t*)stat;
ZF_LOGD("RFFC507X STAT: 0x%04X PLL_LOCK: %d, CT_CAL: %d, KV_CAL: %d, CT_CAL_FAIL: %d",
*temp,
stat->pll_lock, stat->coarse_tune_cal_value,
stat->kv_cal_value, stat->coarse_tune_cal_fail);
}
//===========================================================================
void rffc507x_set_gpo(rffc507x_st* dev, uint8_t gpo)
{
// We set GPO for both paths just in case.
set_RFFC507X_P1GPO(dev, gpo);
set_RFFC507X_P2GPO(dev, gpo);
rffc507x_regs_commit(dev);
}
//===========================================================================
void rffc507x_calibrate(rffc507x_st* dev)
{
// CAL_TIME
set_RFFC507X_WAIT(dev, 1); // If high then the RF sections are not enabled until the PLL calibrations complete
set_RFFC507X_TCT(dev, 31); // Duration of CT acquisition
set_RFFC507X_CTAVG(dev, 3); // Number of samples averaged to compute final value during CT Calibration:
// 0 = average 16 samples;
// 1 = average 32 samples;
// 2 = average 64 samples;
// 3 = average 128 samples
set_RFFC507X_KVAVG(dev, 3); // Number of samples averaged to compute final value during KV compensation
rffc507x_regs_commit(dev);
/*
set_RFFC507X_P2CT (dev, 1); // VCO coarse tune enable, path2 mode
rffc507x_regs_commit(dev);
*/
set_RFFC507X_P1KV(dev, 0);
set_RFFC507X_P2KV(dev, 0);
rffc507x_regs_commit(dev);
}
//===========================================================================
void rffc507x_relock(rffc507x_st* dev)
{
set_RFFC507X_RELOK(dev, 1);
rffc507x_regs_commit(dev);
}
//===========================================================================
void rffc507x_output_lo(rffc507x_st* dev, int state)
{
set_RFFC507X_BYPAS(dev, state);
rffc507x_regs_commit(dev);
}