This commit is contained in:
phil-stumpy
2023-02-16 12:09:23 +00:00
parent 7e20cea6d7
commit 66ba6442b1
35 changed files with 2001 additions and 464 deletions

View File

@@ -103,6 +103,7 @@ set(CSRC
${PLATFORMSRC}
${BOARDSRC}
${FATFSSRC}
firmware_info.c
)
# C++ sources that can be compiled in ARM or THUMB mode depending on the global
@@ -193,6 +194,7 @@ set(CPPSRC
hw/debounce.cpp
hw/encoder.cpp
hw/max2837.cpp
hw/max2839.cpp
hw/max5864.cpp
hw/rffc507x.cpp
hw/rffc507x_spi.cpp

View File

@@ -322,17 +322,19 @@ void DebugControlsView::focus() {
/* DebugPeripheralsMenuView **********************************************/
DebugPeripheralsMenuView::DebugPeripheralsMenuView(NavigationView& nav) {
const char * max283x = hackrf_r9 ? "MAX2839" : "MAX2837";
const char * si5351x = hackrf_r9 ? "Si5351A" : "Si5351C";
add_items({
{ "RFFC5072", ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav](){ nav.push<RegistersView>(
"RFFC5072", RegistersWidgetConfig { 31, 16 },
[](const size_t register_number) { return radio::debug::first_if::register_read(register_number); }
); } },
{ "MAX2837", ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav](){ nav.push<RegistersView>(
"MAX2837", RegistersWidgetConfig { 32, 10 },
{ max283x, ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav, max283x](){ nav.push<RegistersView>(
max283x, RegistersWidgetConfig { 32, 10 },
[](const size_t register_number) { return radio::debug::second_if::register_read(register_number); }
); } },
{ "Si5351C", ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav](){ nav.push<RegistersView>(
"Si5351C", RegistersWidgetConfig { 96, 8 },
{ si5351x, ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav, si5351x](){ nav.push<RegistersView>(
si5351x, RegistersWidgetConfig { 96, 8 },
[](const size_t register_number) { return portapack::clock_generator.read_register(register_number); }
); } },
{ audio::debug::codec_name(), ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav](){ nav.push<RegistersView>(

View File

@@ -29,7 +29,6 @@
#include "ui_navigation.hpp"
#include "rffc507x.hpp"
#include "max2837.hpp"
#include "portapack.hpp"
#include <functional>

View File

@@ -57,13 +57,13 @@ static msg_t ookthread_fn(void * arg) {
v = (symbol < 2) ? 1 : 0; // TX on for dot or dash, off for pause
delay = morse_symbols[symbol];
gpio_tx.write(v);
gpio_og_tx.write(v);
arg_c->on_tx_progress(i, false);
chThdSleepMilliseconds(delay * arg_c->time_unit_ms);
}
gpio_tx.write(0); // Ensure TX is off
gpio_og_tx.write(0); // Ensure TX is off
arg_c->on_tx_progress(0, true);
chThdExit(0);

View File

@@ -307,7 +307,7 @@ void run_image(const portapack::spi_flash::image_tag_t image_tag) {
creg::m4txevent::clear();
m4_init(image_tag, portapack::memory::map::m4_code);
m4_init(image_tag, portapack::memory::map::m4_code, false);
baseband_image_running = true;
creg::m4txevent::enable();

View File

@@ -41,12 +41,18 @@ constexpr si5351::Inputs si5351_inputs {
static_assert(si5351_inputs.f_xtal == si5351_xtal_f, "XTAL output frequency wrong");
static_assert(si5351_inputs.f_clkin_out() == si5351_clkin_f, "CLKIN output frequency wrong");
constexpr si5351::PLLInputSource::Type si5351_pll_input_sources {
constexpr si5351::PLLInputSource::Type si5351c_pll_input_sources {
si5351::PLLInputSource::PLLA_Source_XTAL
| si5351::PLLInputSource::PLLB_Source_CLKIN
| si5351::PLLInputSource::CLKIN_Div1
};
constexpr si5351::PLLInputSource::Type si5351a_pll_input_sources {
si5351::PLLInputSource::PLLA_Source_XTAL
| si5351::PLLInputSource::PLLB_Source_XTAL
| si5351::PLLInputSource::CLKIN_Div1
};
constexpr si5351::PLL si5351_pll_xtal_25m {
.f_in = si5351_inputs.f_xtal,
.a = 32,
@@ -61,7 +67,8 @@ constexpr si5351::PLL si5351_pll_clkin_10m {
.b = 0,
.c = 1,
};
constexpr auto si5351_pll_b_clkin_reg = si5351_pll_clkin_10m.reg(1);
constexpr auto si5351c_pll_b_clkin_reg = si5351_pll_clkin_10m.reg(1);
constexpr auto si5351a_pll_a_clkin_reg = si5351_pll_clkin_10m.reg(0);
static_assert(si5351_pll_xtal_25m.f_vco() == si5351_vco_f, "PLL XTAL frequency wrong");
static_assert(si5351_pll_xtal_25m.p1() == 3584, "PLL XTAL P1 wrong");
@@ -98,7 +105,7 @@ constexpr si5351::MultisynthFractional si5351_ms_0_8m {
.c = 1,
.r_div = 1,
};
constexpr auto si5351_ms_0_8m_reg = si5351_ms_0_8m.reg(clock_generator_output_codec);
constexpr auto si5351c_ms_0_8m_reg = si5351_ms_0_8m.reg(clock_generator_output_og_codec);
constexpr si5351::MultisynthFractional si5351_ms_group {
.f_src = si5351_vco_f,
@@ -107,8 +114,17 @@ constexpr si5351::MultisynthFractional si5351_ms_group {
.c = 1,
.r_div = 0,
};
constexpr auto si5351_ms_1_group_reg = si5351_ms_group.reg(clock_generator_output_cpld);
constexpr auto si5351_ms_2_group_reg = si5351_ms_group.reg(clock_generator_output_sgpio);
constexpr auto si5351c_ms_1_group_reg = si5351_ms_group.reg(clock_generator_output_og_cpld);
constexpr auto si5351c_ms_2_group_reg = si5351_ms_group.reg(clock_generator_output_og_sgpio);
constexpr si5351::MultisynthFractional si5351_ms_16m {
.f_src = si5351_vco_f,
.a = 50,
.b = 0,
.c = 1,
.r_div = 0,
};
constexpr auto si5351a_ms_1_sgpio_16m_reg = si5351_ms_16m.reg(clock_generator_output_r9_sgpio);
constexpr si5351::MultisynthFractional si5351_ms_10m {
.f_src = si5351_vco_f,
@@ -117,7 +133,8 @@ constexpr si5351::MultisynthFractional si5351_ms_10m {
.c = 1,
.r_div = 0,
};
constexpr auto si5351_ms_3_10m_reg = si5351_ms_10m.reg(3);
constexpr auto si5351c_ms_3_10m_reg = si5351_ms_10m.reg(3);
constexpr auto si5351a_ms_2_mcu_10m_reg = si5351_ms_10m.reg(clock_generator_output_r9_mcu_clkin);
constexpr si5351::MultisynthFractional si5351_ms_40m {
.f_src = si5351_vco_f,
@@ -128,10 +145,11 @@ constexpr si5351::MultisynthFractional si5351_ms_40m {
};
constexpr auto si5351_ms_rffc5072 = si5351_ms_40m;
constexpr auto si5351_ms_max2837 = si5351_ms_40m;
constexpr auto si5351_ms_max283x = si5351_ms_40m;
constexpr auto si5351_ms_4_reg = si5351_ms_rffc5072.reg(clock_generator_output_first_if);
constexpr auto si5351_ms_5_reg = si5351_ms_max2837.reg(clock_generator_output_second_if);
constexpr auto si5351c_ms_4_reg = si5351_ms_rffc5072.reg(clock_generator_output_og_first_if);
constexpr auto si5351c_ms_5_reg = si5351_ms_max283x.reg(clock_generator_output_og_second_if);
constexpr auto si5351a_ms_0_if_40m_reg = si5351_ms_40m.reg(clock_generator_output_r9_if);
static_assert(si5351_ms_10m.f_out() == 10000000, "MS 10MHz f_out wrong");
static_assert(si5351_ms_10m.p1() == 9728, "MS 10MHz p1 wrong");
@@ -139,14 +157,8 @@ static_assert(si5351_ms_10m.p2() == 0, "MS 10MHz p2 wrong");
static_assert(si5351_ms_10m.p3() == 1, "MS 10MHz p3 wrong");
static_assert(si5351_ms_rffc5072.f_out() == rffc5072_reference_f, "RFFC5072 reference f_out wrong");
// static_assert(si5351_ms_50m.p1() == 2048, "MS 50MHz P1 wrong");
// static_assert(si5351_ms_50m.p2() == 0, "MS 50MHz P2 wrong");
// static_assert(si5351_ms_50m.p3() == 1, "MS 50MHz P3 wrong");
static_assert(si5351_ms_max2837.f_out() == max2837_reference_f, "MAX2837 reference f_out wrong");
// static_assert(si5351_ms_50m.p1() == 2048, "MS 40MHz P1 wrong");
// static_assert(si5351_ms_50m.p2() == 0, "MS 40MHz P2 wrong");
// static_assert(si5351_ms_50m.p3() == 1, "MS 40MHz P3 wrong");
static_assert(si5351_ms_max283x.f_out() == max283x_reference_f, "MAX283x reference f_out wrong");
constexpr si5351::MultisynthInteger si5351_ms_int_off {
.f_src = si5351_vco_f,
@@ -154,40 +166,60 @@ constexpr si5351::MultisynthInteger si5351_ms_int_off {
.r_div = 0,
};
constexpr si5351::MultisynthInteger si5351_ms_int_mcu_clkin {
constexpr si5351::MultisynthInteger si5351_ms_int_40m {
.f_src = si5351_vco_f,
.a = 20,
.r_div = 0,
};
constexpr auto si5351_ms6_7_off_mcu_clkin_reg = si5351::ms6_7_reg(si5351_ms_int_off, si5351_ms_int_mcu_clkin);
constexpr si5351::MultisynthInteger si5351_ms_int_10m {
.f_src = si5351_vco_f,
.a = 80,
.r_div = 0,
};
constexpr auto si5351c_ms_int_mcu_clkin = si5351_ms_int_40m;
constexpr auto si5351a_ms_int_mcu_clkin = si5351_ms_int_10m;
constexpr auto si5351c_ms6_7_off_mcu_clkin_reg = si5351::ms6_7_reg(si5351_ms_int_off, si5351c_ms_int_mcu_clkin);
constexpr auto si5351a_ms6_7_off_reg = si5351::ms6_7_reg(si5351_ms_int_off, si5351_ms_int_off);
static_assert(si5351_ms_int_off.f_out() == 3137254, "MS int off f_out wrong");
static_assert(si5351_ms_int_off.p1() == 255, "MS int off P1 wrong");
static_assert(si5351_ms_int_mcu_clkin.f_out() == mcu_clkin_f, "MS int MCU CLKIN f_out wrong");
// static_assert(si5351_ms_int_mcu_clkin.p1() == 20, "MS int MCU CLKIN P1 wrong");
static_assert(si5351c_ms_int_mcu_clkin.f_out() == mcu_clkin_og_f, "MS int MCU CLKIN OG f_out wrong");
static_assert(si5351a_ms_int_mcu_clkin.f_out() == mcu_clkin_r9_f, "MS int MCU CLKIN r9 f_out wrong");
using namespace si5351;
static constexpr ClockControl::MultiSynthSource get_reference_clock_generator_pll(const ClockManager::ReferenceSource reference_source) {
static constexpr ClockControl::MultiSynthSource get_si5351c_reference_clock_generator_pll(const ClockManager::ReferenceSource reference_source) {
return (reference_source == ClockManager::ReferenceSource::Xtal)
? ClockControl::MultiSynthSource::PLLA
: ClockControl::MultiSynthSource::PLLB
;
}
constexpr ClockControls si5351_clock_control_common { {
{ ClockControl::ClockCurrentDrive::_8mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Fractional, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Group, ClockControl::ClockInvert::Invert, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Group, ClockControl::ClockInvert::Normal, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_8mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_6mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Invert, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_4mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Fractional, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
constexpr ClockControls si5351c_clock_control_common { {
{ ClockControl::ClockCurrentDrive::_8mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Fractional, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Group, ClockControl::ClockInvert::Invert, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Group, ClockControl::ClockInvert::Normal, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_8mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_6mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Invert, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_4mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Fractional, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
} };
constexpr ClockControls si5351a_clock_control_common { {
{ ClockControl::ClockCurrentDrive::_6mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_4mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Fractional, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_8mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
} };
ClockManager::Reference ClockManager::get_reference() const {
return reference;
}
@@ -212,11 +244,25 @@ void ClockManager::init_clock_generator() {
clock_generator.reset();
clock_generator.set_crystal_internal_load_capacitance(CrystalInternalLoadCapacitance::XTAL_CL_8pF);
clock_generator.enable_fanout();
clock_generator.set_pll_input_sources(si5351_pll_input_sources);
clock_generator.set_pll_input_sources(hackrf_r9
? si5351a_pll_input_sources
: si5351c_pll_input_sources);
auto si5351_clock_control_common = hackrf_r9
? si5351a_clock_control_common
: si5351c_clock_control_common;
auto clock_generator_output_mcu_clkin = hackrf_r9
? clock_generator_output_r9_mcu_clkin
: clock_generator_output_og_mcu_clkin;
clock_generator.set_clock_control(
clock_generator_output_mcu_clkin,
si5351_clock_control_common[clock_generator_output_mcu_clkin].clk_src(ClockControl::ClockSource::CLKIN).clk_pdn(ClockControl::ClockPowerDown::Power_On)
si5351_clock_control_common[clock_generator_output_mcu_clkin]
.clk_src(hackrf_r9
? ClockControl::ClockSource::Xtal
: ClockControl::ClockSource::CLKIN)
.clk_pdn(ClockControl::ClockPowerDown::Power_On)
);
clock_generator.enable_output(clock_generator_output_mcu_clkin);
@@ -224,7 +270,10 @@ void ClockManager::init_clock_generator() {
clock_generator.disable_output(clock_generator_output_mcu_clkin);
const auto ref_pll = get_reference_clock_generator_pll(reference.source);
const auto ref_pll = hackrf_r9
? ClockControl::MultiSynthSource::PLLA
: get_si5351c_reference_clock_generator_pll(reference.source);
const ClockControls si5351_clock_control = ClockControls { {
si5351_clock_control_common[0].ms_src(ref_pll),
si5351_clock_control_common[1].ms_src(ref_pll),
@@ -237,21 +286,35 @@ void ClockManager::init_clock_generator() {
} };
clock_generator.set_clock_control(si5351_clock_control);
clock_generator.write(si5351_pll_a_xtal_reg);
clock_generator.write(si5351_pll_b_clkin_reg);
clock_generator.write(si5351_ms_0_8m_reg);
clock_generator.write(si5351_ms_1_group_reg);
clock_generator.write(si5351_ms_2_group_reg);
clock_generator.write(si5351_ms_3_10m_reg);
clock_generator.write(si5351_ms_4_reg);
clock_generator.write(si5351_ms_5_reg);
clock_generator.write(si5351_ms6_7_off_mcu_clkin_reg);
if (hackrf_r9) {
const PLLReg pll_reg = (reference.source == ReferenceSource::Xtal)
? si5351_pll_a_xtal_reg
: si5351a_pll_a_clkin_reg;
clock_generator.write(pll_reg);
clock_generator.write(si5351a_ms_0_if_40m_reg);
clock_generator.write(si5351a_ms_1_sgpio_16m_reg);
clock_generator.write(si5351a_ms_2_mcu_10m_reg);
clock_generator.write(si5351a_ms6_7_off_reg);
} else {
clock_generator.write(si5351_pll_a_xtal_reg);
clock_generator.write(si5351c_pll_b_clkin_reg);
clock_generator.write(si5351c_ms_0_8m_reg);
clock_generator.write(si5351c_ms_1_group_reg);
clock_generator.write(si5351c_ms_2_group_reg);
clock_generator.write(si5351c_ms_3_10m_reg);
clock_generator.write(si5351c_ms_4_reg);
clock_generator.write(si5351c_ms_5_reg);
clock_generator.write(si5351c_ms6_7_off_mcu_clkin_reg);
}
clock_generator.reset_plls();
// Wait for both PLLs to lock.
// TODO: Disable the unused PLL?
const uint8_t device_status_mask = (ref_pll == ClockControl::MultiSynthSource::PLLB) ? 0x40 : 0x20;
// Wait for PLL(s) to lock.
uint8_t device_status_mask = hackrf_r9
? 0x20
: (ref_pll == ClockControl::MultiSynthSource::PLLB)
? 0x40
: 0x20;
while((clock_generator.device_status() & device_status_mask) != 0);
clock_generator.set_clock_control(
@@ -268,12 +331,18 @@ uint32_t ClockManager::measure_gp_clkin_frequency() {
return get_frequency_monitor_measurement_in_hertz();
}
bool ClockManager::loss_of_signal() {
return hackrf_r9
? clock_generator.plla_loss_of_signal()
: clock_generator.clkin_loss_of_signal();
}
ClockManager::ReferenceSource ClockManager::detect_reference_source() {
if( clock_generator.clkin_loss_of_signal() ) {
if(loss_of_signal()) {
// No external reference. Turn on PortaPack reference (if present).
portapack_tcxo_enable();
if( clock_generator.clkin_loss_of_signal() ) {
if(loss_of_signal()) {
// No PortaPack reference was detected. Choose the HackRF crystal as the reference.
return ReferenceSource::Xtal;
} else {
@@ -285,6 +354,11 @@ ClockManager::ReferenceSource ClockManager::detect_reference_source() {
}
ClockManager::Reference ClockManager::choose_reference() {
if (hackrf_r9) {
gpio_r9_clkin_en.write(1);
volatile uint32_t delay = 240000 + 24000;
while(delay--);
}
const auto detected_reference = detect_reference_source();
if( (detected_reference == ReferenceSource::External) ||
@@ -296,8 +370,12 @@ ClockManager::Reference ClockManager::choose_reference() {
}
}
if (hackrf_r9) {
gpio_r9_clkin_en.write(0);
}
portapack_tcxo_disable();
return { ReferenceSource::Xtal, 10000000 };
return { ReferenceSource::Xtal, 25000000 };
}
void ClockManager::shutdown() {
@@ -305,18 +383,26 @@ void ClockManager::shutdown() {
}
void ClockManager::enable_codec_clocks() {
clock_generator.enable_clock(clock_generator_output_codec);
clock_generator.enable_clock(clock_generator_output_cpld);
clock_generator.enable_clock(clock_generator_output_sgpio);
if (hackrf_r9) {
clock_generator.enable_clock(clock_generator_output_r9_sgpio);
} else {
clock_generator.enable_clock(clock_generator_output_og_codec);
clock_generator.enable_clock(clock_generator_output_og_cpld);
clock_generator.enable_clock(clock_generator_output_og_sgpio);
}
/* Turn on all outputs at the same time. This probably doesn't ensure
* their phase relationships. For example, clocks that output frequencies
* in a 2:1 relationship may start with the slower clock high or low?
*/
clock_generator.enable_output_mask(
(1U << clock_generator_output_codec)
| (1U << clock_generator_output_cpld)
| (1U << clock_generator_output_sgpio)
);
if (hackrf_r9) {
clock_generator.enable_output_mask(1U << clock_generator_output_r9_sgpio);
} else {
clock_generator.enable_output_mask(
(1U << clock_generator_output_og_codec)
| (1U << clock_generator_output_og_cpld)
| (1U << clock_generator_output_og_sgpio)
);
}
}
void ClockManager::disable_codec_clocks() {
@@ -324,34 +410,45 @@ void ClockManager::disable_codec_clocks() {
* be enabled for the output to come to rest at the state specified by
* CLKx_DISABLE_STATE.
*/
clock_generator.disable_output_mask(
(1U << clock_generator_output_codec)
| (1U << clock_generator_output_cpld)
| (1U << clock_generator_output_sgpio)
);
clock_generator.disable_clock(clock_generator_output_codec);
clock_generator.disable_clock(clock_generator_output_cpld);
clock_generator.disable_clock(clock_generator_output_sgpio);
if (hackrf_r9) {
clock_generator.disable_output_mask(1U << clock_generator_output_r9_sgpio);
clock_generator.disable_clock(clock_generator_output_r9_sgpio);
} else {
clock_generator.disable_output_mask(
(1U << clock_generator_output_og_codec)
| (1U << clock_generator_output_og_cpld)
| (1U << clock_generator_output_og_sgpio)
);
clock_generator.disable_clock(clock_generator_output_og_codec);
clock_generator.disable_clock(clock_generator_output_og_cpld);
clock_generator.disable_clock(clock_generator_output_og_sgpio);
}
}
void ClockManager::enable_first_if_clock() {
clock_generator.enable_clock(clock_generator_output_first_if);
clock_generator.enable_output_mask(1U << clock_generator_output_first_if);
void ClockManager::enable_if_clocks() {
if (hackrf_r9) {
clock_generator.enable_clock(clock_generator_output_r9_if);
clock_generator.enable_output_mask(1U << clock_generator_output_r9_if);
} else {
clock_generator.enable_clock(clock_generator_output_og_first_if);
clock_generator.enable_output_mask(1U << clock_generator_output_og_first_if);
clock_generator.enable_clock(clock_generator_output_og_second_if);
clock_generator.enable_output_mask(1U << clock_generator_output_og_second_if);
}
}
void ClockManager::disable_first_if_clock() {
clock_generator.disable_output_mask(1U << clock_generator_output_first_if);
clock_generator.disable_clock(clock_generator_output_first_if);
}
void ClockManager::enable_second_if_clock() {
clock_generator.enable_clock(clock_generator_output_second_if);
clock_generator.enable_output_mask(1U << clock_generator_output_second_if);
}
void ClockManager::disable_second_if_clock() {
clock_generator.disable_output_mask(1U << clock_generator_output_second_if);
clock_generator.disable_clock(clock_generator_output_second_if);
void ClockManager::disable_if_clocks() {
if (hackrf_r9) {
clock_generator.disable_output_mask(1U << clock_generator_output_r9_if);
clock_generator.disable_clock(clock_generator_output_r9_if);
} else {
clock_generator.disable_output_mask(1U << clock_generator_output_og_first_if);
clock_generator.disable_clock(clock_generator_output_og_first_if);
clock_generator.disable_output_mask(1U << clock_generator_output_og_second_if);
clock_generator.disable_clock(clock_generator_output_og_second_if);
}
}
void ClockManager::set_sampling_frequency(const uint32_t frequency) {
@@ -360,14 +457,21 @@ void ClockManager::set_sampling_frequency(const uint32_t frequency) {
* necessary to change the MS0 synth frequency, and ensure the output
* is divided by two.
*/
clock_generator.set_ms_frequency(clock_generator_output_codec, frequency * 2, si5351_vco_f, 1);
if (hackrf_r9) {
clock_generator.set_ms_frequency(clock_generator_output_r9_sgpio, frequency * 2, si5351_vco_f, 0);
} else {
clock_generator.set_ms_frequency(clock_generator_output_og_codec, frequency * 2, si5351_vco_f, 1);
}
}
void ClockManager::set_reference_ppb(const int32_t ppb) {
/* NOTE: This adjustment only affects PLLA, which is derived from the 25MHz crystal.
* It is assumed an external clock coming in to PLLB is sufficiently accurate as to not need adjustment.
/* NOTE: This adjustment only affects PLLA when it is derived from the 25MHz crystal.
* It is assumed an external clock coming in to CLKIN/PLLB is sufficiently accurate as to not need adjustment.
* TODO: Revisit the above policy. It may be good to allow adjustment of the external reference too.
*/
if (hackrf_r9 && reference.source != ReferenceSource::Xtal) {
return;
}
constexpr uint32_t pll_multiplier = si5351_pll_xtal_25m.a;
constexpr uint32_t denominator = 1000000 / pll_multiplier;
const uint32_t new_a = (ppb >= 0) ? pll_multiplier : (pll_multiplier - 1);
@@ -425,11 +529,13 @@ void ClockManager::start_audio_pll() {
/* For 40MHz clock source, 48kHz audio rate, 256Fs MCLK:
* Fout=12.288MHz, Fcco=491.52MHz
* PSEL=20, NSEL=125, MSEL=768
* OG: PSEL=20, NSEL=125, MSEL=768
* PDEC=31, NDEC=45, MDEC=30542
* r9: PSEL=20, NSEL=125, MSEL=3072
* PDEC=31, NDEC=45, MDEC=8308
*/
cgu::pll0audio::mdiv({
.mdec = 30542,
.mdec = hackrf_r9 ? 8308UL : 30542UL,
});
cgu::pll0audio::np_div({
.pdec = 31,
@@ -467,18 +573,25 @@ void ClockManager::stop_audio_pll() {
void ClockManager::enable_clock_output(bool enable) {
if(enable) {
clock_generator.enable_output(clock_generator_output_clkout);
clock_generator.enable_output(clock_generator_output_og_clkout);
if(portapack::persistent_memory::clkout_freq() < 1000) {
clock_generator.set_ms_frequency(clock_generator_output_clkout, portapack::persistent_memory::clkout_freq() * 128000, si5351_vco_f, 7);
clock_generator.set_ms_frequency(clock_generator_output_og_clkout, portapack::persistent_memory::clkout_freq() * 128000, si5351_vco_f, 7);
} else {
clock_generator.set_ms_frequency(clock_generator_output_clkout, portapack::persistent_memory::clkout_freq() * 1000, si5351_vco_f, 0);
clock_generator.set_ms_frequency(clock_generator_output_og_clkout, portapack::persistent_memory::clkout_freq() * 1000, si5351_vco_f, 0);
}
} else {
clock_generator.disable_output(clock_generator_output_clkout);
clock_generator.disable_output(clock_generator_output_og_clkout);
}
auto si5351_clock_control_common = hackrf_r9
? si5351a_clock_control_common
: si5351c_clock_control_common;
const auto ref_pll = hackrf_r9
? ClockControl::MultiSynthSource::PLLA
: get_si5351c_reference_clock_generator_pll(reference.source);
if(enable)
clock_generator.set_clock_control(clock_generator_output_clkout, si5351_clock_control_common[clock_generator_output_clkout].ms_src(get_reference_clock_generator_pll(reference.source)).clk_pdn(ClockControl::ClockPowerDown::Power_On));
clock_generator.set_clock_control(clock_generator_output_og_clkout, si5351_clock_control_common[clock_generator_output_og_clkout].ms_src(ref_pll).clk_pdn(ClockControl::ClockPowerDown::Power_On));
else
clock_generator.set_clock_control(clock_generator_output_clkout, ClockControl::power_off());
clock_generator.set_clock_control(clock_generator_output_og_clkout, ClockControl::power_off());
}

View File

@@ -65,11 +65,8 @@ public:
void enable_codec_clocks();
void disable_codec_clocks();
void enable_first_if_clock();
void disable_first_if_clock();
void enable_second_if_clock();
void disable_second_if_clock();
void enable_if_clocks();
void disable_if_clocks();
void set_sampling_frequency(const uint32_t frequency);
@@ -99,6 +96,7 @@ private:
ReferenceSource detect_reference_source();
Reference choose_reference();
bool loss_of_signal();
};
#endif/*__CLOCK_MANAGER_H__*/

View File

@@ -38,7 +38,7 @@ using namespace lpc43xx;
* I suppose I could force M4MEMMAP to an invalid memory reason which would
* cause an exception and effectively halt the M4. But that feels gross.
*/
void m4_init(const portapack::spi_flash::image_tag_t image_tag, const portapack::memory::region_t to) {
void m4_init(const portapack::spi_flash::image_tag_t image_tag, const portapack::memory::region_t to, const bool full_reset) {
const portapack::spi_flash::chunk_t* chunk = reinterpret_cast<const portapack::spi_flash::chunk_t*>(portapack::spi_flash::images.base());
while(chunk->tag) {
if( chunk->tag == image_tag ) {
@@ -50,8 +50,11 @@ void m4_init(const portapack::spi_flash::image_tag_t image_tag, const portapack:
*/
LPC_CREG->M4MEMMAP = to.base();
/* Reset M4 core */
LPC_RGU->RESET_CTRL[0] = (1 << 13);
/* Reset M4 core and optionally all peripherals */
LPC_RGU->RESET_CTRL[0] = (full_reset) ?
(1 << 1) // PERIPH_RST
: (1 << 13) // M4_RST
;
return;
}

View File

@@ -27,7 +27,7 @@
#include "memory_map.hpp"
#include "spi_image.hpp"
void m4_init(const portapack::spi_flash::image_tag_t image_tag, const portapack::memory::region_t to);
void m4_init(const portapack::spi_flash::image_tag_t image_tag, const portapack::memory::region_t to, const bool full_reset);
void m4_request_shutdown();
void m0_halt();

View File

@@ -0,0 +1,13 @@
#include "../../hackrf/firmware/common/firmware_info.h"
#include "../../hackrf/firmware/common/platform_detect.h"
#define SUPPORTED_PLATFORM (PLATFORM_HACKRF1_OG | PLATFORM_HACKRF1_R9)
#define DFU_MODE_VALUE 0
__attribute__((section(".firmware_info"))) const struct firmware_info_t firmware_info = {
.magic = "HACKRFFW",
.struct_version = 1,
.dfu_mode = DFU_MODE_VALUE,
.supported_platform = SUPPORTED_PLATFORM,
.version_string = VERSION_STRING,
};

View File

@@ -32,8 +32,12 @@ using namespace hackrf::one;
namespace max2837 {
using namespace max283x;
namespace lna {
using namespace max283x::lna;
constexpr std::array<uint8_t, 8> lookup_8db_steps {
0b111, 0b011, 0b110, 0b010,
0b100, 0b000, 0b000, 0b000
@@ -48,6 +52,8 @@ static uint_fast8_t gain_ordinal(const int8_t db) {
namespace vga {
using namespace max283x::vga;
static uint_fast8_t gain_ordinal(const int8_t db) {
const auto db_sat = gain_db_range.clip(db);
return ((db_sat >> 1) & 0b11111) ^ 0b11111;
@@ -57,6 +63,8 @@ static uint_fast8_t gain_ordinal(const int8_t db) {
namespace tx {
using namespace max283x::tx;
static uint_fast8_t gain_ordinal(const int8_t db) {
const auto db_sat = gain_db_range.clip(db);
uint8_t value = db_sat & 0x0f;
@@ -69,6 +77,8 @@ static uint_fast8_t gain_ordinal(const int8_t db) {
namespace filter {
using namespace max283x::filter;
static uint_fast8_t bandwidth_ordinal(const uint32_t bandwidth) {
/* Determine filter setting that will provide bandwidth greater than or
* equal to requested bandwidth.
@@ -84,13 +94,13 @@ static uint_fast8_t bandwidth_ordinal(const uint32_t bandwidth) {
constexpr float seconds_for_temperature_sense_adc_conversion = 30.0e-6;
constexpr halrtcnt_t ticks_for_temperature_sense_adc_conversion = (base_m4_clk_f * seconds_for_temperature_sense_adc_conversion + 1);
constexpr uint32_t reference_frequency = max2837_reference_f;
constexpr uint32_t reference_frequency = max283x_reference_f;
constexpr uint32_t pll_factor = 1.0 / (4.0 / 3.0 / reference_frequency) + 0.5;
void MAX2837::init() {
set_mode(Mode::Shutdown);
gpio_max2837_enable.output();
gpio_max283x_enable.output();
gpio_max2837_rxenable.output();
gpio_max2837_txenable.output();
@@ -140,10 +150,30 @@ void MAX2837::init() {
set_mode(Mode::Standby);
}
enum class Mask {
Enable = 0b001,
RxEnable = 0b010,
TxEnable = 0b100,
Shutdown = 0b000,
Standby = Enable,
Receive = Enable | RxEnable,
Transmit = Enable | TxEnable,
};
Mask mode_mask(const Mode mode) {
switch (mode) {
case Mode::Standby: return Mask::Standby;
case Mode::Receive: return Mask::Receive;
case Mode::Transmit: return Mask::Transmit;
default: return Mask::Shutdown;
}
}
void MAX2837::set_mode(const Mode mode) {
gpio_max2837_enable.write(toUType(mode) & toUType(Mode::Mask_Enable));
gpio_max2837_rxenable.write(toUType(mode) & toUType(Mode::Mask_RxEnable));
gpio_max2837_txenable.write(toUType(mode) & toUType(Mode::Mask_TxEnable));
Mask mask = mode_mask(mode);
gpio_max283x_enable.write(toUType(mask) & toUType(Mask::Enable));
gpio_max2837_rxenable.write(toUType(mask) & toUType(Mask::RxEnable));
gpio_max2837_txenable.write(toUType(mask) & toUType(Mask::TxEnable));
}
void MAX2837::flush() {

View File

@@ -22,6 +22,7 @@
#ifndef __MAX2837_H__
#define __MAX2837_H__
#include "max283x.hpp"
#include "gpio.hpp"
#include "spi_arbiter.hpp"
@@ -29,99 +30,11 @@
#include <array>
#include "dirty_registers.hpp"
#include "rf_path.hpp"
#include "utility.hpp"
namespace max2837 {
enum class Mode {
Mask_Enable = 0b001,
Mask_RxEnable = 0b010,
Mask_TxEnable = 0b100,
Shutdown = 0b000,
Standby = Mask_Enable,
Receive = Mask_Enable | Mask_RxEnable,
Transmit = Mask_Enable | Mask_TxEnable,
};
/*************************************************************************/
namespace lo {
constexpr std::array<rf::FrequencyRange, 4> band { {
{ 2300000000, 2400000000 },
{ 2400000000, 2500000000 },
{ 2500000000, 2600000000 },
{ 2600000000, 2700000000 },
} };
} /* namespace lo */
/*************************************************************************/
namespace lna {
constexpr range_t<int8_t> gain_db_range { 0, 40 };
constexpr int8_t gain_db_step = 8;
constexpr std::array<rf::FrequencyRange, 2> band { {
{ 2300000000, 2500000000 },
{ 2500000000, 2700000000 },
} };
} /* namespace lna */
/*************************************************************************/
namespace vga {
constexpr range_t<int8_t> gain_db_range { 0, 62 };
constexpr int8_t gain_db_step = 2;
} /* namespace vga */
/*************************************************************************/
namespace tx {
constexpr range_t<int8_t> gain_db_range { 0, 47 };
constexpr int8_t gain_db_step = 1;
}
/*************************************************************************/
namespace filter {
constexpr std::array<uint32_t, 16> bandwidths {
/* Assumption: these values are in ascending order */
1750000,
2500000, /* Some documentation says 2.25MHz */
3500000,
5000000,
5500000,
6000000,
7000000,
8000000,
9000000,
10000000,
12000000,
14000000,
15000000,
20000000,
24000000,
28000000,
};
constexpr auto bandwidth_minimum = bandwidths[0];
constexpr auto bandwidth_maximum = bandwidths[bandwidths.size() - 1];
} /* namespace filter */
/*************************************************************************/
using reg_t = uint16_t;
using address_t = uint8_t;
using namespace max283x;
constexpr size_t reg_count = 32;
@@ -826,7 +739,7 @@ constexpr RegisterMap initial_register_values { Register_Type {
},
} };
class MAX2837 {
class MAX2837 : public MAX283x {
public:
constexpr MAX2837(
spi::arbiter::Target& target
@@ -834,13 +747,13 @@ public:
{
}
void init();
void set_mode(const Mode mode);
void init() override;
void set_mode(const Mode mode) override;
void set_tx_vga_gain(const int_fast8_t db);
void set_lna_gain(const int_fast8_t db);
void set_vga_gain(const int_fast8_t db);
void set_lpf_rf_bandwidth(const uint32_t bandwidth_minimum);
void set_tx_vga_gain(const int_fast8_t db) override;
void set_lna_gain(const int_fast8_t db) override;
void set_vga_gain(const int_fast8_t db) override;
void set_lpf_rf_bandwidth(const uint32_t bandwidth_minimum) override;
#if 0
void rx_cal() {
_map.r.spi_en.EN_SPI = 1;
@@ -882,16 +795,16 @@ public:
}
#endif
bool set_frequency(const rf::Frequency lo_frequency);
bool set_frequency(const rf::Frequency lo_frequency) override;
void set_rx_lo_iq_calibration(const size_t v);
void set_rx_lo_iq_calibration(const size_t v) override;
void set_rx_bias_trim(const size_t v);
void set_vco_bias(const size_t v);
void set_rx_buff_vcm(const size_t v);
void set_rx_buff_vcm(const size_t v) override;
reg_t temp_sense();
reg_t temp_sense() override;
reg_t read(const address_t reg_num);
reg_t read(const address_t reg_num) override;
private:
spi::arbiter::Target& _target;

View File

@@ -0,0 +1,354 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2023 Great Scott Gadgets
*
* This file is part of PortaPack.
*
* 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.
*/
#include "max2839.hpp"
#include "hackrf_hal.hpp"
#include "hackrf_gpio.hpp"
using namespace hackrf::one;
#include "ch.h"
#include "hal.h"
#include <algorithm>
namespace max2839 {
namespace lna {
using namespace max283x::lna;
constexpr std::array<uint8_t, 8> lookup_8db_steps {
0b11, 0b11, 0b10, 0b10,
0b01, 0b00, 0b00, 0b00
};
static uint_fast8_t gain_ordinal(const int8_t db) {
const auto db_sat = gain_db_range.clip(db);
return lna::lookup_8db_steps[(db_sat >> 3) & 7];
}
} /* namespace lna */
namespace vga {
using namespace max283x::vga;
constexpr range_t<int8_t> gain_db_range_internal { 0, 63 };
static uint_fast8_t gain_ordinal(const int8_t db) {
const auto db_sat = gain_db_range_internal.clip(db);
return (db_sat & 0b111111) ^ 0b111111;
}
} /* namespace vga */
namespace tx {
using namespace max283x::tx;
static uint_fast8_t gain_ordinal(const int8_t db) {
const auto db_sat = gain_db_range.clip(db);
return 47 - db_sat;
}
} /* namespace tx */
namespace filter {
using namespace max283x::filter;
static uint_fast8_t bandwidth_ordinal(const uint32_t bandwidth) {
/* Determine filter setting that will provide bandwidth greater than or
* equal to requested bandwidth.
*/
return std::lower_bound(bandwidths.cbegin(), bandwidths.cend(), bandwidth) - bandwidths.cbegin();
}
} /* namespace filter */
/* Empirical testing indicates about 25us is necessary to get a valid
* temperature sense conversion from the ADC.
*/
constexpr float seconds_for_temperature_sense_adc_conversion = 30.0e-6;
constexpr halrtcnt_t ticks_for_temperature_sense_adc_conversion = (base_m4_clk_f * seconds_for_temperature_sense_adc_conversion + 1);
constexpr uint32_t reference_frequency = max283x_reference_f;
constexpr uint32_t pll_factor = 1.0 / (4.0 / 3.0 / reference_frequency) + 0.5;
static int_fast8_t requested_rx_lna_gain = 0;
static int_fast8_t requested_rx_vga_gain = 0;
void MAX2839::init() {
set_mode(Mode::Shutdown);
gpio_max283x_enable.output();
gpio_max2839_rxtx.output();
_map.r.rxrf_1.MIMOmode = 1; /* enable RXINB */
_map.r.pa_drv.TXVGA_GAIN_SPI_EN = 1;
_map.r.tx_gain.TXVGA_GAIN_SPI = 0x00;
_map.r.hpfsm_3.HPC_STOP = 1; /* 1kHz */
_map.r.rxrf_2.LNAgain_SPI_EN = 1; /* control LNA gain from SPI */
_map.r.lpf_vga_1.L = 0b000;
_map.r.lpf_vga_2.L = 0b000;
_map.r.rx_top_1.VGAgain_SPI_EN = 1; /* control VGA gain from SPI */
_map.r.lpf_vga_1.VGA = 0b000000;
_map.r.lpf_vga_2.VGA = 0b010101;
_map.r.lpf_vga_2.BUFF_VCM = 0b11; /* maximum RX output common-mode voltage */
_map.r.lpf_vga_1.ModeCtrl = 0b01; /* Rx LPF */
_map.r.lpf.FT = 0b0000; /* 1.75 MHz LPF */
_map.r.spi_en.EN_SPI = 1; /* enable chip functions when ENABLE pin set */
_map.r.lo_gen.LOGEN_2GM = 0;
_map.r.rssi_vga.RSSI_MODE = 1; /* RSSI independent of RXHP */
/*
* There are two LNA band settings, but we only use one of them.
* Switching to the other one doesn't make the overall spectrum any
* flatter but adds a surprise step in the middle.
*/
_map.r.rxrf_1.LNAband = 0; /* 2.3 - 2.5GHz */
_dirty.set();
flush();
set_mode(Mode::Standby);
}
enum class Mask {
Enable = 0b01,
RxTx = 0b10,
Shutdown = 0b00,
Standby = RxTx,
Receive = Enable | RxTx,
Transmit = Enable,
};
Mask mode_mask(const Mode mode) {
switch (mode) {
case Mode::Standby: return Mask::Standby;
case Mode::Receive: return Mask::Receive;
case Mode::Transmit: return Mask::Transmit;
default: return Mask::Shutdown;
}
}
void MAX2839::set_mode(const Mode mode) {
Mask mask = mode_mask(mode);
gpio_max283x_enable.write(toUType(mask) & toUType(Mask::Enable));
gpio_max2839_rxtx.write(toUType(mask) & toUType(Mask::RxTx));
}
void MAX2839::flush() {
if( _dirty ) {
for(size_t n=0; n<reg_count; n++) {
if( _dirty[n] ) {
write(n, _map.w[n]);
}
}
_dirty.clear();
}
}
void MAX2839::flush_one(const Register reg) {
const auto reg_num = toUType(reg);
write(reg_num, _map.w[reg_num]);
_dirty.clear(reg_num);
}
void MAX2839::write(const address_t reg_num, const reg_t value) {
uint16_t t = (0U << 15) | (reg_num << 10) | (value & 0x3ffU);
_target.transfer(&t, 1);
}
reg_t MAX2839::read(const address_t reg_num) {
uint16_t t = (1U << 15) | (reg_num << 10);
_target.transfer(&t, 1U);
return t & 0x3ffU;
}
void MAX2839::write(const Register reg, const reg_t value) {
write(toUType(reg), value);
}
reg_t MAX2839::read(const Register reg) {
return read(toUType(reg));
}
void MAX2839::set_tx_vga_gain(const int_fast8_t db) {
_map.r.tx_gain.TXVGA_GAIN_SPI = tx::gain_ordinal(db);
_dirty[Register::TX_GAIN] = 1;
flush();
}
/*
* MAX2839 gain rain ranges differ slightly from MAX2837's but are close
* enough that it makes sense to emulate MAX2837 gain ranges for a consistent
* user experience.
*/
void MAX2839::configure_rx_gain() {
/* Apply MAX2837 restrictions to requested gain settings. */
int_fast8_t lna_gain = lna::gain_db_range.clip(requested_rx_lna_gain);
lna_gain &= 0x38;
int_fast8_t vga_gain = vga::gain_db_range.clip(requested_rx_vga_gain);
vga_gain &= 0x3e;
/*
* MAX2839 has lower full-scale RX output voltage than MAX2837, so we
* adjust the VGA (baseband) gain to compensate.
*/
vga_gain += 3;
/*
* If that adjustment puts VGA gain out of range, use LNA gain to
* compensate. MAX2839 VGA gain can be any number from 0 through 63.
*/
if (vga_gain > 63) {
if (lna_gain <= 32) {
vga_gain -= 8;
lna_gain += 8;
} else {
vga_gain = 63;
}
}
/*
* MAX2839 lacks max-24 dB (16 dB) and max-40 dB (0 dB) LNA gain
* settings, so we use VGA gain to compensate.
*/
if (lna_gain == 0) {
lna_gain = 8;
vga_gain = (vga_gain >= 8) ? vga_gain - 8 : 0;
}
if (lna_gain == 16) {
if (vga_gain > 32) {
vga_gain -= 8;
lna_gain += 8;
} else {
vga_gain += 8;
lna_gain -= 8;
}
}
_map.r.lpf_vga_2.L = lna::gain_ordinal(lna_gain);
_dirty[Register::RXRF_2] = 1;
_map.r.lpf_vga_2.VGA = vga::gain_ordinal(vga_gain);
_dirty[Register::LPF_VGA_2] = 1;
flush();
}
void MAX2839::set_lna_gain(const int_fast8_t db) {
requested_rx_lna_gain = db;
configure_rx_gain();
}
void MAX2839::set_vga_gain(const int_fast8_t db) {
requested_rx_vga_gain = db;
configure_rx_gain();
}
void MAX2839::set_lpf_rf_bandwidth(const uint32_t bandwidth_minimum) {
_map.r.lpf.FT = filter::bandwidth_ordinal(bandwidth_minimum);
_dirty[Register::LPF] = 1;
flush();
}
bool MAX2839::set_frequency(const rf::Frequency lo_frequency) {
/* TODO: This is a sad implementation. Refactor. */
if( lo::band[0].contains(lo_frequency) ) {
_map.r.syn_int_div.LOGEN_BSW = 0b00; /* 2300 - 2399.99MHz */
} else if( lo::band[1].contains(lo_frequency) ) {
_map.r.syn_int_div.LOGEN_BSW = 0b01; /* 2400 - 2499.99MHz */
} else if( lo::band[2].contains(lo_frequency) ) {
_map.r.syn_int_div.LOGEN_BSW = 0b10; /* 2500 - 2599.99MHz */
} else if( lo::band[3].contains(lo_frequency) ) {
_map.r.syn_int_div.LOGEN_BSW = 0b11; /* 2600 - 2700Hz */
} else {
return false;
}
_dirty[Register::SYN_INT_DIV] = 1;
const uint64_t div_q20 = (lo_frequency * (1 << 20)) / pll_factor;
_map.r.syn_int_div.SYN_INTDIV = div_q20 >> 20;
_dirty[Register::SYN_INT_DIV] = 1;
_map.r.syn_fr_div_2.SYN_FRDIV_19_10 = (div_q20 >> 10) & 0x3ff;
_dirty[Register::SYN_FR_DIV_2] = 1;
/* flush to commit high FRDIV first, as low FRDIV commits the change */
flush();
_map.r.syn_fr_div_1.SYN_FRDIV_9_0 = (div_q20 & 0x3ff);
_dirty[Register::SYN_FR_DIV_1] = 1;
flush();
return true;
}
void MAX2839::set_rx_lo_iq_calibration(const size_t v) {
_map.r.rxrf_2.RX_IQERR_SPI_EN = 1;
_dirty[Register::RXRF_2] = 1;
_map.r.rxrf_1.iqerr_trim = v;
_dirty[Register::RXRF_1] = 1;
flush();
}
void MAX2839::set_rx_buff_vcm(const size_t v) {
_map.r.lpf_vga_2.BUFF_VCM = v;
_dirty[Register::LPF_VGA_2] = 1;
flush();
}
reg_t MAX2839::temp_sense() {
if( !_map.r.rx_top_2.ts_en ) {
_map.r.rx_top_2.ts_en = 1;
flush_one(Register::RX_TOP_2);
chThdSleepMilliseconds(1);
}
_map.r.rx_top_2.ts_adc_trigger = 1;
flush_one(Register::RX_TOP_2);
halPolledDelay(ticks_for_temperature_sense_adc_conversion);
/*
* Things look very similar to MAX2837, so this probably works, but the
* MAX2839 data sheet does not describe the TEMP_SENSE register contents.
*/
const auto value = read(Register::TEMP_SENSE);
_map.r.rx_top_2.ts_adc_trigger = 0;
flush_one(Register::RX_TOP_2);
return value;
}
}

View File

@@ -0,0 +1,688 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2023 Great Scott Gadgets
*
* This file is part of PortaPack.
*
* 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 __MAX2839_H__
#define __MAX2839_H__
#include "max283x.hpp"
#include "gpio.hpp"
#include "spi_arbiter.hpp"
#include <cstdint>
#include <array>
#include "dirty_registers.hpp"
#include "utility.hpp"
namespace max2839 {
using namespace max283x;
constexpr size_t reg_count = 32;
enum class Register : address_t {
RXENABLE = 0,
RXRF_1 = 1,
RXRF_2 = 2,
RXRF_LPF = 3,
LPF = 4,
LPF_VGA_1 = 5,
LPF_VGA_2 = 6,
RSSI_VGA = 7,
RX_TOP_1 = 8,
RX_TOP_2 = 9,
TX_TOP_1 = 10,
TEMP_SENSE = 11,
HPFSM_1 = 12,
HPFSM_2 = 13,
HPFSM_3 = 14,
HPFSM_4 = 15,
SPI_EN = 16,
SYN_FR_DIV_1 = 17,
SYN_FR_DIV_2 = 18,
SYN_INT_DIV = 19,
SYN_CFG_1 = 20,
SYN_CFG_2 = 21,
VAS_CFG = 22,
LO_MISC = 23,
XTAL_CFG = 24,
VCO_CFG = 25,
LO_GEN = 26,
PA_DRV = 27,
PA_DAC = 28,
TX_GAIN = 29,
TX_LO_IQ = 30,
TX_DC_CORR = 31,
};
struct RXENABLE_Type {
reg_t RESERVED0 : 10;
reg_t RESERVED1 : 6;
};
static_assert(sizeof(RXENABLE_Type) == sizeof(reg_t), "RXENABLE_Type wrong size");
struct RXRF_1_Type {
reg_t LNAband : 1;
reg_t RESERVED0 : 1;
reg_t MIMOmode : 1;
reg_t iqerr_trim : 5;
reg_t RESERVED1 : 6;
};
static_assert(sizeof(RXRF_1_Type) == sizeof(reg_t), "RXRF_1_Type wrong size");
struct RXRF_2_Type {
reg_t LNAgain_SPI_EN : 1;
reg_t RESERVED0 : 1;
reg_t RX_IQERR_SPI_EN : 1;
reg_t RESERVED1 : 7;
reg_t RESERVED2 : 6;
};
static_assert(sizeof(RXRF_2_Type) == sizeof(reg_t), "RXRF_2_Type wrong size");
struct RXRF_LPF_Type {
reg_t RESERVED0 : 10;
reg_t RESERVED1 : 6;
};
static_assert(sizeof(RXRF_LPF_Type) == sizeof(reg_t), "RXRF_LPF_Type wrong size");
struct LPF_Type {
reg_t RESERVED0 : 2;
reg_t dF : 2;
reg_t RESERVED1 : 2;
reg_t FT : 4;
reg_t RESERVED2 : 6;
};
static_assert(sizeof(LPF_Type) == sizeof(reg_t), "LPF_Type wrong size");
struct LPF_VGA_1_Type {
reg_t L : 2;
reg_t VGA : 6;
reg_t ModeCtrl : 2;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(LPF_VGA_1_Type) == sizeof(reg_t), "LPF_VGA_1_Type wrong size");
struct LPF_VGA_2_Type {
reg_t L : 2;
reg_t VGA : 6;
reg_t BUFF_VCM : 2;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(LPF_VGA_2_Type) == sizeof(reg_t), "LPF_VGA_2_Type wrong size");
struct RSSI_VGA_Type {
reg_t RESERVED0 : 1;
reg_t RSSI_MUX : 1;
reg_t RSSI_MODE : 1;
reg_t RESERVED1 : 4;
reg_t RXBB_OUT_SEL : 1;
reg_t RESERVED2 : 1;
reg_t RSSI_INPUT : 1;
reg_t RESERVED3 : 6;
};
static_assert(sizeof(RSSI_VGA_Type) == sizeof(reg_t), "RSSI_VGA_Type wrong size");
struct RX_TOP_1_Type {
reg_t RESERVED0 : 1;
reg_t VGAgain_SPI_EN : 1;
reg_t LPF_MODE_SEL : 1;
reg_t RESERVED1 : 7;
reg_t RESERVED2 : 6;
};
static_assert(sizeof(RX_TOP_1_Type) == sizeof(reg_t), "RX_TOP_1_Type wrong size");
struct RX_TOP_2_Type {
reg_t ts_adc_trigger : 1;
reg_t ts_en : 1;
reg_t RESERVED0 : 1;
reg_t DOUT_DRVH : 1;
reg_t DOUT_CSB_SEL : 1;
reg_t DOUT_SEL : 3;
reg_t RESERVED1 : 2;
reg_t RESERVED2 : 6;
};
static_assert(sizeof(RX_TOP_2_Type) == sizeof(reg_t), "RX_TOP_2_Type wrong size");
struct TX_TOP_1_Type {
reg_t TXCAL_GAIN : 2;
reg_t TXCAL_V2I_FILT : 3;
reg_t RESERVED1 : 5;
reg_t RESERVED2 : 6;
};
static_assert(sizeof(TX_TOP_1_Type) == sizeof(reg_t), "TX_TOP_1_Type wrong size");
struct TEMP_SENSE_Type {
reg_t RESERVED0 : 10;
reg_t RESERVED1 : 6;
};
static_assert(sizeof(TEMP_SENSE_Type) == sizeof(reg_t), "TEMP_SENSE_Type wrong size");
struct HPFSM_1_Type {
reg_t HPC_10M : 2;
reg_t HPC_10M_GAIN : 2;
reg_t HPC_600k : 3;
reg_t HPC_600k_GAIN : 3;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(HPFSM_1_Type) == sizeof(reg_t), "HPFSM_1_Type wrong size");
struct HPFSM_2_Type {
reg_t HPC_100k : 2;
reg_t HPC_100k_GAIN : 2;
reg_t HPC_30k : 2;
reg_t HPC_30k_GAIN : 2;
reg_t HPC_1k : 2;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(HPFSM_2_Type) == sizeof(reg_t), "HPFSM_2_Type wrong size");
struct HPFSM_3_Type {
reg_t HPC_1k_B7B6 : 2;
reg_t HPC_DELAY : 2;
reg_t HPC_STOP : 2;
reg_t HPC_STOP_M2 : 2;
reg_t HPC_RXGAIN_EN : 1;
reg_t TXGATE_EN : 1;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(HPFSM_3_Type) == sizeof(reg_t), "HPFSM_3_Type wrong size");
struct HPFSM_4_Type {
reg_t HPC_DIVH : 1;
reg_t RESERVED0 : 5;
reg_t HPC_SEQ_BYP : 1;
reg_t RESERVED1 : 2;
reg_t HPC_MODE : 1;
reg_t RESERVED2 : 6;
};
static_assert(sizeof(HPFSM_4_Type) == sizeof(reg_t), "HPFSM_4_Type wrong size");
struct SPI_EN_Type {
reg_t EN_SPI : 1;
reg_t CAL_SPI : 1;
reg_t RESERVED0 : 4;
reg_t PADAC_SPI_EN : 1;
reg_t PADAC_TX_EN : 1;
reg_t RESERVED1 : 2;
reg_t RESERVED2 : 6;
};
static_assert(sizeof(SPI_EN_Type) == sizeof(reg_t), "SPI_EN_Type wrong size");
struct SYN_FR_DIV_1_Type {
reg_t SYN_FRDIV_9_0 : 10;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(SYN_FR_DIV_1_Type) == sizeof(reg_t), "SYN_FR_DIV_1_Type wrong size");
struct SYN_FR_DIV_2_Type {
reg_t SYN_FRDIV_19_10 : 10;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(SYN_FR_DIV_2_Type) == sizeof(reg_t), "SYN_FR_DIV_2_Type wrong size");
struct SYN_INT_DIV_Type {
reg_t SYN_INTDIV : 8;
reg_t LOGEN_BSW : 2;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(SYN_INT_DIV_Type) == sizeof(reg_t), "SYN_INT_DIV_Type wrong size");
struct SYN_CFG_1_Type {
reg_t RESERVED0 : 1;
reg_t SYN_REF_DIV_RATIO : 2;
reg_t RESERVED1 : 2;
reg_t SYN_CLOCKOUT_DRIVE : 1;
reg_t RESERVED2 : 4;
reg_t RESERVED3 : 6;
};
static_assert(sizeof(SYN_CFG_1_Type) == sizeof(reg_t), "SYN_CFG_1_Type wrong size");
struct SYN_CFG_2_Type {
reg_t RESERVED0 : 10;
reg_t RESERVED1 : 6;
};
static_assert(sizeof(SYN_CFG_2_Type) == sizeof(reg_t), "SYN_CFG_2_Type wrong size");
struct VAS_CFG_Type {
reg_t VAS_MODE : 1;
reg_t VAS_RELOCK_SEL : 1;
reg_t VAS_DIV : 3;
reg_t VAS_DLY : 2;
reg_t VAS_TRIG_EN : 1;
reg_t RESERVED0 : 2;
reg_t RESERVED1 : 6;
};
static_assert(sizeof(VAS_CFG_Type) == sizeof(reg_t), "VAS_CFG_Type wrong size");
struct LO_MISC_Type {
reg_t VAS_SPI : 5;
reg_t XTAL_BIAS_SEL : 2;
reg_t RESERVED0 : 3;
reg_t RESERVED1 : 6;
};
static_assert(sizeof(LO_MISC_Type) == sizeof(reg_t), "LO_MISC_Type wrong size");
struct XTAL_CFG_Type {
reg_t XTAL_FTUNE : 7;
reg_t RESERVED0 : 1;
reg_t XTAL_CLKOUT_DIV : 1;
reg_t XTAL_CORE_EN : 1;
reg_t RESERVED1 : 6;
};
static_assert(sizeof(XTAL_CFG_Type) == sizeof(reg_t), "XTAL_CFG_Type wrong size");
struct VCO_CFG_Type {
reg_t RESERVED0 : 10;
reg_t RESERVED1 : 6;
};
static_assert(sizeof(VCO_CFG_Type) == sizeof(reg_t), "VCO_CFG_Type wrong size");
struct LO_GEN_Type {
reg_t RESERVED0 : 3;
reg_t LOGEN_2GM : 1;
reg_t RESERVED1 : 2;
reg_t VAS_TST : 4;
reg_t RESERVED2 : 6;
};
static_assert(sizeof(LO_GEN_Type) == sizeof(reg_t), "LO_GEN_Type wrong size");
struct PA_DRV_Type {
reg_t TXLO_IQ_SPI : 6;
reg_t TXLO_IQ_SPI_EN : 1;
reg_t TXVGA_GAIN_SPI_EN : 1;
reg_t TX_DCCORR_SPI_EN : 1;
reg_t RESERVED0 : 1;
reg_t RESERVED1 : 6;
};
static_assert(sizeof(PA_DRV_Type) == sizeof(reg_t), "PA_DRV_Type wrong size");
struct PA_DAC_Type {
reg_t PADAC_BIAS : 6;
reg_t PADAC_DLY : 4;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(PA_DAC_Type) == sizeof(reg_t), "PA_DAC_Type wrong size");
struct TX_GAIN_Type {
reg_t TXVGA_GAIN_SPI : 6;
reg_t RESERVED0 : 4;
reg_t RESERVED1 : 6;
};
static_assert(sizeof(TX_GAIN_Type) == sizeof(reg_t), "TX_GAIN_Type wrong size");
struct TX_LO_IQ_Type {
reg_t TX_DCCORR_I : 6;
reg_t RESERVED0 : 2;
reg_t PADAC_IV : 1;
reg_t PADAC_VMODE : 1;
reg_t RESERVED1 : 6;
};
static_assert(sizeof(TX_LO_IQ_Type) == sizeof(reg_t), "TX_LO_IQ_Type wrong size");
struct TX_DC_CORR_Type {
reg_t TX_DCCORR_Q : 6;
reg_t RESERVED0 : 3;
reg_t PADAC_DIVH : 1;
reg_t RESERVED1 : 6;
};
static_assert(sizeof(TX_DC_CORR_Type) == sizeof(reg_t), "TX_DC_CORR_Type wrong size");
struct Register_Type {
RXENABLE_Type rxenable; /* 0 */
RXRF_1_Type rxrf_1;
RXRF_2_Type rxrf_2;
RXRF_LPF_Type rxrf_lpf_1;
LPF_Type lpf; /* 4 */
LPF_VGA_1_Type lpf_vga_1;
LPF_VGA_2_Type lpf_vga_2;
RSSI_VGA_Type rssi_vga;
RX_TOP_1_Type rx_top_1; /* 8 */
RX_TOP_2_Type rx_top_2;
TX_TOP_1_Type tx_top_1;
TEMP_SENSE_Type temp_sense;
HPFSM_1_Type hpfsm_1; /* 12 */
HPFSM_2_Type hpfsm_2;
HPFSM_3_Type hpfsm_3;
HPFSM_4_Type hpfsm_4;
SPI_EN_Type spi_en; /* 16 */
SYN_FR_DIV_1_Type syn_fr_div_1;
SYN_FR_DIV_2_Type syn_fr_div_2;
SYN_INT_DIV_Type syn_int_div;
SYN_CFG_1_Type syn_cfg_1; /* 20 */
SYN_CFG_2_Type syn_cfg_2;
VAS_CFG_Type vas_cfg;
LO_MISC_Type lo_misc;
XTAL_CFG_Type xtal_cfg; /* 24 */
VCO_CFG_Type vco_cfg;
LO_GEN_Type lo_gen;
PA_DRV_Type pa_drv;
PA_DAC_Type pa_dac; /* 28 */
TX_GAIN_Type tx_gain;
TX_LO_IQ_Type tx_lo_iq;
TX_DC_CORR_Type tx_dc_corr;
};
static_assert(sizeof(Register_Type) == reg_count * sizeof(reg_t), "Register_Type wrong size");
struct RegisterMap {
constexpr RegisterMap(
Register_Type values
) : r(values)
{
}
union {
Register_Type r;
std::array<reg_t, reg_count> w;
};
};
static_assert(sizeof(RegisterMap) == reg_count * sizeof(reg_t), "RegisterMap type wrong size");
constexpr RegisterMap initial_register_values { Register_Type {
/* settings recommended by MAX2839 data sheet */
.rxenable = { /* 0 */
.RESERVED0 = 0,
.RESERVED1 = 0,
},
.rxrf_1 = { /* 1 */
.LNAband = 0,
.RESERVED0 = 0,
.MIMOmode = 1,
.iqerr_trim = 0b000001,
.RESERVED1 = 0,
},
.rxrf_2 = { /* 2 */
.LNAgain_SPI_EN = 0,
.RESERVED0 = 0,
.RX_IQERR_SPI_EN = 0,
.RESERVED1 = 0b0010000,
.RESERVED2 = 0,
},
.rxrf_lpf_1 = { /* 3 */
.RESERVED0 = 0b0110111001,
.RESERVED1 = 0,
},
.lpf = { /* 4 */
.RESERVED0 = 0b10,
.dF = 0b01,
.RESERVED1 = 0b10,
.FT = 0b1111,
.RESERVED2 = 0,
},
.lpf_vga_1 = { /* 5 */
.L = 0b00,
.VGA = 0b000000,
.ModeCtrl = 0b01,
.RESERVED0 = 0,
},
.lpf_vga_2 = { /* 6 */
.L = 0b00,
.VGA = 0b000000,
.BUFF_VCM = 0b00,
.RESERVED0 = 0,
},
.rssi_vga = { /* 7 */
.RESERVED0 = 0,
.RSSI_MUX = 0,
.RSSI_MODE = 0,
.RESERVED1 = 0b0001,
.RXBB_OUT_SEL = 0,
.RESERVED2 = 0,
.RSSI_INPUT = 1,
.RESERVED3 = 0,
},
.rx_top_1 = { /* 8 */
.RESERVED0 = 0,
.VGAgain_SPI_EN = 0,
.LPF_MODE_SEL = 0,
.RESERVED1 = 0b1000100,
.RESERVED2 = 0,
},
.rx_top_2 = { /* 9 */
.ts_adc_trigger = 0,
.ts_en = 0,
.RESERVED0 = 0,
.DOUT_DRVH = 1,
.DOUT_CSB_SEL = 1,
.DOUT_SEL = 0b000,
.RESERVED1 = 0b00,
.RESERVED2 = 0,
},
.tx_top_1 = { /* 10 */
.TXCAL_GAIN = 0b00,
.TXCAL_V2I_FILT = 0b011,
.RESERVED1 = 0b00000,
.RESERVED2 = 0,
},
.temp_sense = { /* 11 */
.RESERVED0 = 0b0000000100,
.RESERVED1 = 0,
},
.hpfsm_1 = { /* 12 */
.HPC_10M = 0b11,
.HPC_10M_GAIN = 0b11,
.HPC_600k = 0b100,
.HPC_600k_GAIN = 0b100,
.RESERVED0 = 0,
},
.hpfsm_2 = { /* 13 */
.HPC_100k = 0b00,
.HPC_100k_GAIN = 0b00,
.HPC_30k = 0b01,
.HPC_30k_GAIN = 0b01,
.HPC_1k = 0b01,
.RESERVED0 = 0,
},
.hpfsm_3 = { /* 14 */
.HPC_1k_B7B6 = 0b01,
.HPC_DELAY = 0b01,
.HPC_STOP = 0b00,
.HPC_STOP_M2 = 0b11,
.HPC_RXGAIN_EN = 1,
.TXGATE_EN = 1,
.RESERVED0 = 0,
},
.hpfsm_4 = { /* 15 */
.HPC_DIVH = 1,
.RESERVED0 = 0b00000,
.HPC_SEQ_BYP = 0,
.RESERVED1 = 0b00,
.HPC_MODE = 1,
.RESERVED2 = 0,
},
.spi_en = { /* 16 */
.EN_SPI = 0,
.CAL_SPI = 0,
.RESERVED0 = 0b0111,
.PADAC_SPI_EN = 0,
.PADAC_TX_EN = 0,
.RESERVED1 = 0b00,
.RESERVED2 = 0,
},
.syn_fr_div_1 = { /* 17 */
.SYN_FRDIV_9_0 = 0b0101010101,
.RESERVED0 = 0,
},
.syn_fr_div_2 = { /* 18 */
.SYN_FRDIV_19_10 = 0b0101010101,
.RESERVED0 = 0,
},
.syn_int_div = { /* 19 */
.SYN_INTDIV = 0b01010011,
.LOGEN_BSW = 0b01,
.RESERVED0 = 0,
},
.syn_cfg_1 = { /* 20 */
.RESERVED0 = 1,
.SYN_REF_DIV_RATIO = 0b00,
.RESERVED1 = 0b01,
.SYN_CLOCKOUT_DRIVE = 0,
.RESERVED2 = 0b1001,
.RESERVED3 = 0,
},
.syn_cfg_2 = { /* 21 */
.RESERVED0 = 0b0000101101,
.RESERVED1 = 0,
},
.vas_cfg = { /* 22 */
.VAS_MODE = 1,
.VAS_RELOCK_SEL = 0,
.VAS_DIV = 0b010,
.VAS_DLY = 0b01,
.VAS_TRIG_EN = 1,
.RESERVED0 = 0b01,
.RESERVED1 = 0,
},
.lo_misc = { /* 23 */
.VAS_SPI = 0b01111,
.XTAL_BIAS_SEL = 0b10,
.RESERVED0 = 0b100,
.RESERVED1 = 0,
},
.xtal_cfg = { /* 24 */
.XTAL_FTUNE = 0b0000000,
.RESERVED0 = 1,
.XTAL_CLKOUT_DIV = 1,
.XTAL_CORE_EN = 0,
.RESERVED1 = 0,
},
.vco_cfg = { /* 25 */
.RESERVED0 = 0b0000000000,
.RESERVED1 = 0,
},
.lo_gen = { /* 26 */
.RESERVED0 = 0b000,
.LOGEN_2GM = 0,
.RESERVED1 = 0b00,
.VAS_TST = 0b1111,
.RESERVED2 = 0,
},
.pa_drv = { /* 27 */
.TXLO_IQ_SPI = 0b000000,
.TXLO_IQ_SPI_EN = 0,
.TXVGA_GAIN_SPI_EN = 0,
.TX_DCCORR_SPI_EN = 0,
.RESERVED0 = 1,
.RESERVED1 = 0,
},
.pa_dac = { /* 28 */
.PADAC_BIAS = 0b000000,
.PADAC_DLY = 0b0011,
.RESERVED0 = 0,
},
.tx_gain = { /* 29 */
.TXVGA_GAIN_SPI = 0b111111,
.RESERVED0 = 0b0000,
.RESERVED1 = 0,
},
.tx_lo_iq = { /* 30 */
.TX_DCCORR_I = 0b000000,
.RESERVED0 = 0b00,
.PADAC_IV = 1,
.PADAC_VMODE = 1,
.RESERVED1 = 0,
},
.tx_dc_corr = { /* 31 */
.TX_DCCORR_Q = 0b000000,
.RESERVED0 = 0b101,
.PADAC_DIVH = 1,
.RESERVED1 = 0,
},
} };
class MAX2839 : public MAX283x {
public:
constexpr MAX2839(
spi::arbiter::Target& target
) : _target(target)
{
}
void init() override;
void set_mode(const Mode mode) override;
void set_tx_vga_gain(const int_fast8_t db) override;
void set_lna_gain(const int_fast8_t db) override;
void set_vga_gain(const int_fast8_t db) override;
void set_lpf_rf_bandwidth(const uint32_t bandwidth_minimum) override;
bool set_frequency(const rf::Frequency lo_frequency) override;
void set_rx_lo_iq_calibration(const size_t v) override;
void set_rx_buff_vcm(const size_t v) override;
reg_t temp_sense() override;
reg_t read(const address_t reg_num) override;
private:
spi::arbiter::Target& _target;
RegisterMap _map { initial_register_values };
DirtyRegisters<Register, reg_count> _dirty { };
void flush_one(const Register reg);
void write(const address_t reg_num, const reg_t value);
void write(const Register reg, const reg_t value);
reg_t read(const Register reg);
void flush();
void configure_rx_gain();
};
}
#endif/*__MAX2839_H__*/

View File

@@ -0,0 +1,138 @@
/*
* Copyright (C) 2023 Great Scott Gadgets
*
* This file is part of PortaPack.
*
* 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 __MAX283X_H__
#define __MAX283X_H__
#include "rf_path.hpp"
namespace max283x {
/*************************************************************************/
namespace lo {
constexpr std::array<rf::FrequencyRange, 4> band { {
{ 2300000000, 2400000000 },
{ 2400000000, 2500000000 },
{ 2500000000, 2600000000 },
{ 2600000000, 2700000000 },
} };
} /* namespace lo */
/*************************************************************************/
namespace lna {
constexpr range_t<int8_t> gain_db_range { 0, 40 };
constexpr int8_t gain_db_step = 8;
constexpr std::array<rf::FrequencyRange, 2> band { {
{ 2300000000, 2500000000 },
{ 2500000000, 2700000000 },
} };
} /* namespace lna */
/*************************************************************************/
namespace vga {
constexpr range_t<int8_t> gain_db_range { 0, 62 };
constexpr int8_t gain_db_step = 2;
} /* namespace vga */
/*************************************************************************/
namespace tx {
constexpr range_t<int8_t> gain_db_range { 0, 47 };
constexpr int8_t gain_db_step = 1;
}
/*************************************************************************/
namespace filter {
constexpr std::array<uint32_t, 16> bandwidths {
/* Assumption: these values are in ascending order */
1750000,
2500000, /* Some documentation says 2.25MHz */
3500000,
5000000,
5500000,
6000000,
7000000,
8000000,
9000000,
10000000,
12000000,
14000000,
15000000,
20000000,
24000000,
28000000,
};
constexpr auto bandwidth_minimum = bandwidths[0];
constexpr auto bandwidth_maximum = bandwidths[bandwidths.size() - 1];
} /* namespace filter */
/*************************************************************************/
enum Mode {
Shutdown,
Standby,
Receive,
Transmit,
};
using reg_t = uint16_t;
using address_t = uint8_t;
class MAX283x {
public:
virtual ~MAX283x() = default;
virtual void init();
virtual void set_mode(const Mode mode);
virtual void set_tx_vga_gain(const int_fast8_t db);
virtual void set_lna_gain(const int_fast8_t db);
virtual void set_vga_gain(const int_fast8_t db);
virtual void set_lpf_rf_bandwidth(const uint32_t bandwidth_minimum);
virtual bool set_frequency(const rf::Frequency lo_frequency);
virtual void set_rx_lo_iq_calibration(const size_t v);
virtual void set_rx_buff_vcm(const size_t v);
virtual reg_t temp_sense();
virtual reg_t read(const address_t reg_num);
};
}
#endif/*__MAX283X_H__*/

View File

@@ -372,6 +372,10 @@ public:
while(device_status() & 0x80);
}
bool plla_loss_of_signal() {
return (device_status() >> 5) & 1;
}
bool clkin_loss_of_signal() {
return (device_status() >> 4) & 1;
}

View File

@@ -182,7 +182,7 @@ int main(void) {
portapack::shutdown();
}
m4_init(portapack::spi_flash::image_tag_hackrf, portapack::memory::map::m4_code_hackrf);
m4_init(portapack::spi_flash::image_tag_hackrf, portapack::memory::map::m4_code_hackrf, true);
m0_halt();
return 0;

View File

@@ -381,8 +381,9 @@ static void shutdown_base() {
*
* PLL0USB = powered down
* PLL0AUDIO = GP_CLKIN, Fcco=491.52 MHz, Fout=12.288 MHz
* PLL1 = GP_CLKIN * 10 = 200 MHz
*
* PLL1 =
* OG: GP_CLKIN * 10 = 200 MHz
* r9: GP_CLKIN * 20 = 200 MHz
* IDIVA = IRC / 1 = 12 MHz
* IDIVB = PLL1 / 2 = 100 MHz
* IDIVC = PLL1 / 1 = 200 MHz
@@ -432,9 +433,14 @@ bool init() {
*/
/* Step into the 90-110MHz M4 clock range */
/* Fclkin = 40M
* /N=2 = 20M = PFDin
* Fcco = PFDin * (M=10) = 200M
/* OG:
* Fclkin = 40M
* /N=2 = 20M = PFDin
* Fcco = PFDin * (M=10) = 200M
* r9:
* Fclkin = 10M
* /N=1 = 10M = PFDin
* Fcco = PFDin * (M=20) = 200M
* Fclk = Fcco / (2*(P=1)) = 100M
*/
cgu::pll1::ctrl({
@@ -444,8 +450,8 @@ bool init() {
.direct = 0,
.psel = 0,
.autoblock = 1,
.nsel = 1,
.msel = 9,
.nsel = hackrf_r9 ? 0UL : 1UL,
.msel = hackrf_r9 ? 19UL : 9UL,
.clk_sel = cgu::CLK_SEL::GP_CLKIN,
});
@@ -474,8 +480,7 @@ bool init() {
chThdSleepMilliseconds(10);
clock_manager.set_reference_ppb(persistent_memory::correction_ppb());
clock_manager.enable_first_if_clock();
clock_manager.enable_second_if_clock();
clock_manager.enable_if_clocks();
clock_manager.enable_codec_clocks();
radio::init();

View File

@@ -25,6 +25,7 @@
#include "rffc507x.hpp"
#include "max2837.hpp"
#include "max2839.hpp"
#include "max5864.hpp"
#include "baseband_cpld.hpp"
@@ -52,12 +53,12 @@ static constexpr uint32_t ssp_scr(
return static_cast<uint8_t>(pclk_f / cpsr / spi_f - 1);
}
static constexpr SPIConfig ssp_config_max2837 = {
static constexpr SPIConfig ssp_config_max283x = {
.end_cb = NULL,
.ssport = gpio_max2837_select.port(),
.sspad = gpio_max2837_select.pad(),
.ssport = gpio_max283x_select.port(),
.sspad = gpio_max283x_select.pad(),
.cr0 =
CR0_CLOCKRATE(ssp_scr(ssp1_pclk_f, ssp1_cpsr, max2837_spi_f))
CR0_CLOCKRATE(ssp_scr(ssp1_pclk_f, ssp1_cpsr, max283x_spi_f))
| CR0_FRFSPI
| CR0_DSS16BIT
,
@@ -78,9 +79,9 @@ static constexpr SPIConfig ssp_config_max5864 = {
static spi::arbiter::Arbiter ssp1_arbiter(portapack::ssp1);
static spi::arbiter::Target ssp1_target_max2837 {
static spi::arbiter::Target ssp1_target_max283x {
ssp1_arbiter,
ssp_config_max2837
ssp_config_max283x
};
static spi::arbiter::Target ssp1_target_max5864 {
@@ -90,16 +91,27 @@ static spi::arbiter::Target ssp1_target_max5864 {
static rf::path::Path rf_path;
rffc507x::RFFC507x first_if;
max2837::MAX2837 second_if { ssp1_target_max2837 };
max283x::MAX283x *second_if;
max2837::MAX2837 second_if_max2837 { ssp1_target_max283x };
max2839::MAX2839 second_if_max2839 { ssp1_target_max283x };
static max5864::MAX5864 baseband_codec { ssp1_target_max5864 };
static baseband::CPLD baseband_cpld;
static rf::Direction direction { rf::Direction::Receive };
static bool baseband_invert = false;
static bool mixer_invert = false;
void init() {
if (hackrf_r9) {
gpio_r9_not_ant_pwr.write(1);
gpio_r9_not_ant_pwr.output();
}
rf_path.init();
first_if.init();
second_if.init();
second_if = hackrf_r9
? (max283x::MAX283x *) &second_if_max2839
: (max283x::MAX283x *) &second_if_max2837;
second_if->init();
baseband_codec.init();
baseband_cpld.init();
}
@@ -121,7 +133,30 @@ void set_direction(const rf::Direction new_direction) {
direction = new_direction;
second_if.set_mode((direction == rf::Direction::Transmit) ? max2837::Mode::Transmit : max2837::Mode::Receive);
if (hackrf_r9) {
/*
* HackRF One r9 inverts analog baseband only for RX. Previous hardware
* revisions inverted analog baseband for neither direction because of
* compensation in the CPLD. If we ever simplify the CPLD to handle RX
* and TX the same way, we will need to update this baseband_invert
* logic.
*/
baseband_invert = (direction == rf::Direction::Receive);
} else {
/*
* Analog baseband is inverted in RX but not TX. The RX inversion is
* corrected by the CPLD, but future hardware or CPLD changes may
* change this for either or both directions. For a given hardware+CPLD
* platform, baseband inversion is set here for RX and/or TX. Spectrum
* inversion resulting from the mixer is tracked separately according
* to the tuning configuration. We ask the CPLD to apply a correction
* for the total inversion.
*/
baseband_invert = false;
}
baseband_cpld.set_invert(mixer_invert ^ baseband_invert);
second_if->set_mode((direction == rf::Direction::Transmit) ? max283x::Mode::Transmit : max283x::Mode::Receive);
rf_path.set_direction(direction);
baseband_codec.set_mode((direction == rf::Direction::Transmit) ? max5864::Mode::Transmit : max5864::Mode::Receive);
@@ -142,10 +177,11 @@ bool set_tuning_frequency(const rf::Frequency frequency) {
first_if.enable();
}
const auto result_second_if = second_if.set_frequency(tuning_config.second_lo_frequency);
const auto result_second_if = second_if->set_frequency(tuning_config.second_lo_frequency);
rf_path.set_band(tuning_config.rf_path_band);
baseband_cpld.set_invert(tuning_config.baseband_invert);
mixer_invert = tuning_config.mixer_invert;
baseband_cpld.set_invert(mixer_invert ^ baseband_invert);
return result_second_if;
} else {
@@ -165,19 +201,19 @@ void set_rf_amp(const bool rf_amp) {
}
void set_lna_gain(const int_fast8_t db) {
second_if.set_lna_gain(db);
second_if->set_lna_gain(db);
}
void set_vga_gain(const int_fast8_t db) {
second_if.set_vga_gain(db);
second_if->set_vga_gain(db);
}
void set_tx_gain(const int_fast8_t db) {
second_if.set_tx_vga_gain(db);
second_if->set_tx_vga_gain(db);
}
void set_baseband_filter_bandwidth(const uint32_t bandwidth_minimum) {
second_if.set_lpf_rf_bandwidth(bandwidth_minimum);
second_if->set_lpf_rf_bandwidth(bandwidth_minimum);
}
void set_baseband_rate(const uint32_t rate) {
@@ -186,13 +222,17 @@ void set_baseband_rate(const uint32_t rate) {
void set_antenna_bias(const bool on) {
/* Pull MOSFET gate low to turn on antenna bias. */
first_if.set_gpo1(on ? 0 : 1);
if (hackrf_r9) {
gpio_r9_not_ant_pwr.write(on ? 0 : 1);
} else {
first_if.set_gpo1(on ? 0 : 1);
}
}
void disable() {
set_antenna_bias(false);
baseband_codec.set_mode(max5864::Mode::Shutdown);
second_if.set_mode(max2837::Mode::Standby);
second_if->set_mode(max2837::Mode::Standby);
first_if.disable();
set_rf_amp(false);
@@ -227,11 +267,11 @@ uint32_t register_read(const size_t register_number) {
namespace second_if {
uint32_t register_read(const size_t register_number) {
return radio::second_if.read(register_number);
return radio::second_if->read(register_number);
}
uint8_t temp_sense() {
return radio::second_if.temp_sense() & 0x1f;
return radio::second_if->temp_sense() & 0x1f;
}
} /* namespace second_if */

View File

@@ -27,7 +27,7 @@
#include "message.hpp"
#include "rf_path.hpp"
#include "max2837.hpp"
#include "max283x.hpp"
#include "volume.hpp"
class ReceiverModel {
@@ -94,7 +94,7 @@ private:
bool enabled_ { false };
bool rf_amp_ { false };
int32_t lna_gain_db_ { 32 };
uint32_t baseband_bandwidth_ { max2837::filter::bandwidth_minimum };
uint32_t baseband_bandwidth_ { max283x::filter::bandwidth_minimum };
int32_t vga_gain_db_ { 32 };
int32_t tx_gain_db_ { 47 };
Mode mode_ { Mode::NarrowbandFMAudio };

View File

@@ -20,6 +20,7 @@
*/
#include "rf_path.hpp"
#include "platform.hpp"
#include <array>
#include <initializer_list>
@@ -34,14 +35,12 @@ namespace path {
namespace {
using GPIOs = std::array<GPIO, 13>;
using GPIOs = std::array<GPIO, 11>;
/* TODO: ARM GCC 4.8 2014q3 doesn't like this array inside struct Config.
* No idea why.
*/
constexpr GPIOs gpios {
gpio_tx,
gpio_rx,
gpio_mix_bypass,
gpio_not_mix_bypass,
gpio_tx_mix_bp,
@@ -122,15 +121,39 @@ struct Config {
}
static void gpio_init() {
for(auto gpio : gpios) {
if (hackrf_r9) {
gpio_r9_rx.output();
} else {
gpio_og_tx.output();
gpio_og_rx.output();
}
for (auto gpio : gpios) {
gpio.output();
}
}
void apply() const {
/* NOTE: Assumes order in gpios[] and Config bitfield match. */
for(size_t n=0; n<gpios.size(); n++) {
gpios[n].write((*this)[n]);
/* NOTE: Assumes order in gpios[] and Config bitfield match,
* after the 'tx' and 'rx' fields which are handled specially. */
for (size_t n = 0; n < gpios.size() + 2; n++) {
bool value = (*this)[n];
switch (n) {
case 0:
if (!hackrf_r9) {
gpio_og_tx.write(value);
}
break;
case 1:
if (hackrf_r9) {
gpio_r9_rx.write(value);
} else {
gpio_og_rx.write(value);
}
break;
default:
gpios[n - 2].write(value);
break;
}
}
}
};

View File

@@ -39,8 +39,8 @@ enum class Direction {
namespace path {
constexpr FrequencyRange band_low { 0, 2150000000 };
constexpr FrequencyRange band_high { 2750000000, 7250000000 };
constexpr FrequencyRange band_low { 0, 2170000000 };
constexpr FrequencyRange band_high { 2740000000, 7250000000 };
constexpr FrequencyRange band_mid { band_low.maximum, band_high.minimum };
enum class Band {

View File

@@ -41,7 +41,7 @@ constexpr rf::Frequency high_band_second_lo_regions_2_and_3(const rf::Frequency
constexpr rf::Frequency high_band_second_lo_frequency(const rf::Frequency target_frequency) {
return (target_frequency < 3600000000)
? (2150000000 + (((target_frequency - 2750000000) * 60) / 85))
? (2170000000 + (((target_frequency - 2740000000) * 57) / 86))
: high_band_second_lo_regions_2_and_3(target_frequency)
;
}
@@ -49,8 +49,8 @@ constexpr rf::Frequency high_band_second_lo_frequency(const rf::Frequency target
Config low_band(const rf::Frequency target_frequency) {
const rf::Frequency first_lo_frequency = target_frequency + low_band_second_lo_frequency(target_frequency);
const rf::Frequency second_lo_frequency = first_lo_frequency - target_frequency;
const bool baseband_invert = true;
return { first_lo_frequency, second_lo_frequency, rf::path::Band::Low, baseband_invert };
const bool mixer_invert = true;
return { first_lo_frequency, second_lo_frequency, rf::path::Band::Low, mixer_invert };
}
Config mid_band(const rf::Frequency target_frequency) {
@@ -60,8 +60,8 @@ Config mid_band(const rf::Frequency target_frequency) {
Config high_band(const rf::Frequency target_frequency) {
const rf::Frequency first_lo_frequency = target_frequency - high_band_second_lo_frequency(target_frequency);
const rf::Frequency second_lo_frequency = target_frequency - first_lo_frequency;
const bool baseband_invert = false;
return { first_lo_frequency, second_lo_frequency, rf::path::Band::High, baseband_invert };
const bool mixer_invert = false;
return { first_lo_frequency, second_lo_frequency, rf::path::Band::High, mixer_invert };
}
} /* namespace */

View File

@@ -33,7 +33,7 @@ struct Config {
) : first_lo_frequency(0),
second_lo_frequency(0),
rf_path_band(rf::path::Band::Mid),
baseband_invert(false)
mixer_invert(false)
{
}
@@ -41,11 +41,11 @@ struct Config {
rf::Frequency first_lo_frequency,
rf::Frequency second_lo_frequency,
rf::path::Band rf_path_band,
bool baseband_invert
bool mixer_invert
) : first_lo_frequency(first_lo_frequency),
second_lo_frequency(second_lo_frequency),
rf_path_band(rf_path_band),
baseband_invert(baseband_invert)
mixer_invert(mixer_invert)
{
}
@@ -56,7 +56,7 @@ struct Config {
const rf::Frequency first_lo_frequency;
const rf::Frequency second_lo_frequency;
const rf::path::Band rf_path_band;
const bool baseband_invert;
const bool mixer_invert;
};
Config create(const rf::Frequency target_frequency);

View File

@@ -28,7 +28,7 @@ using namespace portapack;
#include "string_format.hpp"
#include "max2837.hpp"
#include "max283x.hpp"
namespace ui {
@@ -334,8 +334,8 @@ LNAGainField::LNAGainField(
Point parent_pos
) : NumberField {
parent_pos, 2,
{ max2837::lna::gain_db_range.minimum, max2837::lna::gain_db_range.maximum },
max2837::lna::gain_db_step,
{ max283x::lna::gain_db_range.minimum, max283x::lna::gain_db_range.maximum },
max283x::lna::gain_db_step,
' ',
}
{
@@ -359,8 +359,8 @@ VGAGainField::VGAGainField(
Point parent_pos
) : NumberField {
parent_pos, 2,
{ max2837::vga::gain_db_range.minimum, max2837::vga::gain_db_range.maximum },
max2837::vga::gain_db_step,
{ max283x::vga::gain_db_range.minimum, max283x::vga::gain_db_range.maximum },
max283x::vga::gain_db_step,
' ',
}
{