#include <Wire.h>
#include <SPI.h>
#include <LiquidCrystal_I2C.h>
#include <si5351.h>
#define SSB 0
#define CW 1
#define BAND80 0
#define BAND40 1
#define VFOA 0
#define VFOB 1
#define TX 0
#define RX 1
#define ON 0
#define OFF 1
const long band80mStart = 3500000; // start of 80m
const long band80mEnd = 3900000; // end of 80m
const long band40mStart = 7000000; // start of 40m
const long band40mEnd = 7300000; // end of 40m
volatile long VFOA_freq80 = 3690000;
volatile long VFOB_freq80 = 3700000;
volatile long VFOA_freq40 = 7090000;
volatile long VFOB_freq40 = 7100000;
volatile long currentfreq = 7090000;
volatile long radix = 1000; // how much to change the frequency by, clicking the rotary encoder will change this.
volatile long oldradix = 1;
volatile long oldfreq = 0;
volatile long CW_FILTER_CENTRE = 8996856;
volatile long SSB_BFO_freq = 8996190; // 8997975 - 2900/2 - 300Hz = 8996190
volatile long CW_BFO_freq = 8996156; // 8996856 - 700Hz
volatile int band = BAND40;
volatile int oldband = BAND80;
volatile int VFOA_VFOB = VFOA;
volatile int old_VFOA_VFOB = VFOB;
volatile int mode = SSB; // the current mode (0=LSB, 1=USB)
volatile int oldmode = CW; // the previous mode (0=LSB, 1=USB)
volatile int SSB_TX = RX;
volatile int oldSSB_TX = TX;
volatile int CW_TX = RX;
volatile int oldCW_TX = TX;
volatile int CW_SPOT = OFF;
volatile int oldCW_SPOT = ON;
volatile int ANT_TUNE = OFF;
volatile int oldANT_TUNE = ON;
volatile long CW_TX_Timer = 0;
volatile int Display = 0;
// Rotary encoder pins and other inputs
static const int rotAPin = 2;
static const int rotBPin = 3;
static const int pushPin = 4;
// I/O switches
static const int CW_Spot_Switch = 5;
static const int SSB_CW_Switch = 6;
static const int Ant_Tune_Switch = 7;
static const int PTT_Switch = A0;
static const int VFOA_VFOB_Switch = 9;
static const int Band_80m_40m_Switch = 11;
static const int CW_Key = 12;
static const int IF_RELAY = 13;
// Discrete Outputs
static const int Ant_Relay = 8;
// Rotary encoder variables, used by interrupt routines
volatile int rotState = 0;
volatile int rotAval = 1;
volatile int rotBval = 1;
// Instantiate the Objects
LiquidCrystal_I2C lcd(0x27, 20, 4);
Si5351 si5351;
void setup()
{
// Set up input and output pins
pinMode(rotAPin, INPUT);
pinMode(rotBPin, INPUT);
pinMode(pushPin, INPUT);
pinMode(CW_Spot_Switch, INPUT);
pinMode(SSB_CW_Switch, INPUT);
pinMode(Ant_Tune_Switch, INPUT);
pinMode(PTT_Switch, INPUT);
pinMode(VFOA_VFOB_Switch, INPUT);
pinMode(Band_80m_40m_Switch, INPUT);
pinMode(CW_Key, INPUT);
pinMode(Ant_Relay, OUTPUT);
pinMode(IF_RELAY, OUTPUT);
pinMode(A1, OUTPUT);
pinMode(A2, OUTPUT);
// Set up pull-up resistors on input and output pins
digitalWrite(rotAPin, HIGH);
digitalWrite(rotBPin, HIGH);
digitalWrite(pushPin, HIGH);
digitalWrite(CW_Spot_Switch, HIGH);
digitalWrite(SSB_CW_Switch, HIGH);
digitalWrite(Ant_Tune_Switch, HIGH);
digitalWrite(PTT_Switch, HIGH);
digitalWrite(VFOA_VFOB_Switch, HIGH);
digitalWrite(Band_80m_40m_Switch, HIGH);
digitalWrite(CW_Key, HIGH);
digitalWrite(Ant_Relay, LOW);
digitalWrite(IF_RELAY, LOW);
digitalWrite(A1, LOW); // Antenna Relay
digitalWrite(A2, LOW); // External Amplifier PTT
// Set up interrupt pins
attachInterrupt(digitalPinToInterrupt(rotAPin), ISRrotAChange, CHANGE);
attachInterrupt(digitalPinToInterrupt(rotBPin), ISRrotBChange, CHANGE);
// Initialize the display
lcd.begin();
lcd.backlight();
delay(1000);
// Initialize the DDS
si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);
si5351.set_correction(28350, SI5351_PLL_INPUT_XO); // Set to specific Si5351 calibration number
si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA);
si5351.drive_strength(SI5351_CLK1, SI5351_DRIVE_8MA);
si5351.drive_strength(SI5351_CLK2, SI5351_DRIVE_8MA);
si5351.set_freq((currentfreq * 100ULL), SI5351_CLK0);
si5351.set_freq((SSB_BFO_freq * 100ULL), SI5351_CLK2);
}
void loop()
{
ReadBandSwitch();
ReadModeSwitch();
ReadVFOSwitch();
ReadPTTSwitch();
ReadCWKeySwitch();
ReadCWSpotSwitch();
ReadAntTuneSwitch();
ControlAntPARelay();
UpdateFrequency();
if (Display == 1)
UpdateDisplay();
}
void ReadBandSwitch()
{
band = digitalRead(Band_80m_40m_Switch);
if (band != oldband)
{
if ((band == BAND80) && (VFOA_VFOB == VFOA))
currentfreq = VFOA_freq80;
if ((band == BAND80) && (VFOA_VFOB == VFOB))
currentfreq = VFOB_freq80;
if ((band == BAND40) && (VFOA_VFOB == VFOA))
currentfreq = VFOA_freq40;
if ((band == BAND40) && (VFOA_VFOB == VFOB))
currentfreq = VFOB_freq40;
SendFrequency();
Display = 1;
oldband = band;
}
}
void ReadModeSwitch()
{
mode = digitalRead(SSB_CW_Switch);
if (mode != oldmode)
{
if (mode == CW)
radix = 10;
else
radix = 1000;
SendFrequency();
Display = 1;
oldmode = mode;
}
}
void ReadVFOSwitch()
{
VFOA_VFOB = digitalRead(VFOA_VFOB_Switch);
if (VFOA_VFOB != old_VFOA_VFOB)
{
if ((band == BAND80) && (VFOA_VFOB == VFOA))
currentfreq = VFOA_freq80;
if ((band == BAND80) && (VFOA_VFOB == VFOB))
currentfreq = VFOB_freq80;
if ((band == BAND40) && (VFOA_VFOB == VFOA))
currentfreq = VFOA_freq40;
if ((band == BAND40) && (VFOA_VFOB == VFOB))
currentfreq = VFOB_freq40;
SendFrequency();
Display = 1;
old_VFOA_VFOB = VFOA_VFOB;
}
}
void ReadPTTSwitch()
{
SSB_TX = digitalRead(PTT_Switch);
if (SSB_TX != oldSSB_TX)
{
if (SSB_TX == TX)
digitalWrite(IF_RELAY, HIGH);
if (SSB_TX == RX)
digitalWrite(IF_RELAY, LOW);
SendFrequency();
Display = 1;
oldSSB_TX = SSB_TX;
}
}
void ReadCWKeySwitch()
{
if ((digitalRead(CW_Key) == LOW) && (mode == CW))
{
CW_TX = TX;
CW_TX_Timer = millis();
si5351.output_enable(SI5351_CLK1, 1);
}
if ((digitalRead(CW_Key) == HIGH) && (mode == CW))
{
if ((CW_SPOT == OFF) && (ANT_TUNE == OFF))
si5351.output_enable(SI5351_CLK1, 0);
}
if ((digitalRead(CW_Key) == HIGH) && ((millis() - CW_TX_Timer) >= 1000))
{
CW_TX_Timer = 0;
CW_TX = RX;
}
if (CW_TX != oldCW_TX)
{
Display = 1;
oldCW_TX = CW_TX;
}
}
void ReadCWSpotSwitch()
{
CW_SPOT = digitalRead(CW_Spot_Switch);
if (CW_SPOT != oldCW_SPOT)
{
if (CW_SPOT == ON)
{
if (mode == SSB)
si5351.set_freq((currentfreq * 100ULL), SI5351_CLK1);
if (mode == CW)
si5351.set_freq(((currentfreq - 700) * 100ULL), SI5351_CLK1);
si5351.output_enable(SI5351_CLK1, 1);
}
if (CW_SPOT == OFF)
{
si5351.set_freq((currentfreq * 100ULL), SI5351_CLK1);
si5351.output_enable(SI5351_CLK1, 0);
}
SendFrequency();
Display = 1;
oldCW_SPOT = CW_SPOT;
}
}
void ReadAntTuneSwitch()
{
ANT_TUNE = digitalRead(Ant_Tune_Switch);
if (ANT_TUNE != oldANT_TUNE)
{
if (ANT_TUNE == ON)
si5351.output_enable(SI5351_CLK1, 1);
if (ANT_TUNE == OFF)
si5351.output_enable(SI5351_CLK1, 0);
SendFrequency();
Display = 1;
oldANT_TUNE = ANT_TUNE;
}
}
void ControlAntPARelay()
{
if ((SSB_TX == TX) || (CW_TX == TX) || (ANT_TUNE == ON))
{
delay(100);
digitalWrite(A1, HIGH);
digitalWrite(A2, HIGH);
}
if ((SSB_TX == RX) && (CW_TX == RX) && (ANT_TUNE == OFF))
{
digitalWrite(A1, LOW);
digitalWrite(A2, LOW);
}
}
void UpdateFrequency()
{
currentfreq = getfreq(); // Interrupt safe method to get the current frequency
if (currentfreq != oldfreq)
{
SendFrequency();
Display = 1;
oldfreq = currentfreq;
}
if (radix != oldradix)
Display = 1;
}
long getfreq()
{
long temp_freq;
cli();
if ((band == BAND80) && (VFOA_VFOB == VFOA))
temp_freq = VFOA_freq80;
if ((band == BAND80) && (VFOA_VFOB == VFOB))
temp_freq = VFOB_freq80;
if ((band == BAND40) && (VFOA_VFOB == VFOA))
temp_freq = VFOA_freq40;
if ((band == BAND40) && (VFOA_VFOB == VFOB))
temp_freq = VFOB_freq40;
sei();
return temp_freq;
}
// Interrupt routines
void ISRrotAChange()
{
if (digitalRead(rotAPin))
{
rotAval = 1;
UpdateRot();
}
else
{
rotAval = 0;
UpdateRot();
}
}
void ISRrotBChange()
{
if (digitalRead(rotBPin))
{
rotBval = 1;
UpdateRot();
}
else
{
rotBval = 0;
UpdateRot();
}
}
void UpdateRot()
{
switch (rotState)
{
case 0: // Idle state, look for direction
if (!rotBval)
rotState = 1; // CW 1
if (!rotAval)
rotState = 11; // CCW 1
break;
case 1: // CW, wait for A low while B is low
if (!rotBval)
{
if (!rotAval)
{
// either increment radixindex or freq
if (digitalRead(pushPin) == LOW)
{
radix = radix * 10;
if (radix > 100000)
radix = 100000;
}
else
{
if ((band == BAND80) && (VFOA_VFOB == VFOA))
{
VFOA_freq80 = VFOA_freq80 + radix;
if (VFOA_freq80 > band80mEnd)
VFOA_freq80 = band80mEnd;
}
if ((band == BAND80) && (VFOA_VFOB == VFOB))
{
VFOB_freq80 = VFOB_freq80 + radix;
if (VFOB_freq80 > band80mEnd)
VFOB_freq80 = band80mEnd;
}
if ((band == BAND40) && (VFOA_VFOB == VFOA))
{
VFOA_freq40 = VFOA_freq40 + radix;
if (VFOA_freq40 > band40mEnd)
VFOA_freq40 = band40mEnd;
}
if ((band == BAND40) && (VFOA_VFOB == VFOB))
{
VFOB_freq40 = VFOB_freq40 + radix;
if (VFOB_freq40 > band40mEnd)
VFOB_freq40 = band40mEnd;
}
}
rotState = 2; // CW 2
}
}
else if (rotAval)
rotState = 0; // It was just a glitch on B, go back to start
break;
case 2: // CW, wait for B high
if (rotBval)
rotState = 3; // CW 3
break;
case 3: // CW, wait for A high
if (rotAval)
rotState = 0; // back to idle (detent) state
break;
case 11: // CCW, wait for B low while A is low
if (!rotAval)
{
if (!rotBval)
{
// either decrement radixindex or freq
if (digitalRead(pushPin) == LOW)
{
radix = radix / 10;
if (radix < 1)
radix = 1;
}
else
{
if ((band == BAND80) && (VFOA_VFOB == VFOA))
{
VFOA_freq80 = VFOA_freq80 - radix;
if (VFOA_freq80 < band80mStart)
VFOA_freq80 = band80mStart;
}
if ((band == BAND80) && (VFOA_VFOB == VFOB))
{
VFOB_freq80 = VFOB_freq80 - radix;
if (VFOB_freq80 < band80mStart)
VFOB_freq80 = band80mStart;
}
if ((band == BAND40) && (VFOA_VFOB == VFOA))
{
VFOA_freq40 = VFOA_freq40 - radix;
if (VFOA_freq40 < band40mStart)
VFOA_freq40 = band40mStart;
}
if ((band == BAND40) && (VFOA_VFOB == VFOB))
{
VFOB_freq40 = VFOB_freq40 - radix;
if (VFOB_freq40 < band40mStart)
VFOB_freq40 = band40mStart;
}
}
rotState = 12; // CCW 2
}
}
else if (rotBval)
rotState = 0; // It was just a glitch on A, go back to start
break;
case 12: // CCW, wait for A high
if (rotAval)
rotState = 13; // CCW 3
break;
case 13: // CCW, wait for B high
if (rotBval)
rotState = 0; // back to idle (detent) state
break;
}
}
void UpdateDisplay()
{
// VFOA/B
if (VFOA_VFOB == VFOA)
{
lcd.setCursor(0, 0);
lcd.print("*");
lcd.setCursor(0, 1);
lcd.print(" ");
}
if (VFOA_VFOB == VFOB)
{
lcd.setCursor(0, 0);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print("* ");
}
// Freq
if (band == BAND80)
{
lcd.setCursor(2, 0);
lcd.print(VFOA_freq80);
lcd.setCursor(2, 1);
lcd.print(VFOB_freq80);
}
if (band == BAND40)
{
lcd.setCursor(2, 0);
lcd.print(VFOA_freq40);
lcd.setCursor(2, 1);
lcd.print(VFOB_freq40);
}
// Mode
if (mode == SSB)
{
lcd.setCursor(16, 0);
lcd.print("SSB");
}
if (mode == CW)
{
lcd.setCursor(16, 0);
lcd.print("CW ");
}
// Test CW Timer
if (CW_TX == TX)
{
lcd.setCursor(16, 2);
lcd.print("CWTX");
}
if (CW_TX == RX)
{
lcd.setCursor(16, 2);
lcd.print(" ");
}
// CW Spot
if (CW_SPOT == ON)
{
lcd.setCursor(16, 3);
lcd.print("SPOT");
}
if (CW_SPOT == OFF)
{
lcd.setCursor(16, 3);
lcd.print(" ");
}
// Ant Tune
if (ANT_TUNE == ON)
{
lcd.setCursor(2, 3);
lcd.print("TUNE");
}
if (ANT_TUNE == OFF)
{
lcd.setCursor(2, 3);
lcd.print(" ");
}
// TX/RX
if ((SSB_TX == TX) || (CW_TX == TX) || (ANT_TUNE == ON))
{
lcd.setCursor(7, 3);
lcd.print("XMIT");
}
if ((SSB_TX == RX) && (CW_TX == RX) && (ANT_TUNE == OFF))
{
lcd.setCursor(7, 3);
lcd.print(" ");
}
// Radix
if (radix != oldradix) // stops radix display flashing/blinking on freq change
{
lcd.setCursor(12, 1);
lcd.print(" ");
lcd.setCursor(12, 1);
if (radix == 1)
lcd.print(" 1 Hz");
if (radix == 10)
lcd.print(" 10 Hz");
if (radix == 100)
lcd.print(" 100 Hz");
if (radix == 1000)
lcd.print(" 1 kHz");
if (radix == 10000)
lcd.print(" 10 kHz");
if (radix == 100000)
lcd.print("100 kHz");
oldradix = radix;
}
Display = 0;
}
void SendFrequency()
{
if (mode == SSB)
{
if (SSB_TX == RX)
{
SSB_BFO_freq = 8996190;
si5351.set_freq(((SSB_BFO_freq + currentfreq) * 100ULL), SI5351_CLK0);
si5351.set_freq((SSB_BFO_freq * 100ULL), SI5351_CLK2);
}
if (SSB_TX == TX)
{
SSB_BFO_freq = 8995800;
si5351.set_freq(((SSB_BFO_freq + currentfreq) * 100ULL), SI5351_CLK2);
si5351.set_freq((SSB_BFO_freq * 100ULL), SI5351_CLK0);
}
}
if (mode == CW)
{
si5351.set_freq(((CW_FILTER_CENTRE + currentfreq) * 100ULL), SI5351_CLK0);
si5351.set_freq((CW_BFO_freq * 100ULL), SI5351_CLK2);
}
si5351.set_freq((currentfreq * 100ULL), SI5351_CLK1);
}