diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt index c9e3beac..5cdd3c65 100644 --- a/firmware/application/CMakeLists.txt +++ b/firmware/application/CMakeLists.txt @@ -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 diff --git a/firmware/application/apps/ui_debug.cpp b/firmware/application/apps/ui_debug.cpp index e9018c1c..05633e3f 100644 --- a/firmware/application/apps/ui_debug.cpp +++ b/firmware/application/apps/ui_debug.cpp @@ -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( "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( - "MAX2837", RegistersWidgetConfig { 32, 10 }, + { max283x, ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav, max283x](){ nav.push( + 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( - "Si5351C", RegistersWidgetConfig { 96, 8 }, + { si5351x, ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav, si5351x](){ nav.push( + 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( diff --git a/firmware/application/apps/ui_debug.hpp b/firmware/application/apps/ui_debug.hpp index 19118721..43acc888 100644 --- a/firmware/application/apps/ui_debug.hpp +++ b/firmware/application/apps/ui_debug.hpp @@ -29,7 +29,6 @@ #include "ui_navigation.hpp" #include "rffc507x.hpp" -#include "max2837.hpp" #include "portapack.hpp" #include diff --git a/firmware/application/apps/ui_morse.cpp b/firmware/application/apps/ui_morse.cpp index 6378b025..5191047d 100644 --- a/firmware/application/apps/ui_morse.cpp +++ b/firmware/application/apps/ui_morse.cpp @@ -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); diff --git a/firmware/application/baseband_api.cpp b/firmware/application/baseband_api.cpp index b4321577..d7990403 100644 --- a/firmware/application/baseband_api.cpp +++ b/firmware/application/baseband_api.cpp @@ -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(); diff --git a/firmware/application/clock_manager.cpp b/firmware/application/clock_manager.cpp index 93eecc13..beb27b90 100644 --- a/firmware/application/clock_manager.cpp +++ b/firmware/application/clock_manager.cpp @@ -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()); } diff --git a/firmware/application/clock_manager.hpp b/firmware/application/clock_manager.hpp index 0c88bab5..cfecea68 100644 --- a/firmware/application/clock_manager.hpp +++ b/firmware/application/clock_manager.hpp @@ -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__*/ diff --git a/firmware/application/core_control.cpp b/firmware/application/core_control.cpp index a0fcccc8..ab653bc2 100644 --- a/firmware/application/core_control.cpp +++ b/firmware/application/core_control.cpp @@ -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(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; } diff --git a/firmware/application/core_control.hpp b/firmware/application/core_control.hpp index 09737705..e34149c7 100644 --- a/firmware/application/core_control.hpp +++ b/firmware/application/core_control.hpp @@ -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(); diff --git a/firmware/application/firmware_info.c b/firmware/application/firmware_info.c new file mode 100644 index 00000000..f98d5365 --- /dev/null +++ b/firmware/application/firmware_info.c @@ -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, +}; \ No newline at end of file diff --git a/firmware/application/hw/max2837.cpp b/firmware/application/hw/max2837.cpp index 4825da10..dab751b9 100644 --- a/firmware/application/hw/max2837.cpp +++ b/firmware/application/hw/max2837.cpp @@ -32,8 +32,12 @@ using namespace hackrf::one; namespace max2837 { +using namespace max283x; + namespace lna { +using namespace max283x::lna; + constexpr std::array 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() { diff --git a/firmware/application/hw/max2837.hpp b/firmware/application/hw/max2837.hpp index 20d319a8..b62447d3 100644 --- a/firmware/application/hw/max2837.hpp +++ b/firmware/application/hw/max2837.hpp @@ -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 #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 band { { - { 2300000000, 2400000000 }, - { 2400000000, 2500000000 }, - { 2500000000, 2600000000 }, - { 2600000000, 2700000000 }, -} }; - -} /* namespace lo */ - -/*************************************************************************/ - -namespace lna { - -constexpr range_t gain_db_range { 0, 40 }; -constexpr int8_t gain_db_step = 8; - -constexpr std::array band { { - { 2300000000, 2500000000 }, - { 2500000000, 2700000000 }, -} }; - -} /* namespace lna */ - -/*************************************************************************/ - -namespace vga { - -constexpr range_t gain_db_range { 0, 62 }; -constexpr int8_t gain_db_step = 2; - -} /* namespace vga */ - -/*************************************************************************/ - -namespace tx { - -constexpr range_t gain_db_range { 0, 47 }; -constexpr int8_t gain_db_step = 1; -} - -/*************************************************************************/ - -namespace filter { - -constexpr std::array 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; diff --git a/firmware/application/hw/max2839.cpp b/firmware/application/hw/max2839.cpp new file mode 100644 index 00000000..2e0bbf4d --- /dev/null +++ b/firmware/application/hw/max2839.cpp @@ -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 + +namespace max2839 { + +namespace lna { + +using namespace max283x::lna; + +constexpr std::array 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 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 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; +} + +} \ No newline at end of file diff --git a/firmware/application/hw/max2839.hpp b/firmware/application/hw/max2839.hpp new file mode 100644 index 00000000..4b94f6d9 --- /dev/null +++ b/firmware/application/hw/max2839.hpp @@ -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 +#include + +#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 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 _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__*/ \ No newline at end of file diff --git a/firmware/application/hw/max283x.hpp b/firmware/application/hw/max283x.hpp new file mode 100644 index 00000000..e8b4b397 --- /dev/null +++ b/firmware/application/hw/max283x.hpp @@ -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 band { { + { 2300000000, 2400000000 }, + { 2400000000, 2500000000 }, + { 2500000000, 2600000000 }, + { 2600000000, 2700000000 }, +} }; + +} /* namespace lo */ + +/*************************************************************************/ + +namespace lna { + +constexpr range_t gain_db_range { 0, 40 }; +constexpr int8_t gain_db_step = 8; + +constexpr std::array band { { + { 2300000000, 2500000000 }, + { 2500000000, 2700000000 }, +} }; + +} /* namespace lna */ + +/*************************************************************************/ + +namespace vga { + +constexpr range_t gain_db_range { 0, 62 }; +constexpr int8_t gain_db_step = 2; + +} /* namespace vga */ + +/*************************************************************************/ + +namespace tx { + +constexpr range_t gain_db_range { 0, 47 }; +constexpr int8_t gain_db_step = 1; +} + +/*************************************************************************/ + +namespace filter { + +constexpr std::array 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__*/ \ No newline at end of file diff --git a/firmware/application/hw/si5351.hpp b/firmware/application/hw/si5351.hpp index 66cd4b1b..3064798a 100644 --- a/firmware/application/hw/si5351.hpp +++ b/firmware/application/hw/si5351.hpp @@ -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; } diff --git a/firmware/application/main.cpp b/firmware/application/main.cpp index 2e471b9f..d9bef9fb 100755 --- a/firmware/application/main.cpp +++ b/firmware/application/main.cpp @@ -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; diff --git a/firmware/application/portapack.cpp b/firmware/application/portapack.cpp index 99a47d0b..edf064e1 100644 --- a/firmware/application/portapack.cpp +++ b/firmware/application/portapack.cpp @@ -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(); diff --git a/firmware/application/radio.cpp b/firmware/application/radio.cpp index 617f22ac..c4b7d5f9 100644 --- a/firmware/application/radio.cpp +++ b/firmware/application/radio.cpp @@ -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(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 */ diff --git a/firmware/application/receiver_model.hpp b/firmware/application/receiver_model.hpp index ce960ace..2a658ae4 100644 --- a/firmware/application/receiver_model.hpp +++ b/firmware/application/receiver_model.hpp @@ -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 }; diff --git a/firmware/application/rf_path.cpp b/firmware/application/rf_path.cpp index 3f646954..54d4d13c 100644 --- a/firmware/application/rf_path.cpp +++ b/firmware/application/rf_path.cpp @@ -20,6 +20,7 @@ */ #include "rf_path.hpp" +#include "platform.hpp" #include #include @@ -34,14 +35,12 @@ namespace path { namespace { -using GPIOs = std::array; +using GPIOs = std::array; /* 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 static typename T::value_type spectrum_window_none(const T& s, const size_t i) { -static_assert(power_of_two(ARRAY_ELEMENTS(s)), "Array number of elements must be power of 2"); // c/m compile error GCC10 , OK for all GCC versions. + constexpr size_t length = sizeof(s)/sizeof(s[0]); + static_assert(power_of_two(length), "Array length must be power of 2"); return s[i]; }; template static typename T::value_type spectrum_window_hamming_3(const T& s, const size_t i) { - static_assert(power_of_two(ARRAY_ELEMENTS(s)), "Array number of elements must be power of 2"); // c/m compile error GCC10 , OK for all GCC versions. - const size_t mask = s.size() - 1; // c/m compile error GCC10 , constexpr->const + constexpr size_t length = sizeof(s)/sizeof(s[0]); + static_assert((length), "Array length must be power of 2"); + constexpr size_t mask = length - 1; // Three point Hamming window. return s[i] * 0.54f + (s[(i-1) & mask] + s[(i+1) & mask]) * -0.23f; }; template static typename T::value_type spectrum_window_blackman_3(const T& s, const size_t i) { - static_assert(power_of_two(ARRAY_ELEMENTS(s)), "Array number of elements must be power of 2"); // c/m compile error GCC10 , OK for all GCC versions. - const size_t mask = s.size() - 1; // c/m compile error GCC10 , constexpr->const + constexpr size_t length = sizeof(s)/sizeof(s[0]); + static_assert(power_of_two(length), "Array length must be power of 2"); + constexpr size_t mask = length - 1; // Three term Blackman window. constexpr float alpha = 0.42f; constexpr float beta = 0.5f * 0.5f; diff --git a/firmware/chibios-portapack/boards/PORTAPACK_APPLICATION/board.cpp b/firmware/chibios-portapack/boards/PORTAPACK_APPLICATION/board.cpp index 43d297b4..affe8e3e 100755 --- a/firmware/chibios-portapack/boards/PORTAPACK_APPLICATION/board.cpp +++ b/firmware/chibios-portapack/boards/PORTAPACK_APPLICATION/board.cpp @@ -20,6 +20,8 @@ #include +bool hackrf_r9; + #if HAL_USE_PAL || defined(__DOXYGEN__) /** * @brief PAL setup. @@ -56,9 +58,9 @@ const PALConfig pal_default_config = { | (0 << 12) // P1_17: SGPIO11, HOST_DIRECTION | (1 << 11) // P1_4: SSP1_MOSI | (1 << 10) // P1_3: SSP1_MISO - | (0 << 9) // P1_2: 10K PD, BOOT1 - | (1 << 8) // P1_1: 10K PU, BOOT0 - | (1 << 7) // P2_7: 10K PU, ISP + | (0 << 9) // P1_2: Varies by revision, float until detection + | (0 << 8) // P1_1: Varies by revision, float until detection + | (0 << 7) // P2_7: Varies by revision, float until detection | (0 << 6) // P3_6: SPIFI_MISO | (1 << 5) // P6_6: SGPIO5, HOST_DATA5 | (1 << 4) // P1_0: SGPIO7, HOST_DATA7 @@ -74,9 +76,9 @@ const PALConfig pal_default_config = { | (0 << 12) // P1_17: SGPIO11, HOST_DIRECTION | (0 << 11) // P1_4: SSP1_MOSI | (0 << 10) // P1_3: SSP1_MISO - | (0 << 9) // P1_2: 10K PD, BOOT1 - | (0 << 8) // P1_1: 10K PU, BOOT0 - | (0 << 7) // P2_7: 10K PU, ISP + | (0 << 9) // P1_2: Varies by revision, float until detection + | (0 << 8) // P1_1: Varies by revision, float until detection + | (0 << 7) // P2_7: Varies by revision, float until detection | (0 << 6) // P3_6: SPIFI_MISO | (0 << 5) // P6_6: SGPIO5, HOST_DATA5 | (0 << 4) // P1_0: SGPIO7, HOST_DATA7 @@ -130,12 +132,12 @@ const PALConfig pal_default_config = { | (1 << 12) // P5_3: RX_MIX_BP | (0 << 11) // P5_2: TX_MIX_BP | (0 << 10) // P5_1: LP - | (1 << 9) // P5_0: !VAA_ENABLE + | (0 << 9) // P5_0: Varies by revision, float until detection | (0 << 8) // P6_12: LED3 (TX) | (1 << 7) // P5_7: CS_AD | (0 << 6) // P4_6: XCVR_EN, 10K PD | (0 << 5) // P4_5: RXENABLE - | (0 << 4) // P4_4: TXENABLE + | (0 << 4) // P4_4: Varies by revision, float until detection | (1 << 3) // P4_3: SGPIO9, HOST_CAPTURE | (0 << 2) // P4_2: LED2 (RX) | (0 << 1) // P4_1: LED1 (USB) @@ -148,12 +150,12 @@ const PALConfig pal_default_config = { | (1 << 12) // P5_3: RX_MIX_BP | (1 << 11) // P5_2: TX_MIX_BP | (1 << 10) // P5_1: LP - | (1 << 9) // P5_0: !VAA_ENABLE + | (0 << 9) // P5_0: Varies by revision, float until detection | (1 << 8) // P6_12: LED3 (TX) | (1 << 7) // P5_7: CS_AD | (1 << 6) // P4_6: XCVR_EN, 10K PD | (1 << 5) // P4_5: RXENABLE - | (1 << 4) // P4_4: TXENABLE + | (0 << 4) // P4_4: Varies by revision, float until detection | (0 << 3) // P4_3: SGPIO9, HOST_CAPTURE | (1 << 2) // P4_2: LED2 (RX) | (1 << 1) // P4_1: LED1 (USB) @@ -170,7 +172,7 @@ const PALConfig pal_default_config = { | (1 << 9) // P7_1: PortaPack GPIO3_9(IO) | (1 << 8) // P7_0: PortaPack GPIO3_8(IO) | (1 << 7) // P6_11: VREGMODE - | (0 << 6) // P6_10: EN1V8, 10K PD + | (0 << 6) // P6_10: Varies by revision, float until detection | (1 << 5) // P6_9: !TX_AMP_PWR, 10K PU | (1 << 4) // P6_5: HackRF CPLD.TMS(I) (output only when needed, pull-up internal to CPLD when 1V8 present) | (1 << 3) // P6_4: MIXER_SDATA @@ -188,7 +190,7 @@ const PALConfig pal_default_config = { | (0 << 9) // P7_1: PortaPack GPIO3_9(IO) | (0 << 8) // P7_0: PortaPack GPIO3_8(IO) | (1 << 7) // P6_11: VREGMODE - | (1 << 6) // P6_10: EN1V8, 10K PD + | (0 << 6) // P6_10: Varies by revision, float until detection | (1 << 5) // P6_9: !TX_AMP_PWR, 10K PU | (0 << 4) // P6_5: HackRF CPLD.TMS(I) (output only when needed, pull-up internal to CPLD when 1V8 present) | (0 << 3) // P6_4: MIXER_SDATA @@ -207,17 +209,17 @@ const PALConfig pal_default_config = { .data = (1 << 18) // P9:5: HackRF CPLD.TDO(O) (input with pull up) | (1 << 16) // P6_8: MIX_BYPASS - | (0 << 15) // P6_7: TX + | (0 << 15) // P6_7: Varies by revision, float until detection | (1 << 14) // P4_10: SGPIO15, CPLD (unused) | (1 << 13) // P4_9: SGPIO14, CPLD (unused) - | (0 << 12) // P4_8: SGPIO13, HOST_SYNC_EN + | (0 << 12) // P4_8: Varies by revision, float until detection | (1 << 11) // P3_8: SPIFI_CS | (1 << 10) // P3_7: SPIFI_MOSI | (1 << 9) // P3_2: I2S0_RX_SDA | (1 << 8) // P3_1: I2S0_RX_WS | (0 << 7) // P2_8: BOOT2 | (0 << 6) // P2_6: MIXER_SCLK - | (1 << 5) // P2_5: RX + | (0 << 5) // P2_5: Varies by revision, float until detection | (1 << 4) // P2_4: PortaPack LCD_RDX | (0 << 3) // P2_3: PortaPack LCD_TE | (1 << 2) // P2_2: SGPIO6, HOST_DATA6 @@ -227,17 +229,17 @@ const PALConfig pal_default_config = { .dir = (0 << 18) // P9_5: HackRF CPLD.TDO(O) (input with pull up) | (1 << 16) // P6_8: MIX_BYPASS - | (1 << 15) // P6_7: TX + | (0 << 15) // P6_7: Varies by revision, float until detection | (0 << 14) // P4_10: SGPIO15, CPLD (unused) | (0 << 13) // P4_9: SGPIO14, CPLD (unused) - | (0 << 12) // P4_8: SGPIO13, HOST_SYNC_EN + | (0 << 12) // P4_8: Varies by revision, float until detection | (0 << 11) // P3_8: SPIFI_CS | (0 << 10) // P3_7: SPIFI_MOSI | (0 << 9) // P3_2: I2S0_RX_SDA | (0 << 8) // P3_1: I2S0_RX_WS | (0 << 7) // P2_8: BOOT2 | (0 << 6) // P2_6: MIXER_SCLK - | (1 << 5) // P2_5: RX + | (0 << 5) // P2_5: Varies by revision, float until detection | (0 << 4) // P2_4: PortaPack LCD_RDX | (0 << 3) // P2_3: PortaPack LCD_TE | (0 << 2) // P2_2: SGPIO6, HOST_DATA6 @@ -265,8 +267,6 @@ const PALConfig pal_default_config = { { 6, 12, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* LED3: LED3.A(I) */ /* Power control */ - { 5, 0, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* !VAA_ENABLE: 10K PU, Q3.G(I), power to VAA */ - { 6, 10, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* EN1V8/P70: 10K PD, TPS62410.EN2(I), 1V8LED.A(I) */ { 6, 11, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* VREGMODE/P69: TPS62410.MODE/DATA(I) */ /* HackRF: I2C0 */ @@ -297,13 +297,11 @@ const PALConfig pal_default_config = { { 1, 7, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* !MIX_BYPASS/P35: U1.VCTL1(I), U11.VCTL2(I), U9.V2(I) */ { 1, 19, scu_config_normal_drive_t { .mode=1, .epd=0, .epun=0, .ehs=0, .ezi=0, .zif=0 } }, /* SSP1_SCK/P39: MAX2837.SCLK(I), MAX5864.SCLK(I) */ { 1, 20, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* CS_XCVR/P53: MAX2837.CS(I) */ - { 2, 5, scu_config_normal_drive_t { .mode=4, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* RX/P43: U7.VCTL1(I), U10.VCTL1(I), U2.VCTL1(I) */ { 2, 6, scu_config_normal_drive_t { .mode=4, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* MIXER_SCLK/P31: 33pF, RFFC5072.SCLK(I) */ { 2, 10, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* AMP_BYPASS/P50: U14.V2(I), U12.V2(I) */ { 2, 11, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* RX_AMP/P49: U12.V1(I), U14.V3(I) */ { 2, 12, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* !RX_AMP_PWR/P52: 10K PU, Q1.G(I), power to U13 (RX amp) */ { 4, 0, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* HP/P44: U6.VCTL1(I), U5.VCTL2(I) */ - { 4, 4, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* TXENABLE/P55: MAX2837.TXENABLE(I) */ { 4, 5, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* RXENABLE/P56: MAX2837.RXENABLE(I) */ { 4, 6, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* XCVR_EN: 10K PD, MAX2837.ENABLE(I) */ { 5, 1, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* LP/P45: U6.VCTL2(I), U5.VCTL1(I) */ @@ -314,7 +312,6 @@ const PALConfig pal_default_config = { { 5, 6, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* TX_AMP/P48: U12.V3(I), U14.V1(I) */ { 5, 7, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* CS_AD/P54: MAX5864.CS(I) */ { 6, 4, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=0, .ehs=0, .ezi=1, .zif=0 } }, /* MIXER_SDATA/P27: 33pF, RFFC5072.SDATA(IO) */ - { 6, 7, scu_config_normal_drive_t { .mode=4, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* TX/P42: U7.VCTL2(I), U10.VCTL2(I), U2.VCTL2(I) */ { 6, 8, scu_config_normal_drive_t { .mode=4, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* MIX_BYPASS/P34: U1.VCTL2(I), U11.VCTL1(I) */ { 6, 9, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* !TX_AMP_PWR/P51: 10K PU, Q2.G(I), power to U25 (TX amp) */ @@ -332,7 +329,6 @@ const PALConfig pal_default_config = { { 1, 14, scu_config_normal_drive_t { .mode=6, .epd=0, .epun=0, .ehs=1, .ezi=0, .zif=0 } }, /* SGPIO10/P78/BANK2F3M8: CPLD.76/HOST_DISABLE(I) */ { 1, 17, scu_config_normal_drive_t { .mode=6, .epd=1, .epun=1, .ehs=1, .ezi=0, .zif=0 } }, /* SGPIO11/P79/BANK2F3M11: CPLD.71/HOST_DIRECTION(I) */ { 1, 18, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* SGPIO12/BANK2F3M12: CPLD.70/HOST_INVERT(I) */ - { 4, 8, scu_config_normal_drive_t { .mode=4, .epd=1, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* SGPIO13/BANK2F3M2: CPLD.90/HOST_SYNC_EN(I) */ { 4, 9, scu_config_normal_drive_t { .mode=4, .epd=0, .epun=0, .ehs=0, .ezi=0, .zif=0 } }, /* SGPIO14/BANK2F3M4: CPLD.81/CPLD_P81 */ { 4, 10, scu_config_normal_drive_t { .mode=4, .epd=0, .epun=0, .ehs=0, .ezi=0, .zif=0 } }, /* SGPIO15/BANK2F3M6: CPLD.78/CPLD_P78 */ @@ -346,14 +342,165 @@ const PALConfig pal_default_config = { { 1, 5, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=0, .ehs=0, .ezi=1, .zif=0 } }, /* SD_POW: PortaPack CPLD.TDO(O) */ { 1, 8, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=0, .ehs=0, .ezi=0, .zif=0 } }, /* SD_VOLT0: PortaPack CPLD.TMS(I) */ + /* Miscellaneous */ + { 6, 0, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=0, .ehs=0, .ezi=0, .zif=0 } }, /* I2S0_RX_MCLK: Unused */ + { 15, 4, scu_config_normal_drive_t { .mode=7, .epd=0, .epun=0, .ehs=0, .ezi=0, .zif=0 } }, /* I2S0_RX_SCK: Unused */ + + /* Usage of these pins varies by revision, float until detection */ + { 1, 1, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, + { 1, 2, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, + { 2, 5, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, + { 2, 7, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, + { 4, 4, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, + { 4, 8, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, + { 5, 0, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, + { 6, 7, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, + { 6, 10, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, + } +}; + +/* Additional GPIO configuration for HackRF OG */ +static const std::array gpio_setup_og { { + { // GPIO0 + .data + = (0 << 9) // P1_2: 10K PD, BOOT1 + | (1 << 8) // P1_1: 10K PU, BOOT0 + | (1 << 7) // P2_7: 10K PU, ISP + , + .dir + = (0 << 9) // P1_2: 10K PD, BOOT1 + | (0 << 8) // P1_1: 10K PU, BOOT0 + | (0 << 7) // P2_7: 10K PU, ISP + }, + { // GPIO1 + .data = 0, + .dir = 0 + }, + { // GPIO2 + .data + = (1 << 9) // P5_0: !VAA_ENABLE + | (0 << 4) // P4_4: TXENABLE + , + .dir + = (1 << 9) // P5_0: !VAA_ENABLE + | (1 << 4) // P4_4: TXENABLE + }, + { // GPIO3 + .data + = (0 << 6) // P6_10: EN1V8, 10K PD + , + .dir + = (1 << 6) // P6_10: EN1V8, 10K PD + }, + { // GPIO4 + .data = 0, + .dir = 0 + }, + { // GPIO5 + .data + = (0 << 15) // P6_7: TX + | (0 << 12) // P4_8: SGPIO13, HOST_SYNC_EN + | (1 << 5) // P2_5: RX + , + .dir + = (1 << 15) // P6_7: TX + | (0 << 12) // P4_8: SGPIO13, HOST_SYNC_EN + | (1 << 5) // P2_5: RX + }, +} }; + +/* Additional GPIO configuration for HackRF r9 */ +static const std::array gpio_setup_r9 { { + { // GPIO0 + .data + = (0 << 9) // P1_2: 10K PD, BOOT1, CLKOUT_EN + | (1 << 8) // P1_1: 10K PU, BOOT0, MCU_CLK_EN + | (1 << 7) // P2_7: 10K PU, ISP, RX + , + .dir + = (0 << 9) // P1_2: 10K PD, BOOT1, CLKOUT_EN + | (0 << 8) // P1_1: 10K PU, BOOT0, MCU_CLK_EN + | (0 << 7) // P2_7: 10K PU, ISP, RX + }, + { // GPIO1 + .data = 0, + .dir = 0 + }, + { // GPIO2 + .data + = (1 << 9) // P5_0: EN1V8, 10K PD + | (1 << 4) // P4_4: !ANT_BIAS + , + .dir + = (1 << 9) // P5_0: EN1V8, 10K PD + | (1 << 4) // P4_4: !ANT_BIAS + }, + { // GPIO3 + .data + = (1 << 6) // P6_10: !VAA_ENABLE + , + .dir + = (1 << 6) // P6_10: !VAA_ENABLE + }, + { // GPIO4 + .data = 0, + .dir = 0 + }, + { // GPIO5 + .data + = (0 << 15) // P6_7: CLKIN_EN + | (0 << 12) // P4_8: CLKIN_DETECT + | (0 << 5) // P2_5: HOST_SYNC_EN + , + .dir + = (1 << 15) // P6_7: CLKIN_EN + | (0 << 12) // P4_8: CLKIN_DETECT + | (0 << 5) // P2_5: HOST_SYNC_EN + }, +} }; + +/* Additional SCU configuration for HackRF OG */ +static const std::array pins_setup_og { { + /* Power control */ + { 5, 0, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* !VAA_ENABLE: 10K PU, Q3.G(I), power to VAA */ + { 6, 10, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* EN1V8/P70: 10K PD, TPS62410.EN2(I), 1V8LED.A(I) */ + + /* Radio section control */ + { 2, 5, scu_config_normal_drive_t { .mode=4, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* RX/P43: U7.VCTL1(I), U10.VCTL1(I), U2.VCTL1(I) */ + { 4, 4, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* TXENABLE/P55: MAX2837.TXENABLE(I) */ + { 6, 7, scu_config_normal_drive_t { .mode=4, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* TX/P42: U7.VCTL2(I), U10.VCTL2(I), U2.VCTL2(I) */ + + /* SGPIO for sample transfer interface to HackRF CPLD. */ + { 4, 8, scu_config_normal_drive_t { .mode=4, .epd=1, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* SGPIO13/BANK2F3M2: CPLD.90/HOST_SYNC_EN(I) */ + /* Miscellaneous */ { 1, 1, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* P1_1/P74: 10K PU, BOOT0 */ { 1, 2, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* P1_2/P73: 10K PD, BOOT1 */ { 2, 7, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* ISP: 10K PU, Unused */ - { 6, 0, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=0, .ehs=0, .ezi=0, .zif=0 } }, /* I2S0_RX_MCLK: Unused */ - { 15, 4, scu_config_normal_drive_t { .mode=7, .epd=0, .epun=0, .ehs=0, .ezi=0, .zif=0 } }, /* I2S0_RX_SCK: Unused */ - } -}; +} }; + +/* Additional SCU configuration for HackRF r9 */ +static const std::array pins_setup_r9 { { + /* Power control */ + { 6, 10, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* !VAA_ENABLE: 10K PU, Q3.G(I), power to VAA */ + { 5, 0, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* EN1V8: 10K PD, TPS62410.EN2(I), 1V8LED.A(I) */ + + /* Radio section control */ + { 2, 7, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* RX/ISP/P96: U7.VCTL(I), U10.VCTL(I), U2.VCTL(I) */ + { 4, 4, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* !ANT_BIAS: 10K PU, Q4.G(I) */ + + /* SGPIO for sample transfer interface to HackRF CPLD. */ + { 2, 5, scu_config_normal_drive_t { .mode=4, .epd=1, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* BANK2F3M2: CPLD.90/HOST_SYNC_EN(I) */ + + /* Clock control */ + { 1, 1, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* MCU_CLK_EN/BOOT0: 10K PU, U28.1A(I) */ + { 1, 2, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* CLKOUT_EN/BOOT1: 10K PD, U28.2A(I) */ + { 6, 7, scu_config_normal_drive_t { .mode=4, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* CLKIN_EN: U16.SEL(I), U26.1A(I) */ + + /* Miscellaneous */ + { 4, 8, scu_config_normal_drive_t { .mode=1, .epd=1, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* CLKIN_DETECT: U26.2Y(O) */ +} }; + #endif static const std::array pins_setup_portapack { { @@ -398,6 +545,14 @@ static const std::array pins_setup_spifi { { { 3, 8, scu_config_normal_drive_t { .mode=3, .epd=0, .epun=1, .ehs=1, .ezi=1, .zif=1 } }, /* SPIFI_CS/P68: W25Q80BV.CS(I) */ } }; +template +void setup_gpios(const std::array& pins_setup) { + for (size_t i = 0; i < N; i++) { + LPC_GPIO->PIN[i] |= pins_setup[i].data; + LPC_GPIO->DIR[i] |= pins_setup[i].dir; + } +} + static void setup_pin(const scu_setup_t& pin_setup) { LPC_SCU->SFSP[pin_setup.port][pin_setup.pin] = pin_setup.config; } @@ -409,6 +564,19 @@ void setup_pins(const std::array& pins_setup) { } } +/* + * HackRF One r9 has a pull-up on GPIO3_6 (P6_10) and a pull-down on GPIO2_9 (P5_0). + * HackRF One OG has a pull-down on GPIO3_6 (P6_10) and a pull-up on GPIO2_9 (P5_0). + */ +static const scu_setup_t pin_setup_detect { 5, 0, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=1, .zif=0 } }; + +/* Check resistor on GPIO2_9 (P5_0) to detect HackRF hardware revision. */ +extern "C" bool detect_hackrf_r9() { + setup_pin(pin_setup_detect); + LPC_GPIO->DIR[2] &= ~(1 << 9); + return LPC_GPIO->W2[9] == 0; +} + static void configure_spifi(void) { setup_pins(pins_setup_spifi); @@ -449,7 +617,8 @@ static const motocon_pwm_resources_t motocon_pwm_resources = { }; static const scu_setup_t pin_setup_vaa_enablex_pwm = { 5, 0, scu_config_normal_drive_t { .mode=1, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }; -static const scu_setup_t pin_setup_vaa_enablex_gpio = { 5, 0, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }; +static const scu_setup_t pin_setup_vaa_enablex_gpio_og = { 5, 0, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }; +static const scu_setup_t pin_setup_vaa_enablex_gpio_r9 = { 6, 10, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }; /* VAA powers: * MAX5864 analog section. @@ -467,52 +636,69 @@ void vaa_power_on(void) { * * Controlling timing while running from SPIFI flash is tricky, hence use of a PWM peripheral... */ + if (hackrf_r9) { + /* + * There is enough VCC->VAA leakage prior to VAA activation from IO pins on + * HackRF One r9 that slowing down activation like this isn't necessary, but + * we do it just in case a different start-up sequence in the future results + * in less leakage. + */ + setup_pin(pin_setup_vaa_enablex_gpio_r9); // P6_10 GPIO3[ 6]: !VAA_ENABLE, 10K PU + for (uint32_t i = 0; i < 1000; i++) { + LPC_GPIO->W3[6] = 1; + LPC_GPIO->W3[6] = 0; + } + } else { + /* Configure and enable MOTOCONPWM peripheral clocks. + * Assume IDIVC is running the post-bootloader configuration, outputting 96MHz derived from PLL1. + */ + base_clock_enable(&motocon_pwm_resources.base); + branch_clock_enable(&motocon_pwm_resources.branch); + peripheral_reset(&motocon_pwm_resources.reset); - /* Configure and enable MOTOCONPWM peripheral clocks. - * Assume IDIVC is running the post-bootloader configuration, outputting 96MHz derived from PLL1. - */ - base_clock_enable(&motocon_pwm_resources.base); - branch_clock_enable(&motocon_pwm_resources.branch); - peripheral_reset(&motocon_pwm_resources.reset); - - /* Combination of pulse duration and duty cycle was arrived at empirically, to keep supply glitching - * to +/- 0.15V. - */ - const uint32_t cycle_period = 256; - uint32_t enable_period = 2; - LPC_MCPWM->TC2 = 0; - LPC_MCPWM->MAT2 = cycle_period - enable_period; - LPC_MCPWM->LIM2 = cycle_period; - - /* Switch !VAA_ENABLE pin from GPIO to MOTOCONPWM peripheral output, now that the peripheral is configured. */ - setup_pin(pin_setup_vaa_enablex_pwm); // P5_0 /GPIO2[ 9]/MCOB2: !VAA_ENABLE, 10K PU - - /* Start the PWM operation. */ - LPC_MCPWM->CON_SET = (1 << 16); - - /* Wait until VAA rises to approximately 90% of final voltage. */ - /* Timing assumes we're running immediately after the bootloader: 96 MHz from IRC+PLL1 - */ - while(enable_period < cycle_period) { - { volatile uint32_t delay = 2000; while(delay--); } - enable_period <<= 1; + /* Combination of pulse duration and duty cycle was arrived at empirically, to keep supply glitching + * to +/- 0.15V. + */ + const uint32_t cycle_period = 256; + uint32_t enable_period = 2; + LPC_MCPWM->TC2 = 0; LPC_MCPWM->MAT2 = cycle_period - enable_period; + LPC_MCPWM->LIM2 = cycle_period; + + /* Switch !VAA_ENABLE pin from GPIO to MOTOCONPWM peripheral output, now that the peripheral is configured. */ + setup_pin(pin_setup_vaa_enablex_pwm); // P5_0 /GPIO2[ 9]/MCOB2: !VAA_ENABLE, 10K PU + + /* Start the PWM operation. */ + LPC_MCPWM->CON_SET = (1 << 16); + + /* Wait until VAA rises to approximately 90% of final voltage. */ + /* Timing assumes we're running immediately after the bootloader: 96 MHz from IRC+PLL1 + */ + while(enable_period < cycle_period) { + { volatile uint32_t delay = 2000; while(delay--); } + enable_period <<= 1; + LPC_MCPWM->MAT2 = cycle_period - enable_period; + } + + /* Hold !VAA_ENABLE active using a GPIO, so we can reclaim and shut down the MOTOCONPWM peripheral. */ + LPC_GPIO->CLR[2] = (1 << 9); // !VAA_ENABLE + LPC_GPIO->DIR[2] |= (1 << 9); + setup_pin(pin_setup_vaa_enablex_gpio_og); // P5_0 /GPIO2[ 9]/MCOB2: !VAA_ENABLE, 10K PU + + peripheral_reset(&motocon_pwm_resources.reset); + branch_clock_disable(&motocon_pwm_resources.branch); + base_clock_disable(&motocon_pwm_resources.base); } - - /* Hold !VAA_ENABLE active using a GPIO, so we can reclaim and shut down the MOTOCONPWM peripheral. */ - LPC_GPIO->CLR[2] = (1 << 9); // !VAA_ENABLE - LPC_GPIO->DIR[2] |= (1 << 9); - setup_pin(pin_setup_vaa_enablex_gpio); // P5_0 /GPIO2[ 9]/MCOB2: !VAA_ENABLE, 10K PU - - peripheral_reset(&motocon_pwm_resources.reset); - branch_clock_disable(&motocon_pwm_resources.branch); - base_clock_disable(&motocon_pwm_resources.base); } void vaa_power_off(void) { // TODO: There's a lot of other stuff that must be done to prevent // leakage from +3V3 into VAA. - LPC_GPIO->W2[9] = 1; + if (hackrf_r9) { + LPC_GPIO->W3[6] = 1; + } else { + LPC_GPIO->W2[9] = 1; + } } /** @@ -638,12 +824,30 @@ extern "C" void __late_init(void) { * @todo Add your board-specific code, if any. */ extern "C" void boardInit(void) { + /* Detect HackRF variant */ + hackrf_r9 = detect_hackrf_r9(); + /* Configure variant-dependent pins. */ + if (hackrf_r9) { + setup_gpios(gpio_setup_r9); + setup_pins(pins_setup_r9); + } else { + setup_gpios(gpio_setup_og); + setup_pins(pins_setup_og); + } vaa_power_on(); - LPC_GPIO->W3[6] = 1; + if (hackrf_r9) { + LPC_GPIO->W2[9] = 1; + } else { + LPC_GPIO->W3[6] = 1; + } } extern "C" void _default_exit(void) { - LPC_GPIO->W3[6] = 0; + if (hackrf_r9) { + LPC_GPIO->W2[9] = 0; + } else { + LPC_GPIO->W3[6] = 0; + } vaa_power_off(); chSysDisable(); diff --git a/firmware/chibios-portapack/os/ports/GCC/ARMCMx/LPC43xx_M0/ld/LPC43xx_M0.ld b/firmware/chibios-portapack/os/ports/GCC/ARMCMx/LPC43xx_M0/ld/LPC43xx_M0.ld index 6e43abe2..4a6d6a60 100755 --- a/firmware/chibios-portapack/os/ports/GCC/ARMCMx/LPC43xx_M0/ld/LPC43xx_M0.ld +++ b/firmware/chibios-portapack/os/ports/GCC/ARMCMx/LPC43xx_M0/ld/LPC43xx_M0.ld @@ -41,6 +41,10 @@ SECTIONS startup : ALIGN(16) SUBALIGN(16) { KEEP(*(vectors)) + . = ALIGN(0x400); + ASSERT(. == 0x400, "Error: attempting to place firmware information section at incorrect location"); + KEEP(*(.firmware_info)); + } > flash constructors : ALIGN(4) SUBALIGN(4) diff --git a/firmware/common/baseband_cpld.cpp b/firmware/common/baseband_cpld.cpp index ded20b48..8eb8869a 100644 --- a/firmware/common/baseband_cpld.cpp +++ b/firmware/common/baseband_cpld.cpp @@ -28,11 +28,11 @@ namespace baseband { void CPLD::init() { set_invert(false); - gpio_baseband_invert.output(); + gpio_q_invert.output(); } void CPLD::set_invert(const bool invert) { - gpio_baseband_invert.write(invert); + gpio_q_invert.write(invert); } } diff --git a/firmware/common/gpio.hpp b/firmware/common/gpio.hpp index 5d8164c7..c28cda46 100644 --- a/firmware/common/gpio.hpp +++ b/firmware/common/gpio.hpp @@ -197,25 +197,11 @@ struct Pin { constexpr Pin( const uint8_t port, - const uint8_t pad, - const PinConfig initial_config + const uint8_t pad ) : _pin_port { port }, - _pin_pad { pad }, - _initial_config { initial_config } + _pin_pad { pad } { } -/* - constexpr Pin( - const Pin& pin - ) : _pin_port { pin._pin_port }, - _pin_pad { pin._pin_pad }, - _initial_config { pin._initial_config } - { - } -*/ - void init() const { - LPC_SCU->SFSP[_pin_port][_pin_pad] = _initial_config; - } void mode(const uint_fast16_t mode) const { LPC_SCU->SFSP[_pin_port][_pin_pad] = @@ -228,7 +214,6 @@ struct Pin { uint8_t _pin_port; uint8_t _pin_pad; - uint16_t _initial_config; }; struct GPIO { diff --git a/firmware/common/hackrf_gpio.hpp b/firmware/common/hackrf_gpio.hpp index 41f58fc9..8e3fffa2 100644 --- a/firmware/common/hackrf_gpio.hpp +++ b/firmware/common/hackrf_gpio.hpp @@ -38,17 +38,20 @@ constexpr GPIO gpio_led_usb = gpio[GPIO2_1]; constexpr GPIO gpio_led_rx = gpio[GPIO2_2]; constexpr GPIO gpio_led_tx = gpio[GPIO2_8]; -constexpr GPIO gpio_1v8_enable = gpio[GPIO3_6]; +constexpr GPIO gpio_og_1v8_enable = gpio[GPIO3_6]; +constexpr GPIO gpio_r9_1v8_enable = gpio[GPIO2_9]; constexpr GPIO gpio_vregmode = gpio[GPIO3_7]; -constexpr GPIO gpio_vaa_disable = gpio[GPIO2_9]; +constexpr GPIO gpio_og_vaa_disable = gpio[GPIO2_9]; +constexpr GPIO gpio_r9_vaa_disable = gpio[GPIO3_6]; constexpr GPIO gpio_rx_mix_bp = gpio[GPIO2_12]; constexpr GPIO gpio_tx_mix_bp = gpio[GPIO2_11]; constexpr GPIO gpio_mix_bypass = gpio[GPIO5_16]; constexpr GPIO gpio_not_mix_bypass = gpio[GPIO1_0]; -constexpr GPIO gpio_rx = gpio[GPIO5_5]; -constexpr GPIO gpio_tx = gpio[GPIO5_15]; +constexpr GPIO gpio_og_rx = gpio[GPIO5_5]; +constexpr GPIO gpio_og_tx = gpio[GPIO5_15]; +constexpr GPIO gpio_r9_rx = gpio[GPIO0_7]; constexpr GPIO gpio_lp = gpio[GPIO2_10]; constexpr GPIO gpio_hp = gpio[GPIO2_0]; @@ -64,20 +67,26 @@ constexpr GPIO gpio_rffc5072_select = gpio[GPIO2_13]; constexpr GPIO gpio_rffc5072_clock = gpio[GPIO5_6]; constexpr GPIO gpio_rffc5072_data = gpio[GPIO3_3]; -constexpr GPIO gpio_max2837_select = gpio[GPIO0_15]; -constexpr GPIO gpio_max2837_enable = gpio[GPIO2_6]; +constexpr GPIO gpio_max283x_select = gpio[GPIO0_15]; +constexpr GPIO gpio_max283x_enable = gpio[GPIO2_6]; constexpr GPIO gpio_max2837_rxenable = gpio[GPIO2_5]; constexpr GPIO gpio_max2837_txenable = gpio[GPIO2_4]; +constexpr GPIO gpio_max2839_rxtx = gpio[GPIO2_5]; constexpr GPIO gpio_max5864_select = gpio[GPIO2_7]; -constexpr GPIO gpio_baseband_invert = gpio[GPIO0_13]; +constexpr GPIO gpio_q_invert = gpio[GPIO0_13]; constexpr GPIO gpio_cpld_tdo = gpio[GPIO5_18]; constexpr GPIO gpio_cpld_tck = gpio[GPIO3_0]; constexpr GPIO gpio_cpld_tms = gpio[GPIO3_4]; constexpr GPIO gpio_cpld_tdi = gpio[GPIO3_1]; +constexpr GPIO gpio_r9_clkin_en = gpio[GPIO5_15]; +constexpr GPIO gpio_r9_clkout_en = gpio[GPIO0_9]; +constexpr GPIO gpio_r9_mcu_clk_en = gpio[GPIO0_8]; +constexpr GPIO gpio_r9_not_ant_pwr = gpio[GPIO2_4]; + /* LEDs */ constexpr LED led_usb { gpio_led_usb }; diff --git a/firmware/common/hackrf_hal.hpp b/firmware/common/hackrf_hal.hpp index b22df694..60e518ff 100644 --- a/firmware/common/hackrf_hal.hpp +++ b/firmware/common/hackrf_hal.hpp @@ -47,23 +47,28 @@ constexpr ClockFrequency base_apb3_clk_f = base_m4_clk_f; constexpr ClockFrequency ssp1_pclk_f = base_m4_clk_f; constexpr ClockFrequency max5864_spi_f = 20000000U; -constexpr ClockFrequency max2837_spi_f = 20000000U; +constexpr ClockFrequency max283x_spi_f = 20000000U; constexpr ClockFrequency rffc5072_reference_f = 40000000U; -constexpr ClockFrequency max2837_reference_f = 40000000U; -constexpr ClockFrequency mcu_clkin_f = 40000000U; +constexpr ClockFrequency max283x_reference_f = 40000000U; +constexpr ClockFrequency mcu_clkin_og_f = 40000000U; +constexpr ClockFrequency mcu_clkin_r9_f = 10000000U; constexpr uint8_t si5351_i2c_address = 0x60; /* Clock Generator */ -constexpr size_t clock_generator_output_codec = 0; -constexpr size_t clock_generator_output_cpld = 1; -constexpr size_t clock_generator_output_sgpio = 2; -constexpr size_t clock_generator_output_clkout = 3; -constexpr size_t clock_generator_output_first_if = 4; -constexpr size_t clock_generator_output_second_if = 5; -constexpr size_t clock_generator_output_mcu_clkin = 7; +constexpr size_t clock_generator_output_og_codec = 0; +constexpr size_t clock_generator_output_og_cpld = 1; +constexpr size_t clock_generator_output_og_sgpio = 2; +constexpr size_t clock_generator_output_og_clkout = 3; +constexpr size_t clock_generator_output_og_first_if = 4; +constexpr size_t clock_generator_output_og_second_if = 5; +constexpr size_t clock_generator_output_og_mcu_clkin = 7; + +constexpr size_t clock_generator_output_r9_if = 0; +constexpr size_t clock_generator_output_r9_sgpio = 1; +constexpr size_t clock_generator_output_r9_mcu_clkin = 2; /* ADC0 */ diff --git a/firmware/common/pins.hpp b/firmware/common/pins.hpp index cd29e9a4..bbb74d01 100644 --- a/firmware/common/pins.hpp +++ b/firmware/common/pins.hpp @@ -40,92 +40,92 @@ enum Pins { CLK0, CLK2, }; -constexpr Pin pins[] = { - [P0_0] = { 0, 0, PinConfig::sgpio_inout_fast(3) }, /* SGPIO0/P75/BANK2F3M3: CPLD.89/HOST_DATA0(IO) */ - [P0_1] = { 0, 1, PinConfig::sgpio_inout_fast(3) }, /* SGPIO1/BANK2F3M5: CPLD.79/HOST_DATA1(IO) */ - [P1_0] = { 1, 0, PinConfig::sgpio_inout_fast(6) }, /* SGPIO7/P76/BANK2F3M7: CPLD.77/HOST_DATA7(IO) */ - [P1_1] = { 1, 1, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* P1_1/P74: 10K PU, BOOT0 */ - [P1_2] = { 1, 2, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* P1_2/P73: 10K PD, BOOT1 */ - [P1_3] = { 1, 3, { .mode=5, .pd=0, .pu=0, .fast=0, .input=1, .ifilt=1 } }, /* SSP1_MISO/P41: MAX2837.DOUT(O) */ - [P1_4] = { 1, 4, { .mode=5, .pd=1, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* SSP1_MOSI/P40: MAX2837.DIN(I), MAX5864.DIN(I) */ - [P1_5] = { 1, 5, { .mode=0, .pd=0, .pu=1, .fast=0, .input=1, .ifilt=1 } }, /* SD_POW: PortaPack CPLD.TDO(O) */ - [P1_6] = { 1, 6, { .mode=7, .pd=0, .pu=1, .fast=0, .input=1, .ifilt=0 } }, /* SD_CMD: PortaPack SD.CMD(IO) */ - [P1_7] = { 1, 7, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* !MIX_BYPASS/P35: U1.VCTL1(I), U11.VCTL2(I), U9.V2(I) */ - [P1_8] = { 1, 8, { .mode=0, .pd=0, .pu=1, .fast=0, .input=0, .ifilt=1 } }, /* SD_VOLT0: PortaPack CPLD.TMS(I) */ - [P1_9] = { 1, 9, { .mode=7, .pd=0, .pu=1, .fast=0, .input=1, .ifilt=0 } }, /* SD_DAT0: PortaPack SD.DAT0(IO) */ - [P1_10] = { 1, 10, { .mode=7, .pd=0, .pu=1, .fast=0, .input=1, .ifilt=0 } }, /* SD_DAT1: PortaPack SD.DAT1(IO) */ - [P1_11] = { 1, 11, { .mode=7, .pd=0, .pu=1, .fast=0, .input=1, .ifilt=0 } }, /* SD_DAT2: PortaPack SD.DAT2(IO) */ - [P1_12] = { 1, 12, { .mode=7, .pd=0, .pu=1, .fast=0, .input=1, .ifilt=0 } }, /* SD_DAT3: PortaPack SD.DAT3(IO) */ - [P1_13] = { 1, 13, { .mode=7, .pd=0, .pu=1, .fast=0, .input=1, .ifilt=1 } }, /* SD_CD: PortaPack SD.CD(O) */ - [P1_14] = { 1, 14, PinConfig::sgpio_out_fast_with_pullup(6) }, /* SGPIO10/P78/BANK2F3M8: CPLD.76/HOST_DISABLE(I) */ - [P1_15] = { 1, 15, PinConfig::sgpio_inout_fast(2) }, /* SGPIO2/BANK2F3M9: CPLD.74/HOST_DATA2(IO) */ - [P1_16] = { 1, 16, PinConfig::sgpio_inout_fast(2) }, /* SGPIO3/BANK2F3M10: CPLD.72/HOST_DATA3(IO) */ - [P1_17] = { 1, 17, PinConfig::sgpio_out_fast_with_pullup(6) }, /* SGPIO11/P79/BANK2F3M11: CPLD.71/HOST_DIRECTION(I) */ - [P1_18] = { 1, 18, PinConfig::gpio_out_with_pulldown(0) }, /* SGPIO12/BANK2F3M12: CPLD.70/HOST_INVERT(I) */ - [P1_19] = { 1, 19, { .mode=1, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* SSP1_SCK/P39: MAX2837.SCLK(I), MAX5864.SCLK(I) */ - [P1_20] = { 1, 20, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* CS_XCVR/P53: MAX2837.CS(I) */ - [P2_0] = { 2, 0, { .mode=4, .pd=0, .pu=0, .fast=0, .input=1, .ifilt=1 } }, /* U0_TXD: PortaPack P2_0/IO_STBX */ - [P2_1] = { 2, 1, { .mode=4, .pd=0, .pu=0, .fast=0, .input=1, .ifilt=1 } }, /* U0_RXD: PortaPack P2_1/ADDR */ - [P2_2] = { 2, 2, PinConfig::sgpio_inout_fast(0) }, /* SGPIO6/BANK2F3M16: CPLD.61/HOST_DATA6(IO) */ - [P2_3] = { 2, 3, { .mode=4, .pd=0, .pu=0, .fast=0, .input=1, .ifilt=1 } }, /* I2C1_SDA: PortaPack P2_3/LCD_TE */ - [P2_4] = { 2, 4, { .mode=4, .pd=0, .pu=0, .fast=0, .input=1, .ifilt=1 } }, /* I2C1_SCL: PortaPack P2_4/LCD_RDX */ - [P2_5] = { 2, 5, { .mode=4, .pd=0, .pu=1, .fast=0, .input=0, .ifilt=1 } }, /* RX/P43: U7.VCTL1(I), U10.VCTL1(I), U2.VCTL1(I) */ - [P2_6] = { 2, 6, { .mode=4, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* MIXER_SCLK/P31: 33pF, RFFC5072.SCLK(I) */ - [P2_7] = { 2, 7, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* ISP: 10K PU, Unused */ - [P2_8] = { 2, 8, { .mode=4, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* P2_8: 10K PD, BOOT2, DFU switch, PortaPack P2_8/ */ - [P2_9] = { 2, 9, { .mode=0, .pd=0, .pu=0, .fast=0, .input=1, .ifilt=1 } }, /* P2_9: 10K PD, BOOT3, PortaPack P2_9/LCD_WRX */ - [P2_10] = { 2, 10, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* AMP_BYPASS/P50: U14.V2(I), U12.V2(I) */ - [P2_11] = { 2, 11, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* RX_AMP/P49: U12.V1(I), U14.V3(I) */ - [P2_12] = { 2, 12, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* !RX_AMP_PWR/P52: 10K PU, Q1.G(I), power to U13 (RX amp) */ - [P2_13] = { 2, 13, { .mode=0, .pd=0, .pu=0, .fast=0, .input=1, .ifilt=1 } }, /* P2_13: PortaPack P2_13/DIR */ - [P3_0] = { 3, 0, { .mode=2, .pd=0, .pu=1, .fast=0, .input=1, .ifilt=1 } }, /* I2S0_TX_SCK: PortaPack I2S0_TX_SCK(I) */ - [P3_1] = { 3, 1, { .mode=0, .pd=0, .pu=1, .fast=0, .input=1, .ifilt=1 } }, /* I2S0_RX_WS: PortaPack I2S0_TX_WS(I). Input enabled to fold back into RX. */ - [P3_2] = { 3, 2, { .mode=0, .pd=0, .pu=1, .fast=0, .input=0, .ifilt=1 } }, /* I2S0_RX_SDA: PortaPack I2S0_TX_SDA(I) */ - [P4_0] = { 4, 0, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* HP/P44: U6.VCTL1(I), U5.VCTL2(I) */ - [P4_1] = { 4, 1, PinConfig::gpio_led(0) }, /* LED1: LED1.A(I) */ - [P4_2] = { 4, 2, PinConfig::gpio_led(0) }, /* LED2: LED2.A(I) */ - [P4_3] = { 4, 3, PinConfig::sgpio_in_fast(7) }, /* SGPIO9/P77/BANK2F3M1: CPLD.91/HOST_CAPTURE(O) */ - [P4_4] = { 4, 4, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* TXENABLE/P55: MAX2837.TXENABLE(I) */ - [P4_5] = { 4, 5, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* RXENABLE/P56: MAX2837.RXENABLE(I) */ - [P4_6] = { 4, 6, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* XCVR_EN: 10K PD, MAX2837.ENABLE(I) */ - [P4_7] = { 4, 7, { .mode=1, .pd=0, .pu=0, .fast=0, .input=1, .ifilt=0 } }, /* GP_CLKIN/P72/MCU_CLK: SI5351C.CLK7(O) */ - [P4_8] = { 4, 8, PinConfig::floating_input_with_pull(0, 4) }, /* SGPIO13/BANK2F3M2: CPLD.90/HOST_SYNC_EN(I) */ - [P4_9] = { 4, 9, PinConfig::floating_input(4) }, /* SGPIO14/BANK2F3M4: CPLD.81(I) */ - [P4_10] = { 4, 10, PinConfig::floating_input(4) }, /* SGPIO15/BANK2F3M6: CPLD.78(I) */ - [P5_0] = { 5, 0, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* !VAA_ENABLE: 10K PU, Q3.G(I), power to VAA */ - [P5_1] = { 5, 1, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* LP/P45: U6.VCTL2(I), U5.VCTL1(I) */ - [P5_2] = { 5, 2, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* TX_MIX_BP/P46: U9.V1(I) */ - [P5_3] = { 5, 3, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* RX_MIX_BP/P47: U9.V3(I) */ - [P5_4] = { 5, 4, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* MIXER_ENX/P32: 10K PU, 33pF, RFFC5072.ENX(I) */ - [P5_5] = { 5, 5, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* MIXER_RESETX/P33: 10K PU, 33pF, RFFC5072.RESETX(I) */ - [P5_6] = { 5, 6, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* TX_AMP/P48: U12.V3(I), U14.V1(I) */ - [P5_7] = { 5, 7, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* CS_AD/P54: MAX5864.CS(I) */ - [P6_0] = { 6, 0, { .mode=0, .pd=0, .pu=1, .fast=0, .input=0, .ifilt=1 } }, /* I2S0_RX_MCLK: Unused */ - [P6_1] = { 6, 1, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* CPLD_TCK: CPLD.TCK(I), PortaPack CPLD.TCK(I) */ - [P6_2] = { 6, 2, { .mode=0, .pd=0, .pu=0, .fast=0, .input=1, .ifilt=1 } }, /* CPLD_TDI: CPLD.TDI(I), PortaPack I2S0_RX_SDA(O), PortaPack CPLD.TDI(I) */ - [P6_3] = { 6, 3, PinConfig::sgpio_inout_fast(2) }, /* SGPIO4/BANK2F3M14: CPLD.67/HOST_DATA4(IO) */ - [P6_4] = { 6, 4, { .mode=0, .pd=0, .pu=0, .fast=0, .input=1, .ifilt=1 } }, /* MIXER_SDATA/P27: 33pF, RFFC5072.SDATA(IO) */ - [P6_5] = { 6, 5, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* CPLD_TMS: CPLD.TMS(I) */ - [P6_6] = { 6, 6, PinConfig::sgpio_inout_fast(2) }, /* SGPIO5/BANK2F3M15: CPLD.64/HOST_DATA5(IO) */ - [P6_7] = { 6, 7, { .mode=4, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* TX/P42: U7.VCTL2(I), U10.VCTL2(I), U2.VCTL2(I) */ - [P6_8] = { 6, 8, { .mode=4, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* MIX_BYPASS/P34: U1.VCTL2(I), U11.VCTL1(I) */ - [P6_9] = { 6, 9, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* !TX_AMP_PWR/P51: 10K PU, Q2.G(I), power to U25 (TX amp) */ - [P6_10] = { 6, 10, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* EN1V8/P70: 10K PD, TPS62410.EN2(I), 1V8LED.A(I) */ - [P6_11] = { 6, 11, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* VREGMODE/P69: TPS62410.MODE/DATA(I) */ - [P6_12] = { 6, 12, PinConfig::gpio_led(0) }, /* LED3: LED3.A(I) */ - [P7_0] = { 7, 0, PinConfig::gpio_inout_with_pulldown(0) }, /* GPIO3_8: PortaPack GPIO3_8(IO) */ - [P7_1] = { 7, 1, PinConfig::gpio_inout_with_pulldown(0) }, /* GPIO3_9: PortaPack GPIO3_9(IO) */ - [P7_2] = { 7, 2, PinConfig::gpio_inout_with_pulldown(0) }, /* GPIO3_10: PortaPack GPIO3_10(IO) */ - [P7_3] = { 7, 3, PinConfig::gpio_inout_with_pulldown(0) }, /* GPIO3_11: PortaPack GPIO3_11(IO) */ - [P7_4] = { 7, 4, PinConfig::gpio_inout_with_pulldown(0) }, /* GPIO3_12: PortaPack GPIO3_12(IO) */ - [P7_5] = { 7, 5, PinConfig::gpio_inout_with_pulldown(0) }, /* GPIO3_13: PortaPack GPIO3_13(IO) */ - [P7_6] = { 7, 6, PinConfig::gpio_inout_with_pulldown(0) }, /* GPIO3_14: PortaPack GPIO3_14(IO) */ - [P7_7] = { 7, 7, PinConfig::gpio_inout_with_pulldown(0) }, /* GPIO3_15: PortaPack GPIO3_15(IO) */ - [P9_5] = { 9, 5, { .mode=4, .pd=0, .pu=0, .fast=0, .input=1, .ifilt=1 } }, /* CPLD_TDO: CPLD.TDO(O) */ - [P9_6] = { 9, 6, PinConfig::sgpio_in_fast(6) }, /* SGPIO8/SGPIO_CLK/P60: SI5351C.CLK2(O) */ - [PF_4] = { 15, 4, { .mode=7, .pd=0, .pu=1, .fast=0, .input=0, .ifilt=1 } }, /* I2S0_RX_SCK: Unused */ - [CLK0] = { 24, 0, { .mode=4, .pd=1, .pu=0, .fast=0, .input=1, .ifilt=0 } }, /* SD_CLK: PortaPack SD.CLK, enable input buffer for timing feedback? */ - [CLK2] = { 24, 2, { .mode=6, .pd=1, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* I2S0_TX_CLK: PortaPack I2S0_TX_MCLK */ +constexpr Pin pins[] { + [P0_0] = { 0, 0}, + [P0_1] = { 0, 1}, + [P1_0] = { 1, 0}, + [P1_1] = { 1, 1}, + [P1_2] = { 1, 2}, + [P1_3] = { 1, 3}, + [P1_4] = { 1, 4}, + [P1_5] = { 1, 5}, + [P1_6] = { 1, 6}, + [P1_7] = { 1, 7}, + [P1_8] = { 1, 8}, + [P1_9] = { 1, 9}, + [P1_10] = { 1, 10}, + [P1_11] = { 1, 11}, + [P1_12] = { 1, 12}, + [P1_13] = { 1, 13}, + [P1_14] = { 1, 14}, + [P1_15] = { 1, 15}, + [P1_16] = { 1, 16}, + [P1_17] = { 1, 17}, + [P1_18] = { 1, 18}, + [P1_19] = { 1, 19}, + [P1_20] = { 1, 20}, + [P2_0] = { 2, 0}, + [P2_1] = { 2, 1}, + [P2_2] = { 2, 2}, + [P2_3] = { 2, 3}, + [P2_4] = { 2, 4}, + [P2_5] = { 2, 5}, + [P2_6] = { 2, 6}, + [P2_7] = { 2, 7}, + [P2_8] = { 2, 8}, + [P2_9] = { 2, 9}, + [P2_10] = { 2, 10}, + [P2_11] = { 2, 11}, + [P2_12] = { 2, 12}, + [P2_13] = { 2, 13}, + [P3_0] = { 3, 0}, + [P3_1] = { 3, 1}, + [P3_2] = { 3, 2}, + [P4_0] = { 4, 0}, + [P4_1] = { 4, 1}, + [P4_2] = { 4, 2}, + [P4_3] = { 4, 3}, + [P4_4] = { 4, 4}, + [P4_5] = { 4, 5}, + [P4_6] = { 4, 6}, + [P4_7] = { 4, 7}, + [P4_8] = { 4, 8}, + [P4_9] = { 4, 9}, + [P4_10] = { 4, 10}, + [P5_0] = { 5, 0}, + [P5_1] = { 5, 1}, + [P5_2] = { 5, 2}, + [P5_3] = { 5, 3}, + [P5_4] = { 5, 4}, + [P5_5] = { 5, 5}, + [P5_6] = { 5, 6}, + [P5_7] = { 5, 7}, + [P6_0] = { 6, 0}, + [P6_1] = { 6, 1}, + [P6_2] = { 6, 2}, + [P6_3] = { 6, 3}, + [P6_4] = { 6, 4}, + [P6_5] = { 6, 5}, + [P6_6] = { 6, 6}, + [P6_7] = { 6, 7}, + [P6_8] = { 6, 8}, + [P6_9] = { 6, 9}, + [P6_10] = { 6, 10}, + [P6_11] = { 6, 11}, + [P6_12] = { 6, 12}, + [P7_0] = { 7, 0}, + [P7_1] = { 7, 1}, + [P7_2] = { 7, 2}, + [P7_3] = { 7, 3}, + [P7_4] = { 7, 4}, + [P7_5] = { 7, 5}, + [P7_6] = { 7, 6}, + [P7_7] = { 7, 7}, + [P9_5] = { 9, 5}, + [P9_6] = { 9, 6}, + [PF_4] = { 15, 4}, + [CLK0] = { 24, 0}, + [CLK2] = { 24, 2}, }; enum GPIOs { diff --git a/firmware/common/platform.hpp b/firmware/common/platform.hpp new file mode 100644 index 00000000..a47cedc1 --- /dev/null +++ b/firmware/common/platform.hpp @@ -0,0 +1 @@ +extern bool hackrf_r9; \ No newline at end of file diff --git a/firmware/common/portapack_io.hpp b/firmware/common/portapack_io.hpp index adc6179c..148670d2 100644 --- a/firmware/common/portapack_io.hpp +++ b/firmware/common/portapack_io.hpp @@ -26,6 +26,7 @@ #include #include +#include "platform.hpp" #include "gpio.hpp" #include "ui.hpp"