A few weeks ago John, 2E1EFH (now M0RVJ - Rev John!), suggested that the VFO could display the band plans which changed as you tune across. So i have developed the code here to do just that. It means the VFO is a single band job, and I have used the button on the rotary encoder to select tuning steps of 100Hz, 1kHz and 10kHz. Thus making a lot easier to move across the band. The steps are indicated by "+" symbols on the top right of the display.
Code for 16 x 2 display
// VFO_40m_bandplan // 40m only version // 16 x 4 LCD LCD // button changes steps 100Hz ( )/1kHz( +)/10kHz(++) // I2C, Si5351, LCD and rotary Encoder libraries #include "Wire.h" #include "si5351.h" #include "LiquidCrystal_I2C.h" #include "Rotary.h" // LCD #define LCDADDR 0x27 #define LCDCOLS 16 #define LCDROWS 2 // rotary Encoder pins 2 & 3 (DT & CLK), band change pin 4 (SW) #define DT 2 #define CLK 3 #define SW 4 // Rx & Tx signals #define RX 13 #define TX 12 #define KEY 8 // dds object Si5351 dds; // LCD object LiquidCrystal_I2C lcd(LCDADDR, LCDCOLS, LCDROWS); // rotary Encoder object Rotary rot = Rotary(DT, CLK); uint32_t steps = 10000; // init 100Hz steps // define sa structure typedef struct{ uint32_t lower; uint32_t upper; char alloc[30]; } plan; #define PLANS 12 // define he array contents cHz/cHz/Text plan bp[PLANS] = { {700000000, 700100000, "CW QRSS 7000.7 "}, {700100000, 703990000, "CW QRP 7030 "}, {703990000, 704690000, "NB WSPR 7040 "}, {704600000, 704990000, "NB Auto "}, {704990000, 705290000, "ALL Auto "}, {705290000, 705990000, "ALL Digital "}, {705990000, 706990000, "ALL "}, {706990000, 707990000, "ALL HELL 7077 "}, {707990000, 709990000, "ALL SSB QRP 7090"}, {709990000, 712990000, "ALL EMGCY 7110 "}, {712990000, 717490000, "ALL SSB CON 7165"}, {717490000, 720010000, "ALL DX INTNL "}, }; uint32_t freq = 700000000; // indicated frequency void setup() { // init LCD & backlight on lcd.init(); lcd.backlight(); // init dds si5351 module, "0" = default 25MHz XTAL dds.init(SI5351_CRYSTAL_LOAD_8PF, 0); // set 8mA output drive dds.drive_strength(SI5351_CLK2, SI5351_DRIVE_8MA); // enable VFO output CLK0, disable CLK1 & 2 dds.output_enable(SI5351_CLK0, 1); dds.output_enable(SI5351_CLK1, 0); dds.output_enable(SI5351_CLK2, 0); // encoder, button, RX, TX, band and KEY pins pinMode(DT, INPUT_PULLUP); pinMode(CLK, INPUT_PULLUP); pinMode(SW, INPUT_PULLUP); pinMode(RX, OUTPUT); pinMode(TX, OUTPUT); pinMode(KEY, INPUT_PULLUP); xmit(digitalRead(KEY)); // RXTX mode freqOut(freq); // output freq dispFreq(4, 0, freq, 1); // display freq col 6 row 0 dispMsg(0, 1, scanPlan()); // display band plan col 0 row 3 } void loop() { stepit(); // change steps? tune(); // rotary encoder tuning? xmit(digitalRead(KEY)); // TX? } void tune() { unsigned char dir; // tuning direction CW/CCW // tune? dir = rot.process(); // read encoder if(dir != DIR_NONE) // turned? { if(dir == DIR_CW && freq < bp[PLANS-1].upper - steps) freq += steps; // increment freq +/- STEPS if(dir == DIR_CCW && freq >= bp[0].lower + steps) freq -= steps; freqOut(freq); // output freq dispFreq(4, 0, freq, 1); // update freq display dispMsg(0, 1, scanPlan()); // update band plan display } } // change step? void stepit() { if(digitalRead(SW) == LOW) // button pressed? { while(!digitalRead(SW)); // wait for release if(steps == 1000000) steps = 10000; // reset else steps = steps * 10; // or increment by x10 switch (steps) // display status { case 10000: dispMsg(14, 0, " "); break; case 100000: dispMsg(14, 0, " +"); break; case 1000000: dispMsg(14, 0, "++"); break; } } } // search for band info char *scanPlan() { for(int i = 0; i < 15; i++) { if (freq >= bp[i].lower && freq < bp[i].upper) { // find plan return bp[i].alloc; // return when found } } } // frequency (in cHz) for VFO, on CLK0 void freqOut(uint32_t f) { dds.set_freq(f, 0ULL, SI5351_CLK0); // converted to cHz } // KEY HIGH RX, LOW TX void xmit(bool x) { if(x == LOW) // TX { dispMsg(0, 0, "TX "); digitalWrite(RX, HIGH); // RX off digitalWrite(TX, LOW); // TX on } else { dispMsg(0, 0, "VFO"); digitalWrite(RX, LOW); // RX on digitalWrite(TX, HIGH); // TX off } } // display char msg at col c, row r void dispMsg(uint8_t c, uint8_t r, char *m) { lcd.setCursor(c, r); lcd.print(m); } // display freq in kHz,col c, row r, d decimal places void dispFreq(uint8_t c, uint8_t r, uint32_t f, uint8_t d) { lcd.setCursor(c, r); // clear last freq display lcd.print((float)f / 100000, d); // convert to float & kHz for print function lcd.print("kHz"); // + trailing spaces to clear previous display }
You will notice that I have also implimented the RX TX switching which enables the RX when pin D13 goes low, and the TX when pin D12 goes low.
Obviously by just changing the frequency generated to x4, the output and drive of the Si5351 to use CLK2 and the display to "SDR" you can get an SDR version of the software.
Code for 20 x 4 display
// Universal_VFO_40m_BandPlan_lcd2004 // 40m only version // 20 x 4 I2C LCD // button changes steps 100Hz ( )/1kHz( +)/10kHz(++) // I2C, Si5351, LCD and rotary Encoder libraries #include "Wire.h" #include "si5351.h" #include "LiquidCrystal_I2C.h" #include "Rotary.h" // LCD 20 col x 4 rows #define LCDADDR 0x27 #define LCDCOLS 20 #define LCDROWS 4 // rotary Encoder pins 2 & 3 (DT & CLK), band change pin 4 (SW) #define DT 2 #define CLK 3 #define SW 4 // Rx & Tx signals #define RX 13 #define TX 12 #define KEY 8 // dds object Si5351 dds; // LCD object LiquidCrystal_I2C lcd(LCDADDR, LCDCOLS, LCDROWS); // rotary Encoder object Rotary rot = Rotary(DT, CLK); uint32_t steps = 10000; // init 100Hz steps // define sa structure typedef struct{ uint32_t lower; uint32_t upper; char alloc1[21]; char alloc2[21]; } plan; #define PLANS 9 // define he array contents cHz/cHz/Text plan bp[PLANS] = { {700000000, 703990000, "7000-7040 CW PSK31 ", "7000.8 QRSS 7030 QRP"}, {704000000, 704690000, "7040-7047 Nrw Band ", "7040 WSPR "}, {704700000, 704990000, "7047-7050 Nrw Auto ", "Auto-controlled Data"}, {705000000, 705290000, "7050-7053 Auto ", "Unattended Data "}, {705300000, 705990000, "7053-7060 All ", "Digital modes "}, {706000000, 709990000, "7060-7100 SSB Cont ", "7077 HELL 7090 QRP "}, {710000000, 712990000, "7100-7130 SSB Cont ", "7110 Emergency "}, {713000000, 717490000, "7130-7175 All ", "7165 Images "}, {717500000, 720000000, "7175-7200 All ", "Internat priority "}, }; uint32_t freq = 700000000; // start frequency cHz void setup() { // init LCD & backlight on lcd.init(); lcd.backlight(); // init dds si5351 module, "0" = default 25MHz XTAL dds.init(SI5351_CRYSTAL_LOAD_8PF, 0); // set 8mA output drive dds.drive_strength(SI5351_CLK2, SI5351_DRIVE_8MA); // enable VFO output CLK0, disable CLK1 & 2 dds.output_enable(SI5351_CLK0, 1); dds.output_enable(SI5351_CLK1, 0); dds.output_enable(SI5351_CLK2, 0); // encoder, button, RX, TX, band and KEY pins pinMode(DT, INPUT_PULLUP); pinMode(CLK, INPUT_PULLUP); pinMode(SW, INPUT_PULLUP); pinMode(RX, OUTPUT); pinMode(TX, OUTPUT); pinMode(KEY, INPUT_PULLUP); xmit(digitalRead(KEY)); // RX|TX freqOut(freq); // output freq, cHz/100 dispMsg(0, 0, "VFO"); // ident of program dispFreq(7, 0, freq, 1); // display freq in kHz col 7 row 0 dispMsg(0, 1, "Band 40m"); dispMsg(0, 2, bp[scanPlan()].alloc1); // display band plan col 0 row 2 dispMsg(0, 3, bp[scanPlan()].alloc2); // and col 0 row 3 } void loop() { stepchg(); // change steps? / +/++ = 100Hz/1kHz/10kHz tune(); // rotary encoder tuning? xmit(digitalRead(KEY)); // RX|TX } void tune() { unsigned char dir; // tuning direction CW/CCW // tune? dir = rot.process(); // read encoder if(dir != DIR_NONE) // turned? { if(dir == DIR_CW && freq < bp[PLANS-1].upper - steps) freq += steps; // increment freq +/- STEPS if(dir == DIR_CCW && freq >= bp[0].lower + steps) freq -= steps; dispFreq(7, 0, freq, 1); // display freq in kHz col 7 row 0 dispMsg(0, 2, bp[scanPlan()].alloc1); // display band plan col 0 row 2 dispMsg(0, 3, bp[scanPlan()].alloc2); // and col 0 row 3 } } // change step? void stepchg() { if(digitalRead(SW) == LOW) // button pressed? { while(!digitalRead(SW)); // wait for release if(steps == 1000000) steps = 10000; // reset else steps = steps * 10; // or increment by x10 switch (steps) // display status { case 10000: dispMsg(18, 0, " "); break; case 100000: dispMsg(18, 0, " +"); break; case 1000000: dispMsg(18, 0, "++"); break; } } } // search for band info byte scanPlan() { for(int i = 0; i < 15; i++) { if (freq >= bp[i].lower && freq <= bp[i].upper) // find plan return i; // return when found } } // frequency (in cHz) for VFO, on CLK0 void freqOut(uint32_t f) { dds.set_freq(f, 0ULL, SI5351_CLK0); } // KEY HIGH RX, LOW TX void xmit(bool x) { if(x == LOW) // TX { dispMsg(0, 0, "TX "); digitalWrite(RX, HIGH); // RX off digitalWrite(TX, LOW); // TX on } else { dispMsg(0, 0, "VFO"); digitalWrite(RX, LOW); // RX on digitalWrite(TX, HIGH); // TX off } } // display char msg at col c, row r void dispMsg(uint8_t c, uint8_t r, char *m) { lcd.setCursor(c, r); lcd.print(m); } // display freq in kHz,col c, row r, d decimal places void dispFreq(uint8_t c, uint8_t r, uint32_t f, uint8_t d) { lcd.setCursor(c, r); lcd.print((float)f / 100000, d); // convert to float & kHz for print function lcd.print(" kHz"); // + trailing spaces to clear previous display }