From b2345cf54e396917c0aa18c196935a06f4a7abe2 Mon Sep 17 00:00:00 2001 From: mattbk Date: Thu, 25 Jan 2024 09:31:20 -0600 Subject: [PATCH] Init new project with variations on KK9JEF's code. --- .gitignore | 5 + .vscode/extensions.json | 10 + .vscode/settings.json | 3 + README.md | 2 + Vfo Wiring.fzz | Bin 0 -> 17905 bytes include/README | 39 ++++ lib/README | 46 +++++ platformio.ini | 17 ++ src/main.cpp | 395 ++++++++++++++++++++++++++++++++++++++++ test/README | 11 ++ vfo_oled.code-workspace | 8 + 11 files changed, 536 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json create mode 100644 README.md create mode 100644 Vfo Wiring.fzz create mode 100644 include/README create mode 100644 lib/README create mode 100644 platformio.ini create mode 100644 src/main.cpp create mode 100644 test/README create mode 100644 vfo_oled.code-workspace diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..89cc49c --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..080e70d --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,10 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "platformio.platformio-ide" + ], + "unwantedRecommendations": [ + "ms-vscode.cpptools-extension-pack" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..cad7657 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "cmake.configureOnOpen": false +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..8e6c2f0 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# DDS_VFO +Code for the arduino-controlled VFO based on the Si5351 chip diff --git a/Vfo Wiring.fzz b/Vfo Wiring.fzz new file mode 100644 index 0000000000000000000000000000000000000000..a93c2cb3ce64f8e542464699ca9b24288af81fe5 GIT binary patch literal 17905 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 b0R|}NwFG#xvVlYyfzTRAF906w!oUCkF^&rh literal 0 HcmV?d00001 diff --git a/include/README b/include/README new file mode 100644 index 0000000..194dcd4 --- /dev/null +++ b/include/README @@ -0,0 +1,39 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the usual convention is to give header files names that end with `.h'. +It is most portable to use only letters, digits, dashes, and underscores in +header file names, and at most one dot. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/lib/README b/lib/README new file mode 100644 index 0000000..6debab1 --- /dev/null +++ b/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into executable file. + +The source code of each library should be placed in a an own separate directory +("lib/your_library_name/[here are source files]"). + +For example, see a structure of the following two libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +and a contents of `src/main.c`: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +PlatformIO Library Dependency Finder will find automatically dependent +libraries scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..a2a4692 --- /dev/null +++ b/platformio.ini @@ -0,0 +1,17 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:metro] +platform = atmelavr +board = metro +framework = arduino +lib_deps = + etherkit/Etherkit Si5351@^2.1.4 + paulstoffregen/Encoder@^1.4.4 diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..3d3b03e --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,395 @@ +//----------- 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 +/* + */ + +#include +#include +#include +//#include +#include + + +//----------- 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 = 1800000; //in HZ +long ifFreq = 8865000; //in HZ + +//-----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; +//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; + } + 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; + } +} + +//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'; +} + +void setup(){ + // inialize LCD, display welcome message + //lcd.begin(20, 4); + //delay(250); + //lcd.setCursor(4, 1); + //lcd.print("VFO STARTING"); + + 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); + + si5351.output_enable(SI5351_CLK1, 0); + si5351.output_enable(SI5351_CLK2, 0); + delay(750); + + //knob.write(0); + 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); + + 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 (displayNeedsUpdate) {displayInfo();} + //delay(80); + + //detect whether encoder has changed position + long reading = encoder.read(); + long encoderChange = reading - encoderPosition; + encoderPosition = reading; + + 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); break;} + } + } + displayNeedsUpdate = true; + } +} \ No newline at end of file diff --git a/test/README b/test/README new file mode 100644 index 0000000..9b1e87b --- /dev/null +++ b/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PlatformIO Test Runner and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PlatformIO Unit Testing: +- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html diff --git a/vfo_oled.code-workspace b/vfo_oled.code-workspace new file mode 100644 index 0000000..876a149 --- /dev/null +++ b/vfo_oled.code-workspace @@ -0,0 +1,8 @@ +{ + "folders": [ + { + "path": "." + } + ], + "settings": {} +} \ No newline at end of file