|
|
|
@ -1,337 +1,419 @@
|
|
|
|
|
/**********************************************************************************************************
|
|
|
|
|
10kHz to 225MHz VFO / RF Generator with Si5351 and Arduino Nano, with Intermediate Frequency (IF) offset
|
|
|
|
|
(+ or -), RX/TX Selector for QRP Transceivers, Band Presets and Bargraph S-Meter. See the schematics for
|
|
|
|
|
wiring and README.txt for details. By J. CesarSound - ver 2.0 - Feb/2021.
|
|
|
|
|
***********************************************************************************************************/
|
|
|
|
|
//----------- History ---------------
|
|
|
|
|
/*
|
|
|
|
|
/* This code has been modified from that written by Jeff Glass (KK9JEF) and documented in the following locations:
|
|
|
|
|
/* - https://kk9jef.wordpress.com/2015/11/09/40m-direction-conversion-receiver-in-the-polyakov-style/
|
|
|
|
|
/* - https://github.com/JeffersGlass/DDS_VFO
|
|
|
|
|
/*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
//Libraries
|
|
|
|
|
#include <Wire.h> //IDE Standard
|
|
|
|
|
#include <Rotary.h> //Ben Buxton https://github.com/brianlow/Rotary
|
|
|
|
|
#include <si5351.h> //Etherkit https://github.com/etherkit/Si5351Arduino
|
|
|
|
|
#include <Adafruit_GFX.h> //Adafruit GFX https://github.com/adafruit/Adafruit-GFX-Library
|
|
|
|
|
#include <Adafruit_SSD1306.h> //Adafruit SSD1306 https://github.com/adafruit/Adafruit_SSD1306
|
|
|
|
|
#include <Arduino.h>
|
|
|
|
|
#include <Encoder.h>
|
|
|
|
|
#include <Wire.h>
|
|
|
|
|
//#include <LiquidCrystal.h>
|
|
|
|
|
#include <si5351.h>
|
|
|
|
|
#include <Adafruit_GFX.h>
|
|
|
|
|
#include <Adafruit_SSD1306.h>
|
|
|
|
|
|
|
|
|
|
//User preferences
|
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
|
|
|
#ifdef ESP32DOIT //use the ESP32 DEVKIT DOIT environment
|
|
|
|
|
#pragma "ESP32DOIT" //show in console on build
|
|
|
|
|
#define board 1 //Define board
|
|
|
|
|
#define IF 0 //Enter your IF frequency, ex: 455 = 455kHz, 10700 = 10.7MHz, 0 = to direct convert receiver or RF generator, + will add and - will subtract IF offfset.
|
|
|
|
|
#define BAND_INIT 7 //Enter your initial Band (1-21) at startup, ex: 1 = Freq Generator, 2 = 800kHz (MW), 7 = 7.2MHz (40m), 11 = 14.1MHz (20m).
|
|
|
|
|
#define XT_CAL_F 33000 //Si5351 calibration factor, adjust to get exatcly 10MHz. Increasing this value will decreases the frequency and vice versa.
|
|
|
|
|
#define S_GAIN 303 //Adjust the sensitivity of Signal Meter A/D input: 101 = 500mv; 202 = 1v; 303 = 1.5v; 404 = 2v; 505 = 2.5v; 1010 = 5v (max).
|
|
|
|
|
#define tunestep 32 //The pin used by tune step push button.
|
|
|
|
|
#define band 33 //The pin used by band selector push button.
|
|
|
|
|
#define rx_tx 5 //The pin used by RX / TX selector switch, RX = switch open, TX = switch closed to GND. When in TX, the IF value is not considered.
|
|
|
|
|
#define adc 4 //The pin used by Signal Meter A/D input.
|
|
|
|
|
#define rotary1 34 //First pin for rotary encoder.
|
|
|
|
|
#define rotary2 35 //Second pin for rotary encoder.
|
|
|
|
|
|
|
|
|
|
#else // use the ARDUINO1 environment
|
|
|
|
|
#pragma "ARDUINO1" //show in console on build
|
|
|
|
|
#define board 0 //Define board
|
|
|
|
|
#define IF 0 //Enter your IF frequency, ex: 455 = 455kHz, 10700 = 10.7MHz, 0 = to direct convert receiver or RF generator, + will add and - will subtract IF offfset.
|
|
|
|
|
#define BAND_INIT 7 //Enter your initial Band (1-21) at startup, ex: 1 = Freq Generator, 2 = 800kHz (MW), 7 = 7.2MHz (40m), 11 = 14.1MHz (20m).
|
|
|
|
|
#define XT_CAL_F 33000 //Si5351 calibration factor, adjust to get exatcly 10MHz. Increasing this value will decreases the frequency and vice versa.
|
|
|
|
|
#define S_GAIN 303 //Adjust the sensitivity of Signal Meter A/D input: 101 = 500mv; 202 = 1v; 303 = 1.5v; 404 = 2v; 505 = 2.5v; 1010 = 5v (max).
|
|
|
|
|
#define tunestep A0 //The pin used by tune step push button.
|
|
|
|
|
#define band A1 //The pin used by band selector push button.
|
|
|
|
|
#define rx_tx A2 //The pin used by RX / TX selector switch, RX = switch open, TX = switch closed to GND. When in TX, the IF value is not considered.
|
|
|
|
|
#define adc A3 //The pin used by Signal Meter A/D input.
|
|
|
|
|
#define rotary1 2 //First pin for rotary encoder.
|
|
|
|
|
#define rotary2 3 //Second pin for rotary encoder.
|
|
|
|
|
//----------- OLED ------------------
|
|
|
|
|
#define SCREEN_WIDTH 128 // OLED display width, in pixels
|
|
|
|
|
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
|
|
|
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
|
|
|
|
|
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
|
|
|
|
|
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
|
|
|
|
|
|
|
|
|
|
Rotary r = Rotary(rotary1, rotary2);
|
|
|
|
|
Adafruit_SSD1306 display = Adafruit_SSD1306(128, 64, &Wire);
|
|
|
|
|
Si5351 si5351(0x60); //Si5351 I2C Address 0x60
|
|
|
|
|
//----------- Variables & Declarations ---------------
|
|
|
|
|
/*
|
|
|
|
|
* The current and desired LISTENING FREQUENCY, which is not always the frequency being output by the Si5351.
|
|
|
|
|
* In 'testing' and 'basic' modes, the output freqeuncy is equal to currFreq
|
|
|
|
|
* In 'polyakov' mode, the output frequency is half of curFreq
|
|
|
|
|
* In BFO mode, .........
|
|
|
|
|
* These adjustments are mode in the setFrequency_5351 function depending on the current mode held in currMode
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
unsigned long freq, freqold, fstep;
|
|
|
|
|
long interfreq = IF, interfreqold = 0;
|
|
|
|
|
long cal = XT_CAL_F;
|
|
|
|
|
unsigned int smval;
|
|
|
|
|
byte encoder = 1;
|
|
|
|
|
byte stp, n = 1;
|
|
|
|
|
byte count, x, xo;
|
|
|
|
|
bool sts = 0;
|
|
|
|
|
unsigned int period = 100;
|
|
|
|
|
unsigned long time_now = 0;
|
|
|
|
|
long currFreq = 1800000; //in HZ
|
|
|
|
|
long ifFreq = 8865000; //in HZ
|
|
|
|
|
|
|
|
|
|
void set_frequency(short dir) {
|
|
|
|
|
if (encoder == 1) { //Up/Down frequency
|
|
|
|
|
if (dir == 1) freq = freq + fstep;
|
|
|
|
|
if (freq >= 225000000) freq = 225000000;
|
|
|
|
|
if (dir == -1) freq = freq - fstep;
|
|
|
|
|
if (fstep == 1000000 && freq <= 1000000) freq = 1000000;
|
|
|
|
|
else if (freq < 10000) freq = 10000;
|
|
|
|
|
//-----Enumerations of frequency steps and their labels for each mode----//
|
|
|
|
|
|
|
|
|
|
enum modes{mode_testing = 0, mode_basic, mode_polyakov, mode_bfo, mode_if};
|
|
|
|
|
const int NUM_MODES = 5;
|
|
|
|
|
int currMode = mode_basic;
|
|
|
|
|
|
|
|
|
|
const char* modeNames[NUM_MODES] = {"TEST", "VFO", "POLYA", "BFO", "IF"};
|
|
|
|
|
|
|
|
|
|
long steps[][10] = { //don't forget to update the MAX_STEPS_INDEX array below
|
|
|
|
|
{10000000, 5000000, 1000000, 500000, 100000, 10000, 1000, 10, 1}, //testing
|
|
|
|
|
{10000, 1000, 100, 10}, //basic
|
|
|
|
|
{1000, 100, 10, 1}, //polyakov
|
|
|
|
|
{1000, 100, 10, 1}, //bfo
|
|
|
|
|
{1000, 100, 10, 1} //IF Mode
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const int NUM_STEP_OPTIONS[NUM_MODES] = {
|
|
|
|
|
10, //testing
|
|
|
|
|
4, //basic
|
|
|
|
|
4, //polyakov
|
|
|
|
|
4, //bfo
|
|
|
|
|
4 //if
|
|
|
|
|
};
|
|
|
|
|
const char* stepNames[][10] = {
|
|
|
|
|
{" 10MHz", " 5MHz", " 1MHz", "500Khz", "100KHz", " 10KHz", " 1KHz", " 100Hz", " 10Hz", " 1 Hz"}, //basic
|
|
|
|
|
{" 10KHz", " 1KHz", " 100 Hz", " 10 Hz"}, //basic
|
|
|
|
|
{" 1KHz", " 100 Hz", " 10 Hz", " 1 Hz"}, //polyakov
|
|
|
|
|
{" 1KHz", " 100 Hz", " 10 Hz", " 1 Hz"}, //BFO
|
|
|
|
|
{" 1KHz", " 100 Hz", " 10 Hz", " 1 Hz"} //IF
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
int stepIndex = 0; // holds the index of the currently selected step value
|
|
|
|
|
|
|
|
|
|
//-----AMATEUR BAND DEFININTIONS----------------//
|
|
|
|
|
//See function "getCurrentBand" below as well
|
|
|
|
|
const int NUM_BANDS = 9;
|
|
|
|
|
const char* bandNames[NUM_BANDS] = {"160m", "80m", "40m", "30m", "20m", "17m", "15m", "12m", "10m"};
|
|
|
|
|
const char* OUT_OF_BAND_LABEL = "OOB";
|
|
|
|
|
|
|
|
|
|
long bandEdges[NUM_BANDS][2] = {
|
|
|
|
|
{1800000, 2000000}, //160m
|
|
|
|
|
{3500000, 4000000}, //80m
|
|
|
|
|
{7000000, 7300000}, //40m
|
|
|
|
|
{10100000, 10150000}, //30m
|
|
|
|
|
{14000000, 14350000}, //20m
|
|
|
|
|
{18068000, 18168000}, //17m
|
|
|
|
|
{21000000, 21450000}, //15m
|
|
|
|
|
{24890000, 24990000}, //12m
|
|
|
|
|
{28000000, 29700000} //10m
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Holds the last-seen frequency within each band. The list below is also the default location at bootup.
|
|
|
|
|
* This array is updated when the BAND button is used to change between bands.
|
|
|
|
|
* If the used has scrolled outside of a defined band and then presses the BAND button, they will
|
|
|
|
|
* still be advanced to the next band, but the band-return location will not be updated
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
long lastBandFreq[NUM_BANDS] = {
|
|
|
|
|
1800000, //160m
|
|
|
|
|
3500000, //80m
|
|
|
|
|
7000000, //40m
|
|
|
|
|
10100000, //30m
|
|
|
|
|
14000000, //20m
|
|
|
|
|
18068000, //17m
|
|
|
|
|
21000000, //15m
|
|
|
|
|
24890000, //12m
|
|
|
|
|
28000000 //10m
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*Information on bandplan permissions and recommended communication modes is contained in the
|
|
|
|
|
* methods getPermission and getBandplanModes below
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------
|
|
|
|
|
|
|
|
|
|
long lastButtonPress[] = {0,0,0,0,0,0,0}; //holds the last timestamp, from millis(), that a pin changed state. Directly references the arduino output pin numbers, length may need to be increased
|
|
|
|
|
boolean buttonActive[] = {false, false, false, false, false, false, false};
|
|
|
|
|
|
|
|
|
|
long encoderPosition = 0;
|
|
|
|
|
boolean displayNeedsUpdate;
|
|
|
|
|
|
|
|
|
|
const long MIN_FREQ = 8500;
|
|
|
|
|
const long MAX_FREQ = 150000000;
|
|
|
|
|
|
|
|
|
|
//---------LCD SETUP-------//
|
|
|
|
|
// int PIN_RS = 7;
|
|
|
|
|
// int PIN_EN = 8;
|
|
|
|
|
// int PIN_DB4 = 9;
|
|
|
|
|
// int PIN_DB5 = 10;
|
|
|
|
|
// int PIN_DB6 = 11;
|
|
|
|
|
// int PIN_DB7 = 12;
|
|
|
|
|
//LiquidCrystal lcd(PIN_RS, PIN_EN, PIN_DB4, PIN_DB5, PIN_DB6, PIN_DB7);
|
|
|
|
|
|
|
|
|
|
//--------Si5351 Declaration---------------//
|
|
|
|
|
|
|
|
|
|
Si5351 si5351(0x61);
|
|
|
|
|
//SDA is on pin A4 for Arduino Uno
|
|
|
|
|
//SCL is on pin A5 for Arduino Uno
|
|
|
|
|
|
|
|
|
|
//--------Tuning Knob Interrupt Pins-------//
|
|
|
|
|
//Encoder knob(2, 3), pushbutton on 1
|
|
|
|
|
|
|
|
|
|
Encoder encoder(2, 3);
|
|
|
|
|
const int PIN_BUTTON_ENCODER = 1;
|
|
|
|
|
|
|
|
|
|
//Button Pins//
|
|
|
|
|
const int PIN_BUTTON_MODE = 4;
|
|
|
|
|
const int PIN_BUTTON_BAND = 0;
|
|
|
|
|
const int BUTTON_DEBOUNCE_TIME = 10; //milliseconds
|
|
|
|
|
|
|
|
|
|
//SWR Sensor Pins
|
|
|
|
|
const int PIN_SWR_FORWARD = A1;
|
|
|
|
|
const int PIN_SWR_REVERSE = A0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// void displayInfo(){
|
|
|
|
|
// lcd.clear();
|
|
|
|
|
|
|
|
|
|
// // frequency information be centeredw within 11 spaces on the second line:
|
|
|
|
|
// if (currFreq >= 100000000) lcd.setCursor(3, 0);
|
|
|
|
|
// else if (currFreq > 10000000) lcd.setCursor(4, 0);
|
|
|
|
|
// else lcd.setCursor(5, 0);
|
|
|
|
|
// int mhz = int(currFreq/ 1000000);
|
|
|
|
|
// int khz = int((currFreq - (mhz*1000000)) / 1000);
|
|
|
|
|
// int hz = int(currFreq % 1000);
|
|
|
|
|
|
|
|
|
|
// int khzPad = 0;
|
|
|
|
|
// if (khz < 100) khzPad++;
|
|
|
|
|
// if (khz < 10) khzPad++;
|
|
|
|
|
|
|
|
|
|
// int hzPad = 0;
|
|
|
|
|
// if (hz < 100) hzPad++;
|
|
|
|
|
// if (hz < 10) hzPad++;
|
|
|
|
|
|
|
|
|
|
// lcd.print(mhz);
|
|
|
|
|
// lcd.print(".");
|
|
|
|
|
// for (int i = 0; i < khzPad; i++) lcd.print("0");
|
|
|
|
|
// lcd.print(khz);
|
|
|
|
|
// lcd.print(".");
|
|
|
|
|
// for (int i = 0; i < hzPad; i++) lcd.print("0");
|
|
|
|
|
// lcd.print(hz);
|
|
|
|
|
|
|
|
|
|
// //The current amateur band is printed in the top-right corner
|
|
|
|
|
// int currBand = getCurrentBand();
|
|
|
|
|
// if (currBand >= 0){
|
|
|
|
|
// char* currBandName = bandNames[currBand];
|
|
|
|
|
// lcd.setCursor(20-strlen(currBandName), 0);
|
|
|
|
|
// lcd.print(currBandName);
|
|
|
|
|
// }
|
|
|
|
|
// else{
|
|
|
|
|
// lcd.setCursor(20-strlen(OUT_OF_BAND_LABEL), 0);
|
|
|
|
|
// lcd.print(OUT_OF_BAND_LABEL);
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// //The license needed to operate on this frequency (ARRL, USA ONLY) is printed just below the band label
|
|
|
|
|
// lcd.setCursor (19, 1);
|
|
|
|
|
// lcd.print(getPermission());
|
|
|
|
|
|
|
|
|
|
// //Step Information should take the middle 11 spaces on the 3nd line
|
|
|
|
|
// //The first 5 symbols are "STEP:", leaving 6 chars for step info.
|
|
|
|
|
// lcd.setCursor(4, 2);
|
|
|
|
|
// lcd.print("STEP:");
|
|
|
|
|
// lcd.print(stepNames[currMode][stepIndex]);
|
|
|
|
|
|
|
|
|
|
// //Callsign is printed at the beginning of the 4th line
|
|
|
|
|
// lcd.setCursor(0, 3);
|
|
|
|
|
// lcd.print("KK9JEF");
|
|
|
|
|
|
|
|
|
|
// //The mode is printed on the 4th line with no label
|
|
|
|
|
// //lcd.setCursor(6, 3);
|
|
|
|
|
// lcd.setCursor(20-strlen(modeNames[currMode]), 3);
|
|
|
|
|
// lcd.print(modeNames[currMode]);
|
|
|
|
|
|
|
|
|
|
// //DEBUG
|
|
|
|
|
// //lcd.setCursor(0,0);
|
|
|
|
|
// //lcd.print(getCurrentBand());
|
|
|
|
|
|
|
|
|
|
// /*float fwd = analogRead(PIN_SWR_FORWARD);
|
|
|
|
|
// float rev = analogRead(PIN_SWR_REVERSE);
|
|
|
|
|
// float gamma = rev/fwd;
|
|
|
|
|
// float swr = (1 + abs(gamma)) / (1 - abs(gamma));
|
|
|
|
|
|
|
|
|
|
// lcd.setCursor(0, 1);
|
|
|
|
|
// lcd.print(int(fwd));
|
|
|
|
|
// lcd.setCursor(4, 1);
|
|
|
|
|
// lcd.print(int(rev));
|
|
|
|
|
// lcd.setCursor(8, 1);
|
|
|
|
|
// lcd.print(gamma);
|
|
|
|
|
// lcd.setCursor(14, 1);
|
|
|
|
|
// lcd.print(swr);*/
|
|
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
boolean checkButtonPress(int pin){
|
|
|
|
|
long time = millis();
|
|
|
|
|
if (buttonActive[pin] && digitalRead(pin) == HIGH){
|
|
|
|
|
buttonActive[pin] = false;
|
|
|
|
|
lastButtonPress[pin] = time;
|
|
|
|
|
}
|
|
|
|
|
if (encoder == 1) { //Up/Down graph tune pointer
|
|
|
|
|
if (dir == 1) n = n + 1;
|
|
|
|
|
if (n > 42) n = 1;
|
|
|
|
|
if (dir == -1) n = n - 1;
|
|
|
|
|
if (n < 1) n = 42;
|
|
|
|
|
else if (digitalRead(pin) == LOW && !buttonActive[pin] && time > lastButtonPress[pin] + BUTTON_DEBOUNCE_TIME){
|
|
|
|
|
buttonActive[pin] = true;
|
|
|
|
|
lastButtonPress[pin] = time;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void setFrequency_5351(long newFreq){
|
|
|
|
|
switch (currMode){
|
|
|
|
|
case mode_testing:
|
|
|
|
|
si5351.set_freq(newFreq * 100ULL, SI5351_CLK0);
|
|
|
|
|
break;
|
|
|
|
|
case mode_basic:
|
|
|
|
|
si5351.set_freq(newFreq * 100ULL, SI5351_CLK0);
|
|
|
|
|
break;
|
|
|
|
|
case mode_polyakov:
|
|
|
|
|
si5351.set_freq((newFreq / 2) * 100ULL, SI5351_CLK0);
|
|
|
|
|
break;
|
|
|
|
|
case mode_bfo:
|
|
|
|
|
si5351.set_freq(newFreq * 100ULL, SI5351_CLK0);
|
|
|
|
|
break;
|
|
|
|
|
case mode_if:
|
|
|
|
|
si5351.set_freq((newFreq + ifFreq) * 100UL, SI5351_CLK0); //VFO+IF
|
|
|
|
|
//VFO-IF
|
|
|
|
|
//IF-VFO
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef ESP32DOIT //use the ESP32 DEVKIT DOIT environment
|
|
|
|
|
//TODO
|
|
|
|
|
#else
|
|
|
|
|
ISR(PCINT2_vect) {
|
|
|
|
|
char result = r.process();
|
|
|
|
|
if (result == DIR_CW) set_frequency(1);
|
|
|
|
|
else if (result == DIR_CCW) set_frequency(-1);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
void tunegen() {
|
|
|
|
|
si5351.set_freq((freq + (interfreq * 1000ULL)) * 100ULL, SI5351_CLK0);
|
|
|
|
|
//Returns the index of the current amateur radio band based on currFreq. Does not include the 60m band
|
|
|
|
|
//Returns -1 if out of band, but within the HF amateur turning range
|
|
|
|
|
//returns -2 if out of band and lower than the lowest defined band
|
|
|
|
|
//returns -3 if out of band and higher than the highest defined band
|
|
|
|
|
int getCurrentBand(){
|
|
|
|
|
if (currFreq < bandEdges[0][0]) return -2; //we are lower than the lower edge of the lowest defined band
|
|
|
|
|
if (currFreq > bandEdges[NUM_BANDS-1][1]) return -3; //We are higher than the upper edge of the highest defined band
|
|
|
|
|
for (int i = 0; i < NUM_BANDS; i++){
|
|
|
|
|
if (currFreq >= bandEdges[i][0] && currFreq <= bandEdges[i][1]){return i;} //We are within a band
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void displayfreq() {
|
|
|
|
|
unsigned int m = freq / 1000000;
|
|
|
|
|
unsigned int k = (freq % 1000000) / 1000;
|
|
|
|
|
unsigned int h = (freq % 1000) / 1;
|
|
|
|
|
char getPermission(){
|
|
|
|
|
if (getCurrentBand() < 0) return ' ';
|
|
|
|
|
|
|
|
|
|
//160m
|
|
|
|
|
if (currFreq >= 1800000 && currFreq <= 2000000) return 'G';
|
|
|
|
|
|
|
|
|
|
//80m
|
|
|
|
|
if (currFreq >= 3525000 && currFreq <= 3600000) return 'T';
|
|
|
|
|
if ((currFreq >= 3525000 && currFreq <= 3600000) || (currFreq >= 3800000 && currFreq <= 4000000)) return 'G';
|
|
|
|
|
if ((currFreq >= 3525000 && currFreq <= 3600000) || (currFreq >= 3700000 && currFreq <= 4000000)) return 'A';
|
|
|
|
|
if (currFreq >= 3500000 && currFreq <= 4000000) return 'E';
|
|
|
|
|
|
|
|
|
|
//40m
|
|
|
|
|
if (currFreq >= 7025000 && currFreq <= 7125000) return 'T';
|
|
|
|
|
if ((currFreq >= 7025000 && currFreq <= 7125000) || (currFreq >= 7175000 && currFreq <= 7300000)) return 'G';
|
|
|
|
|
if (currFreq >= 7025000 && currFreq <= 7300000) return 'A';
|
|
|
|
|
if (currFreq >= 7000000 && currFreq <= 7300000) return 'E';
|
|
|
|
|
|
|
|
|
|
//30m
|
|
|
|
|
if (currFreq >= 10100000 && currFreq <= 10150000) return 'G';
|
|
|
|
|
|
|
|
|
|
//20m
|
|
|
|
|
if ((currFreq >= 14025000 && currFreq <= 14150000) || (currFreq >= 14225000 && currFreq <= 14350000)) return 'G';
|
|
|
|
|
if ((currFreq >= 14025000 && currFreq <= 14150000) || (currFreq >= 14175000 && currFreq <= 14350000)) return 'A';
|
|
|
|
|
if (currFreq >= 14000000 && currFreq <= 14350000) return 'E';
|
|
|
|
|
|
|
|
|
|
//17m
|
|
|
|
|
if (currFreq >= 18068000 && currFreq <= 18168000) return 'G';
|
|
|
|
|
|
|
|
|
|
//15m
|
|
|
|
|
if (currFreq >= 21025000 && currFreq <= 21200000) return 'T';
|
|
|
|
|
if ((currFreq >= 21025000 && currFreq <= 21200000) || (currFreq >= 21275000 && currFreq <= 21450000)) return 'G';
|
|
|
|
|
if ((currFreq >= 21025000 && currFreq <= 21200000) || (currFreq >= 21225000 && currFreq <= 21450000)) return 'A';
|
|
|
|
|
if (currFreq >= 21000000 && currFreq <= 21450000) return 'E';
|
|
|
|
|
|
|
|
|
|
//12m
|
|
|
|
|
if (currFreq >= 24890000 && currFreq <= 24990000) return 'G';
|
|
|
|
|
|
|
|
|
|
//10m
|
|
|
|
|
if (currFreq >= 28000000 && currFreq <= 28500000) return 'T';
|
|
|
|
|
if (currFreq >= 28000000 && currFreq <= 29700000) return 'G';
|
|
|
|
|
|
|
|
|
|
return 'X';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void setup(){
|
|
|
|
|
Serial.begin(9600); // This program won't work unless baud is 9600...why?
|
|
|
|
|
Serial.println("Start me up.");
|
|
|
|
|
|
|
|
|
|
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
|
|
|
|
|
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
|
|
|
|
|
Serial.println("SSD1306 allocation failed");
|
|
|
|
|
for(;;); // Don't proceed, loop forever
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clear the buffer
|
|
|
|
|
display.clearDisplay();
|
|
|
|
|
display.setTextSize(2);
|
|
|
|
|
display.drawPixel(10, 10, WHITE);
|
|
|
|
|
display.display(); // Call this AFTER any drawing command.
|
|
|
|
|
|
|
|
|
|
char buffer[15] = "";
|
|
|
|
|
if (m < 1) {
|
|
|
|
|
display.setCursor(41, 1); sprintf(buffer, "%003d.%003d", k, h);
|
|
|
|
|
}
|
|
|
|
|
else if (m < 100) {
|
|
|
|
|
display.setCursor(5, 1); sprintf(buffer, "%2d.%003d.%003d", m, k, h);
|
|
|
|
|
}
|
|
|
|
|
else if (m >= 100) {
|
|
|
|
|
unsigned int h = (freq % 1000) / 10;
|
|
|
|
|
display.setCursor(5, 1); sprintf(buffer, "%2d.%003d.%02d", m, k, h);
|
|
|
|
|
}
|
|
|
|
|
display.print(buffer);
|
|
|
|
|
}
|
|
|
|
|
// inialize LCD, display welcome message
|
|
|
|
|
//lcd.begin(20, 4);
|
|
|
|
|
//delay(250);
|
|
|
|
|
//lcd.setCursor(4, 1);
|
|
|
|
|
//lcd.print("VFO STARTING");
|
|
|
|
|
|
|
|
|
|
void setstep() {
|
|
|
|
|
switch (stp) {
|
|
|
|
|
case 1: stp = 2; fstep = 1; break;
|
|
|
|
|
case 2: stp = 3; fstep = 10; break;
|
|
|
|
|
case 3: stp = 4; fstep = 1000; break;
|
|
|
|
|
case 4: stp = 5; fstep = 5000; break;
|
|
|
|
|
case 5: stp = 6; fstep = 10000; break;
|
|
|
|
|
case 6: stp = 1; fstep = 1000000; break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);
|
|
|
|
|
si5351.set_freq(currFreq * 100ULL, SI5351_CLK0);
|
|
|
|
|
si5351.output_enable(SI5351_CLK0, 1);
|
|
|
|
|
si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA);
|
|
|
|
|
|
|
|
|
|
void bandpresets() {
|
|
|
|
|
switch (count) {
|
|
|
|
|
case 1: freq = 100000; tunegen(); break;
|
|
|
|
|
case 2: freq = 800000; break;
|
|
|
|
|
case 3: freq = 1800000; break;
|
|
|
|
|
case 4: freq = 3650000; break;
|
|
|
|
|
case 5: freq = 4985000; break;
|
|
|
|
|
case 6: freq = 6180000; break;
|
|
|
|
|
case 7: freq = 7200000; break;
|
|
|
|
|
case 8: freq = 10000000; break;
|
|
|
|
|
case 9: freq = 11780000; break;
|
|
|
|
|
case 10: freq = 13630000; break;
|
|
|
|
|
case 11: freq = 14100000; break;
|
|
|
|
|
case 12: freq = 15000000; break;
|
|
|
|
|
case 13: freq = 17655000; break;
|
|
|
|
|
case 14: freq = 21525000; break;
|
|
|
|
|
case 15: freq = 27015000; break;
|
|
|
|
|
case 16: freq = 28400000; break;
|
|
|
|
|
case 17: freq = 50000000; break;
|
|
|
|
|
case 18: freq = 100000000; break;
|
|
|
|
|
case 19: freq = 130000000; break;
|
|
|
|
|
case 20: freq = 144000000; break;
|
|
|
|
|
case 21: freq = 220000000; break;
|
|
|
|
|
}
|
|
|
|
|
si5351.pll_reset(SI5351_PLLA);
|
|
|
|
|
stp = 4; setstep();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void inc_preset() {
|
|
|
|
|
count++;
|
|
|
|
|
if (count > 21) count = 1;
|
|
|
|
|
bandpresets();
|
|
|
|
|
delay(50);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void bandlist() {
|
|
|
|
|
display.setTextSize(2);
|
|
|
|
|
display.setCursor(0, 25);
|
|
|
|
|
if (count == 1) display.print("GEN"); if (count == 2) display.print("MW"); if (count == 3) display.print("160m"); if (count == 4) display.print("80m");
|
|
|
|
|
if (count == 5) display.print("60m"); if (count == 6) display.print("49m"); if (count == 7) display.print("40m"); if (count == 8) display.print("31m");
|
|
|
|
|
if (count == 9) display.print("25m"); if (count == 10) display.print("22m"); if (count == 11) display.print("20m"); if (count == 12) display.print("19m");
|
|
|
|
|
if (count == 13) display.print("16m"); if (count == 14) display.print("13m"); if (count == 15) display.print("11m"); if (count == 16) display.print("10m");
|
|
|
|
|
if (count == 17) display.print("6m"); if (count == 18) display.print("WFM"); if (count == 19) display.print("AIR"); if (count == 20) display.print("2m");
|
|
|
|
|
if (count == 21) display.print("1m");
|
|
|
|
|
if (count == 1) interfreq = 0; else if (!sts) interfreq = IF;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void drawbargraph() {
|
|
|
|
|
byte y = map(n, 1, 42, 1, 14);
|
|
|
|
|
display.setTextSize(1);
|
|
|
|
|
|
|
|
|
|
//Pointer
|
|
|
|
|
display.setCursor(0, 48); display.print("TU");
|
|
|
|
|
switch (y) {
|
|
|
|
|
case 1: display.fillRect(15, 48, 2, 6, WHITE); break;
|
|
|
|
|
case 2: display.fillRect(20, 48, 2, 6, WHITE); break;
|
|
|
|
|
case 3: display.fillRect(25, 48, 2, 6, WHITE); break;
|
|
|
|
|
case 4: display.fillRect(30, 48, 2, 6, WHITE); break;
|
|
|
|
|
case 5: display.fillRect(35, 48, 2, 6, WHITE); break;
|
|
|
|
|
case 6: display.fillRect(40, 48, 2, 6, WHITE); break;
|
|
|
|
|
case 7: display.fillRect(45, 48, 2, 6, WHITE); break;
|
|
|
|
|
case 8: display.fillRect(50, 48, 2, 6, WHITE); break;
|
|
|
|
|
case 9: display.fillRect(55, 48, 2, 6, WHITE); break;
|
|
|
|
|
case 10: display.fillRect(60, 48, 2, 6, WHITE); break;
|
|
|
|
|
case 11: display.fillRect(65, 48, 2, 6, WHITE); break;
|
|
|
|
|
case 12: display.fillRect(70, 48, 2, 6, WHITE); break;
|
|
|
|
|
case 13: display.fillRect(75, 48, 2, 6, WHITE); break;
|
|
|
|
|
case 14: display.fillRect(80, 48, 2, 6, WHITE); break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Bargraph
|
|
|
|
|
display.setCursor(0, 57); display.print("SM");
|
|
|
|
|
switch (x) {
|
|
|
|
|
case 14: display.fillRect(80, 58, 2, 6, WHITE);
|
|
|
|
|
case 13: display.fillRect(75, 58, 2, 6, WHITE);
|
|
|
|
|
case 12: display.fillRect(70, 58, 2, 6, WHITE);
|
|
|
|
|
case 11: display.fillRect(65, 58, 2, 6, WHITE);
|
|
|
|
|
case 10: display.fillRect(60, 58, 2, 6, WHITE);
|
|
|
|
|
case 9: display.fillRect(55, 58, 2, 6, WHITE);
|
|
|
|
|
case 8: display.fillRect(50, 58, 2, 6, WHITE);
|
|
|
|
|
case 7: display.fillRect(45, 58, 2, 6, WHITE);
|
|
|
|
|
case 6: display.fillRect(40, 58, 2, 6, WHITE);
|
|
|
|
|
case 5: display.fillRect(35, 58, 2, 6, WHITE);
|
|
|
|
|
case 4: display.fillRect(30, 58, 2, 6, WHITE);
|
|
|
|
|
case 3: display.fillRect(25, 58, 2, 6, WHITE);
|
|
|
|
|
case 2: display.fillRect(20, 58, 2, 6, WHITE);
|
|
|
|
|
case 1: display.fillRect(15, 58, 2, 6, WHITE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void layout() {
|
|
|
|
|
display.setTextColor(WHITE);
|
|
|
|
|
display.drawLine(0, 20, 127, 20, WHITE);
|
|
|
|
|
display.drawLine(0, 43, 127, 43, WHITE);
|
|
|
|
|
display.drawLine(105, 24, 105, 39, WHITE);
|
|
|
|
|
display.drawLine(87, 24, 87, 39, WHITE);
|
|
|
|
|
display.drawLine(87, 48, 87, 63, WHITE);
|
|
|
|
|
display.drawLine(15, 55, 82, 55, WHITE);
|
|
|
|
|
display.setTextSize(1);
|
|
|
|
|
display.setCursor(59, 23);
|
|
|
|
|
display.print("STEP");
|
|
|
|
|
display.setCursor(54, 33);
|
|
|
|
|
if (stp == 2) display.print(" 1Hz"); if (stp == 3) display.print(" 10Hz"); if (stp == 4) display.print(" 1kHz");
|
|
|
|
|
if (stp == 5) display.print(" 5kHz"); if (stp == 6) display.print("10kHz"); if (stp == 1) display.print(" 1MHz");
|
|
|
|
|
display.setTextSize(1);
|
|
|
|
|
display.setCursor(92, 48);
|
|
|
|
|
display.print("IF:");
|
|
|
|
|
display.setCursor(92, 57);
|
|
|
|
|
display.print(interfreq);
|
|
|
|
|
display.print("k");
|
|
|
|
|
display.setTextSize(1);
|
|
|
|
|
display.setCursor(110, 23);
|
|
|
|
|
if (freq < 1000000) display.print("kHz");
|
|
|
|
|
if (freq >= 1000000) display.print("MHz");
|
|
|
|
|
display.setCursor(110, 33);
|
|
|
|
|
if (interfreq == 0) display.print("VFO");
|
|
|
|
|
if (interfreq != 0) display.print("L O");
|
|
|
|
|
display.setCursor(91, 28);
|
|
|
|
|
if (!sts) display.print("RX"); if (!sts) interfreq = IF;
|
|
|
|
|
if (sts) display.print("TX"); if (sts) interfreq = 0;
|
|
|
|
|
bandlist(); drawbargraph();
|
|
|
|
|
display.display();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void sgnalread() {
|
|
|
|
|
smval = analogRead(adc); x = map(smval, 0, S_GAIN, 1, 14); if (x > 14) x = 14;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void statup_text() {
|
|
|
|
|
display.setTextSize(1); display.setCursor(13, 18);
|
|
|
|
|
display.print("Si5351 VFO/RF GEN");
|
|
|
|
|
display.setCursor(6, 40);
|
|
|
|
|
display.print("JCR RADIO - Ver 2.0");
|
|
|
|
|
display.display(); delay(2000);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void setup() {
|
|
|
|
|
Wire.begin();
|
|
|
|
|
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
|
|
|
|
|
display.clearDisplay();
|
|
|
|
|
display.setTextColor(WHITE);
|
|
|
|
|
display.display();
|
|
|
|
|
|
|
|
|
|
pinMode(2, INPUT_PULLUP);
|
|
|
|
|
pinMode(3, INPUT_PULLUP);
|
|
|
|
|
pinMode(tunestep, INPUT_PULLUP);
|
|
|
|
|
pinMode(band, INPUT_PULLUP);
|
|
|
|
|
pinMode(rx_tx, INPUT_PULLUP);
|
|
|
|
|
|
|
|
|
|
//statup_text(); //If you hang on startup, comment
|
|
|
|
|
|
|
|
|
|
si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);
|
|
|
|
|
si5351.set_correction(cal, SI5351_PLL_INPUT_XO);
|
|
|
|
|
si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA);
|
|
|
|
|
si5351.output_enable(SI5351_CLK0, 1); //1 - Enable / 0 - Disable CLK
|
|
|
|
|
si5351.output_enable(SI5351_CLK1, 0);
|
|
|
|
|
si5351.output_enable(SI5351_CLK2, 0);
|
|
|
|
|
si5351.output_enable(SI5351_CLK2, 0);
|
|
|
|
|
delay(750);
|
|
|
|
|
|
|
|
|
|
#ifdef ESP32DOIT
|
|
|
|
|
//TODO
|
|
|
|
|
#else
|
|
|
|
|
PCICR |= (1 << PCIE2);
|
|
|
|
|
PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
|
|
|
|
|
sei();
|
|
|
|
|
#endif
|
|
|
|
|
//knob.write(0);
|
|
|
|
|
pinMode(PIN_BUTTON_ENCODER, INPUT);
|
|
|
|
|
digitalWrite(PIN_BUTTON_ENCODER, HIGH);
|
|
|
|
|
|
|
|
|
|
count = BAND_INIT;
|
|
|
|
|
bandpresets();
|
|
|
|
|
stp = 4;
|
|
|
|
|
setstep();
|
|
|
|
|
pinMode(PIN_BUTTON_MODE, INPUT);
|
|
|
|
|
digitalWrite(PIN_BUTTON_MODE, HIGH);
|
|
|
|
|
pinMode(PIN_BUTTON_BAND, INPUT);
|
|
|
|
|
digitalWrite(PIN_BUTTON_BAND, HIGH);
|
|
|
|
|
|
|
|
|
|
pinMode(PIN_SWR_FORWARD, INPUT);
|
|
|
|
|
pinMode(PIN_SWR_REVERSE, INPUT);
|
|
|
|
|
|
|
|
|
|
//lcd.clear();
|
|
|
|
|
//lcd.setCursor(2, 7);
|
|
|
|
|
//lcd.print("WELCOME!");
|
|
|
|
|
//delay(500);
|
|
|
|
|
//displayInfo();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void loop() {
|
|
|
|
|
if (freqold != freq) {
|
|
|
|
|
time_now = millis();
|
|
|
|
|
tunegen();
|
|
|
|
|
freqold = freq;
|
|
|
|
|
}
|
|
|
|
|
void loop(){
|
|
|
|
|
// //if (displayNeedsUpdate) {displayInfo();}
|
|
|
|
|
// //delay(80);
|
|
|
|
|
|
|
|
|
|
if (interfreqold != interfreq) {
|
|
|
|
|
time_now = millis();
|
|
|
|
|
tunegen();
|
|
|
|
|
interfreqold = interfreq;
|
|
|
|
|
}
|
|
|
|
|
// //detect whether encoder has changed position
|
|
|
|
|
// long reading = encoder.read();
|
|
|
|
|
// long encoderChange = reading - encoderPosition;
|
|
|
|
|
// encoderPosition = reading;
|
|
|
|
|
|
|
|
|
|
if (xo != x) {
|
|
|
|
|
time_now = millis();
|
|
|
|
|
xo = x;
|
|
|
|
|
}
|
|
|
|
|
// displayNeedsUpdate = false;
|
|
|
|
|
|
|
|
|
|
if (digitalRead(tunestep) == LOW) {
|
|
|
|
|
time_now = (millis() + 300);
|
|
|
|
|
setstep();
|
|
|
|
|
delay(300);
|
|
|
|
|
}
|
|
|
|
|
// //step up or down or change step size, for either button presses or encoder turns
|
|
|
|
|
// if ((encoderChange > 0)){currFreq += steps[currMode][stepIndex]; currFreq = min(currFreq, MAX_FREQ); setFrequency_5351(currFreq); displayNeedsUpdate = true;}
|
|
|
|
|
// if ((encoderChange < 0)){currFreq -= steps[currMode][stepIndex]; currFreq = max(currFreq, MIN_FREQ); setFrequency_5351(currFreq); displayNeedsUpdate = true;}
|
|
|
|
|
|
|
|
|
|
if (digitalRead(band) == LOW) {
|
|
|
|
|
time_now = (millis() + 300);
|
|
|
|
|
inc_preset();
|
|
|
|
|
delay(300);
|
|
|
|
|
}
|
|
|
|
|
// //pressing the encoder button increments through the possible step sizes for each mode
|
|
|
|
|
// if (checkButtonPress(PIN_BUTTON_ENCODER)){stepIndex = (stepIndex + 1) % (NUM_STEP_OPTIONS[currMode]); displayNeedsUpdate = true;}
|
|
|
|
|
|
|
|
|
|
if (digitalRead(rx_tx) == LOW) {
|
|
|
|
|
time_now = (millis() + 300);
|
|
|
|
|
sts = 1;
|
|
|
|
|
} else sts = 0;
|
|
|
|
|
// //pressing the mode button cycles through the available modes
|
|
|
|
|
// if (checkButtonPress(PIN_BUTTON_MODE)){currMode = (currMode+1) % NUM_MODES; stepIndex = 0; setFrequency_5351(currFreq); displayNeedsUpdate = true;}
|
|
|
|
|
|
|
|
|
|
if ((time_now + period) > millis()) {
|
|
|
|
|
displayfreq();
|
|
|
|
|
layout();
|
|
|
|
|
}
|
|
|
|
|
sgnalread();
|
|
|
|
|
// /*The mode button: if currFreq is inside an amateur band, save that frequency as the one to return to when
|
|
|
|
|
// * the user returns to this band, and jump to the return frequency for the next higher band. Otherwise,
|
|
|
|
|
// * just jump to the next higher band
|
|
|
|
|
// */
|
|
|
|
|
// if (checkButtonPress(PIN_BUTTON_BAND)){
|
|
|
|
|
// int currBand = getCurrentBand();
|
|
|
|
|
// if (currBand >= 0){
|
|
|
|
|
// lastBandFreq[currBand] = currFreq;
|
|
|
|
|
// currFreq = lastBandFreq[(getCurrentBand() + 1) % NUM_BANDS];
|
|
|
|
|
// setFrequency_5351(currFreq);
|
|
|
|
|
// }
|
|
|
|
|
// else if (currBand == -2 || currBand == -3){
|
|
|
|
|
// currFreq = lastBandFreq[0];
|
|
|
|
|
// setFrequency_5351(currFreq);
|
|
|
|
|
// }
|
|
|
|
|
// else if (currBand == -1){
|
|
|
|
|
// for (int i = 0; i < NUM_BANDS; i++){
|
|
|
|
|
// if (currFreq < lastBandFreq[i]){currFreq = lastBandFreq[i]; setFrequency_5351(currFreq); break;}
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// displayNeedsUpdate = true;
|
|
|
|
|
// }
|
|
|
|
|
}
|