From 58dc6d4d7452902f0b4eabfcb6c767eda2b37eed Mon Sep 17 00:00:00 2001 From: Paul Date: Tue, 19 Dec 2023 16:48:49 +0100 Subject: [PATCH] update si5351B support --- SoapyHifiBerry.h | 15 ++-- SoapyHifiBerrySettings.cpp | 171 +++++++++++++++++++++++++++--------- SoapyHifiBerryStreaming.cpp | 45 +--------- si5351.cpp | 43 ++++----- si5351.h | 7 +- 5 files changed, 155 insertions(+), 126 deletions(-) diff --git a/SoapyHifiBerry.h b/SoapyHifiBerry.h index a97ae4c..e037dbd 100644 --- a/SoapyHifiBerry.h +++ b/SoapyHifiBerry.h @@ -30,16 +30,6 @@ Si5351 --------------------------------------------------------*/ #define SI5351_BUS_BASE_ADDR 0x60 - -#define CLK_VFO_RX SI5351_CLK0 -#define CLK_VFO_TX SI5351_CLK1 -#define CLK_NA SI5351_CLK2 - -#define CLK_VFO_I SI5351_CLK0 -#define CLK_VFO_Q SI5351_CLK1 -#define CLK_VFO_TX_I SI5351_CLK4 -#define CLK_VFO_TX_Q SI5351_CLK5 - const int hifiBerry_BufferSize = 2048; typedef enum hifiberrysdrStreamFormat @@ -198,4 +188,9 @@ class SoapyHifiBerry : public SoapySDR::Device vfoMode vfoIQMode; int multiplier; bool disableOutput; + + int currDirection; + void setOutput(const int direction); + enum si5351_clock iclk, iclkTx; + enum si5351_clock qclk, qclkTx; }; diff --git a/SoapyHifiBerrySettings.cpp b/SoapyHifiBerrySettings.cpp index 779c6c3..ee99ca9 100644 --- a/SoapyHifiBerrySettings.cpp +++ b/SoapyHifiBerrySettings.cpp @@ -6,7 +6,7 @@ **********************************************************************/ const cfg::File::ConfigMap defaultOptions = { - {"si5351", {{"correction", cfg::makeOption("0")}, {"correction_tx", cfg::makeOption("0")}, {"disabletxoutput", cfg::makeOption("on")}, {"mode", cfg::makeOption("IQMULTI")}, {"multiplier", cfg::makeOption("1")}, {"rxdrive", cfg::makeOption("8")}, {"txdrive", cfg::makeOption("8")}}}, + {"si5351", {{"correction", cfg::makeOption("0")}, {"correction_tx", cfg::makeOption("0")}, {"disabletxoutput", cfg::makeOption("off")}, {"mode", cfg::makeOption("IQMULTI")}, {"multiplier", cfg::makeOption("1")}, {"rxdrive", cfg::makeOption("8")}, {"txdrive", cfg::makeOption("8")}}}, {"sound", {{"device", cfg::makeOption("snd_rpi_hifiberry_dacplusadcpro")}, {"samplerate", cfg::makeOption("192000")}, {"samplerate_tx", cfg::makeOption("192000")}, {"input", cfg::makeOption("DIFF")}}}}; int SoapyHifiBerry::get_int(string section, string key) @@ -45,6 +45,12 @@ SoapyHifiBerry::SoapyHifiBerry(const SoapySDR::Kwargs &args) disableOutput = true; uptr_cfg = make_unique(); multiplier = 0; + currDirection = -1; + iclk = SI5351_CLK0; + qclk = SI5351_CLK1; + iclkTx = SI5351_CLK4; + qclkTx = SI5351_CLK5; + if (!uptr_cfg->loadFromFile("hifiberry.cfg")) { uptr_cfg->setDefaultOptions(defaultOptions); @@ -150,7 +156,7 @@ SoapyHifiBerry::SoapyHifiBerry(const SoapySDR::Kwargs &args) { cout << "IQ mode single multiplier = " << multiplier << endl; - pSI5351 = make_unique("/dev/i2c-1", SI5351_BUS_BASE_ADDR, SI5351_CLK0, SI5351_CLK1, SI5351_CLK4, SI5351_CLK5); + pSI5351 = make_unique("/dev/i2c-1", SI5351_BUS_BASE_ADDR); if (!pSI5351) { cout << "No si5351 found" << endl; @@ -170,36 +176,36 @@ SoapyHifiBerry::SoapyHifiBerry(const SoapySDR::Kwargs &args) multiplier = 4; 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, disableOutput ? (0) : (1)); - pSI5351->output_enable(CLK_NA, 0); + pSI5351->drive_strength(iclk, rxDrive); + pSI5351->drive_strength(qclk, txDrive); + pSI5351->output_enable(iclk, 1); + pSI5351->output_enable(qclk, disableOutput ? (0) : (1)); + pSI5351->output_enable(SI5351_CLK2, 0); pSI5351->update_status(); } if (vfoIQMode == IQSingle) { cout << "si5351 found" << endl; pSI5351->set_correction((long)corr, SI5351_PLL_INPUT_XO); - 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, 1); - pSI5351->output_enable(CLK_NA, 0); + pSI5351->drive_strength(iclk, rxDrive); + pSI5351->drive_strength(qclk, rxDrive); + pSI5351->output_enable(iclk, 1); + pSI5351->output_enable(qclk, 1); + pSI5351->output_enable(SI5351_CLK2, 0); pSI5351->update_status(); } if (vfoIQMode == IQSingleMultiPort) { cout << "si5351 found" << endl; pSI5351->set_correction((long)corr, SI5351_PLL_INPUT_XO); - 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, 1); - pSI5351->drive_strength(CLK_VFO_TX_I, txDrive); - pSI5351->drive_strength(CLK_VFO_TX_Q, txDrive); - pSI5351->output_enable(CLK_VFO_TX_I, 1); - pSI5351->output_enable(CLK_VFO_TX_Q, 1); + pSI5351->drive_strength(iclk, rxDrive); + pSI5351->drive_strength(qclk, rxDrive); + pSI5351->output_enable(iclk, 1); + pSI5351->output_enable(qclk, 1); + pSI5351->drive_strength(iclkTx, txDrive); + pSI5351->drive_strength(qclkTx, txDrive); + pSI5351->output_enable(iclkTx, 1); + pSI5351->output_enable(qclkTx, 1); pSI5351->update_status(); } if (vfoIQMode == IQMulti) @@ -207,27 +213,27 @@ SoapyHifiBerry::SoapyHifiBerry(const SoapySDR::Kwargs &args) cout << "IQMulti mode multiplier = " << multiplier << endl; pTCA9548 = std::make_unique("/dev/i2c-1", 0x70); pTCA9548->begin(1); - pSI5351 = make_unique("/dev/i2c-1", SI5351_BUS_BASE_ADDR, CLK_VFO_I, CLK_VFO_Q); + pSI5351 = make_unique("/dev/i2c-1", SI5351_BUS_BASE_ADDR); if (pSI5351->init(SI5351_CRYSTAL_LOAD_8PF, 0, 0)) { pSI5351->set_correction((long)corr, SI5351_PLL_INPUT_XO); - 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, 1); - pSI5351->output_enable(CLK_NA, 0); + pSI5351->drive_strength(iclk, rxDrive); + pSI5351->drive_strength(qclk, rxDrive); + pSI5351->output_enable(iclk, 1); + pSI5351->output_enable(qclk, 1); + pSI5351->output_enable(SI5351_CLK2, 0); pSI5351->update_status(); } pTCA9548->setChannelMask(2); - pSI5351tx = make_unique("/dev/i2c-1", SI5351_BUS_BASE_ADDR, CLK_VFO_I, CLK_VFO_Q); + pSI5351tx = make_unique("/dev/i2c-1", SI5351_BUS_BASE_ADDR); if (pSI5351tx->init(SI5351_CRYSTAL_LOAD_8PF, 0, 0, pSI5351->getFileHandle())) { pSI5351tx->set_correction((long)corrtx, SI5351_PLL_INPUT_XO); - pSI5351tx->drive_strength(CLK_VFO_I, txDrive); - pSI5351tx->drive_strength(CLK_VFO_Q, txDrive); - pSI5351tx->output_enable(CLK_VFO_I, disableOutput ? (0) : (1)); - pSI5351tx->output_enable(CLK_VFO_Q, disableOutput ? (0) : (1)); - pSI5351tx->output_enable(CLK_NA, 0); + pSI5351tx->drive_strength(iclk, txDrive); + pSI5351tx->drive_strength(qclk, txDrive); + pSI5351tx->output_enable(iclk, disableOutput ? (0) : (1)); + pSI5351tx->output_enable(qclk, disableOutput ? (0) : (1)); + pSI5351tx->output_enable(SI5351_CLK2, 0); pSI5351tx->update_status(); } if (pSI5351 == nullptr || pSI5351tx == nullptr) @@ -414,23 +420,23 @@ 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"); switch (vfoIQMode) { case Single: if (pSI5351) - pSI5351->set_freq((uint64_t)frequency * SI5351_FREQ_MULT * multiplier, CLK_VFO_RX); + pSI5351->set_freq((uint64_t)frequency * SI5351_FREQ_MULT * multiplier, iclk); break; case IQSingle: - case IQSingleMultiPort: + case IQSingleMultiPort: { if (pSI5351) - pSI5351->setIQFrequency((long)frequency * (long)multiplier); + pSI5351->setIQFrequency((long)frequency * (long)multiplier, iclk, qclk); + } break; case IQMulti: if (pSI5351) { pTCA9548->setChannelMask(1); - pSI5351->setIQFrequency((long)frequency * (long)multiplier); + pSI5351->setIQFrequency((long)frequency * (long)multiplier, iclk, qclk); } break; } @@ -438,27 +444,25 @@ void SoapyHifiBerry::setFrequency(const int direction, const size_t channel, con if (direction == SOAPY_SDR_TX) { - SoapySDR_log(SOAPY_SDR_INFO, "SoapyHifiBerry::setFrequency called TX"); - switch (vfoIQMode) { case Single: if (pSI5351) - pSI5351->set_freq((uint64_t)frequency * SI5351_FREQ_MULT * multiplier, CLK_VFO_TX); + pSI5351->set_freq((uint64_t)frequency * SI5351_FREQ_MULT * multiplier, qclk); break; case IQSingle: if (pSI5351) - pSI5351->setIQFrequency((long)frequency * (long)multiplier); + pSI5351->setIQFrequency((long)frequency * (long)multiplier, iclk, qclk); break; case IQSingleMultiPort: if (pSI5351) - pSI5351->setIQFrequency((long)frequency * (long)multiplier, true); + pSI5351->setIQFrequency((long)frequency * (long)multiplier, iclkTx, qclkTx); break; case IQMulti: if (pSI5351tx) { pTCA9548->setChannelMask(2); - pSI5351tx->setIQFrequency((long)frequency * (long)multiplier); + pSI5351tx->setIQFrequency((long)frequency * (long)multiplier, iclk, qclk); } break; } @@ -467,3 +471,84 @@ void SoapyHifiBerry::setFrequency(const int direction, const size_t channel, con } // end of source. +void SoapyHifiBerry::setOutput(const int direction) +{ + if (disableOutput) + { + if (currDirection != direction) + { + if (direction == SOAPY_SDR_RX) + { + switch (vfoIQMode) + { + case Single: + if (pSI5351) + { + pSI5351->output_enable(iclk, 1); + pSI5351->output_enable(qclk, 0); + } + break; + case IQSingle: + break; + case IQSingleMultiPort: + if (pSI5351) + { + pSI5351->output_enable(iclk, 1); + pSI5351->output_enable(qclk, 1); + pSI5351->output_enable(iclkTx, 0); + pSI5351->output_enable(qclkTx, 0); + } + break; + case IQMulti: + if (pSI5351tx) + { + pTCA9548->setChannelMask(1); + pSI5351->output_enable(iclk, 1); + pSI5351->output_enable(qclk, 1); + pTCA9548->setChannelMask(2); + pSI5351->output_enable(iclk, 0); + pSI5351->output_enable(qclk, 0); + } + break; + } + } + + if (direction == SOAPY_SDR_TX) + { + switch (vfoIQMode) + { + case Single: + if (pSI5351) + { + pSI5351->output_enable(iclk, 0); + pSI5351->output_enable(qclk, 1); + } + break; + case IQSingle: + break; + case IQSingleMultiPort: + if (pSI5351) + { + pSI5351->output_enable(iclk, 0); + pSI5351->output_enable(qclk, 0); + pSI5351->output_enable(iclkTx, 1); + pSI5351->output_enable(qclkTx, 1); + } + break; + case IQMulti: + if (pSI5351tx) + { + pTCA9548->setChannelMask(1); + pSI5351->output_enable(iclk, 0); + pSI5351->output_enable(qclk, 0); + pTCA9548->setChannelMask(2); + pSI5351->output_enable(iclk, 1); + pSI5351->output_enable(qclk, 1); + } + break; + } + } + currDirection = direction; + } + } +} \ No newline at end of file diff --git a/SoapyHifiBerryStreaming.cpp b/SoapyHifiBerryStreaming.cpp index f163a3d..5bb37b5 100644 --- a/SoapyHifiBerryStreaming.cpp +++ b/SoapyHifiBerryStreaming.cpp @@ -116,29 +116,9 @@ SoapySDR::Stream *SoapyHifiBerry::setupStream( if (direction == SOAPY_SDR_TX) { iqsamples.clear(); - { - if (disableOutput) - { - switch (vfoIQMode) - { - case Single: - if (pSI5351) - pSI5351->output_enable(CLK_VFO_TX, 1); - break; - case IQSingle: - break; - case IQMulti: - if (pSI5351tx) - { - pTCA9548->setChannelMask(2); - pSI5351tx->output_enable(CLK_VFO_I, 1); - pSI5351tx->output_enable(CLK_VFO_Q, 1); - } - break; - } - } - } } + + setOutput(direction); streams.push_back(ptr); return (SoapySDR::Stream *)ptr; } @@ -153,26 +133,7 @@ int SoapyHifiBerry::deactivateStream(SoapySDR::Stream *stream, const int flags, if (ptr->get_direction() == SOAPY_SDR_TX) { SoapySDR_log(SOAPY_SDR_INFO, "SoapyHifiBerry::deactivateStream disable TX "); - if (disableOutput) - { - switch (vfoIQMode) - { - case Single: - if (pSI5351) - pSI5351->output_enable(CLK_VFO_TX, 0); - break; - case IQSingle: - break; - case IQMulti: - if (pSI5351tx) - { - pTCA9548->setChannelMask(2); - pSI5351tx->output_enable(CLK_VFO_I, 0); - pSI5351tx->output_enable(CLK_VFO_Q, 0); - } - break; - } - } + setOutput(SOAPY_SDR_RX); } } return 0; diff --git a/si5351.cpp b/si5351.cpp index c359328..b214dcb 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, si5351_clock i_clock, si5351_clock q_clock, si5351_clock i_clockTx, si5351_clock q_clockTx) - : i2c_bus_addr(i2c_addr), i2c_file(0), i2c_filepath(i2c_device_filepath), iclock(i_clock), qclock(q_clock), iclockTx(i_clockTx), qclockTx(q_clockTx) +Si5351::Si5351(const char *i2c_device_filepath, uint8_t i2c_addr) + : i2c_bus_addr(i2c_addr), i2c_file(0), i2c_filepath(i2c_device_filepath) { @@ -52,7 +52,7 @@ Si5351::Si5351(const char *i2c_device_filepath, uint8_t i2c_addr, si5351_clock i pllb_ref_osc = SI5351_PLL_INPUT_XO; clkin_div = SI5351_CLKIN_DIV_1; i2c_file = 0; - lastMultRx = lastMultTx = -1; + lastMult = -1; } Si5351::~Si5351() @@ -162,8 +162,8 @@ void Si5351::reset(void) pll_assignment[1] = SI5351_PLLA; pll_assignment[2] = SI5351_PLLA; pll_assignment[3] = SI5351_PLLA; - pll_assignment[4] = SI5351_PLLB; - pll_assignment[5] = SI5351_PLLB; + pll_assignment[4] = SI5351_PLLA; + pll_assignment[5] = SI5351_PLLA; pll_assignment[6] = SI5351_PLLB; pll_assignment[7] = SI5351_PLLB; @@ -171,8 +171,8 @@ void Si5351::reset(void) set_ms_source(SI5351_CLK1, SI5351_PLLA); set_ms_source(SI5351_CLK2, SI5351_PLLA); set_ms_source(SI5351_CLK3, SI5351_PLLA); - set_ms_source(SI5351_CLK4, SI5351_PLLB); - set_ms_source(SI5351_CLK5, SI5351_PLLB); + set_ms_source(SI5351_CLK4, SI5351_PLLA); + set_ms_source(SI5351_CLK5, SI5351_PLLA); set_ms_source(SI5351_CLK6, SI5351_PLLB); set_ms_source(SI5351_CLK7, SI5351_PLLB); @@ -1840,6 +1840,7 @@ uint8_t Si5351::select_r_div_ms67(uint64_t *freq) // from Mario AE0GL int Si5351::getEvenDivisor(uint64_t freq) { + if (freq < 6850000) return 126; else if (freq < 9500000) @@ -1868,7 +1869,7 @@ int Si5351::getEvenDivisor(uint64_t freq) return 2; } -void Si5351::setIQFrequency(uint64_t freq, bool highClockPorts) +void Si5351::setIQFrequency(uint64_t freq, enum si5351_clock iclk, enum si5351_clock qclk) { int mult = 0; @@ -1876,26 +1877,14 @@ void Si5351::setIQFrequency(uint64_t freq, bool highClockPorts) uint64_t f = freq * 100ULL; uint64_t pllFreq = freq * mult * 100ULL; - printf("mult = %d pll = %ld Mhz freq %ld\n", mult, pllFreq / 100L, freq); - - if (!highClockPorts) - set_iq_freq_manual(f, pllFreq, iclock, qclock); - else - set_iq_freq_manual(f, pllFreq, iclockTx, qclockTx); - - if (mult != lastMultRx && !highClockPorts) - { - set_phase(iclock, 0); - set_phase(qclock, mult); - pll_reset(pll_assignment[iclock]); - lastMultRx = mult; - } - if (mult != lastMultTx && highClockPorts) + set_iq_freq_manual(f, pllFreq, iclk, qclk); + if (mult != lastMult) { - set_phase(iclockTx, 0); - set_phase(qclockTx, mult); - pll_reset(pll_assignment[iclockTx]); - lastMultTx = mult; + printf("mult = %d pll = %ld Mhz freq %ld\n", mult, pllFreq / 100L, freq); + set_phase(iclk, 0); + set_phase(qclk, mult); + pll_reset(SI5351_PLLA); ////pll_assignment[iclock] all ports should use plla + lastMult = mult; } update_status(); } diff --git a/si5351.h b/si5351.h index 2adf282..07fc401 100644 --- a/si5351.h +++ b/si5351.h @@ -317,14 +317,14 @@ struct Si5351IntStatus class Si5351 { public: - 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_clock iclockTx = SI5351_CLK4, si5351_clock qclockTx = SI5351_CLK5); + Si5351(const char *i2c_device_filepath, uint8_t i2c_addr = SI5351_BUS_BASE_ADDR); ~Si5351(); bool init(uint8_t, uint32_t, int32_t, int fileHandle = 0); void reset(void); uint8_t set_freq(uint64_t, enum si5351_clock); uint8_t set_freq_manual(uint64_t, uint64_t, enum si5351_clock); uint8_t set_iq_freq_manual(uint64_t freq, uint64_t pll_freq, enum si5351_clock iclk, enum si5351_clock qclk); - void setIQFrequency(uint64_t freq, bool highClockPorts = false); + void setIQFrequency(uint64_t freq, enum si5351_clock iclk, enum si5351_clock qclk); int getEvenDivisor(uint64_t freq); void set_pll(uint64_t, enum si5351_pll); void set_ms(enum si5351_clock, struct Si5351RegSet, uint8_t, uint8_t, uint8_t); @@ -376,8 +376,7 @@ class Si5351 int i2c_file; std::string i2c_filepath; - int lastMultRx, lastMultTx; - si5351_clock iclock, qclock, iclockTx, qclockTx; + int lastMult; }; #endif /* SI5351_H_ */