/*************************************************************************************************************************************************************************
Nitin William - VU3GAO September/2021 - Version 1.5
Easy Bitx HF Mono Band Rigs(80m, 40m, 20m) VFO / BFO sketch with Si5351 and Arduino Nano with 10Mhz IF.
Hardware VU3SUA VFO/BFO Board with 16X2 LCD
Code derived from J. CesarSound - ver 1.0 - Dec/2020.
https://groups.io/g/BITX20/topic/bitx_40_dds_vfo/4104090?p=
https://blog.wokwi.com/5-ways-to-blink-an-led-with-arduino/
https://maxpromer.github.io/LCD-Character-Creator/
https://www.riyas.org/2016/12/a-simple-si5351-vfo-and-bfo-with-s-meter.html
*************************************************************************************************************************************************************************/
//Libraries
#include <Wire.h> //Arduino library
#include <EEPROMex.h> //https://thijs.elenbaas.net/2012/07/extended-eeprom-library-for-arduino
#include <Rotary.h> //Ben Buxton https://github.com/brianlow/Rotary
#include <si5351.h> //Etherkit https://github.com/etherkit/Si5351Arduino
#include <EasyButton.h> //https://easybtn.earias.me/docs/introduction
#include <LiquidCrystal.h> //Arduino library
#include <elapsedMillis.h> //https://github.com/pfeerick/elapsedMillis/wiki
#define fMax 7200000UL //7200000UL / 14500000UL
#define fMin 7000000UL //7000000UL / 14000000UL
#define ddsCal 359000
#define LCD_RS 5
#define LCD_E 6
#define LCD_D4 7
#define LCD_D5 8
#define LCD_D6 9
#define LCD_D7 10
#define encoderButtonPin 11
#define bfoAddress 8 //EEPROM location for BFO value
#define vfoAddress 12 //EEPROM for saving vfo
unsigned long vfo = 7050000UL; //Enter your initial frequency at startup, ex: 7000000 = 7MHz, 10000000 = 10MHz
unsigned long bfo = 9996850UL; //BFO frequency for 10Mhz filter Bw = 3.2Khz
unsigned long opsfreq = 0UL; //Operating freq to be generated by Si5351
unsigned long offset = 3200UL; //IF Offset
unsigned long freqold, fstep;
unsigned long bfoold;
int stp; //Frequency step counter
byte tower[8] =
{
B11111,
B10101,
B01110,
B00100,
B00100,
B00100,
B00100,
B00000
};
byte blank[8] =
{
B00000,
B00000,
B00000,
B00000,
B00000,
B00000,
B00000,
B00000
};
bool setupmode = false;
bool memTag = false;
Rotary r = Rotary(3, 2); // Encoder defined for Interrupt Pin 2,3
LiquidCrystal lcd(LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7);
Si5351 si5351(0x60); //Si5351 I2C Address 0x60
EasyButton encoderButton (encoderButtonPin, true); //Create encoderButton Object as input with internal pullup resistors
elapsedMillis memTimer;
ISR(PCINT2_vect)
{
char result = r.process();
if (result == DIR_CW)
{
if (! setupmode )
set_frequency(1);
else
set_bfofrequency(1);
}
else if (result == DIR_CCW)
{
if (! setupmode )
set_frequency(-1);
else
set_bfofrequency(-1);
}
}
void set_frequency(short dir)
{
if (dir == 1) vfo = vfo + fstep;
if (vfo >= fMax) vfo = fMax; //Upper tuning limit
if (dir == -1) vfo = vfo - fstep;
if (vfo < fMin) vfo = fMin; //lower tuning limit
}
void set_bfofrequency(short dir)
{
if (dir == 1) bfo = bfo + 50;
if (bfo >= 10003500) bfo = 10003500;
if (dir == -1) bfo = bfo - 50;
if (bfo < 9996500) bfo = 9996500;
}
void setup()
{
Serial.begin(57600);
encoderButton.begin();
Wire.begin();
r.begin(true); //Enable the Arduino's internal weak pull-ups for the rotary's pins external pullup
lcd.begin(16, 2);
delay (20);
lcd.createChar(0, tower);
lcd.createChar(1, blank);
lcd.clear();
si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, ddsCal); //Initialize Si5351, with 25Mhz Xtal
si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA); //Output current 2MA, 4MA, 6MA or 8MA
si5351.drive_strength(SI5351_CLK2, SI5351_DRIVE_8MA); //Output current 2MA, 4MA, 6MA or 8MA
si5351.output_enable(SI5351_CLK0, 1); //1 - Enable / 0 - Disable CLK
si5351.output_enable(SI5351_CLK1, 0);
si5351.output_enable(SI5351_CLK2, 1);
si5351.update_status();
cli();
PCICR |= (1 << PCIE2);
PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
sei(); // Enable all interrupts
display_banner();
bfoold = EEPROM.readLong(bfoAddress);
if (bfoold < 9996500 || bfoold > 10003500 )
{
EEPROM.updateLong (bfoAddress,bfo);
}
else
{
bfo = bfoold;
}
freqold = EEPROM.readLong(vfoAddress);
if (freqold < fMin || freqold > fMax )
{
EEPROM.updateLong (vfoAddress,vfo);
}
else
{
vfo = freqold;
}
si5351.set_freq(bfo * 100, SI5351_CLK2); //Set bfo
stp = 4;
setsteps();
encoderButton.onPressed(setsteps);
encoderButton.onPressedFor(2000, set_bfo);
tunegen();
display_freq();
display_radix();
}
void loop()
{
encoderButton.read();
if (freqold != vfo)
{
memTimer = 0;
memTag = true;
tunegen();
freqold = vfo;
}
if (memTimer > 60000) save_vfo();
display_freq();
display_radix();
flash_heart();
}
void setsteps()
{
switch (stp)
{
case 1: stp = 2; fstep = 1; break;
case 2: stp = 3; fstep = 10; break;
case 3: stp = 4; fstep = 100; break;
case 4: stp = 5; fstep = 1000; break;
case 5: stp = 1; fstep = 10000; break;
}
}
void tunegen()
{
if (vfo >= 14000000 )
{
lcd.setCursor(2, 1);
lcd.print("USB");
opsfreq = vfo - bfo; //Low side LO injection, no side band inversion
si5351.set_freq(opsfreq * 100, SI5351_CLK0); //Update operating frequency
}
else
{
lcd.setCursor(2, 1);
lcd.print("LSB");
opsfreq = vfo + bfo; //High side LO injection, Side band inversion
si5351.set_freq(opsfreq * 100, SI5351_CLK0); //Update operating frequency
}
}
void display_freq()
{
uint16_t f;
lcd.setCursor(2, 0);
f = vfo / 1000000;
if (f < 10)
lcd.print('0');
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");
}
void display_radix()
{
lcd.setCursor(9, 1);
if (stp == 2) lcd.print("001Hz"); if (stp == 3) lcd.print("010Hz"); if (stp == 4) lcd.print("100Hz");
if (stp == 5) lcd.print("01KHz"); if (stp == 1) lcd.print("10KHz");
lcd.setCursor(2, 1);
}
void set_bfo(short dir)
{
lcd.clear();
lcd.setCursor(3, 0);
lcd.print("BFO SETUP");
delay (1000);
do
{
setupmode = true;
encoderButton.read();
lcd.setCursor(4, 1);
if (bfo < 10000000 )lcd.print("0");
lcd.print(bfo);
si5351.set_freq(bfo * 100, SI5351_CLK2);
} while (!encoderButton.isPressed());
si5351.set_freq(bfo * 100, SI5351_CLK2);
lcd.setCursor(3, 0);
lcd.print("Saving BFO");
EEPROM.updateLong (bfoAddress,bfo);
delay (2000);
lcd.clear();
if ( stp == 1 ) stp = 6;
stp = stp - 1;
setupmode = false;
}
void display_banner()
{
lcd.setCursor (3, 0);
lcd.print("Easy Bitx");
lcd.setCursor (3, 1);
if (fMin >= 3000000 && fMin <= 4000000)
lcd.print("80m Band");
if (fMin >= 7000000 && fMin <= 7500000)
lcd.print("40m Band");
if (fMin >= 14000000 && fMin <= 14500000)
lcd.print("20m Band");
delay (3000);
lcd.clear();
}
void flash_heart()
{
lcd.setCursor (0, 0);
lcd.write (byte((millis() / 1000) % 2 )); //Logic give 0 or 1 as paramtere to function byte()
}
void save_vfo()
{
memTimer = 0;
if (memTag)
{
memTag = false;
EEPROM.writeLong (vfoAddress,vfo);
}
}