Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions firmware/common/fpga_selftest.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2025 Great Scott Gadgets <info@greatscottgadgets.com>
* Copyright 2025-2026 Great Scott Gadgets <info@greatscottgadgets.com>
*
* This file is part of HackRF.
*
Expand Down Expand Up @@ -196,7 +196,7 @@ bool fpga_if_xcvr_selftest(void)
rf_path_set_direction(&rf_path, RF_PATH_DIRECTION_RX_CALIBRATION);
max283x_set_lna_gain(&max283x, 16);
max283x_set_vga_gain(&max283x, 36);
max283x_set_frequency(&max283x, 2500000000);
max283x_set_frequency(&max283x, FP_MHZ(2500), true);

// Capture 1: 4 Msps, tone at 0.5 MHz, narrowband filter OFF
sample_rate_set(SR_FP_MHZ(4), true);
Expand Down
66 changes: 37 additions & 29 deletions firmware/common/max2831.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2025 Great Scott Gadgets <info@greatscottgadgets.com>
* Copyright 2025-2026 Great Scott Gadgets <info@greatscottgadgets.com>
*
* This file is part of HackRF.
*
Expand Down Expand Up @@ -27,14 +27,18 @@
* pirate commands to do the same thing.
*/

#include "max2831.h"

#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "max2831.h"
#include "fixed_point.h"
#include "max2831_regs.def" // private register def macros
#include "selftest.h"
#include "adc.h"

#define MIN(x, y) ((x) < (y) ? (x) : (y))
#define MAX(x, y) ((x) > (y) ? (x) : (y))

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

void max2831_set_frequency(max2831_driver_t* const drv, uint32_t freq)
/* Assume 40 MHz reference clock with R divider of 2. */
#define PFD_FREQ_HZ (20ULL * (1000ULL * 1000ULL))

#define MIN_FREQ FP_MHZ(2000)
#define MAX_FREQ FP_MHZ(3000)

fp_40_24_t max2831_set_frequency(
max2831_driver_t* const drv,
fp_40_24_t freq,
bool program)
{
uint32_t div_frac;
uint32_t div_int;
uint32_t div_rem;
uint32_t div_cmp;
int i;

/* ASSUME 40MHz PLL. Ratio = F*R/40,000,000. */
/* TODO: fixed to R=2. Check if it's worth exploring R=1. */
freq += (20000000 >> 21); /* round to nearest frequency */
div_int = freq / 20000000;
div_rem = freq % 20000000;
div_frac = 0;
div_cmp = 20000000;
for (i = 0; i < 20; i++) {
div_frac <<= 1;
div_rem <<= 1;
if (div_rem >= div_cmp) {
div_frac |= 0x1;
div_rem -= div_cmp;
}
uint64_t div;

freq = MIN(freq, MAX_FREQ);
freq = MAX(freq, MIN_FREQ);

freq += ((PFD_FREQ_HZ * FP_ONE_HZ) >> 21); /* round to nearest frequency */
div = freq / PFD_FREQ_HZ;

/*
* Shift from 40.24 fixed-point to 44.20 to match 20-bit fractional
* divider.
*/
div = div >> 4;
Comment thread
mossmann marked this conversation as resolved.

if (program) {
//set_MAX2831_SYN_REF_DIV(drv, MAX2831_SYN_REF_DIV_2);
set_MAX2831_SYN_INT(drv, (div >> 20) & 0xff);
set_MAX2831_SYN_FRAC_HI(drv, (div >> 6) & 0x3fff);
set_MAX2831_SYN_FRAC_LO(drv, div & 0x3f);
max2831_regs_commit(drv);
}

/* Write order matters? */
//set_MAX2831_SYN_REF_DIV(drv, MAX2831_SYN_REF_DIV_2);
set_MAX2831_SYN_INT(drv, div_int);
set_MAX2831_SYN_FRAC_HI(drv, (div_frac >> 6) & 0x3fff);
set_MAX2831_SYN_FRAC_LO(drv, div_frac & 0x3f);
max2831_regs_commit(drv);
return PFD_FREQ_HZ * (div << 4);
}

typedef struct {
Expand Down
13 changes: 8 additions & 5 deletions firmware/common/max2831.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2025 Great Scott Gadgets <info@greatscottgadgets.com>
* Copyright 2025-2026 Great Scott Gadgets <info@greatscottgadgets.com>
*
* This file is part of HackRF.
*
Expand All @@ -21,9 +21,10 @@

#pragma once

#include <stdint.h>
#include <stdbool.h>
#include <stdint.h>

#include "fixed_point.h"
#include "gpio.h"
#include "spi_bus.h"

Expand Down Expand Up @@ -86,9 +87,11 @@ void max2831_set_mode(max2831_driver_t* const drv, const max2831_mode_t new_mode
extern void max2831_start(max2831_driver_t* const drv);
extern void max2831_stop(max2831_driver_t* const drv);

/* Set frequency in Hz. Frequency setting is a multi-step function
* where order of register writes matters. */
extern void max2831_set_frequency(max2831_driver_t* const drv, uint32_t freq);
/* Set frequency in 1/(2**24) Hz */
extern fp_40_24_t max2831_set_frequency(
max2831_driver_t* const drv,
fp_40_24_t freq,
bool program);
uint32_t max2831_set_lpf_bandwidth(
max2831_driver_t* const drv,
const max2831_mode_t mode,
Expand Down
89 changes: 51 additions & 38 deletions firmware/common/max2837.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2022 Great Scott Gadgets <info@greatscottgadgets.com>
* Copyright 2012-2026 Great Scott Gadgets <info@greatscottgadgets.com>
* Copyright 2012 Will Code <willcode4@gmail.com>
* Copyright 2014 Jared Boone <jared@sharebrained.com>
*
Expand Down Expand Up @@ -29,12 +29,18 @@
* pirate commands to do the same thing.
*/

#include "max2837.h"

#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "max2837.h"
#include "fixed_point.h"
#include "max2837_regs.def" // private register def macros
#include "selftest.h"

#define MIN(x, y) ((x) < (y) ? (x) : (y))
#define MAX(x, y) ((x) > (y) ? (x) : (y))

/* Default register values. */
static const uint16_t max2837_regs_default[MAX2837_NUM_REGS] = {
0x150, /* 0 */
Expand Down Expand Up @@ -228,60 +234,67 @@ void max2837_stop(max2837_driver_t* const drv)
max2837_set_mode(drv, MAX2837_MODE_SHUTDOWN);
}

void max2837_set_frequency(max2837_driver_t* const drv, uint32_t freq)
/* Assume 40 MHz reference clock with R divider of 1. */
#define PFD_FREQ_HZ (40ULL * (1000ULL * 1000ULL))

#define MIN_FREQ FP_MHZ(2000)
#define MAX_FREQ FP_MHZ(3000)

fp_40_24_t max2837_set_frequency(
max2837_driver_t* const drv,
fp_40_24_t freq,
bool program)
{
uint8_t band;
uint8_t lna_band;
uint32_t div_frac;
uint32_t div_int;
uint32_t div_rem;
uint32_t div_cmp;
int i;
uint64_t div;

freq = MIN(freq, MAX_FREQ);
freq = MAX(freq, MIN_FREQ);

/* Select band. Allow tuning outside specified bands. */
if (freq < 2400000000U) {
if (freq < FP_MHZ(2400)) {
band = MAX2837_LOGEN_BSW_2_3;
lna_band = MAX2837_LNAband_2_4;
} else if (freq < 2500000000U) {
} else if (freq < FP_MHZ(2500)) {
band = MAX2837_LOGEN_BSW_2_4;
lna_band = MAX2837_LNAband_2_4;
} else if (freq < 2600000000U) {
} else if (freq < FP_MHZ(2600)) {
band = MAX2837_LOGEN_BSW_2_5;
lna_band = MAX2837_LNAband_2_6;
} else {
band = MAX2837_LOGEN_BSW_2_6;
lna_band = MAX2837_LNAband_2_6;
}

/* ASSUME 40MHz PLL. Ratio = F*(4/3)/40,000,000 = F/30,000,000 */
freq += (30000000 >> 21); /* round to nearest frequency */
div_int = freq / 30000000;
div_rem = freq % 30000000;
div_frac = 0;
div_cmp = 30000000;
for (i = 0; i < 20; i++) {
div_frac <<= 1;
div_rem <<= 1;
if (div_rem >= div_cmp) {
div_frac |= 0x1;
div_rem -= div_cmp;
}
fp_40_24_t vco = (freq * 4) / 3;

vco += ((PFD_FREQ_HZ * FP_ONE_HZ) >> 21); /* round to nearest frequency */
div = vco / PFD_FREQ_HZ;

/*
* Shift from 40.24 fixed-point to 44.20 to match 20-bit fractional
* divider.
*/
div = div >> 4;

if (program) {
/* Band settings */
set_MAX2837_LOGEN_BSW(drv, band);
set_MAX2837_LNAband(drv, lna_band);

/*
* Write order matters here, so commit INT and FRAC_HI before
* committing FRAC_LO, which is the trigger for VCO auto-select.
*/
set_MAX2837_SYN_INT(drv, (div >> 20) & 0xff);
set_MAX2837_SYN_FRAC_HI(drv, (div >> 10) & 0x3ff);
max2837_regs_commit(drv);
set_MAX2837_SYN_FRAC_LO(drv, div & 0x3ff);
max2837_regs_commit(drv);
}

/* Band settings */
set_MAX2837_LOGEN_BSW(drv, band);
set_MAX2837_LNAband(drv, lna_band);

/* Write order matters here, so commit INT and FRAC_HI before
* committing FRAC_LO, which is the trigger for VCO
* auto-select. TODO - it's cleaner this way, but it would be
* faster to explicitly commit the registers explicitly so the
* dirty bits aren't scanned twice. */
set_MAX2837_SYN_INT(drv, div_int);
set_MAX2837_SYN_FRAC_HI(drv, (div_frac >> 10) & 0x3ff);
max2837_regs_commit(drv);
set_MAX2837_SYN_FRAC_LO(drv, div_frac & 0x3ff);
max2837_regs_commit(drv);
return ((PFD_FREQ_HZ * 3) / 4) * (div << 4);
}

typedef struct {
Expand Down
13 changes: 8 additions & 5 deletions firmware/common/max2837.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2022 Great Scott Gadgets <info@greatscottgadgets.com>
* Copyright 2012-2026 Great Scott Gadgets <info@greatscottgadgets.com>
* Copyright 2012 Will Code <willcode4@gmail.com>
* Copyright 2014 Jared Boone <jared@sharebrained.com>
*
Expand All @@ -23,9 +23,10 @@

#pragma once

#include <stdint.h>
#include <stdbool.h>
#include <stdint.h>

#include "fixed_point.h"
#include "gpio.h"
#include "spi_bus.h"

Expand Down Expand Up @@ -77,9 +78,11 @@ void max2837_set_mode(max2837_driver_t* const drv, const max2837_mode_t new_mode
extern void max2837_start(max2837_driver_t* const drv);
extern void max2837_stop(max2837_driver_t* const drv);

/* Set frequency in Hz. Frequency setting is a multi-step function
* where order of register writes matters. */
extern void max2837_set_frequency(max2837_driver_t* const drv, uint32_t freq);
/* Set frequency in 1/(2**24) Hz. */
extern fp_40_24_t max2837_set_frequency(
max2837_driver_t* const drv,
fp_40_24_t freq,
bool program);
uint32_t max2837_set_lpf_bandwidth(
max2837_driver_t* const drv,
const uint32_t bandwidth_hz);
Expand Down
Loading
Loading