As per the YouTube videos.
Sunday, 11 November 2018
Thursday, 1 November 2018
Homebrew Antenna/Scaler Network Analyser
Final software for the antenna/SNA. It's not perfect, but works for me.
#include <UTFTGLUE.h>
#include <SPI.h>
#include <Wire.h>
#include <DDS.h>
#include <Keypad.h>
// Keypad
char keys[4][3] = {
{'1', '2', '3'},
{'4', '5', '6'},
{'7', '8', '9'},
{'*', '0', '#'}
};
byte rowPins[4] = {35, 45, 43, 39}; //connect to the row pinouts of the keypad
byte colPins[3] = {37, 33, 41}; // connect to the column pinouts of the keypad
// AD9850 pins
const int DATA = 47;
const int W_CLK = 49;
const int FQ_UD = 51;
const int RESET = 53;
// SWR bridge pins
const int fwdAntVoltageInput = A15;
const int revAntVoltageInput = A14;
// AD8307 pin
const int SNAVoltageInput = A12;
// Other constants and variables
const int NoBins = 320;
volatile long freq = 7123000;
volatile long Spotfreq = 7123000;
volatile long oldSpotfreq = 7123000;
volatile long UserLowerfreq = 8995000;
volatile long UserUpperfreq = 9005000;
volatile float fwdVoltage = 0;
volatile float revVoltage = 0;
volatile float MaxrevVoltage = 0;
volatile float OldMaxrevVoltage = 0;
volatile float MinrevVoltage = 0;
volatile float OldMinrevVoltage = 0;
volatile float Ripple = 0;
volatile float SWR = 10;
volatile float oldSWR = 0;
volatile long SWRFreq = 0;
volatile float LowestSWR = 10;
volatile int Cal = 0;
volatile long UserFreq = 0;
volatile long oldUserFreq = 0;
volatile boolean EntryComplete = false;
volatile boolean FreqEntered = false;
volatile long UserFreqArray[8] = {0};
volatile float TestArray[NoBins] = {0};
volatile float CalArray[NoBins] = {0};
// Instantiate the Objects
DDS dds(W_CLK, FQ_UD, DATA, RESET);
UTFTGLUE myGLCD(0, A2, A1, A3, A4, A0);
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, 4, 3 );
void setup()
{
Serial.begin(19200);
pinMode(fwdAntVoltageInput, INPUT);
pinMode(revAntVoltageInput, INPUT);
pinMode(SNAVoltageInput, INPUT);
pinMode(5, OUTPUT);
pinMode(4, OUTPUT);
pinMode(3, OUTPUT);
pinMode(2, OUTPUT);
pinMode(8, INPUT_PULLUP);
pinMode(7, INPUT_PULLUP);
pinMode(6, INPUT_PULLUP);
analogReference(INTERNAL2V56); // Built-in 2.56V reference
//Set up display
myGLCD.InitLCD();
myGLCD.clrScr();
myGLCD.setRotation(3);
DisplayMainMenu();
// Initialize the AD9850
dds.init();
dds.trim(125000000); // (Optional) trim if your xtal is not at 125MHz...
}
void loop()
{
char key = keypad.getKey();
if (key != NO_KEY)
{
switch (key) //switch used to specify which button
{
case '1':
EntryComplete = false;
DisplayAntSpotFreqMenu();
break;
case '2':
EntryComplete = false;
DisplayHFMenu();
break;
case '3':
EntryComplete = false;
DisplayHamMenu();
break;
case '4':
EntryComplete = false;
DisplayUserScanMenu();
break;
case '5':
EntryComplete = false;
DisplaySNAMenu();
break;
case '*':
EntryComplete = false;
DisplayMainMenu();
break;
default:
;
}
}
}
void DisplayMainMenu()
{
myGLCD.clrScr();
myGLCD.setTextSize(2);
myGLCD.setColor(0, 0, 255);
myGLCD.drawRect(0, 0, 319, 239);
myGLCD.fillRect(0, 0, 319, 30);
myGLCD.setColor(255, 255, 0);
myGLCD.setBackColor(0, 0, 255);
myGLCD.print("ZL2CTM Ant Analyser/SNA", 20, 8);
myGLCD.setColor(255, 255, 255);
myGLCD.setBackColor(0, 0, 0);
myGLCD.print("1 Spot Frequency", 30, 60);
myGLCD.print("2 HF Spectrum Scan", 30, 80);
myGLCD.print("3 Ham Band Scan", 30, 100);
myGLCD.print("4 User Scan", 30, 120);
myGLCD.print("5 Scaler Network", 30, 140);
myGLCD.print(" Analyser", 30, 160);
myGLCD.print("Select option...", 30, 200);
}
void DisplayAntSpotFreqMenu()
{
myGLCD.clrScr();
myGLCD.setColor(0, 0, 255);
myGLCD.drawRect(0, 0, 319, 239);
myGLCD.setTextSize(2);
myGLCD.setColor(255, 255, 255);
myGLCD.print("1 Enter frequency", 30, 20);
myGLCD.print("2 Start", 30, 40);
myGLCD.print("* Main menu", 30, 60);
myGLCD.print("Select option...", 30, 100);
myGLCD.setColor(255, 255, 0);
myGLCD.print("Freq:", 30, 160);
myGLCD.printNumI(Spotfreq, 110, 160);
while (EntryComplete == false)
{
char key = keypad.getKey();
if (key != NO_KEY)
{
switch (key) //switch used to specify which button
{
case '1':
EntryComplete = false;
Spotfreq = EnterUserFreq();
DisplayAntSpotFreqMenu();
break;
case '2':
EntryComplete = true;
AntSpotFreqScan(Spotfreq);
break;
case '*':
EntryComplete = true;
DisplayMainMenu();
break;
default:
;
}
}
}
}
void DisplayHFMenu()
{
myGLCD.clrScr();
myGLCD.setColor(0, 0, 255);
myGLCD.drawRect(0, 0, 319, 239);
myGLCD.setTextSize(2);
myGLCD.setColor(255, 255, 255);
myGLCD.print("1 Full HF spectrum", 30, 20);
myGLCD.print("2 2000-10000 kHz", 30, 40);
myGLCD.print("3 10000-20000 kHz", 30, 60);
myGLCD.print("4 20000-30000 kHz", 30, 80);
myGLCD.print("* Main menu", 30, 100);
myGLCD.print("Select option...", 30, 140);
while (EntryComplete == false)
{
char key = keypad.getKey();
if (key != NO_KEY)
{
switch (key) //switch used to specify which button
{
case '1':
EntryComplete = true;
SweepAnt(2000000, 30000000);
break;
case '2':
EntryComplete = true;
SweepAnt(2000000, 10000000);
break;
case '3':
EntryComplete = true;
SweepAnt(10000000, 20000000);
break;
case '4':
EntryComplete = true;
SweepAnt(20000000, 30000000);
break;
case '*':
EntryComplete = true;
DisplayMainMenu();
break;
default:
;
}
}
}
}
void DisplayHamMenu()
{
myGLCD.clrScr();
myGLCD.setColor(0, 0, 255);
myGLCD.drawRect(0, 0, 319, 239);
myGLCD.setTextSize(2);
myGLCD.setColor(255, 255, 255);
myGLCD.print("1 80m (3500-3900)", 30, 20);
myGLCD.print("2 40m (7000-7300)", 30, 40);
myGLCD.print("3 30m (10100-10150)", 30, 60);
myGLCD.print("4 20m (14000-14350)", 30, 80);
myGLCD.print("5 17m (18068-18168)", 30, 100);
myGLCD.print("6 15m (21000-21450)", 30, 120);
myGLCD.print("7 12m (24890-24990)", 30, 140);
myGLCD.print("8 10m (28000-29700)", 30, 160);
myGLCD.print("* Main menu", 30, 180);
myGLCD.print("Select option...", 30, 215);
while (EntryComplete == false)
{
char key = keypad.getKey();
if (key != NO_KEY)
{
switch (key) //switch used to specify which button
{
case '1':
EntryComplete = true;
SweepAnt(3500000, 3900000);
break;
case '2':
EntryComplete = true;
SweepAnt(7000000, 7300000);
break;
case '3':
EntryComplete = true;
SweepAnt(10100000, 10150000);
break;
case '4':
EntryComplete = true;
SweepAnt(14000000, 14500000);
break;
case '5':
EntryComplete = true;
SweepAnt(18068000, 18168000);
break;
case '6':
EntryComplete = true;
SweepAnt(21000000, 21450000);
break;
case '7':
EntryComplete = true;
SweepAnt(24890000, 24990000);
break;
case '8':
EntryComplete = true;
SweepAnt(28000000, 29700000);
break;
case '*':
EntryComplete = true;
DisplayMainMenu();
break;
default:
;
}
}
}
}
void DisplayUserScanMenu()
{
myGLCD.clrScr();
myGLCD.setColor(0, 0, 255);
myGLCD.drawRect(0, 0, 319, 239);
myGLCD.setTextSize(2);
myGLCD.setColor(255, 255, 255);
myGLCD.print("1 Enter lower freq", 30, 20);
myGLCD.print("2 Enter upper freq", 30, 40);
myGLCD.print("3 Start scan", 30, 60);
myGLCD.print("* Main menu", 30, 80);
myGLCD.print("Select option...", 30, 120);
myGLCD.setColor(255, 255, 0);
myGLCD.print("Lower:", 30, 160);
myGLCD.print("Upper:", 30, 180);
myGLCD.printNumI(UserLowerfreq, 120, 160);
myGLCD.printNumI(UserUpperfreq, 120, 180);
while (EntryComplete == false)
{
char key = keypad.getKey();
if (key != NO_KEY)
{
switch (key) //switch used to specify which button
{
case '1':
EntryComplete = false;
UserLowerfreq = EnterUserFreq();
DisplayUserScanMenu();
break;
case '2':
EntryComplete = false;
UserUpperfreq = EnterUserFreq();
DisplayUserScanMenu();
break;
case '3':
EntryComplete = true;
if (UserLowerfreq < UserUpperfreq)
SweepAnt(UserLowerfreq, UserUpperfreq);
else
SweepAnt(UserUpperfreq, UserLowerfreq);
break;
case '*':
EntryComplete = true;
DisplayMainMenu();
break;
default:
;
}
}
}
}
void DisplaySNAMenu()
{
myGLCD.clrScr();
myGLCD.setColor(0, 0, 255);
myGLCD.drawRect(0, 0, 319, 239);
myGLCD.setTextSize(2);
myGLCD.setColor(255, 255, 255);
myGLCD.print("1 Enter lower freq", 30, 20);
myGLCD.print("2 Enter upper freq", 30, 40);
myGLCD.print("3 Calibrate", 30, 60);
myGLCD.print("4 Single Scan", 30, 80);
myGLCD.print("5 Continuous Scan", 30, 100);
myGLCD.print("* Main menu", 30, 120);
myGLCD.print("Select option...", 30, 160);
myGLCD.setColor(255, 255, 0);
myGLCD.print("Lower:", 30, 190);
myGLCD.print("Upper:", 30, 210);
myGLCD.printNumI(UserLowerfreq, 120, 190);
myGLCD.printNumI(UserUpperfreq, 120, 210);
while (EntryComplete == false)
{
char key = keypad.getKey();
if (key != NO_KEY)
{
switch (key) //switch used to specify which button
{
case '1':
EntryComplete = false;
UserLowerfreq = EnterUserFreq();
DisplaySNAMenu();
break;
case '2':
EntryComplete = false;
UserUpperfreq = EnterUserFreq();
DisplaySNAMenu();
break;
case '3':
EntryComplete = false;
if (UserLowerfreq < UserUpperfreq)
Calibrate(UserLowerfreq, UserUpperfreq);
else
Calibrate(UserUpperfreq, UserLowerfreq);
DisplaySNAMenu();
break;
case '4':
if (Cal == 0)
{
EntryComplete = false;
myGLCD.clrScr();
myGLCD.setColor(0, 0, 255);
myGLCD.drawRect(0, 0, 319, 239);
myGLCD.setTextSize(2);
myGLCD.setColor(255, 255, 255);
myGLCD.print("Calibrate first", 60, 100);
delay(1500);
DisplaySNAMenu();
}
else
{
EntryComplete = true;
if (UserLowerfreq < UserUpperfreq)
SingleSweepSNA(UserLowerfreq, UserUpperfreq);
else
SingleSweepSNA(UserUpperfreq, UserLowerfreq);
}
break;
case '5':
EntryComplete = true;
if (UserLowerfreq < UserUpperfreq)
MultipleSweepSNA(UserLowerfreq, UserUpperfreq);
else
MultipleSweepSNA(UserUpperfreq, UserLowerfreq);
break;
case '*':
EntryComplete = true;
DisplayMainMenu();
break;
default:
;
}
}
}
}
long EnterUserFreq()
{
myGLCD.clrScr();
myGLCD.setColor(0, 0, 255);
myGLCD.drawRect(0, 0, 319, 239);
myGLCD.setTextSize(2);
myGLCD.setColor(255, 255, 255);
myGLCD.print("Enter freq between", 40, 20);
myGLCD.print("2MHz and 30MHz", 40, 40);
myGLCD.print("then press #", 40, 60);
myGLCD.setColor(105, 105, 105);
myGLCD.setTextSize(3);
myGLCD.print("8", 40, 120);
myGLCD.print("8", 65, 120);
myGLCD.print("8", 90, 120);
myGLCD.print("8", 115, 120);
myGLCD.print("8", 140, 120);
myGLCD.print("8", 165, 120);
myGLCD.print("8", 190, 120);
myGLCD.print("*", 215, 120);
FreqEntered = false;
int x = 0;
for (int y = 0; y <= 7; y++)
UserFreqArray[y] = 0;
while (FreqEntered == false)
{
char key = keypad.getKey();
if (key != NO_KEY)
{
if (key == '#')
{
FreqEntered = true;
if (x == 8)
UserFreq = ((UserFreqArray[0] * 10000000) + (UserFreqArray[1] * 1000000) + (UserFreqArray[2] * 100000) +
(UserFreqArray[3] * 10000) + (UserFreqArray[4] * 1000) + (UserFreqArray[5] * 100) + (UserFreqArray[6] * 10) + UserFreqArray[7]);
if (x == 7)
UserFreq = ((UserFreqArray[0] * 1000000) + (UserFreqArray[1] * 100000) + (UserFreqArray[2] * 10000) +
(UserFreqArray[3] * 1000) + (UserFreqArray[4] * 100) + (UserFreqArray[5] * 10) + UserFreqArray[6]);
if ((x < 7) || (UserFreq < 2000000) || (UserFreq > 30000000))
{
myGLCD.clrScr();
myGLCD.setColor(0, 0, 255);
myGLCD.drawRect(0, 0, 319, 239);
myGLCD.setTextSize(2);
myGLCD.setColor(255, 255, 255);
myGLCD.print("Out of bounds", 80, 100);
UserFreq = oldUserFreq;
delay(1500);
EnterUserFreq();
}
oldUserFreq = UserFreq;
}
else
{
UserFreqArray[x] = (key - 48);
myGLCD.setColor(0, 255, 0);
myGLCD.setTextSize(3);
switch (x)
{
case 0:
myGLCD.printNumI(UserFreqArray[0], 40, 120);
break;
case 1:
myGLCD.printNumI(UserFreqArray[0], 40, 120);
myGLCD.printNumI(UserFreqArray[1], 65, 120);
break;
case 2:
myGLCD.printNumI(UserFreqArray[0], 40, 120);
myGLCD.printNumI(UserFreqArray[1], 65, 120);
myGLCD.printNumI(UserFreqArray[2], 90, 120);
break;
case 3:
myGLCD.printNumI(UserFreqArray[0], 40, 120);
myGLCD.printNumI(UserFreqArray[1], 65, 120);
myGLCD.printNumI(UserFreqArray[2], 90, 120);
myGLCD.printNumI(UserFreqArray[3], 115, 120);
break;
case 4:
myGLCD.printNumI(UserFreqArray[0], 40, 120);
myGLCD.printNumI(UserFreqArray[1], 65, 120);
myGLCD.printNumI(UserFreqArray[2], 90, 120);
myGLCD.printNumI(UserFreqArray[3], 115, 120);
myGLCD.printNumI(UserFreqArray[4], 140, 120);
break;
case 5:
myGLCD.printNumI(UserFreqArray[0], 40, 120);
myGLCD.printNumI(UserFreqArray[1], 65, 120);
myGLCD.printNumI(UserFreqArray[2], 90, 120);
myGLCD.printNumI(UserFreqArray[3], 115, 120);
myGLCD.printNumI(UserFreqArray[4], 140, 120);
myGLCD.printNumI(UserFreqArray[5], 165, 120);
break;
case 6:
myGLCD.printNumI(UserFreqArray[0], 40, 120);
myGLCD.printNumI(UserFreqArray[1], 65, 120);
myGLCD.printNumI(UserFreqArray[2], 90, 120);
myGLCD.printNumI(UserFreqArray[3], 115, 120);
myGLCD.printNumI(UserFreqArray[4], 140, 120);
myGLCD.printNumI(UserFreqArray[5], 165, 120);
myGLCD.printNumI(UserFreqArray[6], 190, 120);
break;
case 7:
myGLCD.printNumI(UserFreqArray[0], 40, 120);
myGLCD.printNumI(UserFreqArray[1], 65, 120);
myGLCD.printNumI(UserFreqArray[2], 90, 120);
myGLCD.printNumI(UserFreqArray[3], 115, 120);
myGLCD.printNumI(UserFreqArray[4], 140, 120);
myGLCD.printNumI(UserFreqArray[5], 165, 120);
myGLCD.printNumI(UserFreqArray[6], 190, 120);
myGLCD.printNumI(UserFreqArray[7], 215, 120);
break;
default:
;
}
x++;
}
}
}
return UserFreq;
}
void AntSpotFreqScan(long userfrequency)
{
EntryComplete = false;
myGLCD.clrScr();
myGLCD.setColor(0, 0, 255);
myGLCD.drawRect(0, 0, 319, 239);
dds.setFrequency(userfrequency);
delay(5);
myGLCD.setTextSize(1);
myGLCD.setColor(0, 255, 0);
myGLCD.fillRect(45, 35, 73, 40);
myGLCD.setColor(255, 255, 0);
myGLCD.fillRect(74, 35, 160, 40);
myGLCD.setColor(255, 0, 0);
myGLCD.fillRect(161, 35, 275, 40);
myGLCD.setColor(255, 255, 255);
myGLCD.drawRect(45, 34, 275, 200);
myGLCD.print("1", 45, 20);
myGLCD.print("1.5", 73, 20);
myGLCD.print("3", 160, 20);
myGLCD.print("5", 192, 20);
myGLCD.print("10", 265, 20);
myGLCD.setTextSize(2);
while (EntryComplete == false)
{
fwdVoltage = analogRead(fwdAntVoltageInput);
revVoltage = analogRead(revAntVoltageInput);
if (revVoltage >= fwdVoltage)
revVoltage = fwdVoltage - 1;
if (revVoltage < 1)
revVoltage = 1;
SWR = (fwdVoltage + revVoltage) / (fwdVoltage - revVoltage);
myGLCD.setColor(0, 0, 0);
if (oldSWR <= 1)
myGLCD.drawLine(160, 200, 46, 41);
if (oldSWR == 3)
myGLCD.drawLine(160, 200, 160, 41);
if (oldSWR >= 10)
myGLCD.drawLine(160, 200, 274, 41);
if ((oldSWR < 3) && (oldSWR > 1))
myGLCD.drawLine(160, 200, (45 + ((oldSWR - 1) * 57.5)), 41);
if ((oldSWR > 3) && (oldSWR < 10))
myGLCD.drawLine(160, 200, (160 + (oldSWR - 3) / 7 * 115), 41);
myGLCD.printNumF(oldSWR, 2, 125, 212); // Print Floating Number 2 decimal places
if (SWR <= 1.5)
myGLCD.setColor(0, 255, 0);
if ((SWR > 1.5) && (SWR <= 3))
myGLCD.setColor(255, 255, 0);
if (SWR > 3)
myGLCD.setColor(255, 0, 0);
if (SWR <= 1)
myGLCD.drawLine(160, 200, 46, 41);
if (SWR == 3)
myGLCD.drawLine(160, 200, 160, 41);
if (SWR >= 10)
myGLCD.drawLine(160, 200, 274, 41);
if ((SWR < 3) && (SWR > 1))
myGLCD.drawLine(160, 200, (45 + ((SWR - 1) * 57.5)), 41);
if ((SWR > 3) && (SWR < 10))
myGLCD.drawLine(160, 200, (160 + (SWR - 3) / 7 * 115), 41);
myGLCD.printNumF(SWR, 2, 125, 212); // Print Floating Number 2 decimal places
oldSWR = SWR;
char key = keypad.getKey();
if (key != NO_KEY) {
switch (key) //switch used to specify which button
{
case '*':
EntryComplete = true;
DisplayMainMenu();
break;
default:
;
}
}
delay(30);
}
dds.init();
dds.trim(125000000);
}
void SweepAnt(long StartFreq, long StopFreq)
{
LowestSWR = 100;
SWRFreq = 0;
myGLCD.clrScr();
myGLCD.setColor(0, 0, 255);
myGLCD.drawRect(0, 0, 319, 239);
myGLCD.setTextSize(2);
myGLCD.setColor(255, 255, 255);
myGLCD.print("Analysing...", 80, 100);
int x = 0;
for (freq = StartFreq; freq <= StopFreq; freq = freq + (((StopFreq - StartFreq) / NoBins)))
{
dds.setFrequency(freq);
delay(10); // Let DDS settle then take SWR readings
fwdVoltage = analogRead(fwdAntVoltageInput);
revVoltage = analogRead(revAntVoltageInput);
if (revVoltage >= fwdVoltage)
revVoltage = fwdVoltage - 1;
if (revVoltage < 1)
revVoltage = 1;
SWR = (fwdVoltage + revVoltage) / (fwdVoltage - revVoltage);
TestArray[x] = SWR;
if (SWR < LowestSWR)
{
LowestSWR = SWR;
SWRFreq = freq;
}
x++;
}
PlotSWR(StartFreq, StopFreq);
}
void Calibrate(long StartFreq, long StopFreq)
{
int x = 0;
float MaxCalVoltage = 0;
long freq = 0;
for (x = 0; x < NoBins; x++)
CalArray[x] = 0;
x = 0;
for (freq = StartFreq; freq <= StopFreq; freq = freq + ((StopFreq - StartFreq) / NoBins))
{
dds.setFrequency(freq);
CalArray[x] = analogRead(SNAVoltageInput);
x++;
}
for (x = 20; x < NoBins; x++) // Start at 20 to skip initial high errors
if (CalArray[x] > MaxCalVoltage) // Determine max calibration voltage
MaxCalVoltage = CalArray[x];
for (x = 0; x < NoBins; x++)
CalArray[x] = MaxCalVoltage - CalArray[x]; // Populate cal array with error offset values
Cal = 1;
}
void SingleSweepSNA(long StartFreq, long StopFreq)
{
int x = 0;
float MinTestdB = 0;
long freq = 0;
for (x = 0; x < NoBins; x++)
TestArray[x] = 0;
x = 0;
for (freq = StartFreq; freq <= StopFreq; freq = freq + ((StopFreq - StartFreq) / NoBins))
{
dds.setFrequency(freq);
TestArray[x] = analogRead(SNAVoltageInput) + CalArray[x]; // test array has corrected raw analog read voltages from 0 to 1024 (0 to 2.56 volts)
x++;
}
for (x = 0; x < NoBins; x++) // 2.56V / 25mV/dB = 102.4dB range.
TestArray[x] = 102.4 - (TestArray[x] * 102.4 / 1024); // test array now has dB levels from 0dB (2.56V = 1024) to 102dB (0V = 0)
PlotSNA();
}
void MultipleSweepSNA(long StartFreq, long StopFreq)
{
int x = 0;
float TestSNAReading = 0;
EntryComplete = false;
myGLCD.clrScr();
myGLCD.setColor(255, 255, 255);
while (EntryComplete == false)
{
for (freq = StartFreq; freq <= StopFreq; freq = freq + ((StopFreq - StartFreq) / NoBins))
{
dds.setFrequency(freq);
TestSNAReading = analogRead(SNAVoltageInput);
TestSNAReading = 102.4 - (TestSNAReading * 102.4 / 1024);
myGLCD.drawPixel(x, TestSNAReading * (240 / 102));
char key = keypad.getKey();
if (key != NO_KEY)
{
switch (key) //switch used to specify which button
{
case '*':
EntryComplete = true;
break;
default:
;
}
}
x++;
}
x = 0;
myGLCD.clrScr();
}
dds.init();
dds.trim(125000000);
DisplayMainMenu();
}
void PlotSWR(long StartFreq, long StopFreq)
{
int ax = 0;
int bx = 0;
float ay = 10;
float by = 10;
dds.init();
dds.trim(125000000);
myGLCD.clrScr();
ay = TestArray[0];
if (by > 10)
ay = 10;
ax = 0;
for (bx = 0; bx < NoBins; bx++)
{
by = TestArray[bx];
if (by > 10)
by = 10;
if (by > 3)
myGLCD.setColor(255, 0, 0); // red
else if (by <= 1.5)
myGLCD.setColor(0, 255, 0); // green
else
myGLCD.setColor(255, 255, 0); // yellow
myGLCD.drawLine(ax, 240 - (ay * 24), bx, 240 - (by * 24));
ax = bx;
ay = by;
}
myGLCD.setFont(1);
myGLCD.setColor(255, 255, 255);
myGLCD.printNumI(StartFreq / 1000, 5, 220);
myGLCD.printNumI(StopFreq / 1000, 278, 220);
myGLCD.printNumI((StopFreq + StartFreq) / 2000, 138, 220);
myGLCD.setFont(2);
myGLCD.setColor(255, 255, 255);
myGLCD.print("Freq:", 120, 20);
myGLCD.print(" SWR:", 120, 40);
myGLCD.printNumI(SWRFreq, 210, 20);
myGLCD.printNumF(LowestSWR, 2, 210, 40);
}
void PlotSNA()
{
int x = 0;
myGLCD.clrScr();
myGLCD.setColor(255, 255, 255);
for (x = 0; x < NoBins; x++)
myGLCD.drawPixel(x, TestArray[x] * (240 / 102));
myGLCD.setTextSize(1);
myGLCD.drawLine(0, 39, 10, 39);
myGLCD.print("20dB", 15, 35);
myGLCD.drawLine(0, 79, 10, 79);
myGLCD.print("40dB", 15, 75);
myGLCD.drawLine(0, 119, 10, 119);
myGLCD.print("60dB", 15, 115);
myGLCD.drawLine(0, 159, 10, 159);
myGLCD.print("80dB", 15, 155);
myGLCD.drawLine(0, 199, 10, 199);
myGLCD.print("100dB", 15, 195);
myGLCD.printNumI(UserLowerfreq / 1000, 5, 230);
myGLCD.printNumI(UserUpperfreq / 1000, 278, 230);
myGLCD.printNumI((UserUpperfreq + UserLowerfreq) / 2000, 138, 230);
}
Friday, 3 August 2018
Homebrew 80/40m SSB/CW Base Rig
Audio Amplifier
Double Balanced Mixers
Another audio amplifier option.
Double Balanced Mixers
Plessey Bidirectional IF Amplifiers
Mic Amp
RF Power Amp
Low Pass Filters
#include <Wire.h>
#include <SPI.h>
#include <LiquidCrystal_I2C.h>
#include <si5351.h>
const uint32_t band80mStart = 3500000; // start of 80m
const uint32_t band80mEnd = 3900000; // end of 80m
const uint32_t band40mStart = 7000000; // start of 40m
const uint32_t band40mEnd = 7300000; // end of 40m
const uint32_t bandInit = 3700000; // where to initially set the frequency
volatile long oldfreq = 0;
volatile long old80mfreq = 3700000;
volatile long old40mfreq = 7100000;
volatile long currentfreq = 0;
volatile int updatedisplay = 0;
volatile int TX = 0;
volatile int oldTX = 0;
volatile int mode = 0;
volatile int oldmode = 0;
volatile int band = 0;
volatile int oldband = 0;
volatile uint32_t freq = bandInit ; // this is a variable (changes) - set it to the beginning of the band
volatile uint32_t radix = 1000; // how much to change the frequency by, clicking the rotary encoder will change this.
volatile uint32_t SSB_BFO_freq = 8999700; // 8998050 (filter centre) + 2700/2 + 300
//volatile uint32_t SSB_BFO_freq = 9000300; // 8998050 (filter centre) + 2700/2 + 300
volatile uint32_t CW_BFO_freq = 8997336; // 8996750 (filter centre) + 700 - 114 (from test)
static const uint32_t CW_Filter_CentreFreq = 8996750;
// Rotary encoder pins and other inputs
static const int rotAPin = 2;
static const int rotBPin = 3;
static const int pushPin = 4;
// Mode switches
static const int BandPin = 5;
static const int ModePin = 6;
// Discrete Outputs
static const int CrystalFilterSelectPin = 10;
static const int PTTPin = 11;
// Rotary encoder variables, used by interrupt routines
volatile int rotState = 0;
volatile int rotAval = 1;
volatile int rotBval = 1;
// Instantiate the Objects
LiquidCrystal_I2C lcd(0x27, 16, 2);
Si5351 si5351;
void setup()
{
// Set up frequency and radix switches
pinMode(rotAPin, INPUT);
pinMode(rotBPin, INPUT);
pinMode(pushPin, INPUT);
pinMode(ModePin, INPUT);
pinMode(BandPin, INPUT);
pinMode(PTTPin, INPUT);
pinMode(CrystalFilterSelectPin, OUTPUT);
// Set up pull-up resistors on inputs
digitalWrite(rotAPin, HIGH);
digitalWrite(rotBPin, HIGH);
digitalWrite(pushPin, HIGH);
digitalWrite(ModePin, HIGH);
digitalWrite(BandPin, HIGH);
digitalWrite(PTTPin, HIGH);
digitalWrite(CrystalFilterSelectPin, LOW);
// Set up interrupt pins
attachInterrupt(digitalPinToInterrupt(rotAPin), ISRrotAChange, CHANGE);
attachInterrupt(digitalPinToInterrupt(rotBPin), ISRrotBChange, CHANGE);
// Initialize the display
lcd.begin();
lcd.backlight();
lcd.cursor();
UpdateDisplay();
delay(1000);
// Initialize the DDS
si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0);
si5351.set_correction(35980);
si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA);
si5351.drive_strength(SI5351_CLK2, SI5351_DRIVE_8MA);
// si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_6MA);
// si5351.drive_strength(SI5351_CLK2, SI5351_DRIVE_6MA); si5351.set_freq((freq * 100ULL), SI5351_PLL_FIXED, SI5351_CLK0);
si5351.set_freq((SSB_BFO_freq * 100ULL), SI5351_PLL_FIXED, SI5351_CLK2);
}
void loop()
{
currentfreq = getfreq(); // Interrupt safe method to get the current frequency
if (currentfreq != oldfreq)
{
UpdateDisplay();
SendFrequency();
oldfreq = currentfreq;
}
if (digitalRead(pushPin) == LOW)
{
delay(10);
while (digitalRead(pushPin) == LOW)
{
if (updatedisplay == 1)
{
UpdateDisplay();
updatedisplay = 0;
}
}
delay(50);
}
if (digitalRead(PTTPin) == HIGH)
TX = 1;
else
TX = 0;
if (digitalRead(ModePin) == LOW)
{
mode = 1; // 0 = LSB, 1 = CW
digitalWrite(CrystalFilterSelectPin, HIGH); // Select CW filter
}
else
{
mode = 0;
digitalWrite(CrystalFilterSelectPin, LOW); // Select SSB filter
}
if (digitalRead(BandPin) == LOW)
band = 1; // 0 = 80m, 1 = 40m
else
band = 0;
if (TX != oldTX) // Only update the display on mode change
{
//UpdateMode();
UpdateDisplay();
oldTX = TX;
}
if (mode != oldmode) // Only update the display on mode change
{
//UpdateMode();
UpdateDisplay();
SendFrequency();
oldmode = mode;
}
if (band != oldband) // Only update the display on band change
{
// 0 = 80m, 1 = 40m
if (band == 0) // now 80m was 40m
{
old40mfreq = freq;
freq = old80mfreq;
}
if (band == 1) // now 40m was 80m
{
old80mfreq = freq;
freq = old40mfreq;
}
UpdateDisplay();
oldband = band;
}
}
long getfreq()
{
long temp_freq;
cli();
temp_freq = freq;
sei();
return temp_freq;
}
// Interrupt routines
void ISRrotAChange()
{
if (digitalRead(rotAPin))
{
rotAval = 1;
UpdateRot();
}
else
{
rotAval = 0;
UpdateRot();
}
}
void ISRrotBChange()
{
if (digitalRead(rotBPin))
{
rotBval = 1;
UpdateRot();
}
else
{
rotBval = 0;
UpdateRot();
}
}
void UpdateRot()
{
switch (rotState)
{
case 0: // Idle state, look for direction
if (!rotBval)
rotState = 1; // CW 1
if (!rotAval)
rotState = 11; // CCW 1
break;
case 1: // CW, wait for A low while B is low
if (!rotBval)
{
if (!rotAval)
{
// either increment radixindex or freq
if (digitalRead(pushPin) == LOW)
{
updatedisplay = 1;
if (radix == 1000000)
radix = 100000;
else if (radix == 100000)
radix = 10000;
else if (radix == 10000)
radix = 1000;
else if (radix == 1000)
radix = 100;
else if (radix == 100)
radix = 10;
else if (radix == 10)
radix = 1;
else
radix = 1000000;
}
else
{
freq = (freq + radix);
if (band == 0) // 80m
if (freq > band80mEnd)
freq = band80mEnd;
if (band == 1) // 80m
if (freq > band40mEnd)
freq = band40mEnd;
}
rotState = 2; // CW 2
}
}
else if (rotAval)
rotState = 0; // It was just a glitch on B, go back to start
break;
case 2: // CW, wait for B high
if (rotBval)
rotState = 3; // CW 3
break;
case 3: // CW, wait for A high
if (rotAval)
rotState = 0; // back to idle (detent) state
break;
case 11: // CCW, wait for B low while A is low
if (!rotAval)
{
if (!rotBval)
{
// either decrement radixindex or freq
if (digitalRead(pushPin) == LOW)
{
updatedisplay = 1;
if (radix == 1)
radix = 10;
else if (radix == 10)
radix = 100;
else if (radix == 100)
radix = 1000;
else if (radix == 1000)
radix = 10000;
else if (radix == 10000)
radix = 100000;
else if (radix == 100000)
radix = 1000000;
else
radix = 1;
}
else
{
freq = (freq - radix);
if (band == 0) // 80m
if (freq < band80mStart)
freq = band80mStart;
if (band == 1) // 80m
if (freq < band40mStart)
freq = band40mStart;
}
rotState = 12; // CCW 2
}
}
else if (rotBval)
rotState = 0; // It was just a glitch on A, go back to start
break;
case 12: // CCW, wait for A high
if (rotAval)
rotState = 13; // CCW 3
break;
case 13: // CCW, wait for B high
if (rotBval)
rotState = 0; // back to idle (detent) state
break;
}
}
void UpdateDisplay()
{
lcd.setCursor(0, 0);
lcd.print(" ");
lcd.setCursor(0, 0);
lcd.print(freq);
lcd.setCursor(10, 0);
lcd.print("ZL2CTM");
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.setCursor(0, 1);
if (mode == 0)
lcd.print("LSB");
if (mode == 1)
lcd.print("CW ");
lcd.setCursor(5, 1);
lcd.print(" ");
lcd.setCursor(5, 1);
if (TX == 1)
lcd.print("TX");
if (freq > 9999999)
{
if (radix == 1)
lcd.setCursor(7, 0);
if (radix == 10)
lcd.setCursor(6, 0);
if (radix == 100)
lcd.setCursor(5, 0);
if (radix == 1000)
lcd.setCursor(4, 0);
if (radix == 10000)
lcd.setCursor(3, 0);
if (radix == 100000)
lcd.setCursor(2, 0);
if (radix == 1000000)
lcd.setCursor(1, 0);
}
if (freq <= 9999999)
{
if (radix == 1)
lcd.setCursor(6, 0);
if (radix == 10)
lcd.setCursor(5, 0);
if (radix == 100)
lcd.setCursor(4, 0);
if (radix == 1000)
lcd.setCursor(3, 0);
if (radix == 10000)
lcd.setCursor(2, 0);
if (radix == 100000)
lcd.setCursor(1, 0);
if (radix == 1000000)
lcd.setCursor(0, 0);
}
}
void SendFrequency()
{
// CW_BFO_freq = 8997336; // 8996750 (filter centre) + 700 - 114 (from test)
// SSB_BFO_freq = 8999700; // 8998050 (filter centre) + 2700/2 + 300
// CW_Filter_CentreFreq = 8996750;
if (mode == 1) // CW
{
si5351.set_freq(((CW_Filter_CentreFreq - freq) * 100ULL), SI5351_PLL_FIXED, SI5351_CLK0);
si5351.set_freq((CW_BFO_freq * 100ULL), SI5351_PLL_FIXED, SI5351_CLK2);
}
else // SSB
{
si5351.set_freq(((SSB_BFO_freq - freq) * 100ULL), SI5351_PLL_FIXED, SI5351_CLK0);
si5351.set_freq((SSB_BFO_freq * 100ULL), SI5351_PLL_FIXED, SI5351_CLK2);
}
}
Subscribe to:
Posts (Atom)