diff --git a/SoapyHifiBerry.h b/SoapyHifiBerry.h index 6f8ff5e..4bda789 100644 --- a/SoapyHifiBerry.h +++ b/SoapyHifiBerry.h @@ -23,6 +23,7 @@ #include "AudioInput.h" #include "AudioOutput.h" #include "si5351.h" +#include "TCA9548.h" #include "configfile.h" /*------------------------------------------------------- @@ -34,6 +35,10 @@ #define CLK_VFO_TX SI5351_CLK1 #define CLK_NA SI5351_CLK2 +#define CLK_VFO_I SI5351_CLK0 +#define CLK_VFO_Q SI5351_CLK1 + + const int hifiBerry_BufferSize = 2048; typedef enum hifiberrysdrStreamFormat @@ -167,16 +172,19 @@ class SoapyHifiBerry : public SoapySDR::Device bool i2c_available; std::vector streams; - unique_ptr uptr_HifiBerryAudioOutputput; - unique_ptr uptr_HifiBerryAudioInput; - unique_ptr uptr_cfg; + std::unique_ptr uptr_HifiBerryAudioOutputput; + std::unique_ptr uptr_HifiBerryAudioInput; + std::unique_ptr uptr_cfg; SoapyHifiBerryDataBuffer source_buffer_rx; SoapyHifiBerryDataBuffer source_buffer_tx; - unique_ptr pSI5351; + std::unique_ptr pSI5351; + std::unique_ptr pSI5351tx; + std::unique_ptr pTCA9548; int get_int(string section, string key); - string get_string(string section, string key); + std::string get_string(string section, string key); si5351_drive txDrive, rxDrive; SoapySDR::Range rxGain{-12, 40}; + bool modeIQ; }; diff --git a/SoapyHifiBerrySettings.cpp b/SoapyHifiBerrySettings.cpp index 680dbb2..c0e63b3 100644 --- a/SoapyHifiBerrySettings.cpp +++ b/SoapyHifiBerrySettings.cpp @@ -5,9 +5,8 @@ * Device interface **********************************************************************/ const cfg::File::ConfigMap defaultOptions = { - {"si5351", {{"correction", cfg::makeOption("0")}, {"rxdrive", cfg::makeOption("2")}, {"t.xdrive", cfg::makeOption("2")}}}, - {"sound", {{"device", cfg::makeOption("snd_rpi_hifiberry_dacplusadcpro")}, {"samplerate", cfg::makeOption("192000")}, {"input", cfg::makeOption("DIFF")}}} -}; + {"si5351", {{"correction", cfg::makeOption("0")}, {"mode", cfg::makeOption("single")} , {"rxdrive", cfg::makeOption("2")}, {"t.xdrive", cfg::makeOption("2")}}}, + {"sound", {{"device", cfg::makeOption("snd_rpi_hifiberry_dacplusadcpro")}, {"samplerate", cfg::makeOption("192000")}, {"input", cfg::makeOption("DIFF")}}}}; int SoapyHifiBerry::get_int(string section, string key) { @@ -41,6 +40,7 @@ SoapyHifiBerry::SoapyHifiBerry(const SoapySDR::Kwargs &args) SoapySDR_log(SOAPY_SDR_INFO, "SoapyHifiBerry::SoapyHifiBerry constructor called"); no_channels = 1; txDrive = rxDrive = SI5351_DRIVE_2MA; + modeIQ = false; uptr_cfg = make_unique(); if (!uptr_cfg->loadFromFile("hifiberry.cfg")) @@ -120,22 +120,59 @@ SoapyHifiBerry::SoapyHifiBerry(const SoapySDR::Kwargs &args) //numid = 25, iface = MIXER, name = 'ADC Mic Bias' uptr_HifiBerryAudioOutputput->controle_alsa(25, 0); - pSI5351 = make_unique("/dev/i2c-1",SI5351_BUS_BASE_ADDR); - if (pSI5351->init(SI5351_CRYSTAL_LOAD_8PF, 0, 0)) + std::string IQMode = SoapyHifiBerry::get_string("si5351", "mode"); + if (IQMode != "IQ") { - cout << "si5351 found" << endl; - pSI5351->set_correction((long)corr, SI5351_PLL_INPUT_XO); - pSI5351->drive_strength(CLK_VFO_RX, rxDrive); - pSI5351->drive_strength(CLK_VFO_TX, txDrive); - pSI5351->output_enable(CLK_VFO_RX, 1); - pSI5351->output_enable(CLK_VFO_TX, 0); - pSI5351->output_enable(CLK_NA, 0); - pSI5351->update_status(); + pSI5351 = make_unique("/dev/i2c-1", SI5351_BUS_BASE_ADDR); + if (pSI5351->init(SI5351_CRYSTAL_LOAD_8PF, 0, 0)) + { + cout << "si5351 found" << endl; + pSI5351->set_correction((long)corr, SI5351_PLL_INPUT_XO); + pSI5351->drive_strength(CLK_VFO_RX, rxDrive); + pSI5351->drive_strength(CLK_VFO_TX, txDrive); + pSI5351->output_enable(CLK_VFO_RX, 1); + pSI5351->output_enable(CLK_VFO_TX, 0); + pSI5351->output_enable(CLK_NA, 0); + pSI5351->update_status(); + } + else + { + cout << "No si5351 found" << endl; + pSI5351.reset(nullptr); + } } else { - cout << "No si5351 found" << endl; - pSI5351.reset(nullptr); + pTCA9548 = std::make_unique("/dev/i2c-1", 0x70); + pTCA9548->begin(1); + pSI5351 = make_unique("/dev/i2c-1", SI5351_BUS_BASE_ADDR); + if (pSI5351->init(SI5351_CRYSTAL_LOAD_8PF, 0, 0)) + { + pSI5351->drive_strength(CLK_VFO_I, rxDrive); + pSI5351->drive_strength(CLK_VFO_Q, rxDrive); + pSI5351->output_enable(CLK_VFO_I, 1); + pSI5351->output_enable(CLK_VFO_Q, 0); + pSI5351->output_enable(CLK_NA, 0); + pSI5351->update_status(); + } + pTCA9548->setChannelMask(2); + pSI5351tx = make_unique("/dev/i2c-1", SI5351_BUS_BASE_ADDR); + if (pSI5351tx->init(SI5351_CRYSTAL_LOAD_8PF, 0, 0)) + { + pSI5351tx->drive_strength(CLK_VFO_I, rxDrive); + pSI5351tx->drive_strength(CLK_VFO_Q, rxDrive); + pSI5351tx->output_enable(CLK_VFO_I, 1); + pSI5351tx->output_enable(CLK_VFO_Q, 0); + pSI5351tx->output_enable(CLK_NA, 0); + pSI5351tx->update_status(); + } + if (pSI5351 == nullptr || pSI5351tx == nullptr) + { + cout << "No si5351 found" << endl; + pSI5351.reset(nullptr); + pSI5351tx.reset(nullptr); + } + modeIQ = true; } } @@ -317,19 +354,41 @@ void SoapyHifiBerry::setFrequency(const int direction, const size_t channel, con if (direction == SOAPY_SDR_RX) { SoapySDR_log(SOAPY_SDR_INFO, "SoapyHifiBerry::setFrequency called RX"); - - uint64_t freq = (uint64_t)frequency * SI5351_FREQ_MULT * 4; - if (pSI5351) - pSI5351->set_freq(freq, CLK_VFO_RX); + + if (modeIQ) + { + if (pSI5351) + { + pTCA9548->setChannelMask(1); + pSI5351->setIQFrequency((long)frequency); + } + } + else + { + uint64_t freq = (uint64_t)frequency * SI5351_FREQ_MULT * 4; + if (pSI5351) + pSI5351->set_freq(freq, CLK_VFO_RX); + } } if (direction == SOAPY_SDR_TX) { SoapySDR_log(SOAPY_SDR_INFO, "SoapyHifiBerry::setFrequency called TX"); - - uint64_t freq = (uint64_t)frequency * SI5351_FREQ_MULT * 4; - if (pSI5351) - pSI5351->set_freq(freq, CLK_VFO_TX); + + if (modeIQ) + { + if (pSI5351tx) + { + pTCA9548->setChannelMask(2); + pSI5351tx->setIQFrequency((long)frequency); + } + } + else + { + uint64_t freq = (uint64_t)frequency * SI5351_FREQ_MULT * 4; + if (pSI5351) + pSI5351->set_freq(freq, CLK_VFO_RX); + } } } diff --git a/si5351.cpp b/si5351.cpp index fbe9ee0..2f52d75 100644 --- a/si5351.cpp +++ b/si5351.cpp @@ -40,8 +40,8 @@ extern "C" { /* Public functions */ /********************/ -Si5351::Si5351(const char *i2c_device_filepath, uint8_t i2c_addr) - : i2c_bus_addr(i2c_addr), i2c_file(0), i2c_filepath(i2c_device_filepath) +Si5351::Si5351(const char *i2c_device_filepath, uint8_t i2c_addr, si5351_clock i_clock, si5351_clock q_clock) + : i2c_bus_addr(i2c_addr), i2c_file(0), i2c_filepath(i2c_device_filepath), iclock(i_clock), qclock(q_clock) { @@ -52,6 +52,7 @@ Si5351::Si5351(const char *i2c_device_filepath, uint8_t i2c_addr) pllb_ref_osc = SI5351_PLL_INPUT_XO; clkin_div = SI5351_CLKIN_DIV_1; i2c_file = 0; + lastMult = -1; } Si5351::~Si5351() @@ -1827,3 +1828,48 @@ uint8_t Si5351::select_r_div_ms67(uint64_t *freq) return r_div; } + +void Si5351::setIQFrequency(long freq) +{ + int mult = 0; + + if (freq < 5000000) + mult = 150; + else if (freq < 6000000) + mult = 120; + else if (freq < 8000000) + mult = 100; + else if (freq < 11000000) + mult = 80; + else if (freq < 15000000) + mult = 50; + else if (freq < 22000000) + mult = 40; + else if (freq < 30000000) + mult = 30; + else if (freq < 40000000) + mult = 20; + else if (freq < 50000000) + mult = 15; + else if (freq < 90000000) + mult = 10; + + uint64_t f = freq * 100ULL; + uint64_t pllFreq = freq * mult * 100ULL; + + int32_t correction = get_correction(SI5351_PLL_INPUT_XO); + pllFreq = pllFreq + (int32_t)((((((int64_t)correction) << 31) / 1000000000LL) * pllFreq) >> 31); + printf("pll = %ld Mhz \n", pllFreq); + + set_freq_manual(f, pllFreq, iclock); + set_freq_manual(f, pllFreq, qclock); + + if (mult != lastMult) + { + set_phase(iclock, 0); + set_phase(qclock, mult); + pll_reset(SI5351_PLLA); + update_status(); + lastMult = mult; + } +} \ No newline at end of file diff --git a/si5351.h b/si5351.h index 048c901..ecb3223 100644 --- a/si5351.h +++ b/si5351.h @@ -316,12 +316,13 @@ struct Si5351IntStatus class Si5351 { public: - Si5351(const char *i2c_device_filepath, uint8_t i2c_addr = SI5351_BUS_BASE_ADDR); + Si5351(const char *i2c_device_filepath, uint8_t i2c_addr = SI5351_BUS_BASE_ADDR, si5351_clock iclock = SI5351_CLK0, si5351_clock qclock = SI5351_CLK1); ~Si5351(); bool init(uint8_t, uint32_t, int32_t); void reset(void); uint8_t set_freq(uint64_t, enum si5351_clock); uint8_t set_freq_manual(uint64_t, uint64_t, enum si5351_clock); + void setIQFrequency(long freq); void set_pll(uint64_t, enum si5351_pll); void set_ms(enum si5351_clock, struct Si5351RegSet, uint8_t, uint8_t, uint8_t); void output_enable(enum si5351_clock, uint8_t); @@ -371,6 +372,8 @@ class Si5351 int i2c_file; std::string i2c_filepath; + int lastMult; + si5351_clock iclock, qclock; }; #endif /* SI5351_H_ */