Friday, 13 November 2020

Go QRP Portable SSB Rig

 


IF Amplifiers (1st and 2nd)







VFO, BFO Frequencies and AF Amplifier





LPF after RF PA


Receive-Transmit Switching



Microphone Amplifier





RF PA








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 = 10748350;          // High side injection thus SB inversion.
volatile long USB_BFO_freq = 10751750;          // 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
    }
  }
}

Thursday, 25 June 2020

Digital Modes Transceiver

Initial CAT Code. The radio emulates a Kenwood TS-480. (https://www.kenwood.com/i/products/info/amateur/ts_480/pdf/ts_480_pc.pdf)
Refer to the YouTube video for an explanation.




#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <SoftwareSerial.h>

int debug = 0;

const int numChars = 14;
volatile long freq = 0;
boolean newCATcmd = false;

char CATcmd[numChars] = {'0'};    // an array to store the received CAT data
int freq10GHz = 0;
int freq1GHz = 0;
int freq100MHz = 0;
int freq10MHz = 1;
int freq1MHz = 4;
int freq100kHz = 1;
int freq10kHz = 2;
int freq1kHz = 3;
int freq100Hz = 4;
int freq10Hz = 5;
int freq1Hz = 6;
int RIT, XIT, MEM1, MEM2, RX, TX, VFO, SCAN, SIMPLEX, CTCSS, TONE1, TONE2 = 0;
int MODE = 2;

SoftwareSerial mySerial(31, 32);      // RX, TX
LiquidCrystal_I2C lcd(0x3F, 16, 2);

void setup()
{
  lcd.begin();
  lcd.backlight();
  CalcFreq();

  Serial.begin(9600);

  // Debug serial port
  mySerial.begin(9600);
}

void loop()
{
  rxCATcmd();
  analyseCATcmd();
}

void rxCATcmd()
{
  int index = 0;
  char endMarker = ';';
  char data;                    // CAT commands are ASCII characters

  while ((Serial.available() > 0) && (newCATcmd == false))
  {
    data = Serial.read();

    if (data != endMarker)
    {
      CATcmd[index] = data;
      index++;

      if (index >= numChars)
        index = numChars - 1;   // leave space for the \0 array termination
    }
    else
    {
      CATcmd[index] = ';';      // Indicate end of command
      debug = index;            // Needed later to print out the command
      CATcmd[index + 1] = '\0'; // terminate the array
      index = 0;                // reset for next CAT command
      newCATcmd = true;
    }
  }
}

void analyseCATcmd()
{
  if (newCATcmd == true)
  {
    newCATcmd = false;        // reset for next CAT time

    // Debug printing
    mySerial.println();
    for (int x = 0; x <= debug; x++)
      mySerial.print(CATcmd[x]);
    mySerial.print("\t");

    if ((CATcmd[0] == 'F') && (CATcmd[1] == 'A') && (CATcmd[2] == ';'))              // must be freq get command
      Command_GETFreqA();

    else if ((CATcmd[0] == 'F') && (CATcmd[1] == 'A') && (CATcmd[13] == ';'))        // must be freq set command
      Command_SETFreqA();

    else if ((CATcmd[0] == 'I') && (CATcmd[1] == 'F') && (CATcmd[2] == ';'))
      Command_IF();

    else if ((CATcmd[0] == 'I') && (CATcmd[1] == 'D') && (CATcmd[2] == ';'))
      Command_ID();

    else if ((CATcmd[0] == 'P') && (CATcmd[1] == 'S') && (CATcmd[2] == ';'))
      Command_PS();

    else if ((CATcmd[0] == 'P') && (CATcmd[1] == 'S') && (CATcmd[2] == '1'))
      Command_PS1();

    else if ((CATcmd[0] == 'A') && (CATcmd[1] == 'I') && (CATcmd[2] == ';'))
      Command_AI();

    else if ((CATcmd[0] == 'A') && (CATcmd[1] == 'I') && (CATcmd[2] == '0'))
      Command_AI0();

    else if ((CATcmd[0] == 'M') && (CATcmd[1] == 'D') && (CATcmd[2] == ';'))
      Command_MD();

    else if ((CATcmd[0] == 'R') && (CATcmd[1] == 'X') && (CATcmd[2] == ';'))
      Command_RX();

    else if ((CATcmd[0] == 'T') && (CATcmd[1] == 'X') && (CATcmd[2] == ';'))
      Command_TX();

    else if ((CATcmd[0] == 'T') && (CATcmd[1] == 'X') && (CATcmd[2] == '1'))
      Command_TX1();

    else if ((CATcmd[0] == 'R') && (CATcmd[1] == 'S') && (CATcmd[2] == ';'))
      Command_RS();

    Serial.flush();       // Get ready for next command
    //delay(50);            // Needed to eliminate WSJT-X connection errors
  }
}

void Command_ID()
{
  Serial.print("ID020;");
  mySerial.print("ID020;");
}

void Command_PS()
{
  Serial.print("PS1;");
  mySerial.print("PS1;");
}

void Command_PS1()
{
  Serial.print("PS1;");
  mySerial.print("PS1;");
}

void Command_GETFreqA()
{
  Serial.print("FA");
  Serial.print(freq10GHz);
  Serial.print(freq1GHz);
  Serial.print(freq100MHz);
  Serial.print(freq10MHz);
  Serial.print(freq1MHz);
  Serial.print(freq100kHz);
  Serial.print(freq10kHz);
  Serial.print(freq1kHz);
  Serial.print(freq100Hz);
  Serial.print(freq10Hz);
  Serial.print(freq1Hz);
  Serial.print(";");

  mySerial.print("FA");
  mySerial.print(freq10GHz);
  mySerial.print(freq1GHz);
  mySerial.print(freq100MHz);
  mySerial.print(freq10MHz);
  mySerial.print(freq1MHz);
  mySerial.print(freq100kHz);
  mySerial.print(freq10kHz);
  mySerial.print(freq1kHz);
  mySerial.print(freq100Hz);
  mySerial.print(freq10Hz);
  mySerial.print(freq1Hz);
  mySerial.print(";");
}

void Command_SETFreqA()
{
  freq10GHz = CATcmd[2] - 48;       // convert ASCII char to int equivalent. int 0 = ASCII 48;
  freq1GHz = CATcmd[3] - 48;
  freq100MHz = CATcmd[4] - 48;
  freq10MHz = CATcmd[5] - 48;
  freq1MHz = CATcmd[6] - 48;
  freq100kHz = CATcmd[7] - 48;
  freq10kHz = CATcmd[8] - 48;
  freq1kHz = CATcmd[9] - 48;
  freq100Hz = CATcmd[10] - 48;
  freq10Hz = CATcmd[11] - 48;
  freq1Hz = CATcmd[12] - 48;

  //Command_GETFreqA();               // now RSP with FA

  Serial.print("FA");
  Serial.print(freq10GHz);
  Serial.print(freq1GHz);
  Serial.print(freq100MHz);
  Serial.print(freq10MHz);
  Serial.print(freq1MHz);
  Serial.print(freq100kHz);
  Serial.print(freq10kHz);
  Serial.print(freq1kHz);
  Serial.print(freq100Hz);
  Serial.print(freq10Hz);
  Serial.print(freq1Hz);
  Serial.print(";");

  mySerial.print("FA");
  mySerial.print(freq10GHz);
  mySerial.print(freq1GHz);
  mySerial.print(freq100MHz);
  mySerial.print(freq10MHz);
  mySerial.print(freq1MHz);
  mySerial.print(freq100kHz);
  mySerial.print(freq10kHz);
  mySerial.print(freq1kHz);
  mySerial.print(freq100Hz);
  mySerial.print(freq10Hz);
  mySerial.print(freq1Hz);
  mySerial.print(";");

  CalcFreq();
}

void CalcFreq()
{
  freq = (
                (10000000000L * freq10GHz) +
                (1000000000L * freq1GHz) +
                (100000000L * freq100MHz) +
                (10000000L * freq10MHz) +
                (1000000L * freq1MHz) +
                (100000L * freq100kHz) +
                (10000L * freq10kHz) +
                (1000L * freq1kHz) +
                (100L * freq100Hz) +
                (10L * freq10Hz) +
                freq1Hz);

  // Debug print
  lcd.setCursor(0, 1);
  lcd.print(freq10GHz);
  lcd.print(freq1GHz);
  lcd.print(freq100MHz);
  lcd.print(freq10MHz);
  lcd.print(freq1MHz);
  lcd.print(freq100kHz);
  lcd.print(freq10kHz);
  lcd.print(freq1kHz);
  lcd.print(freq100Hz);
  lcd.print(freq10Hz);
  lcd.print(freq1Hz);

  lcd.setCursor(0, 0);      // Col, Row
  lcd.print("                ");
  lcd.setCursor(0, 0);
  lcd.print(freq);
}

void Command_IF()
{
  Serial.print("IF");
  Serial.print(freq10GHz);        // P1
  Serial.print(freq1GHz);
  Serial.print(freq100MHz);
  Serial.print(freq10MHz);
  Serial.print(freq1MHz);
  Serial.print(freq100kHz);
  Serial.print(freq10kHz);
  Serial.print(freq1kHz);
  Serial.print(freq100Hz);
  Serial.print(freq10Hz);
  Serial.print(freq1Hz);
  Serial.print("00000");          // P2 Always five 0s
  Serial.print("+0000");          // P3 RIT/XIT freq +/-9990
  Serial.print(RIT);              // P4
  Serial.print(XIT);              // P5
  Serial.print("0");              // P6 Always 0
  Serial.print(MEM1);             // P7
  Serial.print(MEM2);
  Serial.print(RX);               // P8
  Serial.print(MODE);             // P9
  Serial.print(VFO);              // P10  FR/FT 0=VFO
  Serial.print(SCAN);             // P11
  Serial.print(SIMPLEX);          // P12
  Serial.print(CTCSS);            // P13
  Serial.print(TONE1);            // P14
  Serial.print(TONE2);
  Serial.print("0");              // P15 Always 0
  Serial.print(";");

  mySerial.print("IF");
  mySerial.print(freq10GHz);        // P1
  mySerial.print(freq1GHz);
  mySerial.print(freq100MHz);
  mySerial.print(freq10MHz);
  mySerial.print(freq1MHz);
  mySerial.print(freq100kHz);
  mySerial.print(freq10kHz);
  mySerial.print(freq1kHz);
  mySerial.print(freq100Hz);
  mySerial.print(freq10Hz);
  mySerial.print(freq1Hz);
  mySerial.print("00000");          // P2 Always five 0s
  mySerial.print("+0000");          // P3 RIT/XIT freq +/-9990
  mySerial.print(RIT);              // P4
  mySerial.print(XIT);              // P5
  mySerial.print("0");              // P6 Always 0
  mySerial.print(MEM1);             // P7
  mySerial.print(MEM2);
  mySerial.print(RX);               // P8
  mySerial.print(MODE);             // P9
  mySerial.print(VFO);              // P10  FR/FT 0=VFO
  mySerial.print(SCAN);             // P11
  mySerial.print(SIMPLEX);          // P12
  mySerial.print(CTCSS);            // P13
  mySerial.print(TONE1);            // P14
  mySerial.print(TONE2);
  mySerial.print("0");              // P15 Always 0
  mySerial.print(";");
}

void Command_AI()
{
  Serial.print("AI0;");
  mySerial.print("AI0;");
}

void Command_MD()
{
  Serial.print("MD2;");
  mySerial.print("MD2;");
}

void Command_AI0()
{
  Serial.print("AI0;");
  mySerial.print("AI0;");
}

void Command_RX()
{
  RX = 0;
  TX = 0;
  Serial.print("RX0;");
  mySerial.print("RX0;");
}

void Command_TX()
{
  RX = 1;
  TX = 1;
  Serial.print("TX0;");
  mySerial.print("TX0;");
}

void Command_TX1()
{
  RX = 1;
  TX = 1;
  Serial.print("TX2;");
  mySerial.print("TX2;");
}

void Command_RS()
{
  Serial.print("RS0;");
  mySerial.print("RS0;");
}


***********************************************************
Additional code to enable audio and serial data across the single USB connection. This is test code only at this stage.


#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <SoftwareSerial.h>
#include <Audio.h>                         // Teensy audio library

int debug = 0;

const int numChars = 14;
volatile long freq = 0;
boolean newCATcmd = false;

char CATcmd[numChars] = {'0'};    // an array to store the received CAT data
int freq10GHz = 0;
int freq1GHz = 0;
int freq100MHz = 0;
int freq10MHz = 1;
int freq1MHz = 4;
int freq100kHz = 1;
int freq10kHz = 2;
int freq1kHz = 3;
int freq100Hz = 4;
int freq10Hz = 5;
int freq1Hz = 6;
int RIT, XIT, MEM1, MEM2, RX, TX, VFO, SCAN, SIMPLEX, CTCSS, TONE1, TONE2 = 0;
int MODE = 2;

SoftwareSerial mySerial(31, 32);      // RX, TX
AudioControlSGTL5000    audioShield;

// Audio objects
AudioInputUSB           audioInputUSB;
AudioSynthWaveformSine  TestOsc;
AudioOutputUSB          audioOutputUSB;
AudioOutputI2S          audioOutputHeadphones;

// Audio connections
AudioConnection         patchCord5(TestOsc, 0, audioOutputUSB, 0);
AudioConnection         patchCord10(TestOsc, 0, audioOutputUSB, 1);
AudioConnection         patchCord15(audioInputUSB, 0, audioOutputHeadphones, 0);
AudioConnection         patchCord20(audioInputUSB, 1, audioOutputHeadphones, 1);


void setup()
{
  CalcFreq();

  Serial.begin(9600);
  mySerial.begin(9600);

  // Setup the audio shield
  AudioNoInterrupts();
  AudioMemory(12);
  audioShield.enable();
  TestOsc.amplitude(0.1);
  TestOsc.frequency(700);
  audioShield.unmuteHeadphone();                                              // Output to the audio amplifier
  audioShield.volume(0.8);
  AudioInterrupts();
}

void loop()
{
  rxCATcmd();
  analyseCATcmd();
}

void rxCATcmd()
{
  int index = 0;
  char endMarker = ';';
  char data;                    // CAT commands are ASCII characters

  while ((Serial.available() > 0) && (newCATcmd == false))
  {
    data = Serial.read();

    if (data != endMarker)
    {
      CATcmd[index] = data;
      index++;

      if (index >= numChars)
        index = numChars - 1;   // leave space for the \0 array termination
    }
    else
    {
      CATcmd[index] = ';';      // Indicate end of command
      debug = index;            // Needed later to print out the command
      CATcmd[index + 1] = '\0'; // terminate the array
      index = 0;                // reset for next CAT command
      newCATcmd = true;
    }
  }
}

void analyseCATcmd()
{
  if (newCATcmd == true)
  {
    newCATcmd = false;        // reset for next CAT time

    // Debug printing
    mySerial.println();
    for (int x = 0; x <= debug; x++)
      mySerial.print(CATcmd[x]);
    mySerial.print("\t");

    if ((CATcmd[0] == 'F') && (CATcmd[1] == 'A') && (CATcmd[2] == ';'))              // must be freq get command
      Command_GETFreqA();

    else if ((CATcmd[0] == 'F') && (CATcmd[1] == 'A') && (CATcmd[13] == ';'))        // must be freq set command
      Command_SETFreqA();

    else if ((CATcmd[0] == 'I') && (CATcmd[1] == 'F') && (CATcmd[2] == ';'))
      Command_IF();

    else if ((CATcmd[0] == 'I') && (CATcmd[1] == 'D') && (CATcmd[2] == ';'))
      Command_ID();

    else if ((CATcmd[0] == 'P') && (CATcmd[1] == 'S') && (CATcmd[2] == ';'))
      Command_PS();

    else if ((CATcmd[0] == 'P') && (CATcmd[1] == 'S') && (CATcmd[2] == '1'))
      Command_PS1();

    else if ((CATcmd[0] == 'A') && (CATcmd[1] == 'I') && (CATcmd[2] == ';'))
      Command_AI();

    else if ((CATcmd[0] == 'A') && (CATcmd[1] == 'I') && (CATcmd[2] == '0'))
      Command_AI0();

    else if ((CATcmd[0] == 'M') && (CATcmd[1] == 'D') && (CATcmd[2] == ';'))
      Command_MD();

    else if ((CATcmd[0] == 'R') && (CATcmd[1] == 'X') && (CATcmd[2] == ';'))
      Command_RX();

    else if ((CATcmd[0] == 'T') && (CATcmd[1] == 'X') && (CATcmd[2] == ';'))
      Command_TX();

    else if ((CATcmd[0] == 'T') && (CATcmd[1] == 'X') && (CATcmd[2] == '1'))
      Command_TX1();

    else if ((CATcmd[0] == 'R') && (CATcmd[1] == 'S') && (CATcmd[2] == ';'))
      Command_RS();

    Serial.flush();       // Get ready for next command
    //delay(50);            // Needed to eliminate WSJT-X connection errors
  }
}

void Command_ID()
{
  Serial.print("ID020;");
  mySerial.print("ID020;");
}

void Command_PS()
{
  Serial.print("PS1;");
  mySerial.print("PS1;");
}

void Command_PS1()
{
  Serial.print("PS1;");
  mySerial.print("PS1;");
}

void Command_GETFreqA()
{
  Serial.print("FA");
  Serial.print(freq10GHz);
  Serial.print(freq1GHz);
  Serial.print(freq100MHz);
  Serial.print(freq10MHz);
  Serial.print(freq1MHz);
  Serial.print(freq100kHz);
  Serial.print(freq10kHz);
  Serial.print(freq1kHz);
  Serial.print(freq100Hz);
  Serial.print(freq10Hz);
  Serial.print(freq1Hz);
  Serial.print(";");

  mySerial.print("FA");
  mySerial.print(freq10GHz);
  mySerial.print(freq1GHz);
  mySerial.print(freq100MHz);
  mySerial.print(freq10MHz);
  mySerial.print(freq1MHz);
  mySerial.print(freq100kHz);
  mySerial.print(freq10kHz);
  mySerial.print(freq1kHz);
  mySerial.print(freq100Hz);
  mySerial.print(freq10Hz);
  mySerial.print(freq1Hz);
  mySerial.print(";");
}

void Command_SETFreqA()
{
  freq10GHz = CATcmd[2] - 48;       // convert ASCII char to int equivalent. int 0 = ASCII 48;
  freq1GHz = CATcmd[3] - 48;
  freq100MHz = CATcmd[4] - 48;
  freq10MHz = CATcmd[5] - 48;
  freq1MHz = CATcmd[6] - 48;
  freq100kHz = CATcmd[7] - 48;
  freq10kHz = CATcmd[8] - 48;
  freq1kHz = CATcmd[9] - 48;
  freq100Hz = CATcmd[10] - 48;
  freq10Hz = CATcmd[11] - 48;
  freq1Hz = CATcmd[12] - 48;

  //Command_GETFreqA();               // now RSP with FA

  Serial.print("FA");
  Serial.print(freq10GHz);
  Serial.print(freq1GHz);
  Serial.print(freq100MHz);
  Serial.print(freq10MHz);
  Serial.print(freq1MHz);
  Serial.print(freq100kHz);
  Serial.print(freq10kHz);
  Serial.print(freq1kHz);
  Serial.print(freq100Hz);
  Serial.print(freq10Hz);
  Serial.print(freq1Hz);
  Serial.print(";");

  mySerial.print("FA");
  mySerial.print(freq10GHz);
  mySerial.print(freq1GHz);
  mySerial.print(freq100MHz);
  mySerial.print(freq10MHz);
  mySerial.print(freq1MHz);
  mySerial.print(freq100kHz);
  mySerial.print(freq10kHz);
  mySerial.print(freq1kHz);
  mySerial.print(freq100Hz);
  mySerial.print(freq10Hz);
  mySerial.print(freq1Hz);
  mySerial.print(";");

  CalcFreq();
}

void CalcFreq()
{
  freq = (
           (10000000000L * freq10GHz) +
           (1000000000L * freq1GHz) +
           (100000000L * freq100MHz) +
           (10000000L * freq10MHz) +
           (1000000L * freq1MHz) +
           (100000L * freq100kHz) +
           (10000L * freq10kHz) +
           (1000L * freq1kHz) +
           (100L * freq100Hz) +
           (10L * freq10Hz) +
           freq1Hz);
}

void Command_IF()
{
  Serial.print("IF");
  Serial.print(freq10GHz);        // P1
  Serial.print(freq1GHz);
  Serial.print(freq100MHz);
  Serial.print(freq10MHz);
  Serial.print(freq1MHz);
  Serial.print(freq100kHz);
  Serial.print(freq10kHz);
  Serial.print(freq1kHz);
  Serial.print(freq100Hz);
  Serial.print(freq10Hz);
  Serial.print(freq1Hz);
  Serial.print("00000");          // P2 Always five 0s
  Serial.print("+0000");          // P3 RIT/XIT freq +/-9990
  Serial.print(RIT);              // P4
  Serial.print(XIT);              // P5
  Serial.print("0");              // P6 Always 0
  Serial.print(MEM1);             // P7
  Serial.print(MEM2);
  Serial.print(RX);               // P8
  Serial.print(MODE);             // P9
  Serial.print(VFO);              // P10  FR/FT 0=VFO
  Serial.print(SCAN);             // P11
  Serial.print(SIMPLEX);          // P12
  Serial.print(CTCSS);            // P13
  Serial.print(TONE1);            // P14
  Serial.print(TONE2);
  Serial.print("0");              // P15 Always 0
  Serial.print(";");

  mySerial.print("IF");
  mySerial.print(freq10GHz);        // P1
  mySerial.print(freq1GHz);
  mySerial.print(freq100MHz);
  mySerial.print(freq10MHz);
  mySerial.print(freq1MHz);
  mySerial.print(freq100kHz);
  mySerial.print(freq10kHz);
  mySerial.print(freq1kHz);
  mySerial.print(freq100Hz);
  mySerial.print(freq10Hz);
  mySerial.print(freq1Hz);
  mySerial.print("00000");          // P2 Always five 0s
  mySerial.print("+0000");          // P3 RIT/XIT freq +/-9990
  mySerial.print(RIT);              // P4
  mySerial.print(XIT);              // P5
  mySerial.print("0");              // P6 Always 0
  mySerial.print(MEM1);             // P7
  mySerial.print(MEM2);
  mySerial.print(RX);               // P8
  mySerial.print(MODE);             // P9
  mySerial.print(VFO);              // P10  FR/FT 0=VFO
  mySerial.print(SCAN);             // P11
  mySerial.print(SIMPLEX);          // P12
  mySerial.print(CTCSS);            // P13
  mySerial.print(TONE1);            // P14
  mySerial.print(TONE2);
  mySerial.print("0");              // P15 Always 0
  mySerial.print(";");
}

void Command_AI()
{
  Serial.print("AI0;");
  mySerial.print("AI0;");
}

void Command_MD()
{
  Serial.print("MD2;");
  mySerial.print("MD2;");
}

void Command_AI0()
{
  Serial.print("AI0;");
  mySerial.print("AI0;");
}

void Command_RX()
{
  RX = 0;
  TX = 0;
  Serial.print("RX0;");
  mySerial.print("RX0;");
}

void Command_TX()
{
  RX = 1;
  TX = 1;
  Serial.print("TX0;");
  mySerial.print("TX0;");
}

void Command_TX1()
{
  RX = 1;
  TX = 1;
  Serial.print("TX2;");
  mySerial.print("TX2;");
}

void Command_RS()
{
  Serial.print("RS0;");
  mySerial.print("RS0;");
}


*************************************************************************

RF Splitter/Combiner


Sunday, 3 May 2020

Shortwave Receiver

VFO











BPF, RF Amplifier, IF Amplifier, AM Envelope Detector







Final detector was a infinite impedance (J310).


Note: there is another 10nF cap across the 47uF decoupling cap (not shown).




Saturday, 21 March 2020

Homebrew CW Rig

Please see the YouTube videos for an explanation on the code and circuits:
https://www.youtube.com/channel/UCSNPW3_gzuMJcX_ErBZTv2g/

Crystal Filter





IF Amps









Antenna Amp, 700Hz LPF


Initial Class E RF Amplifier Experiment (see YouTube video for explanation)




Final Class E Amplifier












Code

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

const long band80mStart = 3500000;    // start of 80m
const long band80mEnd = 3900000;      // end of 80m
volatile uint32_t FilterCentreFreq = 8996500;
volatile uint32_t BFO = 8997243;      // Filter centre freq plus offset to get 700Hz AF out.

volatile long freq = 3525000;         // this is a variable (changes) - set it to the beginning of the band
volatile long radix = 100;            // how much to change the frequency by, clicking the rotary encoder will change this.
volatile long oldfreq = 0;
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 pushPin = 4;

// 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;

// 700Hz Osc
static const int Osc = 11;
static const int TuneSw = 9;

// Morse Key
static const int Key = 8;
unsigned long BreakInTimer;
unsigned long BreakInDelay = 1000;    //1000mS - 1.0Sec

// Relays
static const int AntRelay = 7;
static const int PowerRelay = 6;

void setup()
{
  // Set up I/O Pins
  pinMode(rotAPin, INPUT_PULLUP);
  pinMode(rotBPin, INPUT_PULLUP);
  pinMode(pushPin, INPUT_PULLUP);
  pinMode(TuneSw, INPUT_PULLUP);
  pinMode(Key, INPUT_PULLUP);
  pinMode(AntRelay, OUTPUT);
  pinMode(PowerRelay, OUTPUT);
  pinMode(Osc, OUTPUT);

  digitalWrite(AntRelay, HIGH);         // Default to Rx
  digitalWrite(PowerRelay, HIGH);       // Default to Rx

  // 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 DDS
  si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);
  si5351.set_correction(-400, 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.output_enable(SI5351_CLK1, 0);                   // Turn off CW carrier

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


void loop()
{
  if (digitalRead(Key) == LOW)                // Check to see if CW key down
  {
    BreakInTimer = millis();                  // Reset the break in timer
    digitalWrite(AntRelay, LOW);              // Swap over the antenna to Tx
    digitalWrite(PowerRelay, LOW);            // Rx 12VDC = 0, Tx 12VDC = 12
    delay(25);                                // Allow time for relays to swap over (Spec sheet 10mS)
    si5351.output_enable(SI5351_CLK1, 1);     // Get ready to transmit CW carrier
    SendFrequency();                          // Send CW carrier
  }
  else                                        // Key up
  {
    si5351.output_enable(SI5351_CLK1, 0);     // Turn off the CW carrier
  }

  if ((millis() - BreakInTimer) >= BreakInDelay)    // Check break in timer
  {
    digitalWrite(AntRelay, HIGH);             // Swap over the antenna to Rx
    digitalWrite(PowerRelay, HIGH);           // Rx 12VDC = 12, Tx 12VDC = 0
  }

  if (digitalRead(TuneSw) == LOW)             // Check tune switch
    tone(Osc, 700);                           // Turn on the 700Hz tome
  else
    noTone(Osc);                              // Turn off the 700Hz tome

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

  // BFO
  si5351.set_freq((BFO * 100ULL), SI5351_CLK2);

  // Transmit CW carrier
  si5351.set_freq(((freq - 43)  * 100ULL), SI5351_CLK1);         // 43Hz to align Tx and Rx
}

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