Thursday, 14 January 2021

Simple SSB Transceiver

 Please see the YouTube channel for details: https://www.youtube.com/channel/UCSNPW3_gzuMJcX_ErBZTv2g




AF Amp






IF Amps










Mixers







BPF







****************************************************************
Initial Arduino code

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

static const long bandStart = 3500000;          // start of VFO range
static const long bandEnd =   3900000;          // end of VFO range
static const long bandInit =  3690000;          // where to initially set the frequency
volatile long freq = 3690000;                   // the current freq
volatile long oldfreq = 0;                      // the previous freq
volatile long LSB_BFO_freq = 8997200;           // High side injection thus SB inversion.
volatile long USB_BFO_freq = 8998700;           // High side injection thus SB inversion.
volatile long radix = 1000;                     // How much to change the frequency by, clicking the Up Down switches
volatile long oldradix = 0;                     // the previous radix
volatile int mode = 0;                          // the current mode (0=LSB, 1=USB)
volatile int oldmode = 0;                       // the previous mode (0=LSB, 1=USB)
volatile int TX = 0;                            // 0=RX, 1=TX
volatile int oldTX = 1;                         // the old TX
unsigned int encoderA, encoderB, encoderC = 1;  // rotary encoder variables

// Rotary encoder pins and other inputs
static const int rotAPin = 2;
static const int rotBPin = 3;
static const int radixPin = 4;
static const int modePin = 5;
static const int PTTPin = 6;

// Instantiate the Objects
LiquidCrystal_I2C lcd(0x3F, 16, 2);              // 3F the address of the LCD
Si5351 si5351;


void setup()
{
  // Set up I/O pins
  pinMode(rotAPin, INPUT);
  digitalWrite(rotAPin, HIGH);                    // internal pull-up enabled
  pinMode(rotBPin, INPUT);
  digitalWrite(rotBPin, HIGH);                    // internal pull-up enabled
  pinMode(radixPin, INPUT);
  digitalWrite(radixPin, HIGH);                   // internal pull-up enabled
  pinMode(modePin, INPUT);
  digitalWrite(modePin, HIGH);                    // internal pull-up enabled
  pinMode(PTTPin, INPUT);
  digitalWrite(PTTPin, LOW);                      // internal pull-up disabled

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

  // Initialize the DDS
  si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 62100);         // 62100 is the specific calibration factor for this Si5351 board
  si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA);
  si5351.drive_strength(SI5351_CLK2, SI5351_DRIVE_8MA);
}

void loop()
{
  CheckEncoder();
  CheckRadixSwitch();
  CheckModeSwitch();
  CheckPTTPin();
}

void CheckEncoder()
{
  byte encoderA = digitalRead(rotAPin);
  byte encoderB = digitalRead(rotBPin);

  if ((encoderA == HIGH) && (encoderC == LOW))
  {
    if (encoderB == HIGH)
      // Decrease frequency
      freq = constrain(freq - radix, bandStart, bandEnd);
    else
      // Increase frequency
      freq = constrain(freq + radix, bandStart, bandEnd);
  }
  encoderC = encoderA;

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

void CheckRadixSwitch()
{
  if (digitalRead(radixPin) == 0)
  {
    radix = radix / 10;
    if (radix < 1)
      radix = 1000;
    delay(200);
  }

  if (radix != oldradix)
  {
    UpdateRadixDisplay();
    oldradix = radix;
  }
}

void CheckModeSwitch()
{
  if (digitalRead(modePin) == 0)
    mode = 0;                                 // 0=LSB
  if (digitalRead(modePin) == 1)
    mode = 1;                                 // 1=USB

  if (mode != oldmode)
  {
    UpdateDisplay();
    SendFrequency();
    oldmode = mode;
  }
}

void CheckPTTPin()
{
  TX = digitalRead(PTTPin);
  if (TX != oldTX)
  {
    UpdateDisplay();
    SendFrequency();
    oldTX = TX;
  }
}

void UpdateDisplay()
{
  // freq
  lcd.setCursor(0, 0);
  lcd.print(freq);
  // mode
  lcd.setCursor(0, 1);
  if (mode == 0)
    lcd.print("LSB");
  else
    lcd.print("USB");
  lcd.setCursor(10, 1);
  // PTT
  lcd.setCursor(4, 1);
  if (TX == 1)
    lcd.print("TX");
  if (TX == 0)
    lcd.print("RX");
  // callsign
  lcd.setCursor(10, 1);
  lcd.print("ZL2CTM");
}

void UpdateRadixDisplay()
{
  // radix
  if (radix == 1000)
  {
    lcd.setCursor(9, 0);
    lcd.print("      ");
    lcd.setCursor(9, 0);
    lcd.print(radix);
    lcd.setCursor(14, 0);
    lcd.print("Hz");
  }
  if (radix == 100)
  {
    lcd.setCursor(9, 0);
    lcd.print("      ");
    lcd.setCursor(10, 0);
    lcd.print(radix);
    lcd.setCursor(14, 0);
    lcd.print("Hz");
  }
  if (radix == 10)
  {
    lcd.setCursor(9, 0);
    lcd.print("      ");
    lcd.setCursor(11, 0);
    lcd.print(radix);
    lcd.setCursor(14, 0);
    lcd.print("Hz");
  }
  if (radix == 1)
  {
    lcd.setCursor(9, 0);
    lcd.print("      ");
    lcd.setCursor(12, 0);
    lcd.print(radix);
    lcd.setCursor(14, 0);
    lcd.print("Hz");
  }
}

void SendFrequency()
{
  if (mode == 0)                                                          // LSB
  {
    if (TX == 1)                                                          // Transmit
    {
      si5351.set_freq(((LSB_BFO_freq + freq) * 100ULL), SI5351_CLK2);     // VFO
      si5351.set_freq((LSB_BFO_freq * 100ULL), SI5351_CLK0);              // BFO
    }
    else                                                                  // Receive
    {
      si5351.set_freq(((LSB_BFO_freq + freq) * 100ULL), SI5351_CLK0);     // VFO
      si5351.set_freq((LSB_BFO_freq * 100ULL), SI5351_CLK2);              // BFO
    }
  }

  if (mode == 1)                                                          // USB
  {
    if (TX == 1)                                                          // Transmit
    {
      si5351.set_freq(((USB_BFO_freq + freq) * 100ULL), SI5351_CLK2);     // VFO
      si5351.set_freq((USB_BFO_freq * 100ULL), SI5351_CLK0);              // BFO
    }
    else                                                                  // Receive
    {
      si5351.set_freq(((USB_BFO_freq + freq) * 100ULL), SI5351_CLK0);     // VFO
      si5351.set_freq((USB_BFO_freq * 100ULL), SI5351_CLK2);              // BFO
    }
  }
}

11 comments:

  1. Hi Charlie, just building the si5351 and nano on a board. Your timing with this vfo/bfo is perfect timing.
    I will take a look at your coding as thats about where I am up to.
    Cheers Colin vk2jcc




    ReplyDelete
  2. That's great Colin. Check out some earlier blogs for other software versions using a standard rotary encoder.

    ReplyDelete
  3. Hi Charlie,

    I can't get my arduino loaded.
    I am getting an error.
    Can you help me.
    I don't think I have the right library.
    Where can I find the right one?

    Arduino: 1.8.5 (Windows 10), Board: "Arduino / Genuino Uno"

    C: \ Program Files (x86) \ Arduino \ libraries \ LiquidCrystal_I2C \ I2CIO.cpp: 35: 10: fatal error: ../Wire/Wire.h: No such file or directory

    #include <../ Wire / Wire.h>

    ^ ~~~~~~~~~~~~~~~

    compilation terminated.

    Found multiple libraries for "LiquidCrystal_I2C.h"
    Used: C: \ Program Files (x86) \ Arduino \ libraries \ LiquidCrystal_I2C
    Not used: C: \ Program Files (x86) \ Arduino \ libraries \ NewLiquidCrystal_lib
    exit status 1
    Error compiling for board Arduino / Genuino Uno

    This report would contain more information with
    "Show verbose output during compilation"
    option in File -> Preferences.

    greetings Arjan

    ReplyDelete
    Replies
    1. I won't be able to help sorry Arjan. I suggest you open the example sketch that comes with your display library and look at the syntax. Copy that syntax in this sketch.

      Charlie

      Delete
  4. Great Build there Charlie, Just finished the VFO using exact same parts as you are, I found that when pin 5 us grounded i get a continuous flicker between Rx Tx and USB LSB. I will try add some resistance at the switch hope that helps.

    ReplyDelete
  5. Not sure sorry. I don't have that problem here. The internal pull-up resistor is enabled.

    ReplyDelete
  6. Hello Charlie! Would you be able to take some close up photographs of both the IF Amp and the AF Amp completed boards? As I am very new to the hobby I it would help me greatly help to see the component placement on the board. I tried to zoom-in on the video but it still isn't very clear to me. Thank you in advance!

    ReplyDelete
  7. Thanks for adding the photos! Seeing your construction style up close is very helpful.

    ReplyDelete