Sunday, 4 September 2022

80m/20m SSB/CW Rig






 

Mic Amp



Crystal Filters

IF Amps and Coupling Transformers




Arduino Code:


#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); 
}