Arduino Quadrature VFO with Si5351a clock generator (schematic and code)

Поділитися
Вставка
  • Опубліковано 22 січ 2025

КОМЕНТАРІ • 6

  • @unrelatedactivities
    @unrelatedactivities  3 місяці тому

    Arduino Code part 1:
    /*
    This code is a modified version of a sketch found at py2ohh.w2c.com.br/ ,
    for use as a quadrature VFO in RF direct conversion phasing receivers.
    I modified it just a bit, mantaining same functionality.
    The VFO is based on a Si5351a clock generator IC mounted on a breakout board,
    controlled by an Arduino Nano via I2C. User interface is just a 16x2 LCD and
    a rotary encoder with switch.
    Frequency range should be 3.2Hz to ~110MHz, but I could only verify until about
    80MHz, then quadrature and equal amplitude degrade rapidly (could be due to test
    setup and oscilloscope employed).
    In order to reach the 3.2MHz lower frequency, a modification in the etherkit
    Si5351 library is necessary: locate the file "si5351.h" and change the line
    #define SI5351_PLL_VCO_MIN 600000000
    to
    #define SI5351_PLL_VCO_MIN 400000000
    In the si5351 library example folder there is a sketch that makes
    the frequency calibration process very easy, look for "si5351_calibration.ino"
    and follow the instructions. I calibrated mine using a receiver tuned to
    10.000.500 Hz and a 500Hz reference tone. It is easier than tuning a guitar...
    The original sketch introduction by py2ohh:
    This entire program is taken from Jason Mildrum, NT7S and Przemek Sadowski, SQ9NJE.
    There is not enough original code written by AK2b to make it worth mentioning.
    nt7s.com/
    sq9nje.pl/
    ak2b.blogspot.com/
    I made some mods ...first updating the sketch to new library from NT7S
    ..in frequency coverage and the mode for frequency change..
    pressing the encoder and turn it at same time ...it will move a underline showing
    the place where it is OK to change
    XXXXXXXXXXXXXXXXXXXXXX
    Now I made other mods ... is OK to use with SDR rigs ... the clock0 and
    clock1 are in the same frequency, but in 90 degrees phase out.. it is ok
    from 3.2MHz to 110MHz (teoric) I tested with 30MHz and it was OK
    (to reach 3.2MHz I changed the NT7S library)
    I made a new function according Hans Summers, for dont have clicks
    when change the frequency ...reseting the SI5351 only when the evendivider is changed
    py2ohh.w2c.com.br/
    */
    #include // Ben Buxton's rotary encoder library github.com/brianlow/Rotary
    #include // Etherkit github.com/etherkit/Si5351Arduino
    #include // native Arduino library, for I2C things
    #include // native Arduino library, for LCD things
    const int encApin = 2; // Encoder pin A nano/uno pin D2
    const int encBpin = 3; // Encoder pin B nano/uno pin D3
    const int encSwPin = 4; // Encoder switch nano/uno pin D4
    const int LCD_RS = 5; // LCD pin 4 nano/uno pin D5
    const int LCD_E = 6; // LCD pin 6 nano/uno pin D6
    const int LCD_D4 = 7; // LCD pin 11 nano/uno pin D7
    const int LCD_D5 = 8; // LCD pin 12 nano/uno pin D8
    const int LCD_D6 = 9; // LCD pin 13 nano/uno pin D9
    const int LCD_D7 = 10; // LCD pin 14 nano/uno pin D10
    const int numRows = 2; // LCD number of rows
    const int numCols = 16; // LCD number of columns
    LiquidCrystal lcd(LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7); // LCD pins assignment
    Si5351 si5351(0x60); // Si5351 I2C Address 0x60
    Rotary r = Rotary(encApin, encBpin); // Rotary encoder, pins A, B
    volatile uint32_t vfo = 700000000ULL / SI5351_FREQ_MULT; //start freq, 7MHz
    volatile uint32_t radix = 1000; //start step size
    double vfoMhz;
    boolean changed_f = false; // freq change flag
    short und = 3; // underline cursor control
    short pot = 3; // multiplier
    int evenmult = 100; // PLL frequency must be an even multiple of clock frequency
    int oldevenmult = 0;
    /* ****************************************************** */
    /* Interrupt service routine for encoder frequency change */
    /* ****************************************************** */
    ISR(PCINT2_vect) {
    unsigned char result = r.process();
    if (result == DIR_CW) set_frequency(1);
    else if (result == DIR_CCW) set_frequency(-1);
    }

  • @chimmychen2314
    @chimmychen2314 Місяць тому +1

    Could u share the complete modified project?Thx!

    • @unrelatedactivities
      @unrelatedactivities  Місяць тому

      If you are referring to the SDR project, it is (in its current form) here: "SDR circuit experiment update, schematics and Arduino code" ua-cam.com/video/MfK98hA9SaY/v-deo.html
      Now I am working on transferring the circuit to a larger box, including four more bandpass filters and a second antenna input. You are welcome!

  • @unrelatedactivities
    @unrelatedactivities  3 місяці тому

    Schematic: ua-cam.com/users/postUgkxFN3pQ9SFDtaa-ssTALJWAV6iQFjHx6WB

  • @unrelatedactivities
    @unrelatedactivities  3 місяці тому

    Arduino Code part 3:
    void loop()
    {
    // Update the display if the frequency has been changed
    if (changed_f)
    {
    vfoMhz = vfo / 10000;
    if (vfoMhz >= 320) { // without lib mod this is 476
    display_frequency();
    }
    else {
    display_frequency();
    lcd.setCursor(0, 1);
    lcd.print("LIMIT >= 3.2MHz");
    delay(1000);
    lcd.clear();
    vfo = 3200000;
    display_frequency();
    }
    if ((vfoMhz > 11000)) {
    display_frequency();
    lcd.setCursor(0, 1);
    lcd.print("LIMIT

  • @unrelatedactivities
    @unrelatedactivities  3 місяці тому

    Arduino Code part 2:
    /* ************************************* */
    /* Change the frequency and underline */
    /* dir = 1 Increment, dir = -1 Decrement */
    /* ************************************* */
    void set_frequency(short dir)
    {
    if (!digitalRead(encSwPin)) { // if encoder switch is pressed, encoder
    lcd.setCursor( 12 - und, 0); // rotation controls underline cursor position
    if (dir == 1) {
    und += 1;
    switch (und) {
    case 4 : und = 5; break;
    case 8 : und = 9; break;
    case 12 : und = 11; break;
    }
    }
    if (dir == -1) {
    und += -1;
    switch (und) {
    case 4 : und = 3; break;
    case 8 : und = 7; break;
    case 0 : und = 1; break;
    }
    }
    pot = und;
    if (und > 3) (pot += -1);
    if (und > 7) (pot += -1);
    lcd.setCursor( 12 - und, 0);
    lcd.cursor();
    }
    else // if encoder switch is not pressed, encoder rotation
    { lcd.noCursor(); // increase or decrease frequency in the selected step
    if (dir == 1) vfo += radix;
    if (dir == -1) {
    if (vfo > radix ) {
    vfo -= radix;
    }
    }
    changed_f = true;
    }
    }
    /* **************************************** */
    /* Read the encoder switvh and debounce it */
    /* **************************************** */
    boolean get_button()
    {
    if (!digitalRead(encSwPin)) // If switch is LOW (pressed)
    {
    delay(20); // wait 20ms for debouncing,
    if (!digitalRead(encSwPin)) // read the pin again, and if still LOW
    {
    while (!digitalRead(encSwPin)); // return true while it stay LOW, so the step size
    return 1; // can be changed by rotating the encoder.
    }
    }
    return 0;
    }
    /* ************************************ */
    /* Displays the frequency */
    /* ************************************ */
    void display_frequency()
    {
    uint16_t f;
    lcd.setCursor(1, 0);
    f = (vfo ) / 1000000; //
    if (f < 100) {
    lcd.print(' ');
    }
    if (f < 10) {
    lcd.print(' ');
    }
    lcd.print(f);
    lcd.print('.');
    f = (vfo % 1000000) / 1000;
    if (f < 100) lcd.print('0');
    if (f < 10) lcd.print('0');
    lcd.print(f);
    lcd.print('.');
    f = vfo % 1000;
    if (f < 100) lcd.print('0');
    if (f < 10) lcd.print('0');
    lcd.print(f);
    lcd.print("Hz");
    }
    /* ***************************************** */
    /* change envenmult for each of the freq */
    /* bands bellow to keep things in quadrature */
    /* ***************************************** */
    void alterevenmult() // change envenmult for each of the freq bands bellow
    { // to keep things in quadrature
    if ( vfoMhz < 685) evenmult = 126;
    if ((vfoMhz >= 685) && (vfoMhz < 950)) evenmult = 88;
    if ((vfoMhz >= 950) && (vfoMhz < 1360)) evenmult = 64;
    if ((vfoMhz >= 1360) && (vfoMhz < 1750)) evenmult = 44;
    if ((vfoMhz >= 1750) && (vfoMhz < 2500)) evenmult = 34;
    if ((vfoMhz >= 2500) && (vfoMhz < 3600)) evenmult = 24;
    if ((vfoMhz >= 3600) && (vfoMhz < 4500)) evenmult = 18;
    if ((vfoMhz >= 4500) && (vfoMhz < 6000)) evenmult = 14;
    if ((vfoMhz >= 6000) && (vfoMhz < 8000)) evenmult = 10;
    if ((vfoMhz >= 8000) && (vfoMhz < 11000)) evenmult = 8;
    }
    void setup()
    {
    //Serial.begin(115200);
    Wire.begin(); // Initialize I2C
    lcd.begin(numCols, numRows); // Initialize LCD
    lcd.clear(); // clear the LCD
    lcd.display();
    lcd.setCursor(5, 0);
    lcd.print("IQ VFO");
    lcd.setCursor(0, 1);
    lcd.print("3.2MHz to 110MHz");
    delay(3000);
    lcd.clear();
    r.begin(false); // disables the Arduino's internal weak pull-ups for the given
    // pins and configures the rotary for use with external pull-ups
    //initialize the Si5351
    si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0); // 0 is the default crystal frequency of 25Mhz.
    si5351.set_correction(10390, SI5351_PLL_INPUT_XO); // correction from calibration sketch
    si5351.output_enable(SI5351_CLK0, 1); // 1 - Enable / 0 - Disable CLK
    si5351.output_enable(SI5351_CLK1, 1);
    si5351.output_enable(SI5351_CLK2, 0);
    //2 mA roughly corresponds to 3 dBm output, 8 mA is approximately 10 dBm output:
    si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA); // 2MA, 4MA, 6MA or 8MA
    si5351.drive_strength(SI5351_CLK1, SI5351_DRIVE_8MA);
    // SI5351_FREQ_MULT is 100, second argument is the desired PLL frequency,
    si5351.set_freq_manual(vfo * SI5351_FREQ_MULT, evenmult * vfo * SI5351_FREQ_MULT, SI5351_CLK0);
    si5351.set_freq_manual(vfo * SI5351_FREQ_MULT, evenmult * vfo * SI5351_FREQ_MULT, SI5351_CLK1);
    si5351.set_phase(SI5351_CLK0, 0); // In-phase signal at CLK0
    si5351.set_phase(SI5351_CLK1, evenmult); // Quadrature at CLK1
    si5351.pll_reset(SI5351_PLLA); // reset the Si5351 to enable changes
    pinMode(encSwPin, INPUT_PULLUP);
    PCICR |= (1