SI5351 with Nano. Controlling outputs with digital pin?

I am needing help with this sketch.
It’s with an Arduino nano and a SI5351 clock generator. The generator has 3 outputs that output a square wave all the way up to 200mhz or so. Clk 0, clk 1, clk 2…. I am only using clk 0 and 1…. What I’m needing it to do is use a pin on the arduino, lets say pin 7. To decide which output turns on or off. What I want is if pin 7 is high clk 0 is off and clk 1 is on… if pin 7 is low clk 0 is on and clk 1 is off… I’ve been scratching my head for a while here without any luck. Any help would be appreciated. I will post the sketch right underneath this.

There must also be something different in this code because the way it currently sits even if I enable clk 1 it does not start outputting a frequency till I go to clock one on the display and turn the encoder left or right then it starts outputting. Not sure why that is either.


#include <si5351.h>
#include <Wire.h>
#include "Rotary.h"
#include "SSD1306Ascii.h"
#include "SSD1306AsciiAvrI2c.h"

// Enconder PINs
#define ENCODER_PIN_A 3 // Encoder pin A
#define ENCODER_PIN_B 2 // Encoder pin B
#define SWITCH_CMD  4   // ENCODER button or regular push button
#define BUTTON_UP   6   // Button up command
#define BUTTON_DOWN 5   // Button down command

#define CMD_STEP     0
#define CMD_FAVORITE 1
#define CMD_CLK      2

// OLED Diaplay constants
#define I2C_ADDRESS 0x3C
#define RST_PIN -1 // Define proper RST_PIN if required.

// Change this value bellow  (CORRECTION_FACTOR) to 0 if you do not know the correction factor of your Si5351A.
//#define CORRECTION_FACTOR -13500 // See how to calibrate your Si5351A (0 if you do not want).
 #define CORRECTION_FACTOR 160300

#define MIN_VFO 3276800LLU
#define MAX_VFO 16000000000LLU    // VFO max. frequency 30MHz

// Struct for step
typedef struct
{
  char *name; // step label: 50Hz, 10Hz, 500Hz and 100KHz
  long value; // Frequency value (unit 0.01Hz See documentation) to increment or decrement
} Step;

// Steps database. You can change the Steps and numbers of steps here if you need.
Step step[] = {
    {(char *)"1Hz", 100},   // Minimum Frequency step (incremente or decrement) 1Hz
     
    {(char *)"100Hz", 10000},
    {(char *)"1KHz", 100000},
    {(char *)"10KHz", 1000000},
    {(char *)"500KHz", 50000000},
    {(char *)"1MHz", 100000000}}; // Maximum frequency step 500KHz

// Calculate the index of last position of step[] array
const int lastStepVFO = (sizeof step / sizeof(Step)) - 1;
int currentStep = 4;

// Your favotite frequencies
uint64_t favorite[] = {3276800LLU, 350000000LLU, 710000000LLU, 1598800000LLU, 1070000000LLU, 1200000000LLU,
                       1350000000LLU, 1600000000LLU, 2000000000LLU, 2400000000LLU, 2700000000LLU, 2800000000LLU,
                       3200000000LLU,4600000000LLU, 5000000000LLU, 10000000000LLU, 13300000000LLU, 14400000000LLU};
const int lastFavorite = (sizeof favorite / sizeof(uint64_t) ) - 1;
int currentFavorite = 3;

uint64_t currentOutputClock[]{1070000000LLU, 1350000000LLU, 3276800LLU}; // Stores the current clock on CLK0, CLK1 and CLK2
const int lastCurrentOutputClock = (sizeof currentOutputClock / sizeof(uint64_t)) - 1;

int currentClock = 0; // current clock output 0

int currentCommand = CMD_STEP;

char *cmd[] = {(char *) "Step", (char *) "Favorite", (char *) "Out. Clk" };
char *clk[] = {(char *) "CLK0", (char *) "CLK1", (char *) "CLK2" };

// Encoder controller
Rotary encoder = Rotary(ENCODER_PIN_A, ENCODER_PIN_B);

// OLED - Declaration for a SSD1306 display connected to I2C (SDA, SCL pins)
SSD1306AsciiAvrI2c display;

// The Si5351 instance.
Si5351 si5351;

bool isFreqChanged = true;
bool clearDisplay = false;

uint64_t vfoFreq;
uint64_t vfoLastValue;

// Encoder control variables
volatile int encoderCount = 0;

void setup()
{
  // Encoder pins
  pinMode(ENCODER_PIN_A, INPUT_PULLUP);
  pinMode(ENCODER_PIN_B, INPUT_PULLUP);
  // Si5351 contrtolers pins

  pinMode(SWITCH_CMD, INPUT_PULLUP);
  pinMode(BUTTON_UP,  INPUT_PULLUP);
  pinMode(BUTTON_DOWN, INPUT_PULLUP);

  // Initiating the OLED Display
  display.begin(&Adafruit128x64, I2C_ADDRESS);
  display.setFont(Adafruit5x7);
  display.set1X();
  display.clear();
  display.setCursor(17, 0);
  display.print("Multipurpose");
  display.setCursor(33, 1);
  display.print("Signal");
  display.setCursor(23, 2);
  display.print("Generator");
  display.setCursor(22, 5);
  display.print("BY WZ5TX");
  delay(4000);
  display.clear();

  vfoFreq = favorite[currentFavorite];
  currentOutputClock[currentClock] = vfoFreq;

  showStatus();
  // Initiating the Signal Generator (si5351)
  si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);
  // Adjusting the frequency (see how to calibrate the Si5351 - example si5351_calibration.ino)
  si5351.set_correction(CORRECTION_FACTOR, SI5351_PLL_INPUT_XO);
  si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
  si5351.set_freq(vfoFreq, (si5351_clock) currentClock); // Start CLK0 (VFO)

  // Disable CLK 1 and 2 outputs
  si5351.output_enable(SI5351_CLK1, 0);
  si5351.output_enable(SI5351_CLK2, 0);
  si5351.update_status();

  delay(500);

  // Encoder interrupt
  attachInterrupt(digitalPinToInterrupt(ENCODER_PIN_A), rotaryEncoder, CHANGE);
  attachInterrupt(digitalPinToInterrupt(ENCODER_PIN_B), rotaryEncoder, CHANGE);

  delay(1000);
}

// Use Rotary.h and  Rotary.cpp implementation to process encoder via interrupt
void rotaryEncoder()
{ // rotary encoder events
  uint8_t encoderStatus = encoder.process();
  if (encoderStatus)
  {
    if (encoderStatus == DIR_CW)
    {
      encoderCount = 1;
    }
    else
    {
      encoderCount = -1;
    }
  }
}

// Show Signal Generator Information
// Verificar setCur
void showStatus()
{
  char aux[15];
  double vfo = vfoFreq / 100000.0;

  dtostrf(vfo,5,3,aux);

  display.setCursor(0, 0);
  display.set2X();
  display.setCursor(0, 0);
  display.print("          ");
  display.setCursor(0, 0);
  display.print(aux);
  display.setCursor(95,2);
  display.set1X();
  display.print("KHz");
  display.setCursor(0, 4);
  display.print("Step: ");
  display.print(step[currentStep].name);
  display.print(" - ");
  display.print(clk[currentClock]);
  display.setCursor(0, 6);
  display.print("Command: ");
  display.print(cmd[currentCommand]);
}

// Change the frequency (increment or decrement)
// direction parameter is 1 (clockwise) or -1 (counter-clockwise)
void changeFreq(int direction)
{
  vfoFreq += step[currentStep].value * direction; // currentStep * direction;
  // Check the VFO limits
  if (vfoFreq > MAX_VFO )
    vfoFreq = MIN_VFO;
  else if (vfoFreq < MIN_VFO)
    vfoFreq = MAX_VFO;

  isFreqChanged = true;
}

void doCommandUp() {
  display.clear();
  if ( currentCommand == CMD_STEP )
    currentStep = (currentStep < lastStepVFO) ? (currentStep + 1) : 0;
  else if (currentCommand == CMD_FAVORITE )
  {
    currentFavorite = (currentFavorite < lastFavorite) ? (currentFavorite + 1) : 0;
    vfoFreq = favorite[currentFavorite];
    currentOutputClock[currentClock] = vfoFreq;
    isFreqChanged = true;
  } else {
    currentClock = (currentClock < lastCurrentOutputClock) ? (currentClock + 1) : 0;
    vfoFreq = currentOutputClock[currentClock];
  }
  delay(200);
}

void doCommandDown() {
  display.clear();
  if ( currentCommand == CMD_STEP )
    currentStep = (currentStep > 0) ? (currentStep - 1) : lastStepVFO;
  else if (currentCommand == CMD_FAVORITE)
  {
    currentFavorite = (currentFavorite > 0) ? (currentFavorite - 1) : lastFavorite;
    vfoFreq = favorite[currentFavorite];
    currentOutputClock[currentClock] = vfoFreq;
    isFreqChanged = true;
  } else {
    currentClock = (currentClock > 0) ? (currentClock - 1) : lastCurrentOutputClock;
    vfoFreq = currentOutputClock[currentClock];
  }
  delay(200);
}

void loop()
{
  // Check if the encoder has moved.
  if (encoderCount != 0)
  {
    if (encoderCount == 1)
      changeFreq(1);
    else
      changeFreq(-1);
    encoderCount = 0;
  }

  // Switch the current command control (step or favorite frequencies)
  if (digitalRead(SWITCH_CMD) == LOW) {
    display.clear();
    currentCommand = (currentCommand >= 2)? 0: (currentCommand + 1);;
    showStatus();
    delay(200);
  } else if (digitalRead(BUTTON_UP) == LOW) {
    doCommandUp();
    showStatus();
  } else if (digitalRead(BUTTON_DOWN) == LOW) {
    doCommandDown();
    showStatus();
  }

  if (isFreqChanged)
  {
    si5351.set_freq(vfoFreq, (si5351_clock) currentClock);
    currentOutputClock[currentClock] = vfoFreq;
    showStatus();
    isFreqChanged = false;
  }
  delay(50);
}

If interpreted literally, you could solve your problem with a multiplexer chip.

But it seems likely that the I2C control would allow directly turning on and off outputs.

Study the data sheet and the library examples while you wait for someone who knows this chip and hands you the answer.

If there are more than one library, read into all of them.

a7

1 Like

Posting datasheet links to not commonly known devices, and sometimes schematics gives You the favour of faster and more precise replies.

1 Like

Addressing Your Issue:

Your problem is not unexpected, and your wiring may be the root cause. Since hardware is involved, it’s crucial to provide an accurate, annotated schematic of your circuit as it is currently wired. Please note that Fritzing diagrams are not considered proper schematics; they are wiring diagrams and are often not helpful for troubleshooting.

What to Include:

  1. Annotated Schematic: Show all connections, including power, ground, and power sources. This helps us understand how your circuit is set up and identify any potential issues.
  2. Technical Information Links: Provide links to technical documentation for each hardware device used in your setup. Avoid links to sales sites like Amazon, as they usually lack the necessary technical details. We need complete specifications to help you effectively.
  3. Additional Information Needed: If the above details are incorrect, more information is required. Tell us what hardware and software you are using, the format of any data (like map data), and how your system determines its position. For example, if your project involves a robot, describe how it navigates and what computers are involved.
  4. Bill of Materials:
  5. A schematic provides a visual representation of the electrical connections and components in a circuit, showing how they interact and function together, while a Bill of Materials (BOM) is simply a list of components with part numbers and quantities needed for assembly. Unlike a BOM, a schematic illustrates the design’s logic and allows for troubleshooting and understanding circuit behavior. A BOM is useful for sourcing parts but doesn’t convey how they connect or operate in the circuit, which is critical information for understanding and working with electronics.

Why This Matters:

We have no way of knowing the specifics of your setup unless you provide that information. Clear and detailed descriptions enable us to offer the most accurate help possible. Without these details, it’s difficult to diagnose and solve the issues you're experiencing.

We need the following:

  • Annotated Schematic: Clearly show all connections, including power, ground, and power sources. This will help us understand your circuit setup and identify potential issues.

  • Technical Information Links: Provide links to technical documentation for each hardware component in your setup. Avoid links to sales sites like Amazon, as they often lack the detailed specifications we need to assist you effectively.

Why Do We Want This:

We can’t accurately assist without knowing the specifics of your setup. Clear, detailed descriptions will allow us to offer the best help possible. Without this information, diagnosing and solving your issues will be much harder.

Give yourself a belated Christmas present and pick up these resources, along with the Arduino Cookbook:

Hi, @dtbuff881
Welcome to the forum.

Did you write this code?

This part should tell you what to do.

  // Disable CLK 1 and 2 outputs
  si5351.output_enable(SI5351_CLK1, 0);
  si5351.output_enable(SI5351_CLK2, 0);
  si5351.update_status();

I haven't had much experience with the 5351, I have one, might dig it out over the weekend.

If you wrote the code you should have done it in stages.
I would suggest you leave your code for now and write some simple code to control and try to change output ports.

When you have got the degree of control you need, then integrate into your main code.

Tom... VK3DMK.. :smiley: :+1: :coffee: :australia:

Tom,
No sir I did not write the code.
I have however got a basic si5351 sketch and am able to control the outputs on or off with a digital pin receiving a high or low signal.
When I try to incorporate that to this sketch it does not work. I will paste the basic sketch below where it does work. But there is no display or encoder.

This basic sketch has fixed frequency’s on the clk 0 and clk 1 outputs, but with the code I want this to work on the frequency can be changed with the rotary encoder.
Thank you for the reply. Hope to hear back from you.

#include "si5351.h"
#include "Wire.h"

Si5351 si5351;


//Operating frequencies go here
float RX1 = 443.860;

float TX1 = 462.700;

//This calculates the xtal frequencies from the operating frequencies
long RC1 = (((10000 * RX1) - 179000) / 8);

long TC1 = ((10000 * TX1) / 9);


void setup()
{
  Serial.begin(57600);
  bool i2c_found;
  i2c_found = si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 148000);
  pinMode(3, INPUT);         //COR
  pinMode(2, INPUT);         //PTT
  
}

void loop()
{

//RX
  if(digitalRead(2) == LOW) //Checking if PTT is in RX
  {
    si5351.output_enable(SI5351_CLK0, 1); //Turning SI RX output on
    si5351.output_enable(SI5351_CLK1, 0); //Turning SI TX output off

    
    {
      si5351.set_freq((RC1 * 10000ULL), SI5351_CLK0);
      delay(50);
    }

  }

//TX
  if(digitalRead(2) == HIGH) //Checking if PTT is in TX
  {
    si5351.output_enable(SI5351_CLK0, 0); //Turning SI RX output off
    si5351.output_enable(SI5351_CLK1, 1); //Turning SI TX output on

    {
      si5351.set_freq((TC1 * 10000ULL), SI5351_CLK1)
      delay(50);
    }
 

  }
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.