mirror of
https://github.com/JeffersGlass/DDS_VFO.git
synced 2024-11-27 02:37:37 -06:00
Added bands, band switch, mode switch
Added band switching functionality with memory, ARRL bandplan baked in, mode switching for test, basic, polyakov, and BFO modes. BFO code not implemented yet.
This commit is contained in:
parent
1356c88589
commit
db663140a4
275
VFO/VFO.ino
275
VFO/VFO.ino
|
@ -5,23 +5,94 @@
|
|||
|
||||
|
||||
//-----------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
|
||||
*/
|
||||
|
||||
long currFreq = 1000000;
|
||||
long currFreq = 1800000;
|
||||
|
||||
//long steps[] = {1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000};
|
||||
//const int MAX_STEP_INDEX = 15;
|
||||
//char* stepNames[] = {" 1 hz", " 2 hz", " 5 hz", " 10 hz", " 20 hz", " 50 hz", "100 hz", "200 hz", "500 hz", " 1 Khz", " 2 Khz", " 5 Khz", "10 Khz", "20 Khz", "50 Khz", "100Khz"};
|
||||
//-----Enumerations of frequency steps and their labels for each mode----//
|
||||
|
||||
long steps[] = {1, 10, 100, 1000, 10000, 100000, 500000, 1000000, 5000000, 10000000};
|
||||
const int MAX_STEP_INDEX = 9;
|
||||
char* stepNames[] = {" 1 hz", " 10 hz", "100 hz", " 1Khz", " 10KHZ", "100Khz", "500Khz", " 1Mhz", " 5Mhz", " 10Mhz"};
|
||||
enum modes{mode_testing = 0, mode_basic, mode_polyakov, mode_bfo};
|
||||
const int NUM_MODES = 4;
|
||||
int currMode = mode_basic;
|
||||
|
||||
int stepIndex = 0;
|
||||
char* modeNames[NUM_MODES] = {"TEST", "VFO", "POLYA", "BFO"};
|
||||
|
||||
long lastButtonPress[] = {0,0,0,0,0};
|
||||
boolean buttonActive[] = {false, false, false, false, false};
|
||||
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, 1}, //basic
|
||||
{1000, 100, 10, 1}, //polyakov
|
||||
{1000, 100, 10, 1}, //bfo
|
||||
};
|
||||
|
||||
const int NUM_STEP_OPTIONS[NUM_MODES] = {
|
||||
10, //testing
|
||||
5, //basic
|
||||
4, //polyakov
|
||||
4, //bfo
|
||||
};
|
||||
char* stepNames[][10] = {
|
||||
{" 10MHz", " 5MHz", " 1MHz", "500Khz", "100KHz", " 10KHz", " 1KHz", " 100Hz", " 10Hz", " 1 Hz"}, //basic
|
||||
{" 10KHz", " 1KHz", " 100 Hz", " 10 Hz", " 1 Hz"}, //basic
|
||||
{" 1KHz", " 100 Hz", " 10 Hz", " 1 Hz"}, //polyakov
|
||||
{" 1KHz", " 100 Hz", " 10 Hz", " 1 Hz"} //BFO
|
||||
};
|
||||
|
||||
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;
|
||||
char* bandNames[NUM_BANDS] = {"160m", "80m", "40m", "30m", "20m", "17m", "15m", "12m", "10m"};
|
||||
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;
|
||||
|
@ -48,9 +119,8 @@ Encoder encoder(2, 3);
|
|||
const int PIN_BUTTON_ENCODER = 1;
|
||||
|
||||
//Button Pins//
|
||||
const int PIN_BUTTON_UP = 4;
|
||||
const int PIN_BUTTON_DOWN = 5;
|
||||
const int PIN_BUTTON_STEP = 6;
|
||||
const int PIN_BUTTON_MODE = 4;
|
||||
const int PIN_BUTTON_BAND = 0;
|
||||
const int BUTTON_DEBOUNCE_TIME = 10; //milliseconds
|
||||
|
||||
void setup(){
|
||||
|
@ -62,45 +132,81 @@ void setup(){
|
|||
|
||||
si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0);
|
||||
si5351.set_freq(currFreq * 100ULL, 0ULL, SI5351_CLK0);
|
||||
si5351.output_enable(SI5351_CLK0, 1);
|
||||
si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA);
|
||||
|
||||
si5351.output_enable(SI5351_CLK1, 0);
|
||||
si5351.output_enable(SI5351_CLK2, 0);
|
||||
delay(750);
|
||||
|
||||
//knob.write(0);
|
||||
pinMode(PIN_BUTTON_UP, INPUT);
|
||||
digitalWrite(PIN_BUTTON_UP, HIGH);
|
||||
pinMode(PIN_BUTTON_DOWN, INPUT);
|
||||
digitalWrite(PIN_BUTTON_DOWN, HIGH);
|
||||
pinMode(PIN_BUTTON_STEP, INPUT);
|
||||
digitalWrite(PIN_BUTTON_STEP, HIGH);
|
||||
pinMode(PIN_BUTTON_ENCODER, INPUT);
|
||||
digitalWrite(PIN_BUTTON_ENCODER, HIGH);
|
||||
|
||||
pinMode(PIN_BUTTON_MODE, INPUT);
|
||||
digitalWrite(PIN_BUTTON_MODE, HIGH);
|
||||
pinMode(PIN_BUTTON_BAND, INPUT);
|
||||
digitalWrite(PIN_BUTTON_BAND, HIGH);
|
||||
|
||||
lcd.clear();
|
||||
lcd.setCursor(2, 7);
|
||||
lcd.print("WELCOME!");
|
||||
delay(500);
|
||||
displayInfo();
|
||||
}
|
||||
|
||||
void loop(){
|
||||
displayInfo();
|
||||
delay(200);
|
||||
if (displayNeedsUpdate) {displayInfo();}
|
||||
delay(80);
|
||||
|
||||
//detect whether encoder has changed position
|
||||
long reading = encoder.read();
|
||||
long encoderChange = reading - encoderPosition;
|
||||
encoderPosition = reading;
|
||||
|
||||
//step up or down or change step size, for either button presses or encoder turns
|
||||
if (checkButtonPress(PIN_BUTTON_UP) || (encoderChange > 0)){currFreq += steps[stepIndex]; currFreq = min(currFreq, MAX_FREQ); si5351.set_freq(currFreq * 100ULL, 0ULL, SI5351_CLK0);}
|
||||
if (checkButtonPress(PIN_BUTTON_DOWN) || (encoderChange < 0)){currFreq -= steps[stepIndex]; currFreq = max(currFreq, MIN_FREQ); si5351.set_freq(currFreq * 100ULL, 0ULL, SI5351_CLK0);}
|
||||
if (checkButtonPress(PIN_BUTTON_STEP) || checkButtonPress(PIN_BUTTON_ENCODER)){stepIndex = (stepIndex + 1) % (MAX_STEP_INDEX+1);}
|
||||
displayNeedsUpdate = false;
|
||||
|
||||
//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;}
|
||||
|
||||
//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;}
|
||||
|
||||
//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;}
|
||||
|
||||
/*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);}
|
||||
}
|
||||
}
|
||||
displayNeedsUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
void displayInfo(){
|
||||
lcd.clear();
|
||||
|
||||
// frequency information should take up the first 11 spaces on the first line:
|
||||
if (currFreq >= 100000000) lcd.setCursor(4, 1);
|
||||
else if (currFreq > 10000000) lcd.setCursor(5, 1);
|
||||
else lcd.setCursor(6, 1);
|
||||
// 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);
|
||||
|
@ -121,11 +227,40 @@ void displayInfo(){
|
|||
for (int i = 0; i < hzPad; i++) lcd.print("0");
|
||||
lcd.print(hz);
|
||||
|
||||
//Step Information should take the first 11 spaces on the 2nd line
|
||||
//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, 3);
|
||||
lcd.setCursor(4, 2);
|
||||
lcd.print("STEP:");
|
||||
lcd.print(stepNames[stepIndex]);
|
||||
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());
|
||||
}
|
||||
|
||||
boolean checkButtonPress(int pin){
|
||||
|
@ -141,3 +276,79 @@ boolean checkButtonPress(int pin){
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void setFrequency_5351(long newFreq){
|
||||
switch (currMode){
|
||||
case mode_testing:
|
||||
si5351.set_freq(newFreq * 100ULL, 0ULL, SI5351_CLK0);
|
||||
break;
|
||||
case mode_basic:
|
||||
si5351.set_freq(newFreq * 100ULL, 0ULL, SI5351_CLK0);
|
||||
break;
|
||||
case mode_polyakov:
|
||||
si5351.set_freq((newFreq / 2) * 100ULL, 0ULL, SI5351_CLK0);
|
||||
break;
|
||||
case mode_bfo:
|
||||
si5351.set_freq(newFreq * 100ULL, 0ULL, SI5351_CLK0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//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;
|
||||
}
|
||||
|
||||
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';
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user