http://www.youtube.com/c/CharlieMorrisZL2CTM
2N3904 Antenna Amplifier (initial)
Voltage gain is set for 4 using Rdegen
J310 Antenna Amplifer
First need to determine Vp and Idss
2N3904 Antenna Amplifier (final)
Note, T2 is now a 10:5
7MHz BandPass Filter
2N3904 IF Amplifiers
Initial RF Power Amplifier Driver
Software
#include <Wire.h>
#include <SPI.h>
#include <TM1637Display.h>
#include <si5351.h>
#include "LowPower.h"
const uint32_t bandStart = 7000000; // start of 40m
const uint32_t bandEnd = 7300000; // end of 40m
const uint32_t bandInit = 7100000; // where to initially set the frequency
volatile long currentfreq = 0;
volatile long oldfreq = 0;
volatile int currentmode = 0;
volatile int oldmode = 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.
//const uint32_t BFO_freq = 9000000; // Low side injection use 9000000;
const uint32_t BFO_freq = 8996400; // High side injection use 8996400;
// Rotary encoder pins and other inputs
static const int rotBPin = 2;
static const int rotAPin = 3;
static const int pushPin = 4;
static const int PTTInput = 9;
static const int brightnessPin = A3;
static const int tunespeedLED = A2;
static const int gnd = 10;
static const int vcc = 11;
static const int DIO = 12;
static const int CLK = 13;
// Rotary encoder variables, used by interrupt routines
volatile int rotState = 0;
volatile int rotAval = 1;
volatile int rotBval = 1;
volatile long remainder = 0;
volatile long OnesHz = 0;
volatile long TensHz = 0;
volatile long HundredsHz = 0;
volatile long OneskHz = 0;
volatile long TenskHz = 0;
volatile long HundredskHz = 0;
volatile long OnesMHz = 0;
volatile long TensMHz = 0;
volatile int Brightness = 3;
volatile int batterySave = 0;
// Instantiate the Objects
TM1637Display display(CLK, DIO); // CLK, DIO
Si5351 si5351;
void setup()
{
// Set up frequency and radix switches
pinMode(rotAPin, INPUT);
pinMode(rotBPin, INPUT);
pinMode(pushPin, INPUT);
pinMode(brightnessPin, INPUT);
pinMode(gnd, OUTPUT);
pinMode(tunespeedLED, OUTPUT);
pinMode(vcc, OUTPUT);
pinMode(PTTInput, INPUT);
// Set up pull-up resistors on inputs
digitalWrite(rotAPin, HIGH);
digitalWrite(rotBPin, HIGH);
digitalWrite(pushPin, HIGH);
digitalWrite(brightnessPin, HIGH);
digitalWrite(gnd, LOW);
digitalWrite(vcc, HIGH);
digitalWrite(tunespeedLED, LOW);
digitalWrite(PTTInput, LOW);
// Set up interrupt pins
attachInterrupt(digitalPinToInterrupt(rotAPin), ISRrotAChange, CHANGE);
attachInterrupt(digitalPinToInterrupt(rotBPin), ISRrotBChange, CHANGE);
// Initialize the display
display.setBrightness(Brightness, true);
UpdateDisplay();
delay(1000);
// Initialize the DDS
si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);
si5351.set_correction(29100, 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_2MA);
si5351.drive_strength(SI5351_CLK2, SI5351_DRIVE_2MA);
SendFrequency();
}
void loop()
{
LowPower.idle(SLEEP_60MS, ADC_OFF, TIMER2_OFF, TIMER1_OFF, TIMER0_ON, SPI_OFF, USART0_OFF, TWI_OFF);
currentmode = digitalRead(PTTInput);
if (currentmode != oldmode)
{
SendFrequency();
oldmode = currentmode;
}
currentfreq = getfreq(); // Interrupt safe method to get the current frequency
if (currentfreq != oldfreq)
{
UpdateDisplay();
SendFrequency();
oldfreq = currentfreq;
}
if (digitalRead(brightnessPin) == LOW)
{
Brightness--;
display.setBrightness(Brightness, true);
if (Brightness == -1)
{
display.setBrightness(0, false);
digitalWrite(tunespeedLED, LOW);
batterySave = 1;
}
if (Brightness == -2)
{
Brightness = 3;
batterySave = 0;
}
UpdateDisplay();
delay(500);
}
if ((radix == 100) && (batterySave == 0))
digitalWrite(tunespeedLED, HIGH);
if (radix == 1000)
digitalWrite(tunespeedLED, LOW);
}
void wakeUp()
{
// Just a handler for the sleep pin interrupt.
}
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)
{
if (radix == 1000)
radix = 100;
else if (radix == 100)
radix = 1000;
}
else
{
freq = (freq + radix);
if (freq > bandEnd)
freq = bandEnd;
}
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)
{
if (radix == 100)
radix = 1000;
else if (radix == 1000)
radix = 100;
}
else
{
freq = (freq - radix);
if (freq < bandStart)
freq = bandStart;
}
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()
{
TensMHz = freq / 10000000; // TensMHz = 12345678 / 10000000 = 1
remainder = freq - (TensMHz * 10000000); // remainder = 12345678 - 10000000 = 2345678
OnesMHz = remainder / 1000000; // OnesMhz = 2345678 / 1000000 = 2
remainder = remainder - (OnesMHz * 1000000); // remainder = 2345678 - (2 * 1000000) = 345678
HundredskHz = remainder / 100000; // HundredskHz = 345678 / 100000 = 3
remainder = remainder - (HundredskHz * 100000); // remainder = 345678 - (3 * 100000) = 45678
TenskHz = remainder / 10000; // TenskHz = 45678 / 10000 = 4
remainder = remainder - (TenskHz * 10000); // remainder = 45678 - (4 * 10000) = 5678
OneskHz = remainder / 1000; // OneskHz = 5678 / 1000 = 5
remainder = remainder - (OneskHz * 1000); // remainder = 5678 - (5 * 1000) = 678
HundredsHz = remainder / 100; // HundredsHz = 678 / 100 = 6
remainder = remainder - (HundredsHz * 100); // remainder = 678 - (6 * 100) = 78
TensHz = remainder / 10; // TensHz = 78 / 10 = 7
remainder = remainder - (TensHz * 10); // remainder = 78 - (7 * 10) = 8
OnesHz = remainder; // OnesHz = 8
display.showNumberDec(((1000 * HundredskHz) + ( 100 * TenskHz) + (10 * OneskHz) + HundredsHz), true);
}
void SendFrequency()
{
if (currentmode == 1) // Transmit
{
si5351.set_freq(((freq + BFO_freq) * 100ULL), SI5351_CLK2);
si5351.set_freq((BFO_freq * 100ULL), SI5351_CLK0);
}
else // Receive
{
//si5351.set_freq(((BFO_freq - freq) * 100ULL), SI5351_CLK0); // Low side injection
si5351.set_freq(((BFO_freq + freq) * 100ULL), SI5351_CLK0); // High side injection
si5351.set_freq((BFO_freq * 100ULL), SI5351_CLK2);
}
}
Great work. I think i can really use this information myself.
ReplyDeleteGood job.
Thanks for the feedback. It is appreciated very much.
DeleteHi Charlie
ReplyDeleteI hope you don’t mind me asking you but do you have a draving showing the connections between the Arduino, Si5351 module and 7 segment display unit for your 40 Meter Tramping transceiver. I have looked on you blog and cannot find one and am not sure sure how to connect it up.
Thanks in advance
Steve
2E0WSF
No I don't sorry. Both the Si5351 and the display are I2C. The Arduino A4 pin goes to both the SDA pins, and the A4 pin to the SCL. The only other issue is to make sure the I2C address for the display matches that in the display initialization call in the code.
ReplyDelete