Showing posts with label Homebrew. Show all posts
Showing posts with label Homebrew. Show all posts
Thursday 6 June 2019
20m WSPR Beacon
Below is the software. Looks like the formatting has been affected when it was copied over. Auto format in the Arduino IDE works great!
*****************************************************************
#include <DDS.h>
#include <SoftwareSerial.h>
#define RESET 2
#define FQ_UD 3
#define W_CLK 4
#define DATA 5
#define HighPWR_LED 6
#define LowPWR_LED 7
#define TX_LED 8
#define Percent25_LED 9
#define Percent50_LED 10
#define Percent75_LED 11
#define Percent100_LED 12
#define SerialTX 13
#define SerialRX A0
#define Tone0_LED A1
#define Tone1_LED A2
#define Tone2_LED A3
#define Tone3_LED A4
const int GPS_BUFFER_SIZE = 200; // The maximum size allowed for an NMEA sentence.
char sentence[GPS_BUFFER_SIZE] = {0};
volatile int _sentenceIndex = 0;
volatile char character = 0;
volatile int SymbolIndex = 0;
volatile int TX = 0;
volatile int Power = 0;
volatile int _UTCMinutes, _UTCSeconds = 0;
volatile long freq = 0;
const byte LowPWR_WSPR_Data[] = {3, 1, 2, 0, 2, 2, 0, 2, 3, 2, 0, 0, 1, 1, 1, 0, 2, 2, 3, 2, 2, 3, 2, 3, 1, 3, 3, 2, 0, 2, 0, 2,
0, 2, 3, 2, 2, 3, 2, 1, 0, 0, 2, 0, 0, 0, 1, 0, 1, 3, 2, 2, 1, 3, 0, 3, 0, 2, 2, 1, 3, 0, 3, 0,
2, 0, 0, 3, 3, 2, 3, 2, 3, 2, 3, 0, 1, 0, 0, 1, 2, 2, 1, 2, 1, 3, 0, 0, 0, 3, 3, 0, 1, 0, 1, 2,
2, 2, 3, 0, 0, 0, 2, 0, 3, 0, 0, 3, 0, 0, 3, 3, 3, 2, 3, 1, 2, 0, 1, 3, 2, 3, 0, 0, 2, 1, 3, 1,
2, 0, 0, 2, 2, 3, 0, 1, 0, 2, 1, 3, 0, 0, 0, 2, 2, 0, 0, 3, 3, 0, 3, 0, 3, 3, 2, 2, 2, 1, 3, 2,
2, 0
};
const byte HighPWR_WSPR_Data[] = {3, 1, 2, 0, 2, 0, 0, 2, 3, 0, 0, 2, 1, 3, 1, 2, 2, 2, 3, 2, 2, 3, 2, 3, 1, 3, 3, 0, 0, 0, 0, 0,
0, 2, 3, 0, 2, 1, 2, 3, 0, 0, 2, 0, 0, 0, 1, 0, 1, 1, 2, 0, 1, 1, 0, 3, 0, 0, 2, 1, 3, 0, 3, 0,
2, 0, 0, 1, 3, 2, 3, 2, 3, 2, 3, 2, 1, 0, 0, 1, 2, 2, 1, 0, 1, 1, 0, 0, 0, 1, 3, 2, 1, 0, 1, 0,
2, 2, 3, 0, 0, 2, 2, 0, 3, 2, 0, 1, 0, 2, 3, 1, 3, 0, 3, 1, 2, 2, 1, 3, 2, 1, 0, 2, 2, 3, 3, 1,
2, 0, 0, 2, 2, 1, 0, 3, 0, 2, 1, 1, 0, 0, 0, 2, 2, 0, 0, 3, 3, 2, 3, 0, 3, 1, 2, 0, 2, 3, 3, 0,
2, 0
};
// Create the objects
DDS dds(W_CLK, FQ_UD, DATA, RESET);
SoftwareSerial mySerial(SerialRX, SerialTX);
void setup()
{
// Initialize the AD9850
dds.init();
dds.trim(125000000);
dds.setFrequency(0);
// Initialize the Software serial port
mySerial.begin(38400);
pinMode(HighPWR_LED, OUTPUT);
pinMode(LowPWR_LED, OUTPUT);
pinMode(TX_LED, OUTPUT);
pinMode(Percent25_LED, OUTPUT);
pinMode(Percent50_LED, OUTPUT);
pinMode(Percent75_LED, OUTPUT);
pinMode(Percent100_LED, OUTPUT);
pinMode(Tone0_LED, OUTPUT);
pinMode(Tone1_LED, OUTPUT);
pinMode(Tone2_LED, OUTPUT);
pinMode(Tone3_LED, OUTPUT);
digitalWrite(HighPWR_LED, LOW);
digitalWrite(LowPWR_LED, LOW);
digitalWrite(TX_LED, LOW);
digitalWrite(Percent25_LED, LOW);
digitalWrite(Percent50_LED, LOW);
digitalWrite(Percent75_LED, LOW);
digitalWrite(Percent100_LED, LOW);
digitalWrite(Tone0_LED, LOW);
digitalWrite(Tone1_LED, LOW);
digitalWrite(Tone2_LED, LOW);
digitalWrite(Tone3_LED, LOW);
}
void loop()
{
if (TX == 0)
{
if (mySerial.available())
{
character = mySerial.read(); // Get a single character
sentence[_sentenceIndex] = character; // Append the current character to the sentence
_sentenceIndex++;
if (_sentenceIndex == GPS_BUFFER_SIZE) // Check for overrun of the sentence array. If so, restart
_sentenceIndex = 0;
if (character == '\r') // Carriage return. Look for RMC sentence
{
_sentenceIndex = 0; // Reset index counter ready for next timet
if ((sentence[4] == 'R') && (sentence[5] == 'M') && (sentence[6] == 'C'))
{
_UTCSeconds = (10 * (sentence[12] - 48) + (sentence[13] - 48));
_UTCMinutes = (10 * (sentence[10] - 48) + (sentence[11] - 48));
if ((_UTCMinutes % 2 == 0) && ((_UTCSeconds == 0) || (_UTCSeconds == 1) || (_UTCSeconds == 2)))
TX = 1;
}
}
}
}
if (TX == 1)
{
digitalWrite(TX_LED, HIGH); // Turn on the TX LED
freq = random(14097000, 14097200); // Select a random TX frequency
if (Power == 0)
for (SymbolIndex = 0; SymbolIndex <= 161; SymbolIndex++)
{
dds.setFrequency(freq + (LowPWR_WSPR_Data[SymbolIndex] * 1.4648)); // Change DDS freq
ToneLEDs(LowPWR_WSPR_Data[SymbolIndex]); // Update tone LEDs
ProgressLEDs(SymbolIndex); // Update progress LEDs
delay(683);
}
if (Power == 1)
for (SymbolIndex = 0; SymbolIndex <= 161; SymbolIndex++)
{
dds.setFrequency(freq + (HighPWR_WSPR_Data[SymbolIndex] * 1.4648)); // Change DDS freq
ToneLEDs(HighPWR_WSPR_Data[SymbolIndex]); // Update tone LEDs
ProgressLEDs(SymbolIndex); // Update progress LEDs
delay(683);
}
dds.setFrequency(0); // Turn off the DDS
ToneLEDs(4); // Turn off the tone LEDs
ProgressLEDs(0); // Turn off the progress LEDs
digitalWrite(TX_LED, LOW); // Turn off the TX LED
TX = 0;
Power = !Power; // Toggle transmit power
PowerLEDs(Power); // Toggle power level LED
}
}
void ToneLEDs(int x)
{
switch (x)
{
case 4:
digitalWrite(Tone0_LED, LOW);
digitalWrite(Tone1_LED, LOW);
digitalWrite(Tone2_LED, LOW);
digitalWrite(Tone3_LED, LOW);
break;
case 3:
digitalWrite(Tone0_LED, LOW);
digitalWrite(Tone1_LED, LOW);
digitalWrite(Tone2_LED, LOW);
digitalWrite(Tone3_LED, HIGH);
break;
case 2:
digitalWrite(Tone0_LED, LOW);
digitalWrite(Tone1_LED, LOW);
digitalWrite(Tone2_LED, HIGH);
digitalWrite(Tone3_LED, LOW);
break;
case 1:
digitalWrite(Tone0_LED, LOW);
digitalWrite(Tone1_LED, HIGH);
digitalWrite(Tone2_LED, LOW);
digitalWrite(Tone3_LED, LOW);
break;
case 0:
digitalWrite(Tone0_LED, HIGH);
digitalWrite(Tone1_LED, LOW);
digitalWrite(Tone2_LED, LOW);
digitalWrite(Tone3_LED, LOW);
break;
}
}
void ProgressLEDs(int x)
{
if (x <= 30)
{
digitalWrite(Percent25_LED, LOW);
digitalWrite(Percent50_LED, LOW);
digitalWrite(Percent75_LED, LOW);
digitalWrite(Percent100_LED, LOW);
}
if ((x > 30) && (x <= 70))
{
digitalWrite(Percent25_LED, LOW);
digitalWrite(Percent50_LED, LOW);
digitalWrite(Percent75_LED, LOW);
digitalWrite(Percent100_LED, HIGH);
}
if ((x > 70) && (x <= 110))
{
digitalWrite(Percent25_LED, LOW);
digitalWrite(Percent50_LED, LOW);
digitalWrite(Percent75_LED, HIGH);
digitalWrite(Percent100_LED, HIGH);
}
if ((x > 110) && (x <= 150))
{
digitalWrite(Percent25_LED, LOW);
digitalWrite(Percent50_LED, HIGH);
digitalWrite(Percent75_LED, HIGH);
digitalWrite(Percent100_LED, HIGH);
}
if (x > 150)
{
digitalWrite(Percent25_LED, HIGH);
digitalWrite(Percent50_LED, HIGH);
digitalWrite(Percent75_LED, HIGH);
digitalWrite(Percent100_LED, HIGH);
}
}
void PowerLEDs(int x)
{
switch (x)
{
case 0:
digitalWrite(LowPWR_LED, HIGH);
digitalWrite(HighPWR_LED, LOW);
break;
case 1:
digitalWrite(LowPWR_LED, LOW);
digitalWrite(HighPWR_LED, HIGH);
break;
}
}
*************************************************************
The link to the GENWSPR file is: http://www.g4jnt.com/JTModesBcns.htm
Saturday 20 April 2019
Portable 20m SSB Rig
See YouTube for video log.
BPF
Ant Amplifier
IF Amp
#include <Wire.h>
#include <SPI.h>
#include <TM1637Display.h>
#include <si5351.h>
#include "LowPower.h"
const uint32_t bandStart = 14000000; // start of 20m
const uint32_t bandEnd = 14350000; // end of 20m
const uint32_t bandInit = 14100000; // 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 = 8998450; // 8998450 = high side injection. For low side injection use 9001350;
// 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 = 8;
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(87000, 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);
si5351.set_freq((freq * 100ULL), SI5351_CLK0);
si5351.set_freq((BFO_freq * 100ULL), SI5351_CLK2);
}
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 + 50) * 100ULL), SI5351_CLK2);
si5351.set_freq((BFO_freq * 100ULL), SI5351_CLK0);
}
else // Receive
{
si5351.set_freq(((freq - BFO_freq + 50) * 100ULL), SI5351_CLK0);
si5351.set_freq((BFO_freq * 100ULL), SI5351_CLK2);
}
}
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);
}
}
Sunday 17 June 2018
Portable 40m CW Transceiver
#include <SPI.h>
#include <TM1637Display.h>
#include <si5351.h>
#include "LowPower.h"
const uint32_t bandStart = 7000000; // start of 80m
const uint32_t bandEnd = 7400000; // end of 80m
const uint32_t bandInit = 7025000; // where to initially set the frequency
volatile long oldfreq = 0;
volatile long currentfreq = 0;
volatile int currentTime = 0;
volatile int previousTime = 0;
volatile int elapsed = 0;
volatile int QSK_Timer = 1000;
int PTT = 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 LSB_IF_freq = 9011500; // Crystal filter centre freq
volatile uint32_t LSB_BFO_freq = 9000000; // Crystal filter centre freq
// Rotary encoder pins and other inputs
static const int rotBPin = 2;
static const int rotAPin = 3;
static const int pushPin = 4;
static const int KeyInput = 7;
static const int PTTOutput = 8;
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(KeyInput, INPUT);
pinMode(PTTOutput, OUTPUT);
// 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(KeyInput, HIGH);
digitalWrite(PTTOutput, 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);
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.set_freq(((8999300 + freq) * 100ULL), SI5351_PLL_FIXED, SI5351_CLK0);
si5351.set_freq((LSB_BFO_freq * 100ULL), SI5351_PLL_FIXED, SI5351_CLK2);
}
void loop()
{
//LowPower.idle(SLEEP_60MS, ADC_OFF, TIMER2_OFF, TIMER1_OFF, TIMER0_OFF, SPI_OFF, USART0_OFF, TWI_OFF);
LowPower.idle(SLEEP_60MS, ADC_OFF, TIMER2_OFF, TIMER1_OFF, TIMER0_ON, SPI_OFF, USART0_OFF, TWI_OFF);
if (digitalRead(KeyInput) == LOW)
{
si5351.output_enable(SI5351_CLK1, 1);
SendFrequency();
}
if (digitalRead(KeyInput) == HIGH)
{
si5351.output_enable(SI5351_CLK1, 0);
}
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()
{
// PA TX Drive
si5351.set_freq((freq * 100ULL), SI5351_PLL_FIXED, SI5351_CLK1);
// VFO
//si5351.set_freq((freq * 100ULL), SI5351_PLL_FIXED, SI5351_CLK0);
si5351.set_freq(((8999300 + freq) * 100ULL), SI5351_PLL_FIXED, SI5351_CLK0);
// BFO
si5351.set_freq((LSB_BFO_freq * 100ULL), SI5351_PLL_FIXED, SI5351_CLK2);
}
Subscribe to:
Posts (Atom)