Skip to content

Commit 62e381e

Browse files
authored
Merge pull request #1708 from greatscottgadgets/radio-config-tuning
Refactor radio tuning
2 parents 4063cdd + 1e69ee0 commit 62e381e

19 files changed

Lines changed: 653 additions & 516 deletions

firmware/common/fpga_selftest.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2025 Great Scott Gadgets <info@greatscottgadgets.com>
2+
* Copyright 2025-2026 Great Scott Gadgets <info@greatscottgadgets.com>
33
*
44
* This file is part of HackRF.
55
*
@@ -196,7 +196,7 @@ bool fpga_if_xcvr_selftest(void)
196196
rf_path_set_direction(&rf_path, RF_PATH_DIRECTION_RX_CALIBRATION);
197197
max283x_set_lna_gain(&max283x, 16);
198198
max283x_set_vga_gain(&max283x, 36);
199-
max283x_set_frequency(&max283x, 2500000000);
199+
max283x_set_frequency(&max283x, FP_MHZ(2500), true);
200200

201201
// Capture 1: 4 Msps, tone at 0.5 MHz, narrowband filter OFF
202202
sample_rate_set(SR_FP_MHZ(4), true);

firmware/common/max2831.c

Lines changed: 37 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2025 Great Scott Gadgets <info@greatscottgadgets.com>
2+
* Copyright 2025-2026 Great Scott Gadgets <info@greatscottgadgets.com>
33
*
44
* This file is part of HackRF.
55
*
@@ -27,14 +27,18 @@
2727
* pirate commands to do the same thing.
2828
*/
2929

30+
#include "max2831.h"
31+
32+
#include <stdbool.h>
3033
#include <stdint.h>
3134
#include <string.h>
32-
#include "max2831.h"
35+
#include "fixed_point.h"
3336
#include "max2831_regs.def" // private register def macros
3437
#include "selftest.h"
3538
#include "adc.h"
3639

3740
#define MIN(x, y) ((x) < (y) ? (x) : (y))
41+
#define MAX(x, y) ((x) > (y) ? (x) : (y))
3842

3943
/* Default register values. */
4044
static const uint16_t max2831_regs_default[MAX2831_NUM_REGS] = {
@@ -222,36 +226,40 @@ void max2831_stop(max2831_driver_t* const drv)
222226
max2831_set_mode(drv, MAX2831_MODE_SHUTDOWN);
223227
}
224228

225-
void max2831_set_frequency(max2831_driver_t* const drv, uint32_t freq)
229+
/* Assume 40 MHz reference clock with R divider of 2. */
230+
#define PFD_FREQ_HZ (20ULL * (1000ULL * 1000ULL))
231+
232+
#define MIN_FREQ FP_MHZ(2000)
233+
#define MAX_FREQ FP_MHZ(3000)
234+
235+
fp_40_24_t max2831_set_frequency(
236+
max2831_driver_t* const drv,
237+
fp_40_24_t freq,
238+
bool program)
226239
{
227-
uint32_t div_frac;
228-
uint32_t div_int;
229-
uint32_t div_rem;
230-
uint32_t div_cmp;
231-
int i;
232-
233-
/* ASSUME 40MHz PLL. Ratio = F*R/40,000,000. */
234-
/* TODO: fixed to R=2. Check if it's worth exploring R=1. */
235-
freq += (20000000 >> 21); /* round to nearest frequency */
236-
div_int = freq / 20000000;
237-
div_rem = freq % 20000000;
238-
div_frac = 0;
239-
div_cmp = 20000000;
240-
for (i = 0; i < 20; i++) {
241-
div_frac <<= 1;
242-
div_rem <<= 1;
243-
if (div_rem >= div_cmp) {
244-
div_frac |= 0x1;
245-
div_rem -= div_cmp;
246-
}
240+
uint64_t div;
241+
242+
freq = MIN(freq, MAX_FREQ);
243+
freq = MAX(freq, MIN_FREQ);
244+
245+
freq += ((PFD_FREQ_HZ * FP_ONE_HZ) >> 21); /* round to nearest frequency */
246+
div = freq / PFD_FREQ_HZ;
247+
248+
/*
249+
* Shift from 40.24 fixed-point to 44.20 to match 20-bit fractional
250+
* divider.
251+
*/
252+
div = div >> 4;
253+
254+
if (program) {
255+
//set_MAX2831_SYN_REF_DIV(drv, MAX2831_SYN_REF_DIV_2);
256+
set_MAX2831_SYN_INT(drv, (div >> 20) & 0xff);
257+
set_MAX2831_SYN_FRAC_HI(drv, (div >> 6) & 0x3fff);
258+
set_MAX2831_SYN_FRAC_LO(drv, div & 0x3f);
259+
max2831_regs_commit(drv);
247260
}
248261

249-
/* Write order matters? */
250-
//set_MAX2831_SYN_REF_DIV(drv, MAX2831_SYN_REF_DIV_2);
251-
set_MAX2831_SYN_INT(drv, div_int);
252-
set_MAX2831_SYN_FRAC_HI(drv, (div_frac >> 6) & 0x3fff);
253-
set_MAX2831_SYN_FRAC_LO(drv, div_frac & 0x3f);
254-
max2831_regs_commit(drv);
262+
return PFD_FREQ_HZ * (div << 4);
255263
}
256264

257265
typedef struct {

firmware/common/max2831.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2025 Great Scott Gadgets <info@greatscottgadgets.com>
2+
* Copyright 2025-2026 Great Scott Gadgets <info@greatscottgadgets.com>
33
*
44
* This file is part of HackRF.
55
*
@@ -21,9 +21,10 @@
2121

2222
#pragma once
2323

24-
#include <stdint.h>
2524
#include <stdbool.h>
25+
#include <stdint.h>
2626

27+
#include "fixed_point.h"
2728
#include "gpio.h"
2829
#include "spi_bus.h"
2930

@@ -86,9 +87,11 @@ void max2831_set_mode(max2831_driver_t* const drv, const max2831_mode_t new_mode
8687
extern void max2831_start(max2831_driver_t* const drv);
8788
extern void max2831_stop(max2831_driver_t* const drv);
8889

89-
/* Set frequency in Hz. Frequency setting is a multi-step function
90-
* where order of register writes matters. */
91-
extern void max2831_set_frequency(max2831_driver_t* const drv, uint32_t freq);
90+
/* Set frequency in 1/(2**24) Hz */
91+
extern fp_40_24_t max2831_set_frequency(
92+
max2831_driver_t* const drv,
93+
fp_40_24_t freq,
94+
bool program);
9295
uint32_t max2831_set_lpf_bandwidth(
9396
max2831_driver_t* const drv,
9497
const max2831_mode_t mode,

firmware/common/max2837.c

Lines changed: 51 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2022 Great Scott Gadgets <info@greatscottgadgets.com>
2+
* Copyright 2012-2026 Great Scott Gadgets <info@greatscottgadgets.com>
33
* Copyright 2012 Will Code <willcode4@gmail.com>
44
* Copyright 2014 Jared Boone <jared@sharebrained.com>
55
*
@@ -29,12 +29,18 @@
2929
* pirate commands to do the same thing.
3030
*/
3131

32+
#include "max2837.h"
33+
34+
#include <stdbool.h>
3235
#include <stdint.h>
3336
#include <string.h>
34-
#include "max2837.h"
37+
#include "fixed_point.h"
3538
#include "max2837_regs.def" // private register def macros
3639
#include "selftest.h"
3740

41+
#define MIN(x, y) ((x) < (y) ? (x) : (y))
42+
#define MAX(x, y) ((x) > (y) ? (x) : (y))
43+
3844
/* Default register values. */
3945
static const uint16_t max2837_regs_default[MAX2837_NUM_REGS] = {
4046
0x150, /* 0 */
@@ -228,60 +234,67 @@ void max2837_stop(max2837_driver_t* const drv)
228234
max2837_set_mode(drv, MAX2837_MODE_SHUTDOWN);
229235
}
230236

231-
void max2837_set_frequency(max2837_driver_t* const drv, uint32_t freq)
237+
/* Assume 40 MHz reference clock with R divider of 1. */
238+
#define PFD_FREQ_HZ (40ULL * (1000ULL * 1000ULL))
239+
240+
#define MIN_FREQ FP_MHZ(2000)
241+
#define MAX_FREQ FP_MHZ(3000)
242+
243+
fp_40_24_t max2837_set_frequency(
244+
max2837_driver_t* const drv,
245+
fp_40_24_t freq,
246+
bool program)
232247
{
233248
uint8_t band;
234249
uint8_t lna_band;
235-
uint32_t div_frac;
236-
uint32_t div_int;
237-
uint32_t div_rem;
238-
uint32_t div_cmp;
239-
int i;
250+
uint64_t div;
251+
252+
freq = MIN(freq, MAX_FREQ);
253+
freq = MAX(freq, MIN_FREQ);
240254

241255
/* Select band. Allow tuning outside specified bands. */
242-
if (freq < 2400000000U) {
256+
if (freq < FP_MHZ(2400)) {
243257
band = MAX2837_LOGEN_BSW_2_3;
244258
lna_band = MAX2837_LNAband_2_4;
245-
} else if (freq < 2500000000U) {
259+
} else if (freq < FP_MHZ(2500)) {
246260
band = MAX2837_LOGEN_BSW_2_4;
247261
lna_band = MAX2837_LNAband_2_4;
248-
} else if (freq < 2600000000U) {
262+
} else if (freq < FP_MHZ(2600)) {
249263
band = MAX2837_LOGEN_BSW_2_5;
250264
lna_band = MAX2837_LNAband_2_6;
251265
} else {
252266
band = MAX2837_LOGEN_BSW_2_6;
253267
lna_band = MAX2837_LNAband_2_6;
254268
}
255269

256-
/* ASSUME 40MHz PLL. Ratio = F*(4/3)/40,000,000 = F/30,000,000 */
257-
freq += (30000000 >> 21); /* round to nearest frequency */
258-
div_int = freq / 30000000;
259-
div_rem = freq % 30000000;
260-
div_frac = 0;
261-
div_cmp = 30000000;
262-
for (i = 0; i < 20; i++) {
263-
div_frac <<= 1;
264-
div_rem <<= 1;
265-
if (div_rem >= div_cmp) {
266-
div_frac |= 0x1;
267-
div_rem -= div_cmp;
268-
}
270+
fp_40_24_t vco = (freq * 4) / 3;
271+
272+
vco += ((PFD_FREQ_HZ * FP_ONE_HZ) >> 21); /* round to nearest frequency */
273+
div = vco / PFD_FREQ_HZ;
274+
275+
/*
276+
* Shift from 40.24 fixed-point to 44.20 to match 20-bit fractional
277+
* divider.
278+
*/
279+
div = div >> 4;
280+
281+
if (program) {
282+
/* Band settings */
283+
set_MAX2837_LOGEN_BSW(drv, band);
284+
set_MAX2837_LNAband(drv, lna_band);
285+
286+
/*
287+
* Write order matters here, so commit INT and FRAC_HI before
288+
* committing FRAC_LO, which is the trigger for VCO auto-select.
289+
*/
290+
set_MAX2837_SYN_INT(drv, (div >> 20) & 0xff);
291+
set_MAX2837_SYN_FRAC_HI(drv, (div >> 10) & 0x3ff);
292+
max2837_regs_commit(drv);
293+
set_MAX2837_SYN_FRAC_LO(drv, div & 0x3ff);
294+
max2837_regs_commit(drv);
269295
}
270296

271-
/* Band settings */
272-
set_MAX2837_LOGEN_BSW(drv, band);
273-
set_MAX2837_LNAband(drv, lna_band);
274-
275-
/* Write order matters here, so commit INT and FRAC_HI before
276-
* committing FRAC_LO, which is the trigger for VCO
277-
* auto-select. TODO - it's cleaner this way, but it would be
278-
* faster to explicitly commit the registers explicitly so the
279-
* dirty bits aren't scanned twice. */
280-
set_MAX2837_SYN_INT(drv, div_int);
281-
set_MAX2837_SYN_FRAC_HI(drv, (div_frac >> 10) & 0x3ff);
282-
max2837_regs_commit(drv);
283-
set_MAX2837_SYN_FRAC_LO(drv, div_frac & 0x3ff);
284-
max2837_regs_commit(drv);
297+
return ((PFD_FREQ_HZ * 3) / 4) * (div << 4);
285298
}
286299

287300
typedef struct {

firmware/common/max2837.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2022 Great Scott Gadgets <info@greatscottgadgets.com>
2+
* Copyright 2012-2026 Great Scott Gadgets <info@greatscottgadgets.com>
33
* Copyright 2012 Will Code <willcode4@gmail.com>
44
* Copyright 2014 Jared Boone <jared@sharebrained.com>
55
*
@@ -23,9 +23,10 @@
2323

2424
#pragma once
2525

26-
#include <stdint.h>
2726
#include <stdbool.h>
27+
#include <stdint.h>
2828

29+
#include "fixed_point.h"
2930
#include "gpio.h"
3031
#include "spi_bus.h"
3132

@@ -77,9 +78,11 @@ void max2837_set_mode(max2837_driver_t* const drv, const max2837_mode_t new_mode
7778
extern void max2837_start(max2837_driver_t* const drv);
7879
extern void max2837_stop(max2837_driver_t* const drv);
7980

80-
/* Set frequency in Hz. Frequency setting is a multi-step function
81-
* where order of register writes matters. */
82-
extern void max2837_set_frequency(max2837_driver_t* const drv, uint32_t freq);
81+
/* Set frequency in 1/(2**24) Hz. */
82+
extern fp_40_24_t max2837_set_frequency(
83+
max2837_driver_t* const drv,
84+
fp_40_24_t freq,
85+
bool program);
8386
uint32_t max2837_set_lpf_bandwidth(
8487
max2837_driver_t* const drv,
8588
const uint32_t bandwidth_hz);

0 commit comments

Comments
 (0)