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
Thursday, 25 June 2020
Sunday, 3 May 2020
Shortwave Receiver
VFO
BPF, RF Amplifier, IF Amplifier, AM Envelope Detector
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
}
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);
}
Subscribe to:
Posts (Atom)