1 | #include <Wire.h>
|
2 | #include <LiquidCrystal_I2C.h>
|
3 | #include <rotary.h>
|
4 | #include <EEPROM.h>
|
5 |
|
6 | LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // 27=A4=SDA, Pin28=A5=SCL
|
7 |
|
8 | int volt = A1; // 24 Atmega Eingang Spannungskontrolle
|
9 |
|
10 | int spg;
|
11 | bool start, obi, unterspg, flagspg;
|
12 | unsigned long unterspgstart, unterspgist;
|
13 | unsigned long flagspgstart, flagspgist;
|
14 | unsigned long unterspgverz = 21;
|
15 |
|
16 | bool ul, uh, uth, utl, uts, us;
|
17 | unsigned long startul, istul, startuh, istuh;
|
18 |
|
19 | int sumeing = A3; // 26 Atmega Eingang Summensignal
|
20 | int diffeing = A2; // 25 Atmega Eingang Differenzsignal
|
21 |
|
22 | int c;
|
23 | int d;
|
24 | int L;
|
25 | int R;
|
26 |
|
27 | int rot = 13; // 19 Atmega Anzeige Clippgrenze erreicht
|
28 | int gelb = 12; // 18 Atmega Anzeige Vollaussteuerung
|
29 | int gruen = 7; // 13 Atmega Anzeige Summensignal (Mono)
|
30 | int blau = 4; // 6 Atmega Anzeige Differenzsignal (Stereo)
|
31 |
|
32 | bool clip;
|
33 | unsigned long cstart;
|
34 | unsigned long cist;
|
35 | unsigned long causverz = 200; // MF Clippanzeige
|
36 |
|
37 | bool voll;
|
38 | unsigned long vstart;
|
39 | unsigned long vist;
|
40 | unsigned long vausverz = 500; // MF Vollaussteuerung
|
41 |
|
42 | bool sum;
|
43 | unsigned long sumstart;
|
44 | unsigned long sumist;
|
45 | unsigned long sumausverz = 1000; // MF Summensignal
|
46 |
|
47 | bool diff;
|
48 | unsigned long diffstart;
|
49 | unsigned long diffist;
|
50 | unsigned long diffausverz = 1000; // MF Differenzsignal
|
51 |
|
52 | int pwmDuty;
|
53 | long sineINC;
|
54 |
|
55 | bool ein;
|
56 | unsigned long einstart;
|
57 | unsigned long einist;
|
58 | unsigned long eindauer = 1200000; //20min (nach Tastendruck)
|
59 |
|
60 | bool button;
|
61 | unsigned long buttonstart;
|
62 | unsigned long buttonist;
|
63 | unsigned long buttoneinverz = 1000; //Tastverzögerung
|
64 | unsigned long previoushMillis = 0;
|
65 | const long intervalh = 1250; //Sperrzeit Herztaster
|
66 |
|
67 | long n;
|
68 |
|
69 | //Setup some items
|
70 | #define W_CLK 8 // Pin 8 - connect to AD9850 module word load clock pin (CLK) 14 Atmega
|
71 | #define FQ_UD 9 // Pin 9 - connect to freq update pin (FQ) 15 Atmega
|
72 | #define DATA 10 // Pin 10 - connect to serial data load pin (DATA) 16 Atmega
|
73 | #define RESET 11 // Pin 11 - connect to reset pin (RST) 17 Atmega
|
74 | #define pulseHigh(pin) {
|
75 | digitalWrite(pin, HIGH);
|
76 | digitalWrite(pin, LOW);
|
77 | }
|
78 |
|
79 | Rotary r = Rotary(2, 3); // sets the pins the rotary encoder uses. Must be interrupt pins.
|
80 |
|
81 | int_fast32_t rx = 1233000; // Starting frequency of VFO (1233KHz)
|
82 | int_fast32_t rx2 = 1; // variable to hold the updated frequency
|
83 | int_fast32_t increment = 9000; // starting VFO update increment in HZ.
|
84 | int buttonstate = 0;
|
85 |
|
86 | String hertz = "Raster 9KHz";
|
87 | int hertzPosition = 0;
|
88 |
|
89 | byte ones, tens, hundreds,
|
90 | thousands, tenthousands,
|
91 | hundredthousands, millions; //Placeholders
|
92 |
|
93 | String freq; // string to hold the frequency
|
94 | int_fast32_t timepassed = millis(); // int to hold the arduino miilis since startup
|
95 |
|
96 | int memstatus = 1; // value to notify if memory is current or old. 0=old, 1=current.
|
97 | int ForceFreq = 1; // Change this to 0 after you upload and run a working sketch to activate the EEPROM memory.
|
98 | // YOU MUST PUT THIS BACK TO 0 AND UPLOAD THE SKETCH AGAIN AFTER STARTING FREQUENCY IS SET!
|
99 |
|
100 |
|
101 | void setup() {
|
102 | pinMode(rot, OUTPUT);
|
103 | pinMode(gelb, OUTPUT);
|
104 | pinMode(gruen, OUTPUT);
|
105 | pinMode(blau, OUTPUT);
|
106 | pinMode(A0, INPUT); // Connect to a button that goes to GND on push
|
107 |
|
108 | digitalWrite(A0, HIGH);
|
109 | lcd.begin(16, 2);
|
110 | // lcd.backlight();
|
111 |
|
112 | PCICR |= (1 << PCIE2);
|
113 | PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
|
114 | sei();
|
115 |
|
116 | pinMode(FQ_UD, OUTPUT);
|
117 | pinMode(W_CLK, OUTPUT);
|
118 | pinMode(DATA, OUTPUT);
|
119 | pinMode(RESET, OUTPUT);
|
120 |
|
121 | pulseHigh(RESET);
|
122 | pulseHigh(W_CLK);
|
123 | pulseHigh(FQ_UD); // this pulse enables serial mode on the AD9850 - Datasheet page 12.
|
124 |
|
125 | lcd.setCursor(hertzPosition, 1);
|
126 | lcd.print(hertz);
|
127 |
|
128 | // Load the stored frequency
|
129 | if (ForceFreq == 0) {
|
130 | freq = String(EEPROM.read(0)) +
|
131 | String(EEPROM.read(1)) +
|
132 | String(EEPROM.read(2)) +
|
133 | String(EEPROM.read(3)) +
|
134 | String(EEPROM.read(4)) +
|
135 | String(EEPROM.read(5)) +
|
136 | String(EEPROM.read(6));
|
137 | rx = freq.toInt();
|
138 | }
|
139 | }
|
140 |
|
141 |
|
142 |
|
143 | void loop() {
|
144 |
|
145 | /*
|
146 | // Anzeige Unterspannung
|
147 |
|
148 | spg = analogRead (volt);
|
149 |
|
150 | if(millis() > 500) {
|
151 | start = HIGH;
|
152 | }
|
153 | else {
|
154 | start = LOW;
|
155 | }
|
156 | if( spg < 292) {
|
157 | obi = HIGH;
|
158 | }
|
159 | else {
|
160 | obi = LOW;
|
161 | }
|
162 |
|
163 | unterspg = obi && start;
|
164 | if(unterspg == LOW) {
|
165 | flagspgist = millis();
|
166 | if(flagspgist - flagspgstart > unterspgverz){
|
167 | flagspg = LOW;
|
168 | }
|
169 | }
|
170 | else {
|
171 | flagspgstart = millis();
|
172 | flagspg = HIGH;
|
173 | }
|
174 |
|
175 | if(flagspg == LOW) {
|
176 | istul = millis();
|
177 | if(istul - startul > unterspgverz){
|
178 | ul = LOW;
|
179 | }
|
180 | }
|
181 | else{
|
182 | startul = millis();
|
183 | ul = HIGH;
|
184 | }
|
185 |
|
186 | utl = (ul && !flagspg);
|
187 |
|
188 | if(!flagspg == LOW) {
|
189 | istuh = millis();
|
190 | if(istuh - startuh > unterspgverz){
|
191 | uh = LOW;
|
192 | }
|
193 | }
|
194 | else {
|
195 | startuh = millis();
|
196 | uh = HIGH;
|
197 | }
|
198 |
|
199 | uth = (uh && flagspg);
|
200 |
|
201 | if(uth == HIGH) {
|
202 | lcd.setCursor(0,1);
|
203 | lcd.print("Unterspannung ");
|
204 | }
|
205 |
|
206 | if(utl == HIGH){
|
207 | lcd.setCursor(0,1);
|
208 | lcd.print(hertz);
|
209 | }
|
210 |
|
211 | // Ende Anzeige Unterspannung
|
212 | */
|
213 |
|
214 | if (ein == HIGH) {
|
215 | lcd.backlight();
|
216 | } else {
|
217 | lcd.noBacklight();
|
218 | }
|
219 |
|
220 | c = analogRead(sumeing);
|
221 | c -= 511; // Nullinie wird von 511 auf 0 geschoben.
|
222 |
|
223 | d = analogRead(diffeing);
|
224 | d -= 511; // Nullinie wird von 511 auf 0 geschoben.
|
225 |
|
226 | R = (c + d);
|
227 | L = (c - d);
|
228 |
|
229 | if (R < -481 || R > 481 ||
|
230 | L < -481 || L > 481 ||
|
231 | d < -481 || d > 481) {
|
232 | clip = HIGH;
|
233 | } // Clipp Abstand 30,
|
234 | else {
|
235 | clip = LOW;
|
236 | }
|
237 |
|
238 | if (clip == LOW) // MonoFlop für Clippanzeige
|
239 | {
|
240 | cist = millis();
|
241 | if (cist - cstart > causverz) {
|
242 | digitalWrite(rot, LOW);
|
243 | }
|
244 | } else {
|
245 | cstart = millis();
|
246 | digitalWrite(rot, HIGH);
|
247 | }
|
248 |
|
249 | if ((R < -356 || R > 356 ||
|
250 | L < -356 || L > 356 ||
|
251 | d < -179 || d > 179
|
252 | ) && ein) {
|
253 | voll = HIGH;
|
254 | } //Vollausst. (511*0,7) (255*0,7) >-3dB
|
255 | else {
|
256 | voll = LOW;
|
257 | }
|
258 |
|
259 | if (voll == LOW) // MonoFlop für Vollaussteuerungsanzeige
|
260 | {
|
261 | vist = millis();
|
262 | if (vist - vstart > vausverz) {
|
263 | digitalWrite(gelb, LOW);
|
264 | }
|
265 | } else {
|
266 | vstart = millis(); //
|
267 | digitalWrite(gelb, HIGH);
|
268 | }
|
269 |
|
270 | if ((c < -50 || c > 50) && ein) {
|
271 | sum = HIGH;
|
272 | } else {
|
273 | sum = LOW;
|
274 | } //Summenanzeige
|
275 |
|
276 | if (sum == LOW) // MonoFlop für Summenanzeige
|
277 | {
|
278 | sumist = millis();
|
279 | if (sumist - sumstart > sumausverz) {
|
280 | digitalWrite(gruen, LOW);
|
281 | }
|
282 | }
|
283 | else {
|
284 | sumstart = millis();
|
285 | digitalWrite(gruen, HIGH);
|
286 | }
|
287 |
|
288 | if ((d < -50 || d > 50) && ein) {
|
289 | diff = HIGH;
|
290 | } //Differenzsignal
|
291 | else {
|
292 | diff = LOW;
|
293 | }
|
294 |
|
295 | if (diff == LOW) // MonoFlop für Differenzsignalanzeige
|
296 | {
|
297 | diffist = millis();
|
298 | if (diffist - diffstart > diffausverz) {
|
299 | digitalWrite(blau, LOW);
|
300 | }
|
301 | } else {
|
302 | diffstart = millis();
|
303 | digitalWrite(blau, HIGH);
|
304 | }
|
305 |
|
306 | n = rx / 7843;
|
307 | analogWrite(6, n); // Spannung als funktion der Frequenz PWM an Pin11 Atmega
|
308 | pwmDuty = 128 + 126 *
|
309 | sin(micros() * 0.000157);
|
310 | analogWrite(5, pwmDuty); // 25Hz PWM an Pin12 Atmega
|
311 |
|
312 | if (rx != rx2) {
|
313 | showFreq();
|
314 | sendFrequency(rx);
|
315 | rx2 = rx;
|
316 | }
|
317 |
|
318 | buttonstate = digitalRead(A0);
|
319 | if (buttonstate == LOW) {
|
320 | buttonist = millis();
|
321 | if (buttonist - buttonstart > buttoneinverz) {
|
322 | button = LOW;
|
323 | }
|
324 | } else {
|
325 | buttonstart = millis();
|
326 | button = HIGH;
|
327 | }
|
328 |
|
329 | if (button == LOW) {
|
330 | setincrement();
|
331 | };
|
332 |
|
333 | if (buttonstate == HIGH) {
|
334 | einist = millis(); //Einschalten mit Tastendruck für Einschaltdauer.
|
335 | if (einist - einstart > eindauer) {
|
336 | ein = LOW;
|
337 | }
|
338 | } else {
|
339 | einstart = millis();
|
340 | ein = HIGH;
|
341 | }
|
342 |
|
343 | if (memstatus == 0) { // Write the frequency to memory if not stored and 2 (10sek) seconds have passed since the last frequency change.
|
344 | if (timepassed + 10000 < millis()) {
|
345 | storeMEM();
|
346 | }
|
347 | }
|
348 | }
|
349 |
|
350 |
|
351 |
|
352 | ISR(PCINT2_vect) {
|
353 | unsigned char result = r.process();
|
354 | if (result) {
|
355 | if (result == DIR_CW) {
|
356 | rx = rx + increment;
|
357 | } else {
|
358 | rx = rx - increment;
|
359 | };
|
360 | if (rx >= 2000000) {
|
361 | rx = rx2;
|
362 | }; // UPPER VFO LIMIT
|
363 | if (rx <= 100000) {
|
364 | rx = rx2;
|
365 | }; // LOWER VFO LIMIT
|
366 | }
|
367 | }
|
368 |
|
369 |
|
370 |
|
371 | // frequency calc from datasheet page 8
|
372 | // = <sys clock> * <frequency tuning word>/2^32
|
373 |
|
374 | void sendFrequency(double frequency) {
|
375 | int32_t freq = frequency *
|
376 | 4294967295 / 125000000; // 4294967295*2=8589934590 note 125 MHz clock on 9850.
|
377 | // You can make 'slight' tuning variations here by
|
378 | // adjusting the clock frequency.
|
379 | for (int b = 0; b < 4; b++, freq >>= 8) {
|
380 | tfr_byte(freq & 0xFF);
|
381 | }
|
382 | tfr_byte(0x000); // Final control byte, all 0 for 9850 chip
|
383 | pulseHigh(FQ_UD); // Done! Should see output
|
384 | }
|
385 |
|
386 |
|
387 |
|
388 | // transfers a byte, a bit at a time,
|
389 | // LSB first to the 9850 via serial DATA line
|
390 |
|
391 | void tfr_byte(byte data) {
|
392 | for (int i = 0; i < 8; i++, data >>= 1) {
|
393 | digitalWrite(DATA, data & 0x01);
|
394 | pulseHigh(W_CLK); //after each bit sent, CLK is pulsed high
|
395 | }
|
396 | }
|
397 |
|
398 |
|
399 |
|
400 | void setincrement() {
|
401 | unsigned long currenthMillis = millis(); //delay Herzbutton
|
402 | if (currenthMillis - previoushMillis >= intervalh) {
|
403 | previoushMillis = currenthMillis;
|
404 |
|
405 | if (increment == 9000) {
|
406 | increment = 1;
|
407 | hertz = "Raster 1Hz";
|
408 | } else if (increment == 1) {
|
409 | increment = 10;
|
410 | hertz = "Raster 10Hz";
|
411 | } else if (increment == 10) {
|
412 | increment = 100;
|
413 | hertz = "Raster 100Hz";
|
414 | } else if (increment == 100) {
|
415 | increment = 1000;
|
416 | hertz = "Raster 1KHz";
|
417 | } else if (increment == 1000) {
|
418 | increment = 10000;
|
419 | hertz = "Raster 10KHz";
|
420 | } else if (increment == 10000) {
|
421 | increment = 99000;
|
422 | hertz = "Raster 99KHz";
|
423 | } else if (increment == 99000) {
|
424 | increment = 100000;
|
425 | hertz = "Raster 100KHz";
|
426 | } else {
|
427 | increment = 9000;
|
428 | hertz = "Raster 9KHz";
|
429 | }
|
430 |
|
431 | lcd.setCursor(0, 1);
|
432 | lcd.print(hertz);
|
433 | }
|
434 | }
|
435 |
|
436 |
|
437 | void showFreq() {
|
438 | millions = int(rx / 1000000);
|
439 | hundredthousands = ((rx / 100000) % 10);
|
440 | tenthousands = ((rx / 10000) % 10);
|
441 | thousands = ((rx / 1000) % 10);
|
442 | hundreds = ((rx / 100) % 10);
|
443 | tens = ((rx / 10) % 10);
|
444 | ones = ((rx / 1) % 10);
|
445 | lcd.setCursor(0, 0);
|
446 | lcd.print(" ");
|
447 |
|
448 | if (millions > 9) {
|
449 | lcd.setCursor(1, 0);
|
450 | } else {
|
451 | lcd.setCursor(2, 0);
|
452 | }
|
453 |
|
454 | lcd.print(millions);
|
455 | lcd.print(".");
|
456 | lcd.print(hundredthousands);
|
457 | lcd.print(tenthousands);
|
458 | lcd.print(thousands);
|
459 | lcd.print(".");
|
460 | lcd.print(hundreds);
|
461 | lcd.print(tens);
|
462 | lcd.print(ones);
|
463 | lcd.print(" Hz ");
|
464 | timepassed = millis();
|
465 | memstatus = 0; // Trigger memory write
|
466 | };
|
467 |
|
468 | void storeMEM() {
|
469 | //Write each frequency section to a EPROM slot.
|
470 | // Yes, it's cheating but it works!
|
471 | EEPROM.write(0, millions);
|
472 | EEPROM.write(1, hundredthousands);
|
473 | EEPROM.write(2, tenthousands);
|
474 | EEPROM.write(3, thousands);
|
475 | EEPROM.write(4, hundreds);
|
476 | EEPROM.write(5, tens);
|
477 | EEPROM.write(6, ones);
|
478 | memstatus = 1; // Let program know memory has been written
|
479 | };
|
480 |
|
481 |
|
482 | /*
|
483 | VFO code by Richard Visokey AD7C - www.ad7c.com
|
484 | 20190528, delay entfernt, Pilotton Sinus auf pin3Atmega
|
485 | https://radio-bastler.de/forum/showthread.php?tid=8756&pid=154185#pid154185
|
486 | 20190614 Frequenzabhängige Steuerspannung auf pin11Atmega
|
487 | 20190906 I2C-Display https://www.instructables.com/id/How-to-Connect-I2C-Lcd-Display-to-Arduino-Uno/
|
488 | 20190920 Herztaster Tastverzögerung gegen versehentliches Antippen des Tasters.
|
489 | 20190924 Audiopegelanzeige mit LEDs
|
490 | 20190929 Unterspannungsanzeige
|
491 | */
|