Showing posts with label SSB. Show all posts
Showing posts with label SSB. Show all posts

Saturday 20 April 2019

Portable 20m SSB Rig



See YouTube for video log.







BPF




Ant Amplifier




IF Amp







#include <Wire.h>
#include <SPI.h>
#include <TM1637Display.h>
#include <si5351.h>
#include "LowPower.h"

const uint32_t bandStart = 14000000;    // start of 20m
const uint32_t bandEnd =   14350000;    // end of 20m
const uint32_t bandInit =  14100000;    // where to initially set the frequency
volatile long currentfreq = 0;
volatile long oldfreq = 0;
volatile int currentmode = 0;
volatile int oldmode = 0;

volatile uint32_t freq = bandInit ;     // this is a variable (changes) - set it to the beginning of the band
volatile uint32_t radix = 1000;         // how much to change the frequency by, clicking the rotary encoder will change this.

const uint32_t BFO_freq = 8998450;      // 8998450 = high side injection. For low side injection use 9001350;

// Rotary encoder pins and other inputs
static const int rotBPin = 2;
static const int rotAPin = 3;
static const int pushPin = 4;
static const int PTTInput = 8;
static const int brightnessPin = A3;
static const int tunespeedLED = A2;
static const int gnd = 10;
static const int vcc = 11;
static const int DIO = 12;
static const int CLK = 13;

// Rotary encoder variables, used by interrupt routines
volatile int rotState = 0;
volatile int rotAval = 1;
volatile int rotBval = 1;


volatile long remainder = 0;
volatile long OnesHz = 0;
volatile long TensHz = 0;
volatile long HundredsHz = 0;
volatile long OneskHz = 0;
volatile long TenskHz = 0;
volatile long HundredskHz = 0;
volatile long OnesMHz = 0;
volatile long TensMHz = 0;
volatile int Brightness = 3;
volatile int batterySave = 0;

// Instantiate the Objects
TM1637Display display(CLK, DIO);    // CLK, DIO
Si5351 si5351;

void setup()
{
  // Set up frequency and radix switches
  pinMode(rotAPin, INPUT);
  pinMode(rotBPin, INPUT);
  pinMode(pushPin, INPUT);
  pinMode(brightnessPin, INPUT);
  pinMode(gnd, OUTPUT);
  pinMode(tunespeedLED, OUTPUT);
  pinMode(vcc, OUTPUT);
  pinMode(PTTInput, INPUT);

  // Set up pull-up resistors on inputs
  digitalWrite(rotAPin, HIGH);
  digitalWrite(rotBPin, HIGH);
  digitalWrite(pushPin, HIGH);
  digitalWrite(brightnessPin, HIGH);
  digitalWrite(gnd, LOW);
  digitalWrite(vcc, HIGH);
  digitalWrite(tunespeedLED, LOW);
  digitalWrite(PTTInput, LOW);

  // Set up interrupt pins
  attachInterrupt(digitalPinToInterrupt(rotAPin), ISRrotAChange, CHANGE);
  attachInterrupt(digitalPinToInterrupt(rotBPin), ISRrotBChange, CHANGE);

  // Initialize the display
  display.setBrightness(Brightness, true);
  UpdateDisplay();
  delay(1000);

  // Initialize the DDS
  si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);
  si5351.set_correction(87000, 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_2MA);
  si5351.drive_strength(SI5351_CLK2, SI5351_DRIVE_2MA);
  si5351.set_freq((freq * 100ULL), SI5351_CLK0);
  si5351.set_freq((BFO_freq * 100ULL), SI5351_CLK2);
}


void loop()
{
  LowPower.idle(SLEEP_60MS, ADC_OFF, TIMER2_OFF, TIMER1_OFF, TIMER0_ON, SPI_OFF, USART0_OFF, TWI_OFF);

  currentmode = digitalRead(PTTInput);
  if (currentmode != oldmode)
  {
    SendFrequency();
    oldmode = currentmode;
  }


  currentfreq = getfreq();                // Interrupt safe method to get the current frequency
  if (currentfreq != oldfreq)
  {
    UpdateDisplay();
    SendFrequency();
    oldfreq = currentfreq;
  }

  if (digitalRead(brightnessPin) == LOW)
  {
    Brightness--;
    display.setBrightness(Brightness, true);
    if (Brightness == -1)
    {
      display.setBrightness(0, false);
      digitalWrite(tunespeedLED, LOW);
      batterySave = 1;
    }
    if (Brightness == -2)
    {
      Brightness = 3;
      batterySave = 0;
    }
    UpdateDisplay();
    delay(500);
  }

  if ((radix == 100) && (batterySave == 0))
    digitalWrite(tunespeedLED, HIGH);

  if (radix == 1000)
    digitalWrite(tunespeedLED, LOW);
}


void wakeUp()
{
  // Just a handler for the sleep pin interrupt.
}


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)
          {
            if (radix == 1000)
              radix = 100;
            else if (radix == 100)
              radix = 1000;
          }
          else
          {
            freq = (freq + radix);
            if (freq > bandEnd)
              freq = bandEnd;
          }
          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)
          {
            if (radix == 100)
              radix = 1000;
            else if (radix == 1000)
              radix = 100;
          }
          else
          {
            freq = (freq - radix);
            if (freq < bandStart)
              freq = bandStart;
          }
          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()
{
  TensMHz = freq / 10000000;                                // TensMHz = 12345678 / 10000000 = 1
  remainder = freq - (TensMHz * 10000000);                  // remainder = 12345678 - 10000000 = 2345678
  OnesMHz = remainder / 1000000;                            // OnesMhz = 2345678 / 1000000 = 2
  remainder = remainder - (OnesMHz * 1000000);              // remainder = 2345678 - (2 * 1000000) = 345678
  HundredskHz = remainder / 100000;                         // HundredskHz = 345678 / 100000 = 3
  remainder = remainder - (HundredskHz * 100000);           // remainder = 345678 - (3 * 100000) = 45678
  TenskHz = remainder / 10000;                              // TenskHz = 45678 / 10000 = 4
  remainder = remainder - (TenskHz * 10000);                // remainder = 45678 - (4 * 10000) = 5678
  OneskHz = remainder / 1000;                               // OneskHz = 5678 / 1000 = 5
  remainder = remainder - (OneskHz * 1000);                 // remainder = 5678 - (5 * 1000) = 678
  HundredsHz = remainder / 100;                             // HundredsHz = 678 / 100 = 6
  remainder = remainder - (HundredsHz * 100);               // remainder = 678 - (6 * 100) = 78
  TensHz = remainder / 10;                                  // TensHz = 78 / 10 = 7
  remainder = remainder - (TensHz * 10);                    // remainder = 78 - (7 * 10) = 8
  OnesHz = remainder;                                       // OnesHz = 8

  display.showNumberDec(((1000 * HundredskHz) + ( 100 * TenskHz) + (10 * OneskHz) + HundredsHz), true);
}


void SendFrequency()
{
  if (currentmode == 1)             // Transmit
  {
    si5351.set_freq(((freq - BFO_freq + 50) * 100ULL), SI5351_CLK2);
    si5351.set_freq((BFO_freq * 100ULL), SI5351_CLK0);
  }
  else                              // Receive
  {
    si5351.set_freq(((freq - BFO_freq + 50) * 100ULL), SI5351_CLK0);
    si5351.set_freq((BFO_freq * 100ULL), SI5351_CLK2);
  }
}

Friday 3 August 2018

Homebrew 80/40m SSB/CW Base Rig

Audio Amplifier


Another audio amplifier option.



Double Balanced Mixers





Plessey Bidirectional IF Amplifiers


Mic Amp



RF Power Amp



Low Pass Filters




#include <Wire.h>
#include <SPI.h>
#include <LiquidCrystal_I2C.h>
#include <si5351.h>

const uint32_t band80mStart = 3500000;    // start of 80m
const uint32_t band80mEnd = 3900000;      // end of 80m
const uint32_t band40mStart = 7000000;    // start of 40m
const uint32_t band40mEnd = 7300000;      // end of 40m

const uint32_t bandInit =  3700000;       // where to initially set the frequency
volatile long oldfreq = 0;
volatile long old80mfreq = 3700000;
volatile long old40mfreq = 7100000;
volatile long currentfreq = 0;
volatile int updatedisplay = 0;
volatile int TX = 0;
volatile int oldTX = 0;
volatile int mode = 0;
volatile int oldmode = 0;
volatile int band = 0;
volatile int oldband = 0;

volatile uint32_t freq = bandInit ;     // this is a variable (changes) - set it to the beginning of the band
volatile uint32_t radix = 1000;         // how much to change the frequency by, clicking the rotary encoder will change this.

volatile uint32_t SSB_BFO_freq = 8999700;   // 8998050 (filter centre) + 2700/2 + 300
//volatile uint32_t SSB_BFO_freq = 9000300;   // 8998050 (filter centre) + 2700/2 + 300
volatile uint32_t CW_BFO_freq = 8997336;    // 8996750 (filter centre) + 700 - 114 (from test)
static const uint32_t CW_Filter_CentreFreq = 8996750;

// Rotary encoder pins and other inputs
static const int rotAPin = 2;
static const int rotBPin = 3;
static const int pushPin = 4;

// Mode switches
static const int BandPin = 5;
static const int ModePin = 6;

// Discrete Outputs
static const int CrystalFilterSelectPin = 10;
static const int PTTPin = 11;

// 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, 16, 2);
Si5351 si5351;

void setup()
{
  // Set up frequency and radix switches
  pinMode(rotAPin, INPUT);
  pinMode(rotBPin, INPUT);
  pinMode(pushPin, INPUT);
  pinMode(ModePin, INPUT);
  pinMode(BandPin, INPUT);
  pinMode(PTTPin, INPUT);
  pinMode(CrystalFilterSelectPin, OUTPUT);

  // Set up pull-up resistors on inputs
  digitalWrite(rotAPin, HIGH);
  digitalWrite(rotBPin, HIGH);
  digitalWrite(pushPin, HIGH);
  digitalWrite(ModePin, HIGH);
  digitalWrite(BandPin, HIGH);
  digitalWrite(PTTPin, HIGH);
  digitalWrite(CrystalFilterSelectPin, LOW);

  // Set up interrupt pins
  attachInterrupt(digitalPinToInterrupt(rotAPin), ISRrotAChange, CHANGE);
  attachInterrupt(digitalPinToInterrupt(rotBPin), ISRrotBChange, CHANGE);

  // Initialize the display
  lcd.begin();
  lcd.backlight();
  lcd.cursor();
  UpdateDisplay();
  delay(1000);

  // Initialize the DDS
  si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0);
  si5351.set_correction(35980);
  si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
  si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA);
  si5351.drive_strength(SI5351_CLK2, SI5351_DRIVE_8MA);
//  si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_6MA);
//  si5351.drive_strength(SI5351_CLK2, SI5351_DRIVE_6MA);  si5351.set_freq((freq * 100ULL), SI5351_PLL_FIXED, SI5351_CLK0);
  si5351.set_freq((SSB_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) == LOW)
  {
    delay(10);
    while (digitalRead(pushPin) == LOW)
    {
      if (updatedisplay == 1)
      {
        UpdateDisplay();
        updatedisplay = 0;
      }
    }
    delay(50);
  }

  if (digitalRead(PTTPin) == HIGH)
    TX = 1;
  else
    TX = 0;

  if (digitalRead(ModePin) == LOW)
  {
    mode = 1;                                       // 0 = LSB, 1 = CW
    digitalWrite(CrystalFilterSelectPin, HIGH);     // Select CW filter
  }
  else
  {
    mode = 0;
    digitalWrite(CrystalFilterSelectPin, LOW);      // Select SSB filter
  }

  if (digitalRead(BandPin) == LOW)
    band = 1;                                       // 0 = 80m, 1 = 40m
  else
    band = 0;

  if (TX != oldTX)                              // Only update the display on mode change
  {
    //UpdateMode();
    UpdateDisplay();
    oldTX = TX;
  }

  if (mode != oldmode)                              // Only update the display on mode change
  {
    //UpdateMode();
    UpdateDisplay();
    SendFrequency();
    oldmode = mode;
  }

  if (band != oldband)                          // Only update the display on band change
  {
    // 0 = 80m, 1 = 40m
    if (band == 0)  // now 80m was 40m
    {
      old40mfreq = freq;
      freq = old80mfreq;
    }
    if (band == 1)  // now 40m was 80m
    {
      old80mfreq = freq;
      freq = old40mfreq;
    }
    UpdateDisplay();
    oldband = band;
  }
}




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 (band == 0)          // 80m
              if (freq > band80mEnd)
                freq = band80mEnd;
            if (band == 1)          // 80m
              if (freq > band40mEnd)
                freq = 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)
          {
            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 (band == 0)          // 80m
              if (freq < band80mStart)
                freq = band80mStart;
            if (band == 1)          // 80m
              if (freq < band40mStart)
                freq = 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()
{
  lcd.setCursor(0, 0);
  lcd.print("        ");
  lcd.setCursor(0, 0);
  lcd.print(freq);
  lcd.setCursor(10, 0);
  lcd.print("ZL2CTM");

  lcd.setCursor(0, 1);
  lcd.print("   ");
  lcd.setCursor(0, 1);

  lcd.setCursor(0, 1);
  if (mode == 0)
    lcd.print("LSB");
  if (mode == 1)
    lcd.print("CW ");

  lcd.setCursor(5, 1);
  lcd.print("  ");
  lcd.setCursor(5, 1);
  if (TX == 1)
    lcd.print("TX");

  if (freq > 9999999)
  {
    if (radix == 1)
      lcd.setCursor(7, 0);
    if (radix == 10)
      lcd.setCursor(6, 0);
    if (radix == 100)
      lcd.setCursor(5, 0);
    if (radix == 1000)
      lcd.setCursor(4, 0);
    if (radix == 10000)
      lcd.setCursor(3, 0);
    if (radix == 100000)
      lcd.setCursor(2, 0);
    if (radix == 1000000)
      lcd.setCursor(1, 0);
  }
  if (freq <= 9999999)
  {
    if (radix == 1)
      lcd.setCursor(6, 0);
    if (radix == 10)
      lcd.setCursor(5, 0);
    if (radix == 100)
      lcd.setCursor(4, 0);
    if (radix == 1000)
      lcd.setCursor(3, 0);
    if (radix == 10000)
      lcd.setCursor(2, 0);
    if (radix == 100000)
      lcd.setCursor(1, 0);
    if (radix == 1000000)
      lcd.setCursor(0, 0);
  }
}


void SendFrequency()
{
  // CW_BFO_freq = 8997336;    // 8996750 (filter centre) + 700 - 114 (from test)
  // SSB_BFO_freq = 8999700;   // 8998050 (filter centre) + 2700/2 + 300
  // CW_Filter_CentreFreq = 8996750;

  if (mode == 1)      // CW
  {
    si5351.set_freq(((CW_Filter_CentreFreq - freq) * 100ULL), SI5351_PLL_FIXED, SI5351_CLK0);
    si5351.set_freq((CW_BFO_freq * 100ULL), SI5351_PLL_FIXED, SI5351_CLK2);
  }
  else                // SSB
  {
    si5351.set_freq(((SSB_BFO_freq - freq) * 100ULL), SI5351_PLL_FIXED, SI5351_CLK0);
    si5351.set_freq((SSB_BFO_freq * 100ULL), SI5351_PLL_FIXED, SI5351_CLK2);
  }
}