kopia lustrzana https://github.com/cariboulabs/cariboulite
480 wiersze
14 KiB
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);
|
|
} |