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);
}
}
Hi Charlie. I have been watching your videos with interest. By the time you read this, your 80-40m rig would be complete. I note you used the Plessy citcuit as bi- lateral IF Amps at 6v. Probably too late now, but at the time, you could have tried the G4GXO bilateral IF Amp circuit. This makes the gain of both stages under AGC and ALC control. I have a design on the drawing board a 80-40m rig myself using a surplus 11.2735MHz Crystal Filter from an old Kraco SSB Deluxe CB rig. The band width is a compromise between SSB and AM, for the same filter is used on both modes. I'll have to put it in a test gig to measure it's perammeters. I'm very interested in the Dual Band VFO used in your project. The code would be interesting to have, but I have no experience in programing. I'll have to find someone to help me make code changes to suit my needs. VFO-A and VFO-B functions would be nice, thinking of using High side injection. The disign is Bringing Valve Tech In To The 21st Century using less than 10 Valves. A challanging project. Have to get the Digital VFO built and tested first. 73s Darryl VK5JDS.
ReplyDeleteWell i don't know bilateral IF Amp circuit it seem a bit of an over kill.
Deletethe Plessay one uses a 2 pole change over relay Why add the complication of an extra IF Amp circuit.
the G4GXO bilateral IF Amp circuit. uses 2 coils,
switching diodes and more bit's another complication.
why not use the switching diodes to do what the relay would do not add the coils. extra bits causes extra noise in the IF I think.
I wish one could do away with them coils seen in the circuits...Could we
circuit configuration. charlie morrison circuits build (out of the junk box), may look good but if you are new to Ham radio there are a lot of components types to buy, he uses IC, why has he not used and IC for the audio Amp.
but whatever tickle your fancy i suppose.
the G4GXO bilateral IF Amp circuit. uses 2 coils,
switching diodes and more bit's another complication.
why not use the switching diodes to do what the relay would do not add the coils. extra bits causes extra noise in the IF I think.
If one wants to match 50R input, one could use a common base transistor circuit. charlie morrison circuits buikd out of the junk box, may look good but if you are new to Ham radio there are a lot of components types to buy
but what ever tickle your fancy i supose.
Hi Darryl. Sounds like you have a fun project. I've updated the blog to include the code. You should be easy to see where the changes need to be made to suit your filter. I'm going to make a start today on the base 80m/40m CW rig. I'm looking forward to that.
ReplyDeleteCharlie ZL2CTM