Skip to content

Latest commit

 

History

History

En

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 

VFO and BFO with Si5351A and Arduino project

Table of contents

  1. Introduction
  2. Arduino ATmega32U4
  3. BFO interface
    1. VFO and BFO and Dial information
  4. Band table for the VFO
  5. Schematic
  6. Components
  7. Arduino sketch
    1. BFO range
    2. Arduino pins and Encoder, Band, Step and Switch VFO/BFO
    3. Bands and frequency ranges
    4. External interrupts
    5. Changing the kind of display device
    6. SI5351 Calibration
  8. Photos
  9. Expetiments and Applications
    1. The BFO and VFO impplemented on Atmega328 (without use of external interruptions)
    2. IR Remote Control Implemetation for the VFO and BFO (Atmega328)
    3. VFO and BFO project with a inexpencive radio based on CD2003GP
  10. References
  11. Videos

Introduction

The Si5351 is an I2C configurable clock generator that is very appropriate for receivers and transceivers projects in amateur radio applications. It is also suited for replacing crystal oscillators. It has three outputs that you can get three distinct frequencies at the same time. A great feature of the Si5351A is the possibility of using it with a microcontroller or platform like Arduino, PIC family and others. See more on Silicon Labs documentation

This project is about a VFO and BFO that you can control two clock outputs of the Si5351A by using the Arduino Micro (Atmega32u4). The VFO (CLK0) can oscillate from 100KHz to 160MHz and the second (CLK1) can oscillate from 452KHz to 458KHz.

The Arduino program has being documented in English lenguage and you can get more details about the Si5351A controls by reading the si5351_vfobfo.ino file.

Arduino ATmega32U4

This project uses the Arduino ATmega32U4 (Known as Arduino Micro) with a built-in USB. The ATmega32U4 has 5 pins (0,1,2, 3 and 7) that allow the developer to use for handling external interrupts. This feature is very useful in this project. See more about Arduino Micro (ATmega32U4) here.

VFO and BFO interface

The user can control the VFO and BFO by using three buttons and an encoder. The Dial was implemented with the OLED Display 128 x 64 Pixels White 0.96 Inch I2C.

  • The button Band changes the range of frequency. This project divides the VFO range (from 100KHz to 160MHz) in 27 bands. See band table below;
  • The button Step changes the increment and decrement step. It can be 10Hz, 50Hz, 100Hz, 500Hz, 1KHz, 2.5KHz, 5KHz, 10KHz, 100KHz and 500KHz;
  • The button VFO/BFO switches from VFO to BFO and vice-versa. It allows the user to control the VFO or BFO by using the same Encoder;
  • The Encoder is used to increment or decrement the current frequency by using the step value as a reference.

VFO and BFO and Dial information

The Dial shows the current VFO and BFO frequencies, the step and who the encoder is controlling (VFO or BFO). When the user switch from VFO to BFO or vice-versa, the frequency information of the VFO or BFO is highlighted on display. If you want to use another display, the item Arduino sketch (Changing the kind of display device) shows how you can do that.

Dial Information Dial Information
Photo dial 01 Photo dial 02
VFO is 100 KHz, BFO is 455 KHz, Band LW/MW, 10Hz setp and the encoder is controlling the VFO) The encoder is controlling the BFO; the BFO now is the first information (highlighted)
Photo dial 03 Photo dial 04
VFO is 1700.08 KHz, BFO is 455 KHz, Band SW1, 10Hz setp and the encoder is controllins the VFO) VFO is 108MHz, BFO is 455 KHz, Band VFH4, 2.5KHz setp and the encoder is controllins the VFO)

Band table for the VFO

The VFO is separated into 27 bands. It first band oscilates from 100KHz to 1700 KHz and the last band oscilates from 135MHz to 160MHz. You might need to change this configuration. The Arduino sketch (Bands and frequency ranges) shows how you can change the band configuration.

N# Band From to
1 LW/MW 100 KHz 1.7 MHz
2 SW1 1.7 MHz 3.5 MHz
3 SW2 3.5 MHz 4.0 MHz
4 SW3 4.0 MHz 7.0 MHz
5 SW4 7.0 MHz 7.3 MHz
6 SW5 7.3 MHz 9.0 MHz
7 SW6 9.0 MHz 10.0 MHz
8 SW7 10.0 MHz 11.0 MHz
9 SW8 11.0 MHz 14.0 MHz
10 SW9 14.0 Mhz 15.0 MHz
11 SW10 15.0 MHz 17.0 MHz
12 SW11 17.0 MHz 18.0 MHz
13 SW12 18.0 MHz 20.0 MHz
14 SW13 20.0 MHz 21.35 MHz
15 SW14 21.35 MHz 22.00 MHz
16 SW15 22.0 MHz 24.88 MHz
17 SW16 24.88 MHz 24.99 MHz
18 SW17 24.99 MHz 26.0 MHz
19 SW18 26.0 MHz 28.0 MHz
20 SW19 28.0 MHz 30.0 MHz
21 VHF1 30.0 MHz 50.0 MHz
22 VHF2 50.0 MHz 54.0 MHz
23 VHF3 54.0 MHz 86.0 MHz
24 FM 86.0 MHz 108.0 MHz
25 VHF4 108.0 MHz 120.0 MHz
26 VHF5 120.0 MHz 135.0 MHz
27 VHF6 135.0 MHz 160.0 MHz

Schematic

The schematic was built using the Fritzing application, an open-source software tools to design circuits. The schematic below shows the Arduino and components wire up.

Esquema do Projeto VFO e BFO com Arduino

Components

  • AZDelivery 1 x OLED Display Arduino 128 x 64 Pixels White 0.96 Inch I2C IIC Module for Arduino. This project uses the Text only Arduino Library for SSD1306 OLED displays Arduino library.
  • Adafruit Si5351A Clock Generator Breakout Board - 8KHz to 160MHz. This project uses the Si5351 Library for Arduino.
  • One regular encoder.
  • Three regular Push Button.
  • Two 10nF ceramic capacitor
  • Six 10K resistor
  • One 1K resistor
  • One Red LED
  • Arduino Micro (Atmega32u4)

About schematic and connections

This project uses the Arduino Atmega32u4 compatible (Micro). With this kind of Arduino, we can use up to 5 external interrupts (pins 0,1,2,3 and 7). Then pins 0,1 and 7 were used to implement the buttons command (Step, Band and switch VFO/BFO).

Si5351A

The device Si5351A is connected to pins D2 (SDA) and D3 (SCL).

OLED Display SSD1306 - 128 x 64/0.96

The OLED Display is also connected to D2(SDA) and D3 (SCL).

Encoder

The Encoder is connected to Arduino on pins D8 and D9. Connect the lead A to pin D8 and lead B to pin D9.

Band push button

The Band push button is connected to pin D0/Rx (see schematic). Use this button to select one of 27 bands available.

Step push button

The Step push button is connected to the pin D1/Tx. Use this button to select the increment and decrement frequency steps.

VFO/BFO switch

The VFO/BFO switch push button is connected to the pin D7. This button changes the Encoder control from VFO to BFO and vice versa.

Arduino sketch

The main Arduino library used on this project to control the Si5351A was developed by NTS7. You can see more about this Library here.

BFO range

If you want to modify the frequency of BFO to 10MHz (for example), just change the lines below.

#define MAX_BFO     1100000000LU   // BFO max. frequency 
#define CENTER_BFO  1000000000LU   // BFO center frequency
#define MIN_BFO      990000000LU   // BFO min. frequency 

Bands and frequency ranges

The bands and ranges can be changed here. You can remove band and change frequencies modifying the array band[]. See code below.

// Band database. You can change the band ranges if you need.
Band band[] = {
    {"LW/MW ", 10000000LLU, 170000000LLU},     // 100KHz to 1700KHz
    {"SW1  ", 170000000LLU, 350000000LLU},
    {"SW2  ", 350000000LLU, 400000001LLU},
    {"SW3  ", 400000000LLU, 700000000LLU},
    {"SW4  ", 700000000LLU, 730000000LLU},    // 7MHz to 7.3 MHz  (Amateur 40m)
    {"SW5  ", 730000000LLU, 900000000LLU},    // 41m
    {"SW6  ", 900000000LLU, 1000000000LLU},   // 31m
    {"SW7  ", 1000000000LLU, 1100000000LLU},  // 10 MHz to 11 MHz (Amateur 30m)
    {"SW8  ", 1100000000LLU, 1400000000LLU},  // 25 and 22 meters
    {"SW9  ", 1400000000LLU, 1500000000LLU},  // 14MHz to 15Mhz (Amateur 20m)
    {"SW10 ", 1500000000LLU, 1700000000LLU},  // 19m
    {"SW11 ", 1700000000LLU, 1800000000LLU},  // 16m
    {"SW12 ", 1800000000LLU, 2000000000LLU}, // 18MHz to 20Mhz (Amateur and comercial 15m)
    {"SW13 ", 2000000000LLU, 2135000000LLU}, // 20MHz to 22Mhz (Amateur and comercial 15m/13m)
    {"SW14 ", 2135000000LLU, 2200000000LLU},
    {"SW15 ", 2235000000LLU, 2498000000LLU},
    {"SW16 ", 2488000000LLU, 2499000000LLU}, // 24.88MHz to 24.99MHz (Amateur 12m)
    {"SW17 ", 2499000000LLU, 2600000000LLU},
    {"SW18 ", 2600000000LLU, 2800000000LLU},
    {"SW19 ", 2800000000LLU, 3000000000LLU}, // 28MHz to 30MHz (Amateur 10M)
    {"VHF1 ", 3000000000LLU, 5000000000LLU},
    {"VHF2 ", 5000000000LLU, 5400000000LLU},
    {"VHF3 ", 5400000000LLU, 8600000000LLU},
    {"FM   ", 8600000000LLU, 10800000000LLU},  // Comercial FM
    {"VHF4 ", 10800000000LLU, 12000000000LLU}, // 108MHz to 160MHz
    {"VHF5 ", 12000000000LLU, 13500000000LLU},
    {"VHF6 ", 13500000000LLU, 16000000000LLU}}; 
// Calculate the last element position (index) of the array band 
const int lastBand = (sizeof band / sizeof(Band)) - 1; // For this case will be 26.
volatile int currentBand = 0; // First band. For this case, AM is the current band.	

The increment and decrement steps can be changed here.

The steps of BFO can be changed here.

// Steps database. You can change the Steps and numbers of steps here if you need.
Step step[] = {
    {"10Hz  ", 1000}, // VFO and BFO min. increment / decrement
    {"50Hz  ", 5000},
    {"100Hz ", 10000},
    {"500Hz ", 50000},
    {"1KHz  ", 100000}, // BFO max. increment / decrement
    {"2.5KHz", 250000},
    {"5KHz  ", 500000},
    {"10KHz ", 1000000},
    {"100KHz", 10000000},
    {"500KHz", 50000000}}; // VFO max. increment / decrement
// Calculate the index of last position of step[] array (in this case will be 8)
const int lastStepVFO = (sizeof step / sizeof(Step)) - 1; // index for max increment / decrement for VFO
volatile int lastStepBFO = 3;   // index for max. increment / decrement for BFO. In this case will be is 1KHz
volatile long currentStep = 0;  // it stores the current step index (50Hz in this case)

Arduino pins and Encoder, Band, Step and Switch VFO/BFO

The pins for encoder and push buttons are defined below. If you need to change some push button pin, you should be aware that the push buttons are connected to pins with support to external interrupts. On Atmega32u4 you can use the pins 0,1,2,3 and 7.

#define ENCODER_PIN_A 8 // Arduino  D8
#define ENCODER_PIN_B 9 // Arduino  D9

#define BUTTON_STEP 0    // Control the frequency increment and decrement
#define BUTTON_BAND 1    // Controls the band
#define BUTTON_VFO_BFO 7 // Switch VFO to BFO

External interrupts

The Band, Step and Switch VFO/BFO buttons are implemented by using external interrupt resource. See more about Using Interrupts on Arduino.

  // Will stop what Arduino is doing and call changeStep(), changeBand() or switchVFOBFO 
  attachInterrupt(digitalPinToInterrupt(BUTTON_STEP), changeStep, RISING);      // whenever the BUTTON_STEP goes from LOW to HIGH
  attachInterrupt(digitalPinToInterrupt(BUTTON_BAND), changeBand, RISING);      // whenever the BUTTON_BAND goes from LOW to HIGH
  attachInterrupt(digitalPinToInterrupt(BUTTON_VFO_BFO), switchVFOBFO, RISING); // whenever the BUTTON_VFO_BFO goes from LOW to HIGH
  // wait for 1/2 second and the system will be ready.

The changeStep(), changeBand() and switchVFOBFO() functions implementation are shown below.

// Change frequency increment rate
void changeStep()
{
  if ((millis() - elapsedTimeInterrupt) < MIN_ELAPSED_TIME)
    return;                                                            // nothing to do if the time less than MIN_ELAPSED_TIME milisecounds
  noInterrupts();                                                      //// disable global interrupts:
  if (currentClock == 0)                                               // Is VFO
    currentStep = (currentStep < lastStepVFO) ? (currentStep + 1) : 0; // Increment the step or go back to the first
  else                                                                 // Is BFO
    currentStep = (currentStep < lastStepBFO) ? (currentStep + 1) : 0;
  isFreqChanged = true;
  clearDisplay = true;
  elapsedTimeInterrupt = millis();
  interrupts(); // enable interrupts
}

// Change band
void changeBand()
{
  if ((millis() - elapsedTimeInterrupt) < MIN_ELAPSED_TIME)
    return;                                                       // nothing to do if the time less than 11 milisecounds
  noInterrupts();                                                 //  disable global interrupts:
  currentBand = (currentBand < lastBand) ? (currentBand + 1) : 0; // Is the last band? If so, go to the first band (AM). Else. Else, next band.
  vfoFreq = band[currentBand].minFreq;
  isFreqChanged = true;
  elapsedTimeInterrupt = millis();
  interrupts(); // enable interrupts
}

// Switch the Encoder control from VFO to BFO and virse versa.
void switchVFOBFO()
{
  if ((millis() - elapsedTimeInterrupt) < MIN_ELAPSED_TIME)
    return;       // nothing to do if the time less than 11 milisecounds
  noInterrupts(); //  disable global interrupts:
  currentClock = !currentClock;
  currentStep = 0; // go back to first Step (100Hz)
  clearDisplay = true;
  elapsedTimeInterrupt = millis();
  interrupts(); // enable interrupts
}

Changing the kind of display device

This project uses the "OLED Display Arduino 128 x 64 Pixels White 0.96 Inch I2C". It is very small and does not provide a good look. If you need to change to a larger display device, you have to know how to connect your display device on your Arduino. Also you have to know how to program your display device for Arduino. If you are able to do that, you can follow the steps bellow.

1) Replacing the libraries declaration

Replace the OLED library include directives below by the library include directive of your device display.

#include "SSD1306Ascii.h"
#include "SSD1306AsciiAvrI2c.h"

2) Replacing the display device class declaration

Replace the OLED display declaration by your display device declaration

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

3) Replacing the OLED display device initialization code

Replace the OLED display device initialization code on setup() function by the your display device initialization code.

 // Initiating the OLED Display
  display.begin(&Adafruit128x64, I2C_ADDRESS);
  display.setFont(Adafruit5x7);
  display.set2X();
  display.clear();
  display.print("\n PU2CLR");
  delay(3000);
  display.clear();
  displayDial();

4) Replacing the OLED displayDial() function

Replace the displayDial function code by the your display device code. Keep the name of the function (diaplayDial()).

// Show Signal Generator Information
// Verificar setCursor() em https://github.com/greiman/SSD1306Ascii/issues/53
void displayDial()
{
  double vfo = vfoFreq / 100000.0;
  double bfo = bfoFreq / 100000.0;
  String mainFreq;
  String secoundFreq;
  String staticFreq;
  String dinamicFreq;

  // Change the display behaviour depending on who is controlled, BFO or BFO.
  if (currentClock == 0)
  { // If the encoder is controlling the VFO
    mainFreq = String(vfo,3);
    secoundFreq = String(bfo,3);
    staticFreq = "BFO";
    dinamicFreq = "VFO";
  }
  else // encoder is controlling the VFO
  {
    mainFreq = String(bfo,3);
    secoundFreq = String(vfo,3);
    staticFreq = "VFO";
    dinamicFreq = "BFO";
  }

  // display.setCursor(0,0)
  // display.clear();

  display.set2X();
  display.setCursor(0, 0);
  display.print(mainFreq);
  display.print("     ");

  display.set1X();
  display.print("\n\n\n");
  display.print(staticFreq);
  display.print(".: ");
  display.print(secoundFreq);

  display.print("\nBand: ");
  display.print(band[currentBand].name);

  display.print("\nStep: ");
  display.print(step[currentStep].name);

  display.print("\n\nCtrl: ");
  display.print(dinamicFreq);

}

SI5351 Calibration

You might need calibrate your si5351 to get more precision during signal generation. To do that you can use the si5351_calibration.ino sketch that comes with the Si5351 library for Arduino. Click here to see more about si5351 calibration. You can also try watch this Portuguese video that show how to calibrate the si5351. Another video about si5351 calibration can be watch here (Homebrew 80/40m SSB/CW Rig - #7a Si5351 Calibration).

The line code below shows the correction factor found during this project.

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

if you do not want calibrate, set CORRECTION_FACTOR to 0 as shown below.

#define CORRECTION_FACTOR 0 

Photos

Photo 01

Prototype01

Photo 02

Prototype02

Photo 03

Prototype03

References

Videos