/* * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. * * 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 "radio.hpp" #include "rf_path.hpp" #include "rffc507x.hpp" #include "max2837.hpp" #include "max2839.hpp" #include "max5864.hpp" #include "baseband_cpld.hpp" #include "tuning.hpp" #include "spi_arbiter.hpp" #include "hackrf_hal.hpp" #include "hackrf_gpio.hpp" using namespace hackrf::one; #include "cpld_update.hpp" #include "portapack.hpp" #include "portapack_persistent_memory.hpp" /* Direct access to the radio. Setting values incorrectly can damage * the device. Applications should use ReceiverModel or TransmitterModel * instead of calling these functions directly. */ namespace radio { static constexpr uint32_t ssp1_cpsr = 2; static constexpr uint32_t ssp_scr( const float pclk_f, const uint32_t cpsr, const float spi_f) { return static_cast(pclk_f / cpsr / spi_f - 1); } static constexpr SPIConfig ssp_config_max283x = { .end_cb = NULL, .ssport = gpio_max283x_select.port(), .sspad = gpio_max283x_select.pad(), .cr0 = CR0_CLOCKRATE(ssp_scr(ssp1_pclk_f, ssp1_cpsr, max283x_spi_f) + 3) | CR0_FRFSPI | CR0_DSS16BIT, .cpsr = ssp1_cpsr, }; static constexpr SPIConfig ssp_config_max5864 = { .end_cb = NULL, .ssport = gpio_max5864_select.port(), .sspad = gpio_max5864_select.pad(), .cr0 = CR0_CLOCKRATE(ssp_scr(ssp1_pclk_f, ssp1_cpsr, max5864_spi_f)) | CR0_FRFSPI | CR0_DSS8BIT, .cpsr = ssp1_cpsr, }; static spi::arbiter::Arbiter ssp1_arbiter(portapack::ssp1); static spi::arbiter::Target ssp1_target_max283x{ ssp1_arbiter, ssp_config_max283x}; static spi::arbiter::Target ssp1_target_max5864{ ssp1_arbiter, ssp_config_max5864}; static rf::path::Path rf_path; rffc507x::RFFC507x first_if; 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; // load_sram() is called at boot in portapack.cpp, including verify CPLD part, so default direction is Receive 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 = hackrf_r9 ? (max283x::MAX283x*)&second_if_max2839 : (max283x::MAX283x*)&second_if_max2837; second_if->init(); baseband_codec.init(); baseband_cpld.init(); } void set_direction(const rf::Direction new_direction) { /* TODO: Refactor all the various "Direction" enumerations into one. */ /* TODO: Only make changes if direction changes, but beware of clock enabling. */ // Prevents ghosting when switching back to RX from TX mode. hackrf::cpld::load_sram_no_verify(); direction = new_direction; 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); if (direction == rf::Direction::Receive) led_rx.on(); else led_tx.on(); } bool set_tuning_frequency(const rf::Frequency frequency) { rf::Frequency final_frequency = frequency; // if converter feature is enabled if (portapack::persistent_memory::config_converter()) { // downconvert if (portapack::persistent_memory::config_updown_converter()) { final_frequency = frequency - portapack::persistent_memory::config_converter_freq(); } else // upconvert { final_frequency = frequency + portapack::persistent_memory::config_converter_freq(); } } // apply frequency correction if (direction == rf::Direction::Transmit) { if (portapack::persistent_memory::config_freq_tx_correction_updown()) // tx freq correction down final_frequency = final_frequency - portapack::persistent_memory::config_freq_tx_correction(); else // tx freq correction up final_frequency = final_frequency + portapack::persistent_memory::config_freq_tx_correction(); } else { if (portapack::persistent_memory::config_freq_rx_correction_updown()) // rx freq correction down final_frequency = final_frequency - portapack::persistent_memory::config_freq_rx_correction(); else // rx freq correction up final_frequency = final_frequency + portapack::persistent_memory::config_freq_rx_correction(); } const auto tuning_config = tuning::config::create(final_frequency); if (tuning_config.is_valid()) { first_if.disable(); // Program first local oscillator frequency (if there is one) into RFFC507x if (tuning_config.first_lo_frequency) { first_if.set_frequency(tuning_config.first_lo_frequency); first_if.enable(); } // Program second local oscillator frequency into MAX283x const auto result_second_if = second_if->set_frequency(tuning_config.second_lo_frequency); rf_path.set_band(tuning_config.rf_path_band); mixer_invert = tuning_config.mixer_invert; baseband_cpld.set_invert(mixer_invert ^ baseband_invert); return result_second_if; } else { return false; } } void set_rf_amp(const bool rf_amp) { rf_path.set_rf_amp(rf_amp); if (direction == rf::Direction::Transmit) { if (rf_amp) led_tx.on(); else led_tx.off(); } } void set_lna_gain(const int_fast8_t db) { second_if->set_lna_gain(db); } void set_vga_gain(const int_fast8_t db) { second_if->set_vga_gain(db); } void set_tx_gain(const int_fast8_t db) { second_if->set_tx_vga_gain(db); } void set_baseband_filter_bandwidth_rx(const uint32_t bandwidth_minimum) { second_if->set_lpf_rf_bandwidth_rx(bandwidth_minimum); } void set_baseband_filter_bandwidth_tx(const uint32_t bandwidth_minimum) { second_if->set_lpf_rf_bandwidth_tx(bandwidth_minimum); } void set_baseband_rate(const uint32_t rate) { portapack::clock_manager.set_sampling_frequency(rate); // TODO: actually set baseband too? } void set_antenna_bias(const bool on) { /* Pull MOSFET gate low to turn on antenna bias. */ if (hackrf_r9) { gpio_r9_not_ant_pwr.write(on ? 0 : 1); } else { first_if.set_gpo1(on ? 0 : 1); } } /*void enable(Configuration configuration) { configure(configuration); } void configure(Configuration configuration) { set_tuning_frequency(configuration.tuning_frequency); set_rf_amp(configuration.rf_amp); set_lna_gain(configuration.lna_gain); set_vga_gain(configuration.vga_gain); set_baseband_rate(configuration.baseband_rate); set_baseband_filter_bandwidth(configuration.baseband_filter_bandwidth); set_direction(configuration.direction); }*/ void disable() { set_antenna_bias(false); baseband_codec.set_mode(max5864::Mode::Shutdown); second_if->set_mode(max2837::Mode::Standby); first_if.disable(); set_rf_amp(false); led_rx.off(); led_tx.off(); } namespace debug { namespace first_if { uint32_t register_read(const size_t register_number) { return radio::first_if.read(register_number); } void register_write(const size_t register_number, uint32_t value) { radio::first_if.write(register_number, value); } } /* namespace first_if */ namespace second_if { uint32_t register_read(const size_t register_number) { return radio::second_if->read(register_number); } void register_write(const size_t register_number, uint32_t value) { radio::second_if->write(register_number, value); } int8_t temp_sense() { return radio::second_if->temp_sense(); } } /* namespace second_if */ } /* namespace debug */ } /* namespace radio */