/* This entire program is taken from Jason Mildrum, NT7S and Przemek Sadowski, SQ9NJE. There is not enough original code written by me to make it worth mentioning. http://nt7s.com/ http://sq9nje.pl/ http://ak2b.blogspot.com/ I made some mods ...first updating the sketch to new library from NT7S ..in frequency coverage and the mode for frequency change.. pressing the encoder and turn it at same time ...it will move a underline showing the place where it is OK to change THIS SKETCH IS A VFO FOR superheterodine or SSB equipments that uses IF http://py2ohh.w2c.com.br/ Via menu (pushing encoder button on startup) - 1 SI5351 xtal freq calibration - 2 Adjust LSB BFO frequency - 3 Adjust USB BFO frequency Button B2 Single click MODO LSB, USB , CW FT8 Double cklick frequency banda depemds of modo long click save the actual frequency who alears in the next startup] - ******************************* 05\24 iniciando o programa *************************************************************************** Include **************************************************************************/ // #include #include #include #include #include "EEPROM.h" #include "PinButton.h" /************************************************************************** Definitions PINS **************************************************************************/ #define ENCODER_A 3 // Encoder pin A #define ENCODER_B 2 // Encoder pin B #define ENCODER_BTN 11 #define LCD_RS 4 #define LCD_E 6 #define LCD_D4 7 #define LCD_D5 8 #define LCD_D6 9 #define LCD_D7 10 #define B1 A1 #define B2 A2 #define pwmi 5 /************************************************************************** EEPROM data adress **************************************************************************/ #define EE_SVD_SI5351XT 0 #define EE_SVD_LSB 4 #define EE_SVD_USB 8 #define EE_SVD_LASTFRQ 12 //#define EE_SVD_RADIX 16 //#define EE_SVD_MODO 20 LiquidCrystal lcd(LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7); Si5351 si5351; Rotary r = Rotary(ENCODER_A, ENCODER_B); /************************************************************************** Declarations **************************************************************************/ unsigned long LSB = 10000000; unsigned long USB = 10000000; unsigned long bfo = 10000000; unsigned long xfo = 10000000; unsigned long vfo = 7000000; unsigned long xtalfreq = 25000000; volatile uint32_t radix = 1000; // unsigned long freq = 7074000; boolean changed_f = 1; String tbfo = ""; short und = 3; short pot = 3; int press1; int count = 1; int valor = 1; int lateral = 0; String modo = "LSB"; int banda = 0; PinButton myButton(B2); int an = A6; float value = 1; unsigned long agora = 0; unsigned long antes = 0; String smtr ; String oldsmtr ; // Caracter drawing Smeter smiling //https://www.riyas.org/2014/06/online-lcd-custom-character-font-lcd-shield-display.html byte grf3[8] = { B11111, B10101, B11111, B11011, B11111, B11111, B10001, B01110 }; byte grf2[8] = { B11111, B10101, B11111, B11011, B11111, B11111, B10001, B11111 }; byte grf1[8] = { B11111, B10101, B11111, B11011, B11111, B01110, B10001, B11111 }; byte grf0[8] = { B11111, B10101, B11111, B11011, B01110, B01110, B10001, B11111 }; /**************************************/ /* Interrupt service routine for */ /* encoder frequency change */ /**************************************/ ISR(PCINT2_vect) { unsigned char result = r.process(); if (result == DIR_CW) { set_frequency(1); } else if (result == DIR_CCW) { set_frequency(-1); } } /**************************************/ /* Change the frequency */ /* dir = 1 Increment */ /* dir = -1 Decrement /**************************************/ ///////////////////////////////////////////////////// /**************************************/ /* Menu press encoder btn at power on, for calibrate and save in eeprom si5351 xtal value for acurate frequency. also adjust the ladder filter frequency borders to use with LSB, CW and USB, also save it in eeprom */ /**************************************/ void menu() { xfo = vfo; lcd.clear(); lcd.setCursor(0, 0); lcd.print("CALIBRAR"); delay(3000); lcd.setCursor(0, 1); lcd.print("oK=B1 sai=B2 "); while (count > 0) { if ((!digitalRead(B1))) { calibrar(); count = 0; } if ((!digitalRead(B2))) { count = 0; } } lcd.clear(); count = 1; lcd.setCursor(0, 0); lcd.print("AJUSTE LSB"); delay(3000); lcd.setCursor(0, 1); lcd.print("oK=B1 sai=B2 "); while (count > 0) { if ((!digitalRead(B1))) { lsbadj(); } if ((!digitalRead(B2))) { count = 0; } } lcd.clear(); count = 1; lcd.setCursor(0, 0); lcd.print("AJUSTE USB"); delay(3000); lcd.setCursor(0, 1); lcd.print("oK=B1 sai=B2 "); while (count > 0) { if ((!digitalRead(B1))) { usbadj(); } if ((!digitalRead(B2))) { count = 0; } } vfo = xfo; lcd.clear(); count = 1; } //////////////////////////calibração da frequecia si5351 void calibrar() { //si5351.set_freq( (bfo - vfo)* 100ULL, SI5351_CLK0); lcd.setCursor(0, 0); lcd.print("CALIBRAR XTAL FR"); lcd.setCursor(0, 1); lcd.print("Medir freq CLK0"); delay(5000); lcd.clear(); lcd.setCursor(0, 0); lcd.print("Qdo ok clique B1"); lcd.setCursor(0, 1); lcd.print("Ajuste fq medida"); delay(5000); vfo = 25000000; si5351.set_freq( (vfo) * 100ULL, SI5351_CLK0); lcd.clear(); lcd.setCursor(0, 1); lcd.print("Qdo ok clique B1"); display_frequency(); while (digitalRead(B1)) { if (changed_f) { display_frequency(); changed_f = 0; } potencia(); } lcd.setCursor(1, 1); lcd.print(vfo); lcd.print(" espere "); delay (3000); lcd.setCursor(0, 1); lcd.print("B2=SAI B1=GRAVA "); while (digitalRead(B2)) { if (!digitalRead(B1)) { eeprom_write_dword((uint32_t *)EE_SVD_SI5351XT, vfo); lcd.setCursor(0, 1); lcd.print(" GRAVADO "); delay(2000); lcd.setCursor(0, 1); lcd.print("B2=SAI "); } } lcd.clear(); } /////////////////////ajuste da frequencia do bfo para LSB void lsbadj() { si5351.set_freq( (17100000) * 100ULL, SI5351_CLK0); si5351.set_freq( (LSB) * 100ULL, SI5351_CLK2); lcd.setCursor(0, 0); lcd.print("VFO em 17100kHz"); lcd.setCursor(0, 1); lcd.print(" BFO em "); lcd.print(LSB); delay(5000); lcd.clear(); lcd.setCursor(0, 0); lcd.print("AJ. ruido alto"); lcd.setCursor(0, 1); lcd.print("AJ TOM + GRAVE"); delay(5000); vfo = 10003000; lcd.clear(); lcd.setCursor(0, 1); lcd.print("Qdo ok clique B1"); display_frequency(); while (digitalRead(B1)) { if (changed_f) { display_frequency(); changed_f = 0; } potencia(); si5351.set_freq( (vfo) * 100ULL, SI5351_CLK2); } LSB = vfo; lcd.clear(); lcd.setCursor(0, 0); lcd.print("Encontre um QSO "); lcd.setCursor(0, 1); lcd.print("Com 0 ou 5kHz "); delay(5000); lcd.setCursor(0, 1); lcd.print("Qdo ok clique B1"); lcd.clear(); vfo = 17100000; display_frequency(); lcd.setCursor(0, 1); lcd.print("Qdo ok clique B1"); while (digitalRead(B1)) { if (changed_f) { display_frequency(); changed_f = 0; } potencia(); si5351.set_freq( (vfo) * 100ULL, SI5351_CLK0); } lcd.setCursor(0, 0); lcd.print("AJUSTE AGORA BFO"); lcd.setCursor(0, 1); lcd.print("ATE FICAR CLARO "); delay(5000); lcd.clear(); vfo = LSB; display_frequency(); lcd.setCursor(0, 1); lcd.print("Qdo ok clique B1"); while (digitalRead(B1)) { if (changed_f) { display_frequency(); changed_f = 0; } potencia(); si5351.set_freq( (vfo) * 100ULL, SI5351_CLK2); } lcd.setCursor(1, 1); lcd.print(vfo); lcd.print(" espere"); delay (3000); lcd.setCursor(0, 1); lcd.print("B2 SAI B1 GRAVA "); while (digitalRead(B2)) { if (!digitalRead(B1)) { eeprom_write_dword((uint32_t *)EE_SVD_LSB, vfo); lcd.setCursor(0, 1); lcd.print(" GRAVADO "); delay(2000); lcd.setCursor(0, 1); lcd.print("B2=SAI "); } } lcd.clear(); } //////////////////////ajuste da frequencia do bfo para USB void usbadj() { si5351.set_freq( (2900000) * 100ULL, SI5351_CLK0); si5351.set_freq( (USB) * 100ULL, SI5351_CLK2); lcd.setCursor(0, 0); lcd.print("VFO em 2900kHz"); lcd.setCursor(0, 1); lcd.print("BFO em "); lcd.print(USB); delay(5000); lcd.clear(); lcd.setCursor(0, 0); lcd.print("AJ. ruido alto"); lcd.setCursor(0, 1); lcd.print("AJ TOM + GRAVE"); delay(5000); vfo = 9995000; lcd.clear(); lcd.setCursor(0, 1); lcd.print("Qdo ok clique B1"); display_frequency(); while (digitalRead(B1)) { if (changed_f) { display_frequency(); changed_f = 0; } potencia(); si5351.set_freq( (vfo) * 100ULL, SI5351_CLK2); } USB = vfo; lcd.clear(); lcd.setCursor(0, 0); lcd.print("Encontre um QSO"); lcd.setCursor(0, 1); lcd.print("Com 0 ou 5kHz "); delay(5000); lcd.clear(); lcd.setCursor(0, 1); lcd.print("Qdo ok clique B2"); vfo = 2900000; display_frequency(); while (digitalRead(B1)) { if (changed_f) { display_frequency(); changed_f = 0; } potencia(); si5351.set_freq( (vfo) * 100ULL, SI5351_CLK0); } lcd.setCursor(0, 0); lcd.print("AJUSTE AGORA BFO"); lcd.setCursor(0, 1); lcd.print("ATE FICAR CLARO "); delay(5000); lcd.clear(); vfo = USB; display_frequency(); lcd.setCursor(0, 1); lcd.print("Qdo ok clique B1"); while (digitalRead(B1)) { if (changed_f) { display_frequency(); changed_f = 0; //delay (2000); } potencia(); si5351.set_freq( (vfo) * 100ULL, SI5351_CLK2); } lcd.setCursor(1, 1); lcd.print(vfo); lcd.print(" espere"); delay (3000); lcd.setCursor(0, 1); lcd.print("B2=SAI B1=GRAVA "); while (digitalRead(B2)) { if (!digitalRead(B1)) { eeprom_write_dword((uint32_t *)EE_SVD_USB, vfo); lcd.setCursor(0, 1); lcd.print(" GRAVADO "); delay(2000); lcd.setCursor(0, 1); lcd.print("B2=SAI "); } } lcd.clear(); } ///////////////////////////////////////////////////// /* Gera tensão para ajuste do diodo varicap do amplificador de RF a correção é feita por formulas geradas pelo excell baseadas em valores medidos*/ /*---------------------------------------------*/ void bpf() { float pwmvfo; pwmvfo = float(vfo) / 1000000 ; if (pwmvfo < 6.5) { valor = 1; } if ((pwmvfo >= 6.5) && (pwmvfo < 10.6)) { valor = round ((16.56 * pwmvfo ) - 107.5); } if ((pwmvfo >= 10.6) && (pwmvfo < 13)) { valor = round ((11.36 * pwmvfo ) - 52.34); } if ((pwmvfo >= 13) && (pwmvfo < 14)) { valor = round ((6.06 * pwmvfo ) + 16.88); } if ((pwmvfo >= 14) && (pwmvfo < 20)) { valor = round ((8.928 * pwmvfo ) - 23); } if ((pwmvfo >= 20) && (pwmvfo < 23)) { valor = round ((8.333 * pwmvfo ) - 10.66); } if ((pwmvfo >= 23) && (pwmvfo < 26)) { valor = round ((5.319 * pwmvfo ) + 58.66); } if ((pwmvfo >= 26) && (pwmvfo < 29)) { valor = round ((7.299 * pwmvfo ) + 7.106); } if ((pwmvfo >= 29) && (pwmvfo < 31)) { valor = round ((11.76 * pwmvfo ) - 121.8); } if ((pwmvfo >= 31) && (pwmvfo < 31.5)) { valor = round ((18.18 * pwmvfo ) - 320.8); } if ((pwmvfo >= 31.5)) { valor = 255; } lcd.setCursor(2, 1); //lcd.print(pwmvfo); //lcd.print(" "); //lcd.print(valor); analogWrite(pwmi, valor); } // seleciona banda conforme modo de operação // select band using operation mode LSB CW USB or FT8 void bandsel () { banda = banda + 1; if (banda == 10) { banda = 1; } switch (banda) { //80m case 1 : vfo = 3700000; if (modo == "CW ") { vfo = 3530000; } if (modo == "FT8") { vfo = 3573000; } break; } switch (banda) { // 40m case 2 : vfo = 7100000; //if (modo == "LSB") {vfo =7100000;} if (modo == "CW ") { vfo = 7030000; } if (modo == "FT8") { vfo = 7074000; } break; } switch (banda) { //30m case 3 : vfo = 10100000; // if (modo == "LSB") {vfo =10100000;} if (modo == "CW ") { vfo = 10100000; } if (modo == "FT8") { vfo = 10136000; } break; } switch (banda) {//20m case 4 : vfo = 14200000; //if (modo == "USB") {vfo =14200000;} if (modo == "CW ") { vfo = 14030000; } if (modo == "FT8") { vfo = 14074000; } break; } switch (banda) {//17m case 5 : vfo = 18100000; // if (modo == "USB") {vfo =18100000;} if (modo == "CW ") { vfo = 18030000; } if (modo == "FT8") { vfo = 18100000; } break; } switch (banda) {//15m case 6 : vfo = 21200000; //if (modo == "USB") {vfo =21200000;} if (modo == "CW ") { vfo = 21030000; } if (modo == "FT8") { vfo = 21074000; } break; } switch (banda) {//12m case 7 : vfo = 24800000; //if (modo == "USB") {vfo =24800000;} if (modo == "CW ") { vfo = 24830000; } if (modo == "FT8") { vfo = 24918000; } break; } switch (banda) {//11m case 8 : vfo = 27500000; // if (modo == "USB") {vfo =28500000;} if (modo == "FT8") { vfo = 27265000; } break; } switch (banda) {//10m case 9 : vfo = 28500000; // if (modo == "USB") {vfo =28500000;} if (modo == "CW ") { vfo = 28030000; } if (modo == "FT8") { vfo = 28074000; } break; } lcd.setCursor(0, 1); lcd.print(modo); } /**************************************/ /* Displays and adjust the frequency and underscore cursor/ /**************************************/ void set_frequency(short dir) { if (!digitalRead(ENCODER_BTN)) { lcd.setCursor( 12 - und, 0); if (dir == 1) { und += 1; switch (und) { case 4 : und = 5; break; case 8 : und = 9; break; case 12 : und = 11; break; } } if (dir == -1) { und += -1; switch (und) { case 4 : und = 3; break; case 8 : und = 7; break; case 0 : und = 1; break; } } pot = und; if (und > 3) (pot += -1); if (und > 7) (pot += -1); lcd.setCursor( 12 - und, 0); lcd.cursor(); } else { lcd.noCursor(); if (dir == 1) vfo += radix; if (dir == -1) { if (vfo > radix ) { vfo -= radix; } } changed_f = 1; } } /**************************************/ /* Read the button with debouncing */ /**************************************/ boolean get_button() { if (!digitalRead(ENCODER_BTN)) { delay(20); if (!digitalRead(ENCODER_BTN)) { while (!digitalRead(ENCODER_BTN)); return 1; } } return 0; } /**************************************/ /* Displays the frequency */ /**************************************/ void display_frequency() { uint16_t f, g; lcd.setCursor(1, 0); f = (vfo ) / 1000000; //variable is now vfo instead of 'frequency' if (f < 100) { lcd.print(' '); } if (f < 10) { lcd.print(' '); } lcd.print(f); //valor = f; lcd.print('.'); f = (vfo % 1000000) / 1000; if (f < 100) lcd.print('0'); if (f < 10) lcd.print('0'); lcd.print(f); lcd.print('.'); f = vfo % 1000; if (f < 100) lcd.print('0'); if (f < 10) lcd.print('0'); lcd.print(f); lcd.print("Hz"); //lcd.setCursor(6, 1); // only for to check //lcd.print(valor); //analogWrite(pwmi, valor); lcd.setCursor(0, 1); lcd.print(modo); lcd.print(" S"); } /**************************************/ /* S E T U P /* le os valores da frequencia preferida, dos bfos de LSB e USB, e da calibração do SI5352*/ /**************************************/ void setup() { Serial.begin(19200); lcd.begin(16, 2); // Initialize and clear the LCD lcd.clear(); Wire.begin(); //pinMode(B3, INPUT_PULLUP); pinMode(B1, INPUT_PULLUP); pinMode(B2, INPUT_PULLUP); pinMode(pwmi, OUTPUT); pinMode(an, INPUT); lcd.createChar(0, grf0); lcd.createChar(1, grf1); lcd.createChar(2, grf2); lcd.createChar(3, grf3); xtalfreq = eeprom_read_dword((const uint32_t *)EE_SVD_SI5351XT); if (xtalfreq == 0) { xtalfreq = 25000000 ; } LSB = eeprom_read_dword((const uint32_t *)EE_SVD_LSB); if (LSB == 0) { LSB = 10000000 ; } USB = eeprom_read_dword((const uint32_t *)EE_SVD_USB); if (USB == 0) { USB = 10000000 ; } lcd.setCursor(0, 1); lcd.print("xtal "); lcd.print(xtalfreq); delay(3000); lcd.clear(); lcd.setCursor(1, 1); lcd.print("USB "); lcd.print(USB); delay(3000); lcd.setCursor(0, 1); lcd.clear(); lcd.print("LSB "); lcd.print(LSB); delay(3000); lcd.clear(); vfo = eeprom_read_dword((const uint32_t *)EE_SVD_LASTFRQ); //initialize the Si5351 // si5351.init(SI5351_CRYSTAL_LOAD_8PF, 25003870,0); //If you're using a 27Mhz crystal, put in 27000000 instead of 0 si5351.init(SI5351_CRYSTAL_LOAD_8PF, xtalfreq, 0); // 0 is the default crystal frequency of 25Mhz. si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA); // Set CLK0 to output the starting "vfo" frequency as set above by vfo = ? pinMode(ENCODER_BTN, INPUT_PULLUP); PCICR |= (1 << PCIE2); // Enable pin change interrupt for the encoder PCMSK2 |= (1 << PCINT18) | (1 << PCINT19); sei(); if (!digitalRead(ENCODER_BTN)) { //aciona o menu com botão encoder acionado no startup menu(); } bfo = LSB; lcd.clear(); display_frequency(); // Update the display } /**************************************/ /* L O O P */ /**************************************/ void loop() { agora = millis();// aciona o diplay de barras do smeter a cada 0,5s if (agora - antes >= 500) { antes = agora; smeter() ; if (value >= 21) { ///S4 as barrras smiling foram estabelecidas no inicio lcd.setCursor(9, 1); lcd.write(byte(3)); } if (value >= 44) { //S5 lcd.setCursor(10, 1); lcd.write(byte(3)); } if (value >= 83) { lcd.setCursor(11, 1); lcd.write(byte(2)); } if (value >= 167) { lcd.setCursor(12, 1); lcd.write(byte(1)); } if (value >= 338) { lcd.setCursor(13, 1); lcd.write(byte(1)); } if (value >= 672) { lcd.setCursor(14, 1); lcd.write(byte(0)); } if (value >= 1340) { //S9+10 lcd.setCursor(15, 1); lcd.write(byte(0)); } lcd.noCursor(); } myButton.update(); // botão 2 com multiplas funções 1clic=modo 2clic= banda 3clic longo=grava atual freq if (myButton.isSingleClick()) { // um click muda o modo LSB CW USB e FT8 lateral = lateral + 1; changed_f = 1; if (lateral == 5) { lateral = 1; } switch (lateral) { case 1: modo = "LSB"; bfo = LSB; break; case 2: modo = "USB"; bfo = USB; break; case 3: modo = "CW "; bfo = LSB; break; case 4: modo = "FT8"; bfo = USB; break; } lcd.setCursor(0, 1); lcd.print(modo); } if (myButton.isDoubleClick()) { // dois clicks muda de banda bandsel(); changed_f = 1; //display_frequency(); } if (myButton.isLongClick()) {// click longo grava a frequencia atual na eeprom lcd.clear(); lcd.setCursor(0, 0); lcd.print("GRAVA ATUAL FREQ"); delay(3000); lcd.setCursor(0, 1); lcd.print("OK=B1 sai=B2 "); while (count > 0) { if ((!digitalRead(B1))) { eeprom_write_dword((uint32_t *)EE_SVD_LASTFRQ, vfo); lcd.setCursor(0, 1); lcd.print(" GRAVADO "); delay(2000); break; } if ((!digitalRead(B2))) { break; } } lcd.clear(); display_frequency(); } // Update the display / bpf if the frequency has been changed if (changed_f) { if (modo == "CW ") { bfo = LSB; } if (modo == "USB") { bfo = USB; } if (modo == "LSB") { bfo = LSB; } if (modo == "FT8") { bfo = USB; } bpf();// ajuste varicap display_frequency(); si5351.set_freq( (bfo + vfo) * 100ULL, SI5351_CLK0); si5351.set_freq( (bfo) * 100ULL , SI5351_CLK2); } changed_f = 0; potencia(); } void smeter ()//os valores de Smetr estão na escala log da Collins, não calibardos { value = analogRead(an); value = (value * 5000 / 1024); if (value < 4) { smtr = 1; } if ((value >= 4) && (value < 8)) { smtr = 1; } if ((value >= 8) && (value < 12)) { smtr = 2; } if ((value >= 12) && (value < 20)) { smtr = 3; } if ((value >= 20) && (value < 39)) { smtr = 4; } if ((value >= 39) && (value < 74)) { smtr = 5; } if ((value >= 74) && (value < 148)) { smtr = "6 "; } if ((value >= 148) && (value < 300)) { smtr = "7 "; } if ((value >= 300) && (value < 597)) { smtr = "8 "; } if ((value >= 597) && (value < 1190)) { smtr = "9 "; } if ((value >= 1190) && (value < 3760)) { smtr = "+10"; } if (value >= 3760) { smtr = "+20"; } if (oldsmtr != smtr) { oldsmtr = smtr; lcd.setCursor(5, 1); lcd.print(" "); lcd.setCursor(5, 1); lcd.print(smtr); } } void potencia() { if (get_button()) { switch (pot) { case 1: radix = 1; break; case 2: radix = 10; break; case 3: radix = 100; break; case 4: radix = 1000; break; case 5: radix = 10000; break; case 6: radix = 100000; break; case 7: radix = 1000000; break; case 8: radix = 10000000; break; case 9: radix = 100000000; break; } } }