Components and supplies
rotary encoder
Generic 128x64 OLED I2C
Electric Switches
Push Button
SI5351 CLOCK GEN MODULE
Arduino Nano
Tools and machines
Soldering kit
Apps and platforms
Arduino IDE
Project description
Code
Code
cpp
...
1#include <Wire.h> 2#include <Rotary.h> 3#include <si5351.h> 4#include <U8g2lib.h> 5 6// Pin definitions 7#define PIN_TUNESTEP A0 8#define PIN_BAND A1 9#define PIN_RX_TX A2 10#define PIN_ADC A3 11#define PIN_ROT_1 2 12#define PIN_ROT_2 3 13#define PIN_RST 8 14#define PIN_CS 10 15#define PIN_MOSI 11 16#define PIN_SCK 13 17 18// Constants 19#define IF_FREQ 455 20#define BAND_INIT 7 21#define XT_CAL_F 33000 22#define S_GAIN 303 23 24// Frequency range limits 25const uint32_t MIN_FREQ = 10000UL; // 10 kHz 26const uint32_t MAX_FREQ = 225000000UL; // 225 MHz 27 28// Band names stored in program memory 29const char BAND_0[] PROGMEM = " GEN"; 30const char BAND_1[] PROGMEM = " MW"; 31const char BAND_2[] PROGMEM = " 160m"; 32const char BAND_3[] PROGMEM = " 80m"; 33const char BAND_4[] PROGMEM = " 60m"; 34const char BAND_5[] PROGMEM = " 49m"; 35const char BAND_6[] PROGMEM = " 40m"; 36const char BAND_7[] PROGMEM = " 31m"; 37const char BAND_8[] PROGMEM = " 25m"; 38const char BAND_9[] PROGMEM = " 22m"; 39const char BAND_10[] PROGMEM = " 20m"; 40const char BAND_11[] PROGMEM = " 19m"; 41const char BAND_12[] PROGMEM = " 16m"; 42const char BAND_13[] PROGMEM = " 13m"; 43const char BAND_14[] PROGMEM = " 11m"; 44const char BAND_15[] PROGMEM = " 10m"; 45const char BAND_16[] PROGMEM = " 6m"; 46const char BAND_17[] PROGMEM = " WFM"; 47const char BAND_18[] PROGMEM = " AIR"; 48const char BAND_19[] PROGMEM = " 2m"; 49const char BAND_20[] PROGMEM = " 1m"; 50 51const char* const BAND_NAMES[] PROGMEM = { 52 BAND_0, BAND_1, BAND_2, BAND_3, BAND_4, BAND_5, BAND_6, BAND_7, BAND_8, BAND_9, 53 BAND_10, BAND_11, BAND_12, BAND_13, BAND_14, BAND_15, BAND_16, BAND_17, 54 BAND_18, BAND_19, BAND_20 55}; 56 57// Frequency presets stored in program memory 58const uint32_t FREQ_PRESETS[] PROGMEM = { 59 100000UL, // GEN 60 800000UL, // MW 61 1800000UL, // 160m 62 3650000UL, // 80m 63 4985000UL, // 60m 64 6180000UL, // 49m 65 7200000UL, // 40m 66 10000000UL, // 31m 67 11780000UL, // 25m 68 13630000UL, // 22m 69 14100000UL, // 20m 70 15000000UL, // 19m 71 17655000UL, // 16m 72 21525000UL, // 13m 73 27015000UL, // 11m 74 28400000UL, // 10m 75 50000000UL, // 6m 76 100000000UL, // WFM 77 130000000UL, // AIR 78 144000000UL, // 2m 79 220000000UL // 1m 80}; 81 82// Frequency steps 83const uint32_t FREQ_STEPS[] PROGMEM = { 84 1000000UL, // 1 MHz 85 1UL, // 1 Hz 86 10UL, // 10 Hz 87 1000UL, // 1 kHz 88 5000UL, // 5 kHz 89 10000UL // 10 kHz 90}; 91 92// Object initialization 93U8G2_ST7920_128X64_1_SW_SPI u8g2(U8G2_R0, PIN_SCK, PIN_MOSI, PIN_CS, PIN_RST); 94Rotary r = Rotary(PIN_ROT_1, PIN_ROT_2); 95Si5351 si5351; 96 97// Global variables 98uint32_t freq = 7200000UL; // Start at 7.2MHz 99uint32_t freqold; 100uint32_t fstep = 1000; // Default step 1kHz 101int16_t interfreq = IF_FREQ; 102int16_t cal = XT_CAL_F; 103uint8_t smval; 104uint8_t encoder = 1; 105uint8_t stp = 4; 106uint8_t n = 1; 107uint8_t count = BAND_INIT; 108uint8_t prevCount = BAND_INIT; 109uint8_t x, xo; 110bool sts = 0; 111bool displayOK = false; 112 113// Function prototypes 114bool setSi5351Frequency(Si5351& si5351, uint32_t freq, int16_t interfreq); 115void check_inputs(); 116void update_display_paged(); 117void initializeSi5351(); 118 119// Encoder interrupt service routine 120ISR(PCINT2_vect) { 121 char result = r.process(); 122 if (result == DIR_CW) { 123 if (encoder == 1) { 124 uint32_t new_freq = freq + fstep; 125 if (new_freq <= MAX_FREQ) { 126 freq = new_freq; 127 n = (n >= 42) ? 1 : n + 1; 128 } 129 } 130 } 131 else if (result == DIR_CCW) { 132 if (encoder == 1) { 133 uint32_t new_freq = freq; 134 if (freq >= fstep) { 135 new_freq = freq - fstep; 136 if (new_freq >= MIN_FREQ) { 137 freq = new_freq; 138 n = (n <= 1) ? 42 : n - 1; 139 } 140 } 141 } 142 } 143} 144 145void setup() { 146 Serial.begin(9600); 147 Serial.println(F("VFO Starting...")); 148 149 Wire.begin(); 150 151 if (!u8g2.begin()) { 152 Serial.println(F("Display init failed!")); 153 while (1) { delay(1000); } 154 } 155 156 // Display initialization test 157 u8g2.setFont(u8g2_font_6x12_tr); 158 u8g2.firstPage(); 159 do { 160 u8g2.drawFrame(0, 0, 128, 64); 161 u8g2.drawStr(20, 32, "Initializing..."); 162 } while (u8g2.nextPage()); 163 delay(1000); 164 165 Serial.println(F("Display initialized")); 166 displayOK = true; 167 168 // Initialize pins 169 pinMode(PIN_ROT_1, INPUT_PULLUP); 170 pinMode(PIN_ROT_2, INPUT_PULLUP); 171 pinMode(PIN_TUNESTEP, INPUT_PULLUP); 172 pinMode(PIN_BAND, INPUT_PULLUP); 173 pinMode(PIN_RX_TX, INPUT_PULLUP); 174 175 // Initialize Si5351 176 initializeSi5351(); 177 178 // Setup rotary encoder interrupts 179 PCICR |= (1 << PCIE2); 180 PCMSK2 |= (1 << PCINT18) | (1 << PCINT19); 181 sei(); 182 183 // Set initial frequency 184 freq = pgm_read_dword(&FREQ_PRESETS[count - 1]); 185 186 Serial.println(F("Setup complete")); 187} 188 189void initializeSi5351() { 190 Serial.println(F("Initializing Si5351...")); 191 if (!si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0)) { 192 Serial.println(F("Si5351 init failed!")); 193 } 194 si5351.reset(); 195 delay(10); 196 si5351.set_correction(cal, SI5351_PLL_INPUT_XO); 197 si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA); 198 si5351.output_enable(SI5351_CLK0, 1); 199} 200 201bool setSi5351Frequency(Si5351& si5351, uint32_t freq, int16_t interfreq) { 202 // Check if frequency is within valid range 203 if (freq < MIN_FREQ || freq > MAX_FREQ) { 204 return false; 205 } 206 207 uint64_t output_freq = (freq + (interfreq * 1000ULL)) * 100ULL; 208 209 // Handle GEN mode specially 210 if (count == 1) { 211 si5351.reset(); 212 delay(10); 213 si5351.set_correction(cal, SI5351_PLL_INPUT_XO); 214 si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA); 215 } 216 217 // Set the frequency 218 si5351.set_freq(output_freq, SI5351_CLK0); 219 si5351.output_enable(SI5351_CLK0, 1); 220 221 return true; 222} 223 224void loop() { 225 if (!displayOK) return; 226 227 // Process frequency changes with error handling 228 if (freqold != freq) { 229 if (!setSi5351Frequency(si5351, freq, interfreq)) { 230 // If frequency setting fails, try to recover 231 si5351.reset(); 232 delay(10); 233 si5351.set_correction(cal, SI5351_PLL_INPUT_XO); 234 si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA); 235 setSi5351Frequency(si5351, freq, interfreq); 236 } 237 freqold = freq; 238 } 239 240 // Check inputs 241 check_inputs(); 242 243 // Update display 244 update_display_paged(); 245 246 // Read signal meter 247 smval = analogRead(PIN_ADC); 248 x = constrain(map(smval, 0, S_GAIN, 1, 14), 1, 14); 249} 250 251void check_inputs() { 252 if (digitalRead(PIN_TUNESTEP) == LOW) { 253 stp = (stp % 6) + 1; 254 fstep = pgm_read_dword(&FREQ_STEPS[stp - 1]); 255 delay(300); 256 } 257 258 if (digitalRead(PIN_BAND) == LOW) { 259 uint8_t newCount = (count % 21) + 1; 260 261 // Reset Si5351 when entering or leaving GEN mode 262 if (newCount == 1 || count == 1) { 263 si5351.reset(); 264 delay(10); 265 si5351.set_correction(cal, SI5351_PLL_INPUT_XO); 266 si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA); 267 si5351.output_enable(SI5351_CLK0, 1); 268 } 269 270 count = newCount; 271 freq = pgm_read_dword(&FREQ_PRESETS[count - 1]); 272 prevCount = count; 273 delay(300); 274 } 275 276 sts = (digitalRead(PIN_RX_TX) == LOW); 277 interfreq = (sts || count == 1) ? 0 : IF_FREQ; 278} 279 280void update_display_paged() { 281 u8g2.firstPage(); 282 do { 283 // Display frequency 284 char buffer[16]; 285 uint32_t m = freq / 1000000UL; 286 uint32_t k = (freq % 1000000UL) / 1000UL; 287 uint32_t h = (freq % 1000UL); 288 289 u8g2.setFont(u8g2_font_10x20_tr); 290 291 if (m < 1) { 292 sprintf(buffer, "%03lu.%03lu", k, h); 293 u8g2.drawStr(41, 17, buffer); 294 } else if (m < 100) { 295 sprintf(buffer, "%lu.%03lu.%03lu", m, k, h); 296 u8g2.drawStr(15, 17, buffer); 297 } else { 298 sprintf(buffer, "%lu.%03lu.%03lu", m, k, h); 299 u8g2.drawStr(15, 17, buffer); 300 } 301 302 // Draw interface elements 303 u8g2.setFont(u8g2_font_6x12_tr); 304 u8g2.drawHLine(0, 22, 128); 305 u8g2.drawHLine(0, 45, 128); 306 u8g2.drawHLine(15, 54, 67); 307 u8g2.drawVLine(105, 26, 15); 308 u8g2.drawVLine(87, 26, 15); 309 u8g2.drawVLine(87, 50, 15); 310 311 // Display RX/TX status 312 u8g2.drawStr(91, 37, sts ? "TX" : "RX"); 313 314 // Display IF frequency 315 sprintf(buffer, "IF:%d", interfreq); 316 u8g2.drawStr(90, 59, buffer); 317 318 // Display LO value 319 sprintf(buffer, "LO:%d", interfreq); 320 u8g2.drawStr(110, 38, buffer); 321 322 // Display step 323 u8g2.drawStr(54, 32, "STEP"); 324 switch(stp) { 325 case 1: u8g2.drawStr(54, 42, "1MHz"); break; 326 case 2: u8g2.drawStr(54, 42, "1Hz"); break; 327 case 3: u8g2.drawStr(54, 42, "10Hz"); break; 328 case 4: u8g2.drawStr(54, 42, "1kHz"); break; 329 case 5: u8g2.drawStr(54, 42, "5kHz"); break; 330 case 6: u8g2.drawStr(54, 42, "10kHz"); break; 331 } 332 333 // Display band name 334 u8g2.setFont(u8g2_font_10x20_tr); 335 strcpy_P(buffer, (char*)pgm_read_word(&(BAND_NAMES[count - 1]))); 336 u8g2.drawStr(0, 40, buffer); 337 338 // Draw meters 339 u8g2.setFont(u8g2_font_6x12_tr); 340 byte y = map(n, 1, 42, 1, 14); 341 342 u8g2.drawStr(0, 54, "TU"); 343 u8g2.drawBox(15 + (y-1)*5, 47, 2, 6); 344 345 u8g2.drawStr(0, 63, "SM"); 346 for (byte i = 1; i <= x; i++) { 347 u8g2.drawBox(15 + (i-1)*5, 57, 2, 6); 348 } 349 350 } while (u8g2.nextPage()); 351}
Documentation
Schematic
...
SchematicJPG.jpg
Comments
Only logged in users can leave comments