mirror of
https://github.com/greatscottgadgets/hackrf.git
synced 2026-03-04 14:35:54 +01:00
Configure sample rate more precisely
This commit is contained in:
@@ -190,7 +190,7 @@ bool fpga_if_xcvr_selftest(void)
|
||||
max2831_set_frequency(&max283x, 2500000000);
|
||||
|
||||
// Capture 1: 4 Msps, tone at 0.5 MHz, narrowband filter OFF
|
||||
sample_rate_frac_set(4000000, 1);
|
||||
sample_rate_set(4ULL * FP_ONE_MHZ, true);
|
||||
delay_us_at_mhz(1000, 204);
|
||||
if (rx_samples(num_samples, 2000000) == -1) {
|
||||
timeout = true;
|
||||
@@ -213,7 +213,7 @@ bool fpga_if_xcvr_selftest(void)
|
||||
|
||||
// Capture 3: 20 Msps, tone at 5 MHz, narrowband filter OFF
|
||||
fpga_set_tx_nco_pstep(&fpga, 255);
|
||||
sample_rate_frac_set(20000000, 1);
|
||||
sample_rate_set(20ULL * FP_ONE_MHZ, true);
|
||||
narrowband_filter_set(0);
|
||||
delay_us_at_mhz(1000, 204);
|
||||
if (rx_samples(num_samples, 2000000) == -1) {
|
||||
@@ -236,7 +236,7 @@ bool fpga_if_xcvr_selftest(void)
|
||||
&selftest.xcvr_measurements[3]);
|
||||
|
||||
// Restore default settings.
|
||||
sample_rate_set(10000000);
|
||||
sample_rate_set(10ULL * FP_ONE_MHZ, true);
|
||||
rf_path_set_direction(&rf_path, RF_PATH_DIRECTION_OFF);
|
||||
narrowband_filter_set(0);
|
||||
fpga_init(&fpga);
|
||||
|
||||
@@ -446,16 +446,26 @@ static uint32_t gcd(uint32_t u, uint32_t v)
|
||||
return u << s;
|
||||
}
|
||||
|
||||
bool sample_rate_frac_set(uint32_t rate_num, uint32_t rate_denom)
|
||||
/*
|
||||
* Configure clock generator to produce sample clock in units of 1/(2**24) Hz.
|
||||
* Can be called with program=false for a dry run that returns the resultant
|
||||
* frequency without actually configuring the clock generator.
|
||||
*
|
||||
* The clock generator output frequency is:
|
||||
*
|
||||
* fs = 128 * vco / (512 + p1 + p2/p3))
|
||||
*
|
||||
* where p1, p2, and p3 are register values.
|
||||
*
|
||||
* For more information see:
|
||||
* https://www.pa3fwm.nl/technotes/tn42a-si5351-programming.html
|
||||
*/
|
||||
fp_40_24_t sample_rate_set(const fp_40_24_t sample_rate, const bool program)
|
||||
{
|
||||
const uint64_t VCO_FREQ = 800 * 1000 * 1000; /* 800 MHz */
|
||||
uint32_t MSx_P1, MSx_P2, MSx_P3;
|
||||
uint32_t a, b, c;
|
||||
uint32_t rem;
|
||||
|
||||
/* Round to the nearest Hz for display. */
|
||||
uint32_t rate_hz = (rate_num + (rate_denom >> 1)) / rate_denom;
|
||||
hackrf_ui()->set_sample_rate(rate_hz);
|
||||
const fp_40_24_t vco = 800 * FP_ONE_MHZ;
|
||||
uint64_t p1, p2, p3;
|
||||
uint64_t n, d, q;
|
||||
fp_40_24_t remainder, resultant_rate;
|
||||
|
||||
/*
|
||||
* First double the sample rate so that we can produce a clock at twice
|
||||
@@ -463,36 +473,72 @@ bool sample_rate_frac_set(uint32_t rate_num, uint32_t rate_denom)
|
||||
* and it is divided by two in an output divider to produce the actual
|
||||
* AFE clock.
|
||||
*/
|
||||
rate_num *= 2;
|
||||
fp_40_24_t rate = sample_rate * 2;
|
||||
|
||||
/* Find best config */
|
||||
a = (VCO_FREQ * rate_denom) / rate_num;
|
||||
p1 = ((128 * vco) / rate) - 512;
|
||||
if (vco % rate) {
|
||||
/*
|
||||
* Compute numerator (p2) for denominator (p3) matching
|
||||
* fixed-point type.
|
||||
*/
|
||||
n = (128 * vco) - (rate * (p1 + 512));
|
||||
d = rate / FP_ONE_HZ;
|
||||
n += (d / 2);
|
||||
p2 = n / d;
|
||||
p3 = 1 << 24;
|
||||
|
||||
rem = (VCO_FREQ * rate_denom) - (a * rate_num);
|
||||
/* Reduce fraction. */
|
||||
uint32_t g = gcd(p2, p3);
|
||||
p2 /= g;
|
||||
p3 /= g;
|
||||
|
||||
if (!rem) {
|
||||
/* Integer mode */
|
||||
b = 0;
|
||||
c = 1;
|
||||
} else {
|
||||
/* Fractional */
|
||||
uint32_t g = gcd(rem, rate_num);
|
||||
rem /= g;
|
||||
rate_num /= g;
|
||||
|
||||
if (rate_num < (1 << 20)) {
|
||||
/* Perfect match */
|
||||
b = rem;
|
||||
c = rate_num;
|
||||
} else {
|
||||
/* Approximate */
|
||||
c = (1 << 20) - 1;
|
||||
b = ((uint64_t) c * (uint64_t) rem) / rate_num;
|
||||
|
||||
g = gcd(b, c);
|
||||
b /= g;
|
||||
c /= g;
|
||||
/* Convert fraction to valid denominator. */
|
||||
const uint64_t p3_max = 0xfffff;
|
||||
if (p3 > p3_max) {
|
||||
p2 *= p3_max;
|
||||
p2 += (p3 / 2);
|
||||
p2 /= p3;
|
||||
p3 = p3_max;
|
||||
}
|
||||
|
||||
/* Roll over to next p1 to enable integer mode. */
|
||||
if (p2 >= p3) {
|
||||
p1++;
|
||||
p2 = 0;
|
||||
}
|
||||
} else {
|
||||
p2 = 0;
|
||||
}
|
||||
|
||||
/* Maximum: (128 * 2048) - 512 */
|
||||
if (p1 > 0x3fe00) {
|
||||
p1 = 0x3fe00;
|
||||
p2 = 0;
|
||||
}
|
||||
|
||||
if (p2 == 0) {
|
||||
/* Use unity denominator for integer mode. */
|
||||
p3 = 1;
|
||||
n = (vco * 128);
|
||||
d = (p1 + 512);
|
||||
n += (d / 2);
|
||||
resultant_rate = n / d;
|
||||
} else {
|
||||
const uint64_t vco_hz = vco / FP_ONE_HZ;
|
||||
n = p3 * vco_hz * 128;
|
||||
d = p3 * (p1 + 512) + p2;
|
||||
const uint64_t rate_hz = n / d;
|
||||
remainder = (n - (d * rate_hz)) * FP_ONE_HZ;
|
||||
remainder += (d / 2);
|
||||
q = remainder / d;
|
||||
resultant_rate = (rate_hz * FP_ONE_HZ) + q;
|
||||
}
|
||||
|
||||
/* Return MCU sample rate, not AFE clock rate. */
|
||||
resultant_rate = (resultant_rate + 1) / 2;
|
||||
|
||||
if (!program) {
|
||||
return resultant_rate;
|
||||
}
|
||||
|
||||
bool streaming = sgpio_cpld_stream_is_enabled(&sgpio_config);
|
||||
@@ -501,103 +547,13 @@ bool sample_rate_frac_set(uint32_t rate_num, uint32_t rate_denom)
|
||||
sgpio_cpld_stream_disable(&sgpio_config);
|
||||
}
|
||||
|
||||
/* Can we enable integer mode ? */
|
||||
if (a & 0x1 || b) {
|
||||
/* Integer mode can be enabled if p1 is even and p2 is zero. */
|
||||
if (p1 & 0x1 || p2) {
|
||||
si5351c_set_int_mode(&clock_gen, 0, 0);
|
||||
} else {
|
||||
si5351c_set_int_mode(&clock_gen, 0, 1);
|
||||
}
|
||||
|
||||
/* Final MS values */
|
||||
MSx_P1 = 128 * a + (128 * b / c) - 512;
|
||||
MSx_P2 = (128 * b) % c;
|
||||
MSx_P3 = c;
|
||||
|
||||
#ifndef PRALINE
|
||||
if (detected_platform() == BOARD_ID_HACKRF1_R9) {
|
||||
/*
|
||||
* On HackRF One r9 all sample clocks are externally derived
|
||||
* from MS1/CLK1 operating at twice the sample rate.
|
||||
*/
|
||||
si5351c_configure_multisynth(&clock_gen, 1, MSx_P1, MSx_P2, MSx_P3, 0);
|
||||
} else {
|
||||
/*
|
||||
* On other platforms the clock generator produces three
|
||||
* different sample clocks, all derived from multisynth 0.
|
||||
*/
|
||||
/* MS0/CLK0 is the source for the MAX5864/CPLD (CODEC_CLK). */
|
||||
si5351c_configure_multisynth(&clock_gen, 0, MSx_P1, MSx_P2, MSx_P3, 1);
|
||||
|
||||
/* MS0/CLK1 is the source for the CPLD (CODEC_X2_CLK). */
|
||||
si5351c_configure_multisynth(&clock_gen, 1, 0, 0, 0, 0); //p1 doesn't matter
|
||||
|
||||
/* MS0/CLK2 is the source for SGPIO (CODEC_X2_CLK) */
|
||||
si5351c_configure_multisynth(&clock_gen, 2, 0, 0, 0, 0); //p1 doesn't matter
|
||||
}
|
||||
#else
|
||||
/* MS0/CLK0 is the source for the MAX5864/FPGA (AFE_CLK). */
|
||||
si5351c_configure_multisynth(&clock_gen, 0, MSx_P1, MSx_P2, MSx_P3, 1);
|
||||
#endif
|
||||
|
||||
if (streaming) {
|
||||
sgpio_cpld_stream_enable(&sgpio_config);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sample_rate_set(const uint32_t sample_rate_hz)
|
||||
{
|
||||
uint32_t p1 = 4608;
|
||||
uint32_t p2 = 0;
|
||||
uint32_t p3 = 0;
|
||||
|
||||
switch (sample_rate_hz) {
|
||||
case 8000000:
|
||||
p1 = SI_INTDIV(50); // 800MHz / 50 = 16 MHz (SGPIO), 8 MHz (codec)
|
||||
break;
|
||||
|
||||
case 9216000:
|
||||
// 43.40277777777778: a = 43; b = 29; c = 72
|
||||
p1 = 5043;
|
||||
p2 = 40;
|
||||
p3 = 72;
|
||||
break;
|
||||
|
||||
case 10000000:
|
||||
p1 = SI_INTDIV(40); // 800MHz / 40 = 20 MHz (SGPIO), 10 MHz (codec)
|
||||
break;
|
||||
|
||||
case 12288000:
|
||||
// 32.552083333333336: a = 32; b = 159; c = 288
|
||||
p1 = 3654;
|
||||
p2 = 192;
|
||||
p3 = 288;
|
||||
break;
|
||||
|
||||
case 12500000:
|
||||
p1 = SI_INTDIV(32); // 800MHz / 32 = 25 MHz (SGPIO), 12.5 MHz (codec)
|
||||
break;
|
||||
|
||||
case 16000000:
|
||||
p1 = SI_INTDIV(25); // 800MHz / 25 = 32 MHz (SGPIO), 16 MHz (codec)
|
||||
break;
|
||||
|
||||
case 18432000:
|
||||
// 21.70138888889: a = 21; b = 101; c = 144
|
||||
p1 = 2265;
|
||||
p2 = 112;
|
||||
p3 = 144;
|
||||
break;
|
||||
|
||||
case 20000000:
|
||||
p1 = SI_INTDIV(20); // 800MHz / 20 = 40 MHz (SGPIO), 20 MHz (codec)
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifndef PRALINE
|
||||
if (detected_platform() == BOARD_ID_HACKRF1_R9) {
|
||||
/*
|
||||
@@ -614,32 +570,21 @@ bool sample_rate_set(const uint32_t sample_rate_hz)
|
||||
si5351c_configure_multisynth(&clock_gen, 0, p1, p2, p3, 1);
|
||||
|
||||
/* MS0/CLK1 is the source for the CPLD (CODEC_X2_CLK). */
|
||||
si5351c_configure_multisynth(
|
||||
&clock_gen,
|
||||
1,
|
||||
p1,
|
||||
0,
|
||||
1,
|
||||
0); //p1 doesn't matter
|
||||
si5351c_configure_multisynth(&clock_gen, 1, 0, 0, 0, 0); //p1 doesn't matter
|
||||
|
||||
/* MS0/CLK2 is the source for SGPIO (CODEC_X2_CLK) */
|
||||
si5351c_configure_multisynth(
|
||||
&clock_gen,
|
||||
2,
|
||||
p1,
|
||||
0,
|
||||
1,
|
||||
0); //p1 doesn't matter
|
||||
si5351c_configure_multisynth(&clock_gen, 2, 0, 0, 0, 0); //p1 doesn't matter
|
||||
}
|
||||
#else
|
||||
/* MS0/CLK0 is the source for the MAX5864/FPGA (AFE_CLK). */
|
||||
si5351c_configure_multisynth(&clock_gen, 0, p1, p2, p3, 1);
|
||||
|
||||
/* MS0/CLK1 is the source for SCT_CLK (CODEC_X2_CLK). */
|
||||
si5351c_configure_multisynth(&clock_gen, 1, p1, p2, p3, 0);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
if (streaming) {
|
||||
sgpio_cpld_stream_enable(&sgpio_config);
|
||||
}
|
||||
|
||||
return resultant_rate;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -926,7 +871,7 @@ void clock_gen_init(void)
|
||||
/* MS7/CLK7 is unused. */
|
||||
|
||||
/* Set to 10 MHz, the common rate between Jawbreaker and HackRF One. */
|
||||
sample_rate_set(10000000);
|
||||
sample_rate_set(10ULL * FP_ONE_MHZ, true);
|
||||
|
||||
si5351c_set_clock_source(&clock_gen, PLL_SOURCE_XTAL);
|
||||
// soft reset
|
||||
|
||||
@@ -45,6 +45,7 @@ extern "C" {
|
||||
#include "cpld_jtag.h"
|
||||
#include "ice40_spi.h"
|
||||
#include "fpga.h"
|
||||
#include "fixed_point.h"
|
||||
|
||||
/*
|
||||
* SCU PinMux
|
||||
@@ -435,8 +436,7 @@ void enable_1v8_power(void);
|
||||
void disable_1v8_power(void);
|
||||
#endif
|
||||
|
||||
bool sample_rate_frac_set(uint32_t rate_num, uint32_t rate_denom);
|
||||
bool sample_rate_set(const uint32_t sampling_rate_hz);
|
||||
fp_40_24_t sample_rate_set(const fp_40_24_t sample_rate, const bool program);
|
||||
|
||||
clock_source_t activate_best_clock_source(void);
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "platform_detect.h"
|
||||
#include "radio.h"
|
||||
#include "fixed_point.h"
|
||||
#include "hackrf_ui.h"
|
||||
|
||||
#define MIN(x, y) ((x) < (y) ? (x) : (y))
|
||||
#define MAX(x, y) ((x) > (y) ? (x) : (y))
|
||||
@@ -119,46 +120,6 @@ static bool radio_update_direction(radio_t* const radio, uint64_t* bank)
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert sample rate in units of 1/(2**24) Hz to fraction.
|
||||
*/
|
||||
static inline uint64_t frac_sample_rate(const fp_40_24_t rate)
|
||||
{
|
||||
uint64_t num = rate;
|
||||
uint64_t denom = FP_ONE_HZ;
|
||||
while ((num & 1) == 0) {
|
||||
num >>= 1;
|
||||
denom >>= 1;
|
||||
if (denom == 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (denom << 32) | (num & 0xffffffff);
|
||||
}
|
||||
|
||||
static inline uint32_t numerator(const uint64_t frac)
|
||||
{
|
||||
return frac & 0xffffffff;
|
||||
}
|
||||
|
||||
static inline uint32_t denominator(const uint64_t frac)
|
||||
{
|
||||
return frac >> 32;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert fractional sample rate to units of 1/(2**24) Hz.
|
||||
*/
|
||||
static inline fp_40_24_t round_sample_rate(const uint64_t frac)
|
||||
{
|
||||
uint64_t num = (uint64_t) numerator(frac) * FP_ONE_HZ;
|
||||
uint32_t denom = denominator(frac);
|
||||
if (denom == 0) {
|
||||
denom = 1;
|
||||
}
|
||||
return (num + (denom >> 1)) / denom;
|
||||
}
|
||||
|
||||
#define MAX_AFE_RATE_HZ (40000000UL)
|
||||
|
||||
static inline uint8_t compute_resample_log(
|
||||
@@ -191,50 +152,47 @@ static fp_40_24_t applied_afe_rate = RADIO_UNSET;
|
||||
|
||||
static bool radio_update_sample_rate(radio_t* const radio, uint64_t* bank)
|
||||
{
|
||||
fp_40_24_t rate;
|
||||
uint64_t frac, previous_n;
|
||||
fp_40_24_t rate, afe_rate;
|
||||
uint64_t previous_n;
|
||||
uint8_t n = 0;
|
||||
bool new_afe_rate = false;
|
||||
bool new_rate = false;
|
||||
bool new_n = false;
|
||||
|
||||
const uint64_t requested_frac = bank[RADIO_SAMPLE_RATE_FRAC];
|
||||
const uint64_t requested_rate = bank[RADIO_SAMPLE_RATE];
|
||||
const uint64_t requested_n = bank[RADIO_RESAMPLE_RX];
|
||||
|
||||
if (requested_rate != RADIO_UNSET) {
|
||||
rate = MIN(requested_rate, MAX_MCU_RATE);
|
||||
rate = MAX(rate, MIN_MCU_RATE);
|
||||
frac = frac_sample_rate(rate);
|
||||
} else if (requested_frac != RADIO_UNSET) {
|
||||
frac = requested_frac;
|
||||
if (round_sample_rate(frac) > MAX_MCU_RATE) {
|
||||
frac = frac_sample_rate(MAX_MCU_RATE);
|
||||
}
|
||||
if (round_sample_rate(frac) < MIN_MCU_RATE) {
|
||||
frac = frac_sample_rate(MIN_MCU_RATE);
|
||||
}
|
||||
rate = round_sample_rate(frac);
|
||||
} else {
|
||||
rate = radio->config[RADIO_BANK_APPLIED][RADIO_SAMPLE_RATE];
|
||||
if (rate == RADIO_UNSET) {
|
||||
rate = DEFAULT_MCU_RATE;
|
||||
}
|
||||
frac = RADIO_UNSET;
|
||||
}
|
||||
|
||||
const uint64_t previous_opmode = radio->config[RADIO_BANK_APPLIED][RADIO_OPMODE];
|
||||
uint64_t opmode = bank[RADIO_OPMODE];
|
||||
if (opmode == RADIO_UNSET) {
|
||||
opmode = radio->config[RADIO_BANK_APPLIED][RADIO_OPMODE];
|
||||
opmode = previous_opmode;
|
||||
}
|
||||
switch (previous_opmode) {
|
||||
case TRANSCEIVER_MODE_TX:
|
||||
case TRANSCEIVER_MODE_SS:
|
||||
previous_n = radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_TX];
|
||||
break;
|
||||
default:
|
||||
previous_n = radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_RX];
|
||||
}
|
||||
switch (opmode) {
|
||||
case TRANSCEIVER_MODE_TX:
|
||||
case TRANSCEIVER_MODE_SS:
|
||||
previous_n = radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_TX];
|
||||
if (n != previous_n) {
|
||||
if (n != radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_TX]) {
|
||||
#ifdef PRALINE
|
||||
fpga_set_tx_interpolation_ratio(&fpga, n);
|
||||
#endif
|
||||
radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_TX] = n;
|
||||
new_rate = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -243,32 +201,33 @@ static bool radio_update_sample_rate(radio_t* const radio, uint64_t* bank)
|
||||
* spectrum inversion bug with TX interpolation.
|
||||
*/
|
||||
n = compute_resample_log(rate / FP_ONE_HZ, requested_n);
|
||||
previous_n = radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_RX];
|
||||
if (n != previous_n) {
|
||||
if (n != radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_RX]) {
|
||||
#ifdef PRALINE
|
||||
fpga_set_rx_decimation_ratio(&fpga, n);
|
||||
#endif
|
||||
radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_RX] = n;
|
||||
new_rate = true;
|
||||
}
|
||||
}
|
||||
new_n = (n != previous_n);
|
||||
|
||||
fp_40_24_t afe_rate = rate << n;
|
||||
fp_40_24_t previous_afe_rate = RADIO_UNSET;
|
||||
if (previous_n != RADIO_UNSET) {
|
||||
previous_afe_rate = radio->config[RADIO_BANK_APPLIED][RADIO_SAMPLE_RATE]
|
||||
<< previous_n;
|
||||
}
|
||||
|
||||
if (afe_rate != previous_afe_rate) {
|
||||
sample_rate_frac_set(numerator(frac) << n, denominator(frac));
|
||||
afe_rate = rate << n;
|
||||
afe_rate = sample_rate_set(afe_rate, false);
|
||||
new_afe_rate = (afe_rate != applied_afe_rate);
|
||||
if (new_afe_rate) {
|
||||
afe_rate = sample_rate_set(afe_rate, true);
|
||||
applied_afe_rate = afe_rate;
|
||||
radio->config[RADIO_BANK_APPLIED][RADIO_SAMPLE_RATE_FRAC] = frac;
|
||||
}
|
||||
rate = afe_rate >> n;
|
||||
new_rate = (rate != radio->config[RADIO_BANK_APPLIED][RADIO_SAMPLE_RATE]);
|
||||
if (new_rate) {
|
||||
radio->config[RADIO_BANK_APPLIED][RADIO_SAMPLE_RATE] = rate;
|
||||
new_rate = true;
|
||||
|
||||
/* Round to the nearest Hz for display. */
|
||||
const uint32_t rate_hz = (rate + (FP_ONE_HZ >> 1)) / FP_ONE_HZ;
|
||||
hackrf_ui()->set_sample_rate(rate_hz);
|
||||
}
|
||||
|
||||
return new_rate;
|
||||
return (new_afe_rate || new_rate || new_n);
|
||||
}
|
||||
|
||||
#define DEFAULT_RF (2450ULL * FP_ONE_MHZ)
|
||||
@@ -691,8 +650,8 @@ bool radio_update(radio_t* const radio)
|
||||
bool dc = false;
|
||||
|
||||
if ((dirty &
|
||||
((1 << RADIO_SAMPLE_RATE) | (1 << RADIO_SAMPLE_RATE_FRAC) |
|
||||
(1 << RADIO_RESAMPLE_TX) | (1 << RADIO_RESAMPLE_RX))) ||
|
||||
((1 << RADIO_SAMPLE_RATE) | (1 << RADIO_RESAMPLE_TX) |
|
||||
(1 << RADIO_RESAMPLE_RX))) ||
|
||||
((detected_platform() == BOARD_ID_PRALINE) &&
|
||||
(dirty & (1 << RADIO_OPMODE)))) {
|
||||
rate = radio_update_sample_rate(radio, &tmp_bank[0]);
|
||||
|
||||
@@ -166,6 +166,18 @@ usb_request_status_t usb_vendor_request_set_freq_explicit(
|
||||
return USB_REQUEST_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert fractional sample rate to units of 1/(2**24) Hz.
|
||||
*/
|
||||
static inline fp_40_24_t round_sample_rate(uint64_t num, uint32_t denom)
|
||||
{
|
||||
num *= FP_ONE_HZ;
|
||||
if (denom == 0) {
|
||||
denom = 1;
|
||||
}
|
||||
return (num + (denom >> 1)) / denom;
|
||||
}
|
||||
|
||||
usb_request_status_t usb_vendor_request_set_sample_rate_frac(
|
||||
usb_endpoint_t* const endpoint,
|
||||
const usb_transfer_stage_t stage)
|
||||
@@ -179,9 +191,9 @@ usb_request_status_t usb_vendor_request_set_sample_rate_frac(
|
||||
NULL);
|
||||
} else if (stage == USB_TRANSFER_STAGE_DATA) {
|
||||
uint32_t numerator = set_sample_r_params.freq_hz;
|
||||
uint64_t denominator = set_sample_r_params.divider;
|
||||
uint64_t value = (denominator << 32) | numerator;
|
||||
radio_reg_write(&radio, RADIO_BANK_ACTIVE, RADIO_SAMPLE_RATE_FRAC, value);
|
||||
uint32_t denominator = set_sample_r_params.divider;
|
||||
uint64_t value = round_sample_rate(numerator, denominator);
|
||||
radio_reg_write(&radio, RADIO_BANK_ACTIVE, RADIO_SAMPLE_RATE, value);
|
||||
usb_transfer_schedule_ack(endpoint->in);
|
||||
}
|
||||
return USB_REQUEST_STATUS_OK;
|
||||
|
||||
Reference in New Issue
Block a user