2015-07-08 08:39:24 -07:00
/*
* 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"
2016-02-16 10:28:13 -08:00
#include "rffc507x.hpp"
#include "max2837.hpp"
2023-02-16 12:09:23 +00:00
#include "max2839.hpp"
2015-07-08 08:39:24 -07:00
#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 ;
2017-11-09 22:25:37 +00:00
#include "cpld_update.hpp"
2015-08-01 13:42:27 -07:00
#include "portapack.hpp"
2023-03-22 08:46:58 +01:00
#include "portapack_persistent_memory.hpp"
2015-07-08 08:39:24 -07:00
2023-05-23 21:22:30 -07:00
/* Direct access to the radio. Setting values incorrectly can damage
* the device. Applications should use ReceiverModel or TransmitterModel
* instead of calling these functions directly. */
2015-07-08 08:39:24 -07:00
namespace radio {
2023-05-19 08:16:05 +12:00
static constexpr uint32_t ssp1_cpsr = 2 ;
2015-07-08 08:39:24 -07:00
static constexpr uint32_t ssp_scr (
2023-05-19 08:16:05 +12:00
const float pclk_f ,
const uint32_t cpsr ,
const float spi_f ) {
return static_cast < uint8_t > ( pclk_f / cpsr / spi_f - 1 );
2015-07-08 08:39:24 -07:00
}
2023-02-16 12:09:23 +00:00
static constexpr SPIConfig ssp_config_max283x = {
2023-05-19 08:16:05 +12:00
. end_cb = NULL ,
. ssport = gpio_max283x_select . port (),
. sspad = gpio_max283x_select . pad (),
. cr0 =
2023-12-09 07:09:20 -06:00
CR0_CLOCKRATE ( ssp_scr ( ssp1_pclk_f , ssp1_cpsr , max283x_spi_f ) + 3 ) | CR0_FRFSPI | CR0_DSS16BIT ,
2023-05-19 08:16:05 +12:00
. cpsr = ssp1_cpsr ,
2015-07-08 08:39:24 -07:00
};
static constexpr SPIConfig ssp_config_max5864 = {
2023-05-19 08:16:05 +12:00
. 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 ,
2015-07-08 08:39:24 -07:00
};
2015-08-01 13:42:27 -07:00
static spi :: arbiter :: Arbiter ssp1_arbiter ( portapack :: ssp1 );
2015-07-08 08:39:24 -07:00
2023-05-19 08:16:05 +12:00
static spi :: arbiter :: Target ssp1_target_max283x {
ssp1_arbiter ,
ssp_config_max283x };
2015-07-08 08:39:24 -07:00
2023-05-19 08:16:05 +12:00
static spi :: arbiter :: Target ssp1_target_max5864 {
ssp1_arbiter ,
ssp_config_max5864 };
2015-07-08 08:39:24 -07:00
static rf :: path :: Path rf_path ;
rffc507x :: RFFC507x first_if ;
2023-05-19 08:16:05 +12:00
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 };
2015-07-08 08:39:24 -07:00
static baseband :: CPLD baseband_cpld ;
2023-10-14 03:28:44 -05:00
// 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 };
2023-02-16 12:09:23 +00:00
static bool baseband_invert = false ;
static bool mixer_invert = false ;
2015-07-08 08:39:24 -07:00
void init () {
2023-05-19 08:16:05 +12:00
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 ();
2015-07-08 08:39:24 -07:00
}
void set_direction ( const rf :: Direction new_direction ) {
2023-05-19 08:16:05 +12:00
/* TODO: Refactor all the various "Direction" enumerations into one. */
/* TODO: Only make changes if direction changes, but beware of clock enabling. */
2021-05-22 20:57:48 +01:00
2024-02-11 23:16:29 +01:00
// That below code line , was used to prevent RX interf ghosting when switching back to RX from any TX mode, but in recent code. it seems not necessary.
// Deleting that load_sram_no_verify() (or the original , load_sram() ), solves random TX swap I/Q problem in H1R1 , others OK- (and no side effects to all).
// hackrf::cpld::load_sram_no_verify(); // After commit "removed the use of the hackrf cpld eeprom #1732", in a H1R1, Mic App wrong SSB TX with random USB/LSB change.
2015-07-08 08:39:24 -07:00
2023-05-19 08:16:05 +12:00
direction = new_direction ;
2017-01-16 13:36:28 +00:00
2023-05-19 08:16:05 +12:00
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 ();
2015-07-08 08:39:24 -07:00
}
bool set_tuning_frequency ( const rf :: Frequency frequency ) {
2023-05-19 08:16:05 +12:00
rf :: Frequency final_frequency = frequency ;
2023-05-20 09:30:08 +02:00
// if converter feature is enabled
2023-05-19 08:16:05 +12:00
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 ();
}
}
2023-05-20 09:30:08 +02:00
// 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 ();
}
2023-05-19 08:16:05 +12:00
const auto tuning_config = tuning :: config :: create ( final_frequency );
if ( tuning_config . is_valid ()) {
first_if . disable ();
2015-07-08 08:39:24 -07:00
2024-01-28 10:02:11 -06:00
// Program first local oscillator frequency (if there is one) into RFFC507x
2023-05-19 08:16:05 +12:00
if ( tuning_config . first_lo_frequency ) {
first_if . set_frequency ( tuning_config . first_lo_frequency );
first_if . enable ();
}
2015-07-08 08:39:24 -07:00
2024-01-28 10:02:11 -06:00
// Program second local oscillator frequency into MAX283x
2023-05-19 08:16:05 +12:00
const auto result_second_if = second_if -> set_frequency ( tuning_config . second_lo_frequency );
2015-07-08 08:39:24 -07:00
2023-05-19 08:16:05 +12:00
rf_path . set_band ( tuning_config . rf_path_band );
mixer_invert = tuning_config . mixer_invert ;
baseband_cpld . set_invert ( mixer_invert ^ baseband_invert );
2015-07-08 08:39:24 -07:00
2023-05-19 08:16:05 +12:00
return result_second_if ;
} else {
return false ;
}
2015-07-08 08:39:24 -07:00
}
void set_rf_amp ( const bool rf_amp ) {
2023-05-19 08:16:05 +12:00
rf_path . set_rf_amp ( rf_amp );
if ( direction == rf :: Direction :: Transmit ) {
if ( rf_amp )
led_tx . on ();
else
led_tx . off ();
}
2015-07-08 08:39:24 -07:00
}
void set_lna_gain ( const int_fast8_t db ) {
2023-05-19 08:16:05 +12:00
second_if -> set_lna_gain ( db );
2015-07-08 08:39:24 -07:00
}
void set_vga_gain ( const int_fast8_t db ) {
2023-05-19 08:16:05 +12:00
second_if -> set_vga_gain ( db );
2015-07-08 08:39:24 -07:00
}
2016-08-03 16:12:22 -07:00
void set_tx_gain ( const int_fast8_t db ) {
2023-05-19 08:16:05 +12:00
second_if -> set_tx_vga_gain ( db );
2016-08-03 16:12:22 -07:00
}
2023-10-08 17:50:31 +02:00
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 );
2015-07-08 08:39:24 -07:00
}
2016-02-05 16:36:36 -08:00
void set_baseband_rate ( const uint32_t rate ) {
2023-05-19 08:16:05 +12:00
portapack :: clock_manager . set_sampling_frequency ( rate );
2023-07-22 10:06:55 -07:00
// TODO: actually set baseband too?
2016-02-05 16:36:36 -08:00
}
2016-01-22 23:24:20 -08:00
void set_antenna_bias ( const bool on ) {
2023-05-19 08:16:05 +12:00
/* 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 );
}
2016-01-22 23:24:20 -08:00
}
2024-02-04 00:57:45 +01:00
void set_tx_max283x_iq_phase_calibration ( const size_t v ) {
second_if -> set_tx_LO_iq_phase_calibration ( v );
}
2024-03-09 23:46:38 +01:00
void set_rx_max283x_iq_phase_calibration ( const size_t v ) {
second_if -> set_rx_LO_iq_phase_calibration ( v );
}
2023-05-23 21:22:30 -07:00
/*void enable(Configuration configuration) {
2023-05-19 08:16:05 +12:00
configure(configuration);
2016-02-10 16:58:25 -08:00
}
void configure(Configuration configuration) {
2023-05-19 08:16:05 +12:00
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);
2023-05-23 21:22:30 -07:00
}*/
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 ();
2016-02-10 16:58:25 -08:00
}
2016-02-16 10:28:13 -08:00
namespace debug {
namespace first_if {
uint32_t register_read ( const size_t register_number ) {
2023-05-19 08:16:05 +12:00
return radio :: first_if . read ( register_number );
2016-02-16 10:28:13 -08:00
}
2023-11-17 12:10:11 -06:00
void register_write ( const size_t register_number , uint32_t value ) {
radio :: first_if . write ( register_number , value );
}
2016-02-16 10:28:13 -08:00
} /* namespace first_if */
namespace second_if {
uint32_t register_read ( const size_t register_number ) {
2023-05-19 08:16:05 +12:00
return radio :: second_if -> read ( register_number );
2016-02-16 10:28:13 -08:00
}
2023-11-17 12:10:11 -06:00
void register_write ( const size_t register_number , uint32_t value ) {
radio :: second_if -> write ( register_number , value );
}
2023-11-06 13:56:09 -06:00
int8_t temp_sense () {
return radio :: second_if -> temp_sense ();
2016-02-16 10:28:13 -08:00
}
} /* namespace second_if */
} /* namespace debug */
2015-07-08 08:39:24 -07:00
} /* namespace radio */