#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <si5351.h>
const uint32_t bandStart = 3500000; // start of 80m
const uint32_t bandEnd = 3900000; // end of 80m
const uint32_t bandInit = 3690000; // where to initially set the frequency
volatile long oldfreq = 0;
volatile long currentfreq;
volatile int updatedisplay = 0;
volatile uint32_t freq = bandInit ;
volatile uint32_t vfo = bandInit;
volatile uint32_t radix = 1000;
volatile uint32_t LSB_IF_freq = 9000000; // Crystal filter centre freq
volatile uint32_t LSB_BFO_freq = 9001500; // Crystal filter centre freq
// Rotary encoder pins and other inputs
static const int pushPin = 9;
static const int rotBPin = 3;
static const int rotAPin = 2;
// Rotary encoder variables, used by interrupt routines
volatile int rotState = 0;
volatile int rotAval = 1;
volatile int rotBval = 1;
volatile int rotAcc = 0;
// Instantiate the Objects
Adafruit_SSD1306 display(4);
Si5351 si5351;
void setup()
{
// Set up frequency and radix switches
pinMode(rotAPin, INPUT);
pinMode(rotBPin, INPUT);
pinMode(pushPin, INPUT);
// set up pull-up resistors on inputs
digitalWrite(rotAPin, HIGH);
digitalWrite(rotBPin, HIGH);
digitalWrite(pushPin, HIGH);
attachInterrupt(digitalPinToInterrupt(rotAPin), ISRrotAChange, CHANGE);
attachInterrupt(digitalPinToInterrupt(rotBPin), ISRrotBChange, CHANGE);
// Initialize the display with the I2C addr 0x3C
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.setTextColor(WHITE);
display.clearDisplay();
display.display();
delay(1000);
UpdateDisplay();
delay(1000);
// Initialize the DDS
si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0);
si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
si5351.set_freq(bandInit, SI5351_PLL_FIXED, SI5351_CLK0);
si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA);
si5351.drive_strength(SI5351_CLK2, SI5351_DRIVE_8MA);
si5351.set_freq((freq * 100ULL), SI5351_PLL_FIXED, SI5351_CLK0);
si5351.set_freq((LSB_BFO_freq * 100ULL), SI5351_PLL_FIXED, SI5351_CLK2);
}
void loop()
{
currentfreq = getfreq(); // Interrupt safe method to get the current frequency
if (currentfreq != oldfreq)
{
UpdateDisplay();
SendFrequency();
oldfreq = currentfreq;
}
if (!digitalRead(pushPin)) {
delay(10);
while (!digitalRead(pushPin))
{
if (updatedisplay == 1)
{
UpdateDisplay();
updatedisplay = 0;
}
}
delay(50);
}
}
long getfreq()
{
long temp_freq;
cli();
temp_freq = freq;
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)
{
updatedisplay = 1;
if (radix == 1000000)
radix = 100000;
else if (radix == 100000)
radix = 10000;
else if (radix == 10000)
radix = 1000;
else if (radix == 1000)
radix = 100;
else if (radix == 100)
radix = 10;
else if (radix == 10)
radix = 1;
else
radix = 1000000;
} else
{
freq = (freq + radix);
if (freq > 3900000)
freq = 3900000;
}
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)
{
updatedisplay = 1;
if (radix == 1)
radix = 10;
else if (radix == 10)
radix = 100;
else if (radix == 100)
radix = 1000;
else if (radix == 1000)
radix = 10000;
else if (radix == 10000)
radix = 100000;
else if (radix == 100000)
radix = 1000000;
else
radix = 1;
} else
{
freq = (freq - radix);
if (freq < 3500000)
freq = 3500000;
}
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()
{
display.clearDisplay();
display.setCursor(0, 0);
display.setTextSize(1);
display.println("ZL2CTM Tramping Rig");
display.setCursor(0, 20);
display.setTextSize(2);
display.println(freq);
if (freq > 9999999)
display.setCursor(12, 30);
if (freq < 9999999)
display.setCursor(0, 30);
switch (radix)
{
case 1:
display.println(" -");
break;
case 10:
display.println(" -");
break;
case 100:
display.println(" -");
break;
case 1000:
display.println(" -");
break;
case 10000:
display.println(" -");
break;
case 100000:
display.println(" -");
break;
case 1000000:
display.println("-");
break;
}
display.setCursor(0, 48);
display.setTextSize(2);
display.println("LSB");
display.setCursor(60, 48);
if ((freq >= 3500000) && (freq <= 3900000))
display.println("80m");
display.display();
}
void SendFrequency()
{
// VFO
si5351.set_freq(((LSB_IF_freq - freq + 1500 - 70) * 100ULL), SI5351_PLL_FIXED, SI5351_CLK0);
// BFO
si5351.set_freq((LSB_BFO_freq * 100ULL), SI5351_PLL_FIXED, SI5351_CLK2);
}