/* This Arduino sketch is for generating the VFO and BFO from Si5351 chip.
Both are tunable and so provide a tool needed by all homebrewers for rigs.
Thanks to AK2B, SK9NJE, NT7S , VU2SWX and many others who have shared their valuable
knowledge for the development of HAM radio. This program is largely based on their efforts and in
their spirit it is made available to every Ham for use in their lovely homebrewed rigs - VU2SPF/ SP Bhatnagar, QTH Bhavnagar-India.
More information and pictures on vu2spf.blogspot.in */
/* the BFO can be tuned by pressing the encoder button for longer than 1 sec. Step size for change of freq is set by
momentary press of the encoder button */
//-----------------------------
#include <Rotary.h>
#include <si5351.h>
#include <Wire.h>
//#include <LiquidCrystal.h>
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>
#define F_MIN 100000000UL // Lower frequency limit
#define F_MAX 5000000000UL
#define ENCODER_A 3 // Encoder pin A
#define ENCODER_B 2 // Encoder pin B
#define ENCODER_BTN 4 // 11
//#define LCD_RS 5
//#define LCD_E 6
//#define LCD_D4 7
//#define LCD_D5 8
//#define LCD_D6 9
//#define LCD_D7 10
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // Set the LCD I2C address
//LiquidCrystal_I2C lcd(0x27, 16, 2);
//LiquidCrystal lcd(LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7); // LCD - pin assignement in
Si5351 si5351;
Rotary r = Rotary(ENCODER_A, ENCODER_B);
//9996000UL;
// MODIFIED FOR 10MHZ LADDER FILTER
volatile uint32_t LSB = 10001500UL; // FOR BEL FIL 9001500UL; // BFO values tune once and change these for your rig
volatile uint32_t USB = 9998500UL; // FOR BEL FIL 8998500UL;
volatile uint32_t bfo = 9998500UL; //FOR BEL FIL 8998500UL;
volatile uint32_t Vfo2 = 14250000UL;
//These USB/LSB frequencies are added to or subtracted from the vfo frequency in the "Loop()"
//In this example my start frequency will be 7.050MHz Minus 9.994Mhz BFO on clk0, suits my BITX-multibander for 40m
volatile uint32_t vfo = 14200800UL ; //start freq - change to suit 7050000UL ;
volatile uint32_t opfreq=00LL; // Operating freq to be generated by Si5351 feeding to VFO input
volatile uint32_t radix = 1000; //starting step size - change to suit
boolean changed_f = 0;
String tbfo = "";
int_fast32_t dig0;
String freq,freq_bfo, dig1; // string to hold the frequency
byte ones,tens,hundreds,thousands,tenthousands,hundredthousands,millions; //Placeholders for vfo
byte one,ten,hundred,thousand,tenthousand,hundredthousand,million; //Placeholders for BFO
volatile uint32_t newfreq=00LL;
boolean setbfo = false; // long press of switch to tune bfo
int enc_cnt = 0; //encoder count
//------------------------------- Set Optional Features here --------------------------------------
//Remove comment (//) from the option you want to use. Pick only one
#define IF_Offset //Output is the display plus or minus the bfo frequency
//#define Direct_conversion //What you see on display is what you get
//#define FreqX4 //output is four times the display frequency
//--------------------------------------------------------------------------------------------------
/**************************************/
/* Interrupt service routine for */
/* encoder frequency change */
/**************************************/
ISR(PCINT2_vect) {
unsigned char result = r.process();
if (result == DIR_CW)
enc_cnt = +1;
else if (result == DIR_CCW)
enc_cnt = -1;
if (setbfo)
set_bfo_freq();
else
set_frequency();
//enc_cnt=0;
}
/**************************************/
/* Change the frequencies */
/**************************************/
void set_frequency()
{
vfo += enc_cnt * radix;
enc_cnt =0;
changed_f = 1;
}
void set_bfo_freq()
{
bfo += enc_cnt * radix;
enc_cnt = 0;
changed_f = 1;
}
/**************************************/
/* Read the button with debouncing */
/**************************************/
boolean get_button()
{
if (!digitalRead(ENCODER_BTN))
{
delay(20);
if (!digitalRead(ENCODER_BTN))
{
long strttime=millis();
while (!digitalRead(ENCODER_BTN));
if((millis() - strttime) > 1000) // check if it was a long press
{
setbfo = !setbfo; // flip-flop between set and not set bfo on long press
Serial.println(bfo);
changed_f = 1;
storeMEM_BFO();
return 0;
}
else
return 1;
}
}
return 0;
}
/**************************************/
/* Displays the frequency */
/**************************************/
void display_frequency()
{
uint16_t f, g;
lcd.setCursor(3, 0);
f = vfo / 1000000; //variable is now vfo instead of 'frequency'
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");
// if bfo needs tuning only then display it on second line
if(setbfo)
{
lcd.setCursor(0,1);
// lcd.print("B:");
f = bfo / 1000000; //variable is now bfo instead of 'frequency'
if (f < 10)
lcd.print(' ');
lcd.print(f);
lcd.print('.');
f = (bfo % 1000000) / 1000;
if (f < 100)
lcd.print('0');
if (f < 10)
lcd.print('0');
lcd.print(f);
lcd.print('.');
f = bfo % 1000;
if (f < 100)
lcd.print('0');
if (f < 10)
lcd.print('0');
lcd.print(f);
}
else
{
lcd.setCursor(0, 1);
lcd.print(tbfo);
lcd.print(" ");
//lcd.print(" VU3JOJ ");
lcd.setCursor(0, 3);
lcd.print(" ");
}
}
/**************************************/
/* Displays the frequency change step */
/**************************************/
void display_radix()
{
lcd.setCursor(12, 1);
switch (radix)
{
case 1:
lcd.print(" 1");
break;
case 10:
lcd.print(" 10");
break;
case 100:
lcd.print(" 100");
break;
case 1000:
lcd.print(" 1k");
break;
case 10000:
lcd.print(" 10k");
break;
case 100000:
//lcd.setCursor(10, 1);
lcd.print("100k");
break;
case 1000000:
// lcd.setCursor(9, 1);
lcd.print(" 1M"); //1MHz increments needed when bands need to be changed
break;
}
//lcd.print("Hz"); // no space on second line so skip
}
void setup()
{
Serial.begin(9600);
lcd.begin(20, 4); // Initialize and clear the LCD
lcd.clear();
Wire.begin();
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//eeprom blank check
dig1= String(EEPROM.read(0))+String(EEPROM.read(1))+String(EEPROM.read(2))+String(EEPROM.read(3)); //check if eeprom 4 location are empty
dig0 = dig1.toInt(); //convert
if ((dig0==255255255255)|| ( dig0==0 )) // if eeprom is empty ie in first time or new one's, wirte defualt value of vfo=14.200
{
Serial.println("Eeprom is blank ");
vfo = 14200000UL;
}
else
{
Serial.println("Eeprom is not blank ");
// read eeprom and check
freq = String(EEPROM.read(0))+String(EEPROM.read(1))+String(EEPROM.read(2))+String(EEPROM.read(3))+String(EEPROM.read(4))+String(EEPROM.read(5))+String(EEPROM.read(6));
//convert string to int
vfo = freq.toInt();
Serial.println("VFO = ");
Serial.println(vfo);
}
//Serial.println("vfo ");
// set_frequency();
// display_frequency();
pinMode(13, OUTPUT);
///////////////////////////////////BFO/////////////////////////
freq_bfo = String(EEPROM.read(10))+String(EEPROM.read(11))+String(EEPROM.read(12))+String(EEPROM.read(13))+String(EEPROM.read(14))+String(EEPROM.read(15))+String(EEPROM.read(16));
//convert string to int
bfo = freq_bfo.toInt();
Serial.println("BFO = ");
Serial.println(bfo);
setbfo = true;
display_frequency();
delay(2000); //shows bfo value at start
setbfo=false;
lcd.clear();
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
si5351.set_correction(243000); //**mine. There is a calibration sketch in File/Examples/si5351Arduino-Jason
//where you can determine the correction by using the serial monitor.
//initialize the Si5351
//si5351.init(SI5351_CRYSTAL_LOAD_8PF, 27000000); //If you're using a 27Mhz crystal, put in 27000000 instead of 0
si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0); //If you're using a 27Mhz crystal, put in 27000000 instead of 0
// 0 is the default crystal frequency of 25Mhz.
si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
// Set CLK0 to output the starting "vfo" frequency as set above by vfo = ?
// First VFO generation
#ifdef IF_Offset
if (vfo > 10000000UL)
{
opfreq=vfo - bfo; // USB
tbfo = "USB";
}
else
{
opfreq= bfo - vfo ; // LSB
si5351.set_freq(opfreq*100, 0, SI5351_CLK0); // SI5351_PLL_FIXED did not give any output so 0
volatile uint32_t vfoT = opfreq;
tbfo = "LSB";
// Now produce BFO
// Use CLK2 output to generate bfo frequency
si5351.set_freq( bfo*100, 0, SI5351_CLK2); // spb
// if needed use one of these
// si5351.drive_strength(SI5351_CLK0,SI5351_DRIVE_2MA); // vfo you can set this to 2MA, 4MA, 6MA or 8MA
// si5351.drive_strength(SI5351_CLK1,SI5351_DRIVE_2MA); //be careful though - measure into 50ohms
// si5351.drive_strength(SI5351_CLK2,SI5351_DRIVE_2MA); // bfo
}
set_frequency(); //set vfo
#endif
// Options for making a sig gen
#ifdef Direct_conversion
si5351.set_freq((vfo * SI5351_FREQ_MULT), SI5351_PLL_FIXED, SI5351_CLK0);
#endif
#ifdef FreqX4
si5351.set_freq((vfo * SI5351_FREQ_MULT) * 4, SI5351_PLL_FIXED, SI5351_CLK0);
#endif
pinMode(ENCODER_BTN, INPUT_PULLUP);
// In Mega PCINT 18/19 are A10, A11
PCMSK2 |= (1 << PCINT18)| (1 << PCINT19); // Change interrupts 18 and 19 correspond to A10,A11 on Mega
// on ATMega386 these are D2 and D3
PCICR |= (1 << PCIE2); // Enable pin change interrupt for the encoder
sei();
display_frequency(); // Update the display
display_radix();
}
void loop()
{
//lock vfo and bfo range from 1Mhz to 30Mhz, feature added for vu3joj
if (vfo > 30000000UL) vfo= 1000000UL ;
if (vfo < 1000000UL) vfo= 30000000UL ;
if (bfo > 30000000UL) bfo= 1000000UL ;
if (bfo < 1000000UL) bfo= 30000000UL ;
// Update the display if the frequency has been changed
if (changed_f)
{
display_frequency();
newfreq=vfo;
storeMEM_VFO();
#ifdef IF_Offset
if (vfo > 10000000UL)
opfreq= vfo - bfo; // USB
else
opfreq= bfo - vfo; // LSB
// VFO
si5351.set_freq(opfreq*100, 0, SI5351_CLK0); // SI5351_PLL_FIXED was not OK on 7MHz
// BFO
si5351.set_freq( bfo*100 , 0, SI5351_CLK2); // spb
//OR you can also add the bfo to suit your needs
if (vfo >= 10000000UL & tbfo != "USB") // when the freq is tuned BFO changes to USB above 10MHz
{
bfo = USB;
tbfo = "USB";
si5351.set_freq( bfo , 0, SI5351_CLK2);
}
else if (vfo < 10000000UL & tbfo != "LSB") // and to LSB below 10MHz ( Digital modes need USB, perhaps!)
{
bfo = LSB;
tbfo = "LSB";
si5351.set_freq( bfo , 0, SI5351_CLK2);
}
#endif
// Sig gen features
#ifdef Direct_conversion
si5351.set_freq((vfo * SI5351_FREQ_MULT), SI5351_PLL_FIXED, SI5351_CLK0);
tbfo = "";
#endif
opfreq= opfreq *(-1ULL);
#ifdef FreqX4
si5351.set_freq((vfo * SI5351_FREQ_MULT) * 4, SI5351_PLL_FIXED, SI5351_CLK0);
tbfo = "";
#endif
changed_f = 0;
}
// Button press changes the frequency change step for 1 Hz steps
if (get_button())
{
switch (radix)
{
case 1:
radix = 10;
break;
case 10:
radix = 100;
break;
case 100:
radix = 1000;
break;
case 1000:
radix = 10000;
break;
case 10000:
radix = 100000;
break;
case 100000:
radix = 1000000;
break;
case 1000000:
radix = 1;
break;
}
display_radix();
}
}
void storeMEM_VFO(){
digitalWrite(13, HIGH); // Let us know memory has been written
millions = int(vfo/1000000);
hundredthousands = ((vfo/100000)%10);
tenthousands = ((vfo/10000)%10);
thousands = ((vfo/1000)%10);
hundreds = ((vfo/100)%10);
tens = ((vfo/10)%10);
ones = ((vfo/1)%10);
//Write each frequency section to a EPROM slot. Yes, it's cheating but it works!
EEPROM.write(0,millions);
EEPROM.write(1,hundredthousands);
EEPROM.write(2,tenthousands);
EEPROM.write(3,thousands);
EEPROM.write(4,hundreds);
EEPROM.write(5,tens);
EEPROM.write(6,ones);
digitalWrite(13, LOW); // Let program know memory has been written
}
void storeMEM_BFO(){
digitalWrite(13, HIGH); // Let us know memory has been written
million = int(bfo/1000000);
hundredthousand = ((bfo/100000)%10);
tenthousand = ((bfo/10000)%10);
thousand = ((bfo/1000)%10);
hundred = ((bfo/100)%10);
ten = ((bfo/10)%10);
one = ((bfo/1)%10);
//Write each frequency section to a EPROM slot. Yes, it's cheating but it works!
EEPROM.write(10,million);
EEPROM.write(11,hundredthousand);
EEPROM.write(12,tenthousand);
EEPROM.write(13,thousand);
EEPROM.write(14,hundred);
EEPROM.write(15,ten);
EEPROM.write(16,one);
digitalWrite(13, LOW); // Let program know memory has been written
}