Saturday, 15 February 2020

Homebrew DSB-SC Rig

AF Amps Ideas






The AF amp now has a pi filter on the input made up of 100nF - 1mH - 100nF
That removes the high freq noise.



Ant RF Amp



Mic Amp



Test Low Power RF Amplifier
Out of the junk box hence the 13.8VDC VCC.




Initial code for the radio. Please see the YouTube video for an explanation:

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

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 freq = 3700000;         // this is a variable (changes) - set it to the beginning of the band
volatile long radix = 1000;           // how much to change the frequency by, clicking the rotary encoder will change this.
volatile long oldfreq = 0;
volatile long old80mfreq = 3700000;
volatile long old40mfreq = 7100000;
volatile long currentfreq = 0;
volatile int updatedisplay = 0;
volatile int band = 0;                // 0=80m, 1=40m
volatile int oldband = 0;

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

// Band switch
static const int BandSwPin = 5;

// 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_PULLUP);
  pinMode(rotBPin, INPUT_PULLUP);
  pinMode(pushSwPin, INPUT_PULLUP);
  pinMode(BandSwPin, INPUT_PULLUP);

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

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

  // Initialize the Si5351
  si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);
  si5351.set_correction(191, SI5351_PLL_INPUT_XO);
  si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
  si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA);

  // Update display and send start frequency
  UpdateDisplay();
  SendFrequency();
}


void loop()
{
  // Check to see if the freq has changed
  currentfreq = getfreq();                    // Interrupt safe method to get the current frequency

  if (currentfreq != oldfreq)
  {
    UpdateDisplay();
    SendFrequency();
    oldfreq = currentfreq;
  }

  // Check the rotary encoder (radix) swith
  if (digitalRead(pushSwPin) == LOW)           // Read the rotary encoder switch
  {
    delay(10);
    while (digitalRead(pushSwPin) == LOW)
    {
      if (updatedisplay == 1)
      {
        UpdateDisplay();
        updatedisplay = 0;
      }
    }
    delay(50);
  }

  // Check the band swith
  if (digitalRead(BandSwPin) == LOW)
    band = 1;                                  // 1=40m
  else
    band = 0;                                  // 0=80m

  if (band != oldband)                         // Only update the display on band change
  {
    if (band == 0)                             // Now 80m was 40m
    {
      old40mfreq = freq;                       // Store old 40m freq
      freq = old80mfreq;                       // Recall old 40m freq
    }
    if (band == 1)                             // Now 40m was 80m
    {
      old80mfreq = freq;                       // Store old 80m freq
      freq = old40mfreq;                       // Recall old 40m freq
    }
    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();
  }
}


// Determine which way the rotary encoder is rotating and action as required
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 the radix or freq
          if (digitalRead(pushSwPin) == 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)                          // 40m
              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 the radix or freq
          if (digitalRead(pushSwPin) == 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)                          // 40m
              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");

  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()
{
  si5351.set_freq((freq * 100ULL), SI5351_CLK0);
}

5 comments:

  1. Hi Charlie,

    Started building this today, it will be called the Covo 40, Just wondering with the LCD display, I have the vfo running on a 2x16 just fine but I want to use a 2x8 for a smaller form factor, Even better an oled sh1106, I may look at that later though. with the 2x8 I have to butcher an i2c module and graft it on the 2x8. Is there an easy way to knock a couple of digits off the frequency displayed whilst keeping all the formulas intact in the sketch? Just a thought, I may play around with it later and see what happens.
    Cheers and thanks, Nigel

    ReplyDelete