/* 6/2018 From N6QW This skecth is for a single conversion 9.0 MHz IF transceiver operatiing on 20 Meters Modified 6/15/2018 to operate with the dual VFO code 4/2019 Code Modified for two VFO displaying on a 1/2 OLED Screen. Includes the tune functiom The Si5351 and Rotary are included files and must be in the same sketch folder */ #include #include #include "si5351.h" #include "Rotary.h" #include #include #define OLED_RESET 4 Adafruit_SSD1306 display(OLED_RESET); #include "Tone.h" #define TONE_PIN 6 #define NOTE_B5 988 #define ENCODER_B 2 // Encoder pin A #define ENCODER_A 3 // Encoder pin B #define ENCODER_BTN A3 Si5351 si5351; long int frq; int_fast32_t rx = 23201500L; // Starting frequency of VFO operating frequency plus offset which is selectable IF = 9.0 MHz int_fast32_t rx2= 0L; // variable to hold the updated frequency int_fast32_t increment = 10; // starting VFO update increment in HZ. int_fast32_t bfo = 9001500L; // default offset sideband inversion thus USB int_fast32_t rx1 = 23201500L; int_fast32_t rx3 = 23075500L; //FT8 Frequency 14.074 MHz USB int_fast32_t vfoA = 1; int_fast32_t vfoB = 1; int_fast32_t old_vfoA = 1; int_fast32_t old_vfoB = 1; int RunOnce = 1; String hertz = "100"; //initial tune step rate. byte ones,tens,hundreds,thousands,tenthousands,hundredthousands,millions ; //Placeholders Rotary r = Rotary(3,2); // sets the pins the rotary encoder uses. Must be interrupt pins. int i = 0; int z = 0; const int TX = 4; //USE VFO A only on transmit const int VS = 5; //VFO Select const int SW = A1; // Toggle Switch USB/LSB const int SW1 = A2; // provides the TUNE fucntion const int VFO = A0; const int tonepin = 6; int buttonstate = 0; int buttonState = 0; int lastButtonState = 0; int lastbuttonstate = 0; void setup() { display.clearDisplay(); // clears the screen and buffer PCICR |= (1 << PCIE2); PCMSK2 |= (1 << PCINT18) | (1 << PCINT19); sei(); display.setRotation(2); // display.begin(0x3C); display.begin(SSD1306_SWITCHCAPVCC, 0x3C); delay(20); // Connect to a button that goes to GND on push to set the 4 step rates pinMode(SW1, INPUT); // USB/LSB Select digitalWrite(SW1, HIGH); pinMode(SW,INPUT); digitalWrite(SW,HIGH); pinMode(4, INPUT); digitalWrite(4, HIGH); pinMode(5, INPUT); digitalWrite(5, HIGH); pinMode(7, OUTPUT); digitalWrite(7, LOW); pinMode(A0, INPUT); pinMode(A3,INPUT); digitalWrite(A3,HIGH); #if defined(__MK20DX128__) || defined(__MK20DX256__) tft.setBitrate(24000000); #endif si5351.init(SI5351_CRYSTAL_LOAD_8PF); si5351.set_correction(100); si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA); //si5351.set_freq(rx , SI5351_PLL_FIXED, SI5351_CLK0); si5351.set_freq(bfo, 0, SI5351_CLK2); si5351.drive_strength(SI5351_CLK0,SI5351_DRIVE_8MA); // Higher Drive since it is a TUF-1 DBM si5351.drive_strength(SI5351_CLK2,SI5351_DRIVE_8MA); } void setincrement(){ if (increment == 10){increment = 100; hertz = "100";} else if (increment == 100){increment = 1000; hertz="1K";} // else if (increment == 1000){increment = 10000; hertz="10K"; } // else if (increment == 10000){increment = 100000; hertz="100K";} // else if (increment == 100000){increment = 1000000; hertz=" 1M";} else{increment = 10; hertz = " 10"; } ; delay(200); // Adjust this delay to speed up/slow down the button menu scroll speed. }; void showFreq(){ millions = int(((rx)-bfo)/1000000); hundredthousands = ((((rx)-bfo )/100000)%10); tenthousands = ((((rx)-bfo )/10000)%10); thousands = ((((rx)-bfo )/1000)%10); hundreds = ((((rx)-bfo )/100)%10); tens = ((((rx)-bfo )/10)%10); ones = ((((rx)-bfo )/1)%10); } ISR(PCINT2_vect) { unsigned char result = r.process(); if (result) { if (result == DIR_CW){rx=rx-(1*increment);} else {rx = rx+(1*increment);}; if (rx >=27000000L){rx=rx2;}; //This sets the upper LO range if (rx <=5000000L){rx=rx2;}; // This sets the lower LO range } } //*********************************************** LooP****************** void loop() { SplashScreen(); RunOnce = 0; checkMode(); CheckSB(); CheckDisplay(); CheckVS(); // CheckTX(); lastButtonState = buttonState; lastbuttonstate = buttonstate; showFreq(); display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(87,0); display.println(hertz); display.display(); if (rx != rx2){ showFreq(); si5351.set_freq(rx , SI5351_PLL_FIXED, SI5351_CLK0); rx2=rx ; } buttonstate = digitalRead(A3); if(buttonstate != lastbuttonstate){ if(buttonstate == LOW) { display.setTextSize(1); display.setTextColor(BLACK); display.setCursor(87,0); display.println(hertz); } setincrement(); } } //********************************************************** void checkMode(){ buttonState = digitalRead(SW1); // creates a 10 second tuning pulse trani 50% duty cycle and makes TUNE appear on the screen if(buttonState != lastButtonState){ if(buttonState == LOW){ useVFOA(); display.setTextColor(WHITE); display.setTextSize(1); display.setCursor(50,0); display.print("TUNE"); display.display(); delay(12); for(int i = 0; i < 100; i++) { tone(6, NOTE_B5); delay(50); noTone(6); delay(50); } ; } else{ display.setTextSize(1); // This prints a Black TUNE over the RED TUNE and makes it disappear from the scereen display.setTextColor(BLACK); display.setCursor(50, 0); display.print("TUNE"); noTone(6); } delay(50); } } //*********************Check Sideband ************************************ void CheckSB(){ // *** S Meter code smaple the audio output and uses a log function if(digitalRead(SW)){ //********If SW is true do the following. bfo = 9001500L; //Sideband inversion with the LO above the operating frequency si5351.set_freq( bfo, 0, SI5351_CLK2);{ display.setTextSize(1); // lets you toggle bewteen USB and LSB without having a screen overwrite display.setTextColor(BLACK); display.setCursor(0,0); display.println("LSB"); display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(0,0); display.println("USB");} } else{ //**********if not, do this. bfo =8998500L; // Keep in mind the LO is above the operating frequency so sideband inversion si5351.set_freq( bfo, 0, SI5351_CLK2); display.setTextSize(1); // lets you toggle bewteen USB and LSB without having a screen overwrite display.setTextColor(BLACK); display.setCursor(0,0); display.println("USB"); display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(0,0); display.println("LSB"); } } //*************************** Splash Screen ******************** void SplashScreen() { if (RunOnce == 1) { display.setTextColor(WHITE); display.setCursor(0, 0); display.setTextSize(1); display.println("N6QW"); display.println("AKA Pete"); display.println("Only in America..."); display.display(); display.clearDisplay(); delay(2000); display.setTextColor(WHITE); display.setTextSize(2); display.setCursor(0,8); display.println("Ham Genius"); display.display(); delay(6000); RunOnce = 0; } display.clearDisplay(); } // ********************** Check Display *************************** void CheckDisplay(){ display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(0,10); display.println("A"); display.setCursor(0,22); display.println("B"); display.setCursor(25,0); display.println("N6QW"); display.setCursor(110,0); display.print("Hz"); } //*************************************** VFO A or B **************** void CheckVS(){ //thus a toggle switch controls writing which VFO indirectly digitalRead(5); if(digitalRead(5) == HIGH){ digitalWrite(A0, HIGH); useVFOA(); z = 1; } else { digitalWrite(A0, LOW); useVFOB(); z = 2; } } // *************************************VFO A******************* void useVFOA(){ // Has logic so you can have the correct display in concert with USB/LSB CheckFreq1(); digitalRead(A0) == HIGH; display.setTextSize(1); rx = rx1; display.setTextColor(WHITE); display.setCursor(23,10); display.print(rx1-bfo); display.setTextSize(1); display.setCursor(110,10); display.print("ON"); display.setCursor(110,22); display.print("OFF"); rx1 == vfoA; vfoA = rx1; old_vfoA = vfoA; showFreq(); si5351.set_freq(vfoA , SI5351_PLL_FIXED, SI5351_CLK0); } // ******************************** VFO B ***************************** void useVFOB(){ CheckFreq2(); digitalRead(A0) == LOW; display.setTextSize(1); rx = rx3; display.setTextColor(WHITE); display.setCursor(23,22); display.print(rx3-bfo); display.setTextSize(1); display.setCursor(110,22); display.print("ON"); display.setCursor(110,10); display.print("OFF"); rx3 == vfoB; vfoB = rx3; old_vfoB = vfoB; si5351.set_freq(vfoB , SI5351_PLL_FIXED, SI5351_CLK0); } //************* /* //Not Needed void CheckFreq(){ if (rx != rx2){ showFreq(); si5351.set_freq(rx , SI5351_PLL_FIXED, SI5351_CLK0); Serial.println(rx); //si5351.set_freq(bfo , SI5351_PLL_FIXED, SI5351_CLK2); rx2 = rx; } } */ //************************* Frequency Change 1 ************************* void CheckFreq1(){ if( rx != rx2){ // This is the key to memory si5351.set_freq(rx1 , SI5351_PLL_FIXED, SI5351_CLK0); Serial.println(rx1); si5351.set_freq(bfo , SI5351_PLL_FIXED, SI5351_CLK2); rx1 = rx; showFreq(); } } //************************* Check Frequency 2 ******************************* void CheckFreq2(){ if( rx != rx2){ si5351.set_freq(rx3 , SI5351_PLL_FIXED, SI5351_CLK0); si5351.set_freq(bfo , SI5351_PLL_FIXED, SI5351_CLK2); rx3 = rx; showFreq(); } } // *************************** Check for Transmit ************************* // ***************************************Check for TRansmit ****************************************** /* This has proven to be a nasty problem for me as I have not been able to implement this in software. Try as I might * the software does not follow what I want to do. I have total functionality with two separate VFO's --my failure is how * to transmit on one while receiving on the other. Finally I hit on a simple hardware solution. Pin A0 is either set high or * low depedning on whether Pin 5 is grounded via a switch. So lets add some hardware in that loop like a NC relay. So here is * how it would work. * 1) In the receiove mode by engaging Pin 5 high or low with an external switch we have a choice of VFO A or VFO B * 2) Let us say we want to make VFO A the Receive and Transmit VFO and VFO B Receive only * 3) When the PTT Switch ground Pin 4 it also causes a NC relay to open. So if you were receiving on VFO B the break in the circuit * would cause the controlling VFO to be VFO A whihc means you would be transmitting on VFO A. * 4) If you purposefully open the VFO A/B sweitch then you would receive and transmit entirely on VFO A. * 5) Adding yet another switch in the loop in the line feeding the juice (or ground) to the NC relay Then you would receive and transmit only on VFO B * 6) Normal Opeartion would entail opening the A/B select swithc so that you would pick the transmit frequency on VFO A. Then closing the switch you would * have VFO A fixed for transmit and tune around on VFO B. Opening the switch then tuning and transmitting is on VFO A */ /* void CheckTX() { //Green Dot comes on to PTT Also selects which VFO A is used for tranmit digitalRead(4); if(digitalRead(4) == LOW){ display.setCursor(1,35); //We are dsiplayin the received Freq display.setTextSize(2); display.setTextColor(WHITE); display.println(rx3 - bfo); display.setCursor(52,50); //We are dsiplayin the received Freq display.setTextSize(2); display.setTextColor(WHITE); display.println("T=A"); display.fillCircle(108, 40, 4, WHITE); useVFOA(); digitalWrite(7, HIGH); } } */