-
Notifications
You must be signed in to change notification settings - Fork 4
/
SignalGeneratorSD.ino
1114 lines (944 loc) · 48.5 KB
/
SignalGeneratorSD.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*=================== Signal Generator -SD mod- based on AD9833 Module
# SignalGeneratorSD based on AD9833 Module
by **[email protected]**, June-July 2020, FEB-2021
github: https://github.com/ShaggyDog18/SignalGeneratorSD
License: [GNU GPLv3](https://choosealicense.com/licenses/gpl-3.0/)
Original firmware and hardware solution/schematic by: **Cezar Chirila**;
URL: https://www.allaboutcircuits.com/projects/how-to-DIY-waveform-generator-analog-devices-ad9833-ATmega328p/
Also, the project was promoted by **GreatScott** with some simplified schematic: https://www.instructables.com/id/DIY-FunctionWaveform-Generator/;
https://www.youtube.com/watch?v=Y1KE8eAC9Bk
**If you like the new look and feel of SignalGeneratorSD, please, consider making a small "cup of coffee" donation using [PayPal](https://paypal.me/shaggyDog18/5USD)**
## Change Log:
- Use **MD_AD9833**(modified) library to control the AD9833 Module: compact and bug-free library with great functions.
- Improved, simplified, optimized, fixed bugs, used better/"standard" libraries for all components: the display, rotary encoder, button.
- Improved navigation, essentially, coded from scratch (refer to **Improved Navigation** section below).
- Improved the way frequency value is displayed (coded from scratch):
- option to hide leading zeros in frequency value (toggled by triple click of encoder button).
- option to delimit thousands by a separation sign (toggled by triple click to encoder button).
- a selected option (one out of four possible combinations) is stored to EEPROM (if enabled) and is set at start up.
- Use EEPROM to store and recover settings at switch on (to disable comment `#define ENABLE_EEPROM`).
- Added graphic icons for signal form representation on the display (if you still like the old way, comment `#define GRAPH_ICONS`).
- Tied a signal mode to a Channel; so, now you may change signal form along with its frequency by selecting a channel.
- Added a new signal mode: square/meander signal wave at 1/2 frequency (for more accuracy of the output signal frequency). This is a standard feature of AD9833 module. Comment `#define ENABLE_MEANDRE05F_SIGMODE` if you do not need it.
- More convenient and fast way of input frequency value by rotary encoder:
- continuous input: if reach either '9' or '0' in a digit position, then it jumps over to the senior digit and decreases/increases it.
- fast input: if fast encoder rotation is detected, then it increases/decreases ten times of a current digit position
- **NEW** "Running" frequency - the value of frequency is applied "on a fly" with a small 0.5 sec delay, so that you keep adjusting the frequency by encoder and the value is applied in 0.5 sec after your input is complete.
- **NEW** - `Stepped Sweep Generator`: the frequency is varied in a range defined by values set in Ch#0 (start of the range) and Ch#1 (end of the range) with signal settings of Ch#0 and discrete steps of 0,1 of a current running frequency (kind of logarithmic steps).
Frequency value steps either up or down from the start of the range depends on what channels' frequency is larger. Frequency is changed discretely every 250 mSec (can be changed at compilation).
The running `Stepped Sweep Generator` cycle is indicated by a blinking cursor at the end of a frequency value. Can be activated for Ch#0 only and uses its signal settings.
While running, can be cancelled by short press and hold of OK button. When the end of the range is reached, it pauses for 3 sec and switches back to Ch#0 settings.
Sweep generators are commonly used to test the frequency response of electronic filter circuits. Read more about [Sweep Generator in Wikipedia](https://en.wikipedia.org/wiki/Sweep_generator)
**Note**: I’ve never used/tested a PHASE option... Use it at your risk...
## Hardware:
- Any ATMega328P (tested) or ATMega168P (tested) chip based Arduino board (UNO, Nano, Pro Mini)
- AD9833 Programmable Waveform Generator Module
- LCD1602 Display with I2C Module
- Rotary Encoder
- Schmitt-trigger 74LVC1G14 (optional)
## Libraries
Download and install all below libraries as regular libraries in your Arduino IDE environment (to the `Library` folder):
- **MD_A9833** (modified): https://github.com/ShaggyDog18/MD_AD9833 (modification allows right functioning of ON / OFF functions)
- **RotaryEncoder** (modified): https://github.com/ShaggyDog18/RotaryEncoder
- **OneButton**: https://github.com/ShaggyDog18/OneButton
- **LCD1602 I2C**: https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library
- **WatchDog**: https://github.com/AlexGyver/GyverLibs/tree/master/GyverWDT
## Compile Options/Firmware Configuration:
You may pick up a pre-compiled firmware HEX file that includes all the below advanced options (except ENABLE_VOUT_SWITCH and USE_PHASE which are disabled).
- `#define GRAPH_ICONS` - use graphic icons for signal representation on the display; Still, the original text labels can be used if commented.
- `#define ENABLE_EEPROM`- save settings to EEPROM, recover them at startup.
- `#define ENABLE_MEANDRE05F_SIGMODE` - extra signal mode: squarewave signal at 0.5 frequency. This is one of the AD9833 module's features, used for more precise frequency setting.
- `#define RUNNING_FREQUENCY` - the value of frequency is applied "on the fly" with a small 0.5 sec delay so that you keep adjusting the frequency by encoder and the value is applied in 0.5 sec after your input is complete.
- **NEW** `#define STEPPED_SWEEP_GENERATOR` - the value of frequency is varied in a range defined by frequency values set in Ch#0 and Ch#1 with signal settings of Ch#0 and a discrete step of 0,1 of a current frequency.
- `#define ENABLE_VOUT_SWITCH` - (disabled by default) developed an extra output circuit that switch meander logic level to either 3.3v or 5v. Switched from menu by pin 6. See explanation and EasyEDA link below in the **Squarewave Signal Amplitude Feature** chapter below.
- `#define LCD_I2C_ADDRESS 0x3f` - may need to change I2C address of the display module.
- `#define EEPROM_ADDRESS_SHIFT` - start address in EEPROM to store settings; if EEPROM resource is vanished and you start getting `"EEPROM CRC Error"` at launch, change the settings block start address shifting it to the other unused EEPROM area. The entire settings block takes 14 bytes only.
- **NEW** `#define ENABLE_WATCHDOG` - use WatchDog timer to prevent firmware from hanging out.
- `#define SWAP_ENCODER_DIRECTION` - (disabled by default) swap encoder pins if encoder is detecting rotation incorrectly.
- `#define USE_PHASE` - (disabled by default) use Phase instead of the FREQ register; never used nor tested :-) Sorry, no guarantee it works...
## Improved Navigation:
- Default Operation Screen:
- Single button click -> go to Settings Mode.
- Double click -> save settings to EEPROM (in case EEPROM is enabled). Display backlight will blink 2 times to confirm the operation.
- Frequency Input Mode:
- Single click -> change frequency inout mode: input digit value by encoder or change digit position.
- Long press -> exit Frequncy Input Mode to Settings Mode.
- Encoder rotation -> change value of the current digit of the frequency value (digit is underlined by a blinking cursor) OR change an input digit postion (flat cursor). (depends of the frequency inout mode).
- Fast encoder rotation -> change value of more significant digit rather than the current digit position.
- **NEW** "running" frequency -> the value of frequency is applied "on the fly" with a small 0.5 sec delay; keep adjusting the frequency by encoder and the set value is applied in 0.5 sec after your've stopped rotating encoder, so the input is completet.
- Settings Mode:
- Encoder rotation any direction -> switch from one input parameter to another in a loop; a current input parament is highlighted by underline cursor.
- Single click at active input parameter -> change parameter's value. The new value is immediately applied (except Frequeency Input mode).
- Long button press anywhere in settings mode -> save and apply the current value of a parameter and jump to Operation Screen (blinking cursor at the "f=" letter).
- Triple click anywhere -> change the way the frequency value is displayed: with/without leading zeros; with/without thousands separation sign. All four possible combinations are toggled in a loop. Default set: no leading zeros with a separation apostrophe.
The thousands delimiter is different from country to country. In the United States, this character is a comma (,) in Germany, a period (.), in Sweden, a space. So, you may re-define `DELIMITER` sign to one you аrе accustomed to: comma, period, space, astrisk, etc... Just search for `DELIMITER` definition.
- **NEW** `Stepped Sweep Generator` feature:
- Double click in SETTING_MODE while in channel Ch#0 position -> initiate the `Stepped Sweep Generator` feature; runs one cycle of a frequency variation.
- Single click or click and hold -> cancel a `Stepped Sweep Generator` sweep cycle.
#### If EEPROM is enabled:
- Press and hold button during start up -> reset settings to default (just for the current session, does not write default settings to EEPROM).
Hold the button until display's backlight starts blinking. Backlight will blink 3 times to confirm the reset.
- Double click anywhere except input frequency and Ch#0 -> save settings to EEPROM. Display backlight will blink 2 times to confirm.
- At the first launch of the firmware `Error:CRC EEPROM` will be shown because no settings are in the EEPROM yet. Settings will be automatically set to default and saved to EEPROM. The error will not appear any more.
## Squarewave Signal Amplitude Feature
The AD98333 module generates meandre (squarewave signal) with amplitude equal to its VCC level. So, if VCC bus is +5v, then the amplitude of the squarewave sugnal is +5v. In some cases a signal of +3.3v TTL may be required.
There are several solutions:
1. use +3.3v power bus for entire solution (will not have +5v output squarewave signal then).
2. add +3.3v voltage regulator and switch between +5v and +3.3v power bus for entire setup including both uController and A9833 module (plain-rough solution).
3. add +3.3v voltage regulator and an output buffer; flip power bus between +5v and +3.3v for the output buffer only (deployed, switching from menu by flipping pin#6).
**Note:** The +5v/+3.3v power buss switch may also be a simple mechanical 2-position toggle switch!
So, I deployed the option#3: added an output buffer for meander/squarewave signal only based on Schmitt-trigger (for example, 74LVC1G14) which is connected right to the AD9833 out pin. By flipping the Schmitt-trigger's power bus between +5v and +3.3v, the output signal amplitude is switched accordingly. The switching is done from firmware (menu) by pin#6.
To activate the feature in the firmware uncomment: `#define ENABLE_VOUT_SWITCH`
Schematic of the "ouput buffer" based on the Schmitt-trigger 74LVC1G14 is awailable at [EasyEDA](https://easyeda.com/Sergiy/switch-5-3-3v-power-bus)
## Compact Case
Developed a compact case for the generator (see pictures), available at:
- Thingiverse : https://www.thingiverse.com/thing:4552227
- MyMiniFactory: https://www.myminifactory.com/object/3d-print-128983
## Gratitude
**If you like the new look and feel of SignalGeneratorSD, please, consider making a small "cup of coffee" donation using [PayPal](https://paypal.me/shaggyDog18/5USD)**
Inhale a new life into your Signal Generator! Enjoy!
*/
// --- COMPILER CONFIG ----
#define GRAPH_ICONS // use graphical icons for sign representation on display
#define ENABLE_EEPROM // sacve settings to EEPROM, recover them at startup
#define ENABLE_MEANDRE05F_SIGMODE // compatible with the new MD_AD9833 library only; if you do not need it - comment!
#define RUNNING_FREQUENCY // The value of frequency is applied "on the fly" with a small 0.4 sec delay. The new frequency value is applied in 0.4 sec after your input is complete.
//#define ENABLE_VOUT_SWITCH // developped an extra output circuit that switch meander logic level of eather 3.3v or 5v; switched from menu by pin 6
#define EEPROM_ADDRESS_SHIFT 0x00 // start address in EEPROM to store settings; if EEPROM is vanished and you start getting "EEPROM CRC Error" at launch, change the start address to shift to the other unused EEPROM area
#define STEPPED_SWEEP_GENERATOR // NEW feature: veries frequency in a range defined by frequency values set in Ch#0 and Ch#1 with a signal settings of Ch#0 and a discrete step of 0,1 of a current frequency. RUNNING_FREQUENCY should be defined.
#define ENABLE_WATCHDOG // size encresed to 11092/452 vs 11038/452 bytes
//#define SWAP_ENCODER_DIRECTION // swap if encoder is rotating in the wrong direction
//#define USE_PHASE //Uncomment if you want to change the Phase instead of the FREQ register // never use nor tested
// ------------------------
//Check up and correct Compiler Configuration
#if defined( STEPPED_SWEEP_GENERATOR ) && !defined( RUNNING_FREQUENCY )
#define RUNNING_FREQUENCY
#endif
//-------------------------
// --- INCLUDE SECTION ---
#include <avr/delay.h>
#include <LiquidCrystal_I2C.h>
#include <RotaryEncoder.h>
#include <OneButton.h>
#include <MD_AD9833.h> // modified library. Midifications are commented with ShaggyDog keyword
#ifdef ENABLE_EEPROM
#include <avr/eeprom.h>
#endif
#ifdef ENABLE_WATCHDOG
#include <GyverWDT.h>
#endif
// ------------------------
#ifdef SWAP_ENCODER_DIRECTION
#define DT 2 // encoder pin
#define CLK 3 // encoder pin
#else
#define DT 3 // encoder pin
#define CLK 2 // encoder pin
#endif
#define BUTTON_OK 4
#define TOGGLE_OUT 5 // switch the out off if toggle to Meandre/CLK which is 5V out to protect the output from saturation (under development)
#define TOGGLE_MEANDRE_VOUT 6 // switch output sine voltage between 3.3v (default) and 5v
#define FSYNC_PIN 10
// LCD Settings
#define LCD_I2C_ADDRESS 0x3f // 0x27 // 0x3f - alternative i2c address (blue screen module)
#define LCD_DISP_COLS 16
#define LCD_DISP_ROWS 2
#define FREQ_N_DIGITS 8 // number of digits for frequency to display
#define PHASE_N_DIGITS 4 // number of digits for phase to display
// --- Initialize hardware ---
LiquidCrystal_I2C lcd( LCD_I2C_ADDRESS, LCD_DISP_COLS, LCD_DISP_ROWS ); // LCD Initialise
RotaryEncoder encoder( DT, CLK ); // initialise the encoder on pins 2 and 3 (interrupt pins)
OneButton buttonOK( BUTTON_OK ); // initialize Encoder Button
MD_AD9833 sigGen( FSYNC_PIN ); // initialize AS9833 module, connected to hardware SPI
// Variables used to position cursor for data input and walk through menu
#ifdef ENABLE_VOUT_SWITCH // on/off signal state is represented by one symbol/char
const uint8_t settingInputPos[] = {0, 15, 19, 22, 29};// used to be {0, 14, 20, 23, 29}; for CHAN
#else
const uint8_t settingInputPos[] = {0, 15, 19, 29}; // used to be {0, 14, 20, 29};
#endif
// grapic single chars to represent ON and OFF state of the generator's out signal, taken from LCD1602's Font Table, datasheet, page 14
const char ONchar = 0b01111110; // looks like '->'
const char OFFchar = 0b11011011; // looks like empty square '|_|'. Also can use ']' or any other symbol...
// --- Define data types/structures ---
enum menuState_t : uint8_t {
DEFAULT_SCREEN = 0,
SETTING_MENU,
FREQUENCY_SETTING,
#ifdef USE_PHASE
PHASE_SETTING,
#endif
} menuState = DEFAULT_SCREEN;
enum sigmode_t : uint8_t { // SigMode <=> Signal Mode
SIGMODE_SINE = 0,
SIGMODE_TRIANGLE,
SIGMODE_MEANDRE,
#ifdef ENABLE_MEANDRE05F_SIGMODE
SIGMODE_MEANDRE05F,
#endif
NUMBER_SIGMODES
};
enum inputPositions_t : uint8_t {
IP_FREQUENCY = 0, // IP means Input Position
IP_ONOFF,
IP_CHANNEL,
#ifdef ENABLE_VOUT_SWITCH
IP_VOUT_SWITCH,
#endif
IP_SIGNAL_MODE,
NUMBER_INPUT_POSITIONS
};
enum frequencyValuePresentation_t : uint8_t {
LEAD_ZERO_and_NO_DELIMITER = 0, // 0b00
LEAD_ZERO_and_DELIMITER, // 0b01
NO_LEAD_ZERO_and_NO_DELIMITER, // 0b10
NO_LEAD_ZERO_and_DELIMITER, // 0b11
NUMBER_FREQUENCY_DISPLAY_MODES
};
// masks for fast if() with binary '&' to be applied to displayFrequencyMode
const uint8_t NO_LEAD_ZERO_MASK = 0b00000010;
const uint8_t THOUSANDS_DELIMITER_MASK = 0b00000001;
// thousands delimiter sign for frequency value presentation
const char DELIMITER = 0b00100111; // regular apostrophe sign '
//const char DELIMITER = '`'; // another "angled" apostrophe sign;
//const char DELIMITER = ','; // comma for the United States
//const char DELIMITER = '.'; // period for Germany
//const char DELIMITER = ' '; // space for Sweeden
// Settings structure to keep all settings together in one structure and store it in EEPROM "at once", size of 14 bytes
#pragma pack(1)
struct settings_t {
unsigned long frequency[2];
sigmode_t currentMode[2]; // uint8_t type
bool currentChannel; // current FREQ register/Channel#0; FALSE for Ch#0, TRUE for Ch#1
bool toggleMeandreOutVoltage; // 3.3v CLK signal by default
uint8_t displayFrequencyMode; // 4 ways to display frequency value: with/without leading zeros, with/without separation delimiter
uint8_t checkSum;
#ifdef ENABLE_EEPROM
} settings;
#else
} settings = { 1000UL, 1000UL, SIGMODE_SINE, SIGMODE_MEANDRE, false, false, NO_LEAD_ZERO_and_DELIMITER, 0 }; // dafault values
#endif
int8_t digitPos = 0; // current input position for frequency value
const unsigned long maxFrequency = 12500000UL; // according to AS9833 Datasheet the max frequency is 12.5 MHz
#ifdef USE_PHASE
const unsigned int maxPhase = 4095; // Only used if you enable PHASE setting instead of FREQ register
uint16_t phase = 0;
#endif
// flags
struct flags_t{
bool defaultScreenUpdateFlag = true;
bool updateDisplayFlag = true;
bool signalOn = true;
bool frequencySettingMode = true; // encoder change: false->value of a digit; true->position of a digit
#ifdef RUNNING_FREQUENCY
bool frequencyUpdatedFlag = false;
#endif
} flags;
// Variables used to temporary store frequency value
unsigned long frequency;
uint8_t cursorInputPos = IP_FREQUENCY;
#ifdef RUNNING_FREQUENCY
const unsigned long frequencyUpdateApplyDelay = 400UL; // 0.4 sec delay between change in frequency value and its communication/application to AD9833 module;
// use delay to avoid every small freq value change to be communicated to the module; allow fast change in freq value without application
unsigned long lastFrequencyUpdate; // the most recent time in millis when the frequency value was updated
#endif
#ifdef STEPPED_SWEEP_GENERATOR // very frequency in a range defined by frequencies set in Ch#0 and Ch#1 with a step of 0,1 of current frequency with signal settings in Ch#0
const unsigned long STEPPED_SWEEP_DELAY = 250UL;
#endif
#ifndef GRAPH_ICONS
// signal mode depicting constants
#ifdef ENABLE_MEANDRE05F_SIGMODE
const String mode[] = {"SIN", "TRI", "CLK", "C/2"};
#else
const String mode[] = {"SIN", "TRI", "CLK"};
#endif
#endif
// Greek PHI symbol for phase shift
// Only used if you enable PHASE setting instead of FREQ register
#ifdef USE_PHASE
const uint8_t phi[] = { // greek 'ф' sign for phase
0b01110,0b00100,0b01110,0b10101,0b10101,0b01110,0b00100,0b01110 };
#else
const uint8_t ff[] = { // "FF" sign to indicate "OFF" state
0b11100,0b10000,0b11000,0b10111,0b00100,0b00110,0b00100,0b00000 };
#endif
#if defined( GRAPH_ICONS ) or defined( ENABLE_VOUT_SWITCH )
const uint8_t meander[] = { // meander sign _||_
0b00000,0b01110,0b01010,0b01010,0b01010,0b01010,0b11011,0b00000 };
#endif
#ifdef GRAPH_ICONS
// LCD mode depicting icons
const uint8_t sine[3][8] = {
0b00000,0b00000,0b00000,0b00000,0b10000,0b01000,0b00100,0b00011,
0b00000,0b00001,0b00010,0b00100,0b00100,0b01000,0b10000,0b00000,
0b11000,0b00100,0b00010,0b00001,0b00000,0b00000,0b00000,0b00000 };
const uint8_t triangle[2][8] = {
0b00000,0b00000,0b10000,0b01000,0b00100,0b00010,0b00001,0b00000, // '\'
// use a standard '/' char
0b10000,0b01000,0b00100,0b00010,0b00001,0b00000,0b00000,0b00000 }; //'\'
#endif
//---------------------
// setup()
//---------------------
void setup() {
// Initialise the LCD, start the backlight and print a "bootup" message
lcd.begin();
// create graphic signs
#ifdef USE_PHASE
lcd.createChar(0, phi); // Custom graphic PHI char for LCD
#else
lcd.createChar(0, ff); // Custom graphic FF char for LCD to indicate "Signal OFF" state
#endif
#if defined( GRAPH_ICONS ) or defined( ENABLE_VOUT_SWITCH )
lcd.createChar(1, meander); // Custom graphic meandre char for LCD
#endif
#ifdef GRAPH_ICONS
lcd.createChar(2, sine[0]); // Custom graphic sine 1/3 char for LCD
lcd.createChar(3, sine[1]); // Custom graphic sine 2/3 char for LCD
lcd.createChar(4, sine[2]); // Custom graphic sine 3/3 char for LCD
lcd.createChar(5, triangle[0]); // Custom graphic triangle 1/3 char for LCD
lcd.createChar(6, triangle[1]); // Custom graphic triangle 3/3 char for LCD
// lcd.createChar(7, vacant); // Custom graphic - vacant position
#endif
buttonOK.attachClick( processSingleClick ); // single click
buttonOK.attachLongPressStart( processLongPress ); // long press
buttonOK.attachDoubleClick( processDoubleClick ); // double click
buttonOK.attachMultiClick( processTripleClick ); // triple click
// Launch Screen
lcd.home();
lcd.backlight();
lcd.print(F("Signal Generator"));
lcd.setCursor(0, 1);
lcd.print(F("-ShaggyDog 2021-"));
_delay_ms(1500);
// Initite AD9833 Module
sigGen.begin(); // it also sets up all default values
#ifdef ENABLE_EEPROM
bool resetSettings = false;
if( ! readSettingsFromEEPROM() ) {
lcd.setCursor(0, 0);
lcd.print(F("Error:CRC EEPROM"));
lcdPrintResetMsg( 1 ); // print Reset message in line # 1
_delay_ms(3000);
resetSettings = true;
lcd.clear();
}
if( !digitalRead( BUTTON_OK ) || resetSettings ) { // if button is pressed at start up then all settings are reset to default
lcdPrintResetMsg( 0 ); // print Reset message in line # 0
settings.frequency[0] = settings.frequency[1] = 1000UL;
settings.currentMode[0] = SIGMODE_SINE;
settings.currentMode[1] = SIGMODE_MEANDRE;
settings.toggleMeandreOutVoltage = false;
settings.currentChannel = false; // means CH#0; true means CH#1
settings.displayFrequencyMode = NO_LEAD_ZERO_and_DELIMITER;
blinkDisplayBacklight(3);
while( !digitalRead( BUTTON_OK ) ) ; // wait for button to be released
}
if( resetSettings ) writeSettingsToEEPROM();
applyCurrentSettings(); // set settings to the AD module that were read from EEPROM
#endif
// Set encoder pins as interrupts
attachInterrupt( digitalPinToInterrupt(DT), encoderTickISR, CHANGE );
attachInterrupt( digitalPinToInterrupt(CLK), encoderTickISR, CHANGE );
#ifdef ENABLE_VOUT_SWITCH
// init pins and set them to default state
pinMode( TOGGLE_MEANDRE_VOUT, OUTPUT );
toggleMeandreVoltage( settings.toggleMeandreOutVoltage ); // 3.3v by default (LOW signal output)
pinMode( TOGGLE_OUT, OUTPUT ); // decrease amplitude of CLK signal to 650 mV by toggling an output divider (under development)
toggleOut( settings.currentMode[(uint8_t)settings.currentChannel] ); // under development
#endif
// Clear the screen
lcd.clear();
#ifdef ENABLE_WATCHDOG
Watchdog.enable(RESET_MODE, WDT_PRESCALER_256); // timeout 2sec
#endif
} // end of setup()
//---------------------------
// print settings Reset message: called twice, defined as a separate function to optimize the code
void lcdPrintResetMsg( const uint8_t _line ) { // print Reset message in a line # x
lcd.setCursor( 0, _line );
lcd.print( F("Reset to default") );
} // lcdPrintResetMsg()
//---------------------
// ISR routine for Encoder
void encoderTickISR(void) {
encoder.tick();
}
//---------------------
//---------------------
// loop()
//---------------------
void loop() {
buttonOK.tick(); // Check if encoder button has been pressed
processEncoder( encoder.getDirection() );
#ifdef RUNNING_FREQUENCY
if( flags.frequencyUpdatedFlag ) { // frequency value was changed
if( millis() - lastFrequencyUpdate > frequencyUpdateApplyDelay ) { // apply new frequency value after a delay
settings.frequency[(uint8_t)settings.currentChannel] = frequency;
setADfrequency( settings.currentChannel, settings.frequency[(uint8_t)settings.currentChannel] );
flags.frequencyUpdatedFlag = false;
}
}
#endif
if( flags.updateDisplayFlag ) { // Update display if needed
// Display: 1st line
displayFrequency( frequency );
displaySignalONOFF( flags.signalOn );
// Display: 2nd line
#ifdef USE_PHASE
displayPhase( phase );
#else
displayCurrentChannel( settings.currentChannel );
#endif
#ifdef ENABLE_VOUT_SWITCH
displayMeandreOutVolt( settings.toggleMeandreOutVoltage, settings.currentMode[(uint8_t)settings.currentChannel] );
#endif
displaySignalMode( settings.currentMode[(uint8_t)settings.currentChannel] );
setCursor2inputPosition( cursorInputPos );
flags.updateDisplayFlag = false;
}
cursorPositionPostProcessing(); // set cursor to the right position after display update
#ifdef ENABLE_WATCHDOG
Watchdog.reset(); // Reset watchdog, means loop() is operating OK...
#endif
} // end of loop()
//---------------------
void cursorPositionPostProcessing(void) { // set cursor to the right position after display update
switch( menuState ) {
case DEFAULT_SCREEN: // Default state
if( flags.defaultScreenUpdateFlag ) {
lcd.setCursor(0, 0);
lcd.noCursor();
lcd.blink();
flags.defaultScreenUpdateFlag = false;
}
break;
case FREQUENCY_SETTING: // Frequency setting
uint8_t pos;
if( settings.displayFrequencyMode & THOUSANDS_DELIMITER_MASK ) { // binary '&' - that's correct!!! // thousands delimiter sign is ON
pos = FREQ_N_DIGITS+3 - digitPos;
if( digitPos > 2 ) pos--; // skip place for separation point#1
if( digitPos > 5 ) pos--; // skip place for separation point#2
} else { // no separation points
pos = FREQ_N_DIGITS+1 - digitPos;
}
lcd.setCursor( pos, 0 );
break;
#ifdef USE_PHASE // never tested
case PHASE_SETTING: // Phase setting
lcd.setCursor( PHASE_N_DIGITS+1 - digitPos, 1 );
break;
#endif
} // switch( menuState )
} // menuPostProcessing()
//---------------------
void processSingleClick(void) {
switch( menuState ) {
case DEFAULT_SCREEN:
jump2settingMenu();
break;
case SETTING_MENU:
switch( cursorInputPos ) {
case IP_FREQUENCY: // go to frequesncy setting
menuState = FREQUENCY_SETTING;
lcd.cursor();
break;
case IP_ONOFF: // switch an output generator signal on / off
flags.signalOn = ! flags.signalOn;
sigGen.setModeSD(flags.signalOn ? MD_AD9833::MODE_ON : MD_AD9833::MODE_OFF);
break;
#ifndef USE_PHASE // If USE_PHASE has not been set
case IP_CHANNEL: // select channel 0 or 1
settings.currentChannel = ! settings.currentChannel; // flip bool value
setADchannel( settings.currentChannel );
setADsignalMode( settings.currentMode[(uint8_t)settings.currentChannel] );
break;
#endif
#ifdef ENABLE_VOUT_SWITCH
case IP_VOUT_SWITCH: // switch meandre out voltage 5v vs. 3v
settings.toggleMeandreOutVoltage = ! settings.toggleMeandreOutVoltage; // flip bool value
toggleMeandreVoltage( settings.toggleMeandreOutVoltage );
break;
#endif
case IP_SIGNAL_MODE: // Change the signal mode (sine/triangle/clock)
++(*((uint8_t*)&(settings.currentMode[(uint8_t)settings.currentChannel]))); // increment currentMode value :-)
if( settings.currentMode[(uint8_t)settings.currentChannel] == NUMBER_SIGMODES )
settings.currentMode[(uint8_t)settings.currentChannel] = SIGMODE_SINE;
// switch output to direct out
#ifdef ENABLE_VOUT_SWITCH
toggleOut( settings.currentMode[(uint8_t)settings.currentChannel] );
#endif
setADsignalMode( settings.currentMode[(uint8_t)settings.currentChannel] );
break;
} // switch( cursorInputPos )
flags.updateDisplayFlag = true;
break;
case FREQUENCY_SETTING: // swap between two frequency input modes: change a selected digit value -> change a digit position
flags.frequencySettingMode = !flags.frequencySettingMode;
if( flags.frequencySettingMode )
lcd.noBlink(); // change a digit position
else
lcd.blink(); // change a digit value
break;
#ifdef USE_PHASE // never tested
case PHASE_SETTING: // Phase setting
if( digitPos < PHASE_N_DIGITS-1 ) {
digitPos++;
} else {
digitPos = 0;
sigGen.setPhase( settings.currentChannel ? MD_AD9833::CHAN_1 : MD_AD9833::CHAN_0, phase );
jump2settingMenu();
}
break;
#endif
} // switch( menuState )
} // processSingleClick(void)
//---------------------
void processLongPress(void) {
switch( menuState ) {
case SETTING_MENU:
menuState = DEFAULT_SCREEN;
cursorInputPos = IP_FREQUENCY;
flags.defaultScreenUpdateFlag = true;
break;
#ifdef USE_PHASE // never tested
case PHASE_SETTING:
// setPhase here
sigGen.setPhase( settings.currentChannel ? MD_AD9833::CHAN_1 : MD_AD9833::CHAN_0, phase );
jump2settingMenu();
break;
#endif
case FREQUENCY_SETTING:
#ifndef RUNNING_FREQUENCY
setADfrequency( settings.currentChannel, settings.frequency[(uint8_t)settings.currentChannel] );
#endif
flags.frequencySettingMode = true; // default value:change position of the digit
jump2settingMenu();
break;
} // switch( menuState )
} // processLongPress(void)
//---------------------
void processDoubleClick(void) {
switch( menuState ) {
#ifdef STEPPED_SWEEP_GENERATOR
case SETTING_MENU:
if( cursorInputPos == IP_CHANNEL && !settings.currentChannel ) { // for Ch#0 only;
steppedSweepGenerator();
}
break;
#endif
#ifdef ENABLE_EEPROM
default:
writeSettingsToEEPROM();
blinkDisplayBacklight(2); // double blink to confirm operation of writing to EEPROM
#endif
}
} // processDoubleClick(void)
//---------------------
void processTripleClick(void) {
settings.displayFrequencyMode++;
settings.displayFrequencyMode %= NUMBER_FREQUENCY_DISPLAY_MODES; // same as below 2 lines
//if( settings.displayFrequencyMode == NUMBER_FREQUENCY_DISPLAY_MODES )
// settings.displayFrequencyMode = 0; // LEAD_ZERO_and_NO_DELIMITER
flags.updateDisplayFlag = true;
lcd.clear();
} // processTripleClick()
//---------------------
void setCursor2inputPosition( const uint8_t _cursorPosition ) {
// Move the cursor to display position in case it changed
uint8_t realPosR = 0;
uint8_t realPosC = settingInputPos[_cursorPosition];
if( realPosC >= LCD_DISP_COLS ) {
realPosC -= LCD_DISP_COLS;
realPosR = 1;
}
lcd.setCursor( realPosC, realPosR );
lcd.cursor();
} // setCursor2inputPosition()
//---------------------
void processEncoder( const RotaryEncoder::Direction _rotaryDirection ) {
// Depending in which menu state you are the encoder will either change the value of a setting:
//-+ frequency or it will change the cursor position
switch( _rotaryDirection ) {
case RotaryEncoder::Direction::CLOCKWISE:
case RotaryEncoder::Direction::FAST_CW:
switch( menuState ) {
case DEFAULT_SCREEN:
jump2settingMenu();
break;
case SETTING_MENU:
cursorInputPos++; // changing input positions in a loop
#ifdef ENABLE_VOUT_SWITCH // jump over the VOUT_SWITCH input position in case of sine or triangle signal modes
#ifdef ENABLE_MEANDRE05F_SIGMODE
if( (settings.currentMode[(uint8_t)settings.currentChannel] != SIGMODE_MEANDRE &&
settings.currentMode[(uint8_t)settings.currentChannel] != SIGMODE_MEANDRE05F) && cursorInputPos == IP_VOUT_SWITCH ) cursorInputPos++;
#else
if( settings.currentMode[(uint8_t)settings.currentChannel] != SIGMODE_MEANDRE && cursorInputPos == IP_VOUT_SWITCH ) cursorInputPos++;
#endif
#endif
if( cursorInputPos == NUMBER_INPUT_POSITIONS ) cursorInputPos = IP_FREQUENCY;
setCursor2inputPosition( cursorInputPos );
break;
case FREQUENCY_SETTING:
// false: encoder change value of a digit; true: encoder change a digit position
if( flags.frequencySettingMode ) { // change a digit position (--)
if( digitPos > 0 ) digitPos--;
} else { // change a digit value
if( _rotaryDirection == RotaryEncoder::Direction::FAST_CW )
frequency += power(10, digitPos+1);
else // slow encoder rotation
frequency += power(10, digitPos);
if( frequency > maxFrequency ) frequency = maxFrequency;
#ifdef RUNNING_FREQUENCY
lastFrequencyUpdate = millis();
flags.frequencyUpdatedFlag = true;
#else
settings.frequency[(uint8_t)settings.currentChannel] = frequency;
#endif
flags.updateDisplayFlag = true;
}
break;
#ifdef USE_PHASE
case PHASE_SETTING: {
// if USE_PHASE has been defined, changes in the encoder will vary the phase value (upto 4096)
// A better implementation would be to use increment of pi/4 or submultiples of pi where 2pi = 4096
unsigned long newPhase = phase + power(10, digitPos);
unsigned char dispDigit = phase % power(10, digitPos + 1) / power(10, digitPos);
if( newPhase < maxPhase && dispDigit < PHASE_N_DIGITS+1 ) {
phase += power(10, digitPos);
flags.updateDisplayFlag = true;
}
} break;
#endif
} // switch( menuState )
break;
case RotaryEncoder::Direction::COUNTERCLOCKWISE:
case RotaryEncoder::Direction::FAST_CCW:
switch( menuState ) {
case SETTING_MENU:
if( cursorInputPos == IP_FREQUENCY ) { // changing input positions in a loop
cursorInputPos = NUMBER_INPUT_POSITIONS-1;
} else {
cursorInputPos--;
#ifdef ENABLE_VOUT_SWITCH // jump over the VOUT_SWITCH input position in case of sine or triangle signal modes
#ifdef ENABLE_MEANDRE05F_SIGMODE
if( (settings.currentMode[(uint8_t)settings.currentChannel] != SIGMODE_MEANDRE &&
settings.currentMode[(uint8_t)settings.currentChannel] != SIGMODE_MEANDRE05F) && cursorInputPos == IP_VOUT_SWITCH ) cursorInputPos--;
#else
if( settings.currentMode[(uint8_t)settings.currentChannel] != SIGMODE_MEANDRE && cursorInputPos == IP_VOUT_SWITCH ) cursorInputPos--;
#endif
#endif
}
setCursor2inputPosition( cursorInputPos );
break;
case FREQUENCY_SETTING:
// false: encoder change value of a digit; true: encoder change a digit position
if( flags.frequencySettingMode ) { // change a digit position (++)
if( digitPos < FREQ_N_DIGITS-1 ) {
digitPos++;
} else { // exit frequency Input mode -> jump to Settings Menu
digitPos = 0;
#ifndef RUNNING_FREQUENCY
setADfrequency( settings.currentChannel, settings.frequency[(uint8_t)settings.currentChannel] );
#endif
jump2settingMenu();
}
} else { // change a digit value
if( _rotaryDirection == RotaryEncoder::Direction::FAST_CCW ) {
if( frequency > power(10, digitPos+1) )
frequency -= power(10, digitPos+1);
else
break;
} else { // slow encoder rotation
if( frequency > power(10, digitPos) )
frequency -= power(10, digitPos);
else
break;
}
#ifdef RUNNING_FREQUENCY
lastFrequencyUpdate = millis();
flags.frequencyUpdatedFlag = true;
#else
settings.frequency[(uint8_t)settings.currentChannel] = frequency;
#endif
flags.updateDisplayFlag = true;
}
break;
#ifdef USE_PHASE
case PHASE_SETTING: {
// if USE_PHASE has been defined, changes in the encoder will vary the phase value (upto 4096)
// A better implementation would be to use increment of pi/4 or submultiples of pi where 2pi = 4096
unsigned long newPhase = phase + power(10, digitPos);
unsigned char dispDigit = phase % power(10, digitPos + 1) / power(10, digitPos);
if(newPhase > 0 && dispDigit > 0) {
phase -= power(10, digitPos);
flags.updateDisplayFlag = true;
}
} break;
#endif
} // switch( menuState )
break;
} // switch( rotaryDirection )
} // processingEncoder()
//---------------------
void jump2settingMenu(void) {
menuState = SETTING_MENU;
cursorInputPos = IP_FREQUENCY;
lcd.noBlink();
lcd.setCursor(0,0);
lcd.cursor();
} // jump2settingMenu()
//---------------------
// Function to display the current frequency in the top left corner
void displayFrequency( unsigned long _frequencyToDisplay ) {
lcd.setCursor(0,0);
lcd.print( F("f=") );
uint8_t dispBuffer[FREQ_N_DIGITS]; // digits order is from the least significant to the most significant digit of frequency value
uint8_t i;
for( i=0; i<FREQ_N_DIGITS; i++ ) {
dispBuffer[i] = _frequencyToDisplay % 10;
_frequencyToDisplay /= 10;
if( _frequencyToDisplay == 0 ) break;
}
// after performing the `break` command above i indicates the place of the last most significant meaningful digit in the freq value
// correct cursor input position in freq value to the most significant digit if it's happen to orfan at the left
if( digitPos > i ) digitPos = i;
// print freq value from left to right starting from the most significant digit
for( int8_t j=FREQ_N_DIGITS-1; j >= 0; j-- ) {
if( settings.displayFrequencyMode & NO_LEAD_ZERO_MASK ) { // hide leaading Zeros
if( j>i ) { // digits (zeros) before most significant digit in frequency value
if( settings.displayFrequencyMode & THOUSANDS_DELIMITER_MASK ) { // delimiter sign is ON
if( j == 5 || j == 2 ) lcd.print( ' ' ); // print space
}
lcd.print( ' ' );
} else { // digits after (including) most significant digit in frequency value
if( settings.displayFrequencyMode & THOUSANDS_DELIMITER_MASK ) { // delimiter sign is ON
if( j == 5 ) {
if( i == 5 ) lcd.print( ' ' ); else lcd.print( DELIMITER ); // print either space or delimiter sign
}
if( j == 2 ) {
if( i == 2 ) lcd.print( ' ' ); else lcd.print( DELIMITER ); // print either space or delimiter sign
}
}
lcd.print( dispBuffer[j] );
}
} else { // show leading zeros
if( settings.displayFrequencyMode & THOUSANDS_DELIMITER_MASK ) { // delimiter sign is ON
if( j == 5 || j == 2 ) lcd.print( DELIMITER );
}
if( j>i ) lcd.print( '0' ); else lcd.print( dispBuffer[j] ); //lcd.print( dispBuffer[j] ); //try
} // if() hide leaading Zeros
} // for()
lcd.print( F("Hz") );
} // displayFrequency()
//---------------------
// Function to display power state (ON/OFF) in the top right corner
void displaySignalONOFF( const bool _signalOn ) {
#ifdef USE_PHASE
lcd.setCursor(13, 0);
lcd.print( _signalOn ? F("ON ") : F("OFF") );
#else
if( settings.displayFrequencyMode & THOUSANDS_DELIMITER_MASK ) { // separation sign ON
lcd.setCursor(15, 0);
//lcd.print( _signalOn ? ONchar : OFFchar ); // same as below
if( _signalOn ) lcd.print( ONchar ); else lcd.print( OFFchar ); // more compact than the above line
} else { // separation sign OFF
lcd.setCursor(14, 0);
lcd.print('O');
if( _signalOn )
lcd.print('N'); // ON
else
lcd.write( 0 ); // OFF sign
}
#endif
}
//---------------------
// Function to display the mode in the bottom right corner
void displaySignalMode( const sigmode_t _currentMode ) {
lcd.setCursor(13, 1);
#ifdef GRAPH_ICONS
switch( _currentMode ) {
case SIGMODE_SINE: lcd.write( 2 );lcd.write( 3 );lcd.write( 4 );break;
case SIGMODE_TRIANGLE: lcd.write( 5 );lcd.print('/');lcd.write( 6 );break;
case SIGMODE_MEANDRE: lcd.write( 1 );lcd.write( 1 );lcd.write( 1 );break;
#ifdef ENABLE_MEANDRE05F_SIGMODE
case SIGMODE_MEANDRE05F: lcd.write( 1 );lcd.print( F("/2") );break;
#endif
}
#else
lcd.print( mode[(uint8_t)_currentMode ] );
#endif
} // displaySignalMode()
//---------------------
// Only used if you enable PHASE setting instead of FREQ register
void displayPhase( unsigned int _phaseToDisplay ) {
lcd.setCursor(0, 1);
lcd.write(0); // phase sign
lcd.print('=');
uint8_t dispBuffer[PHASE_N_DIGITS] = {0,0,0,0};
for( uint8_t i=0; i<PHASE_N_DIGITS; i++ ) {
dispBuffer[i] = _phaseToDisplay % 10;
_phaseToDisplay /= 10;
if( _phaseToDisplay == 0 ) break;
}
for( int8_t j=PHASE_N_DIGITS-1; j >= 0; j-- ) lcd.print( dispBuffer[j] );
} // displayPhase()
//---------------------
// Function to display the FREQ register/Channel (either 0 or 1) in the bottom left corner
void displayCurrentChannel( const bool _channel ) {
lcd.setCursor(0, 1);
lcd.print( F("Ch#") ); // display channel as Ch#1 Ch#0 vs. old style CHAN1 CHAN0
lcd.print( (uint8_t)_channel );
} // displayCurrentChannel()
//---------------------
void displayMeandreOutVolt( const bool _toggleMeandreOutVoltage, const sigmode_t _currentMode ) {
lcd.setCursor(6, 1);
#ifdef ENABLE_MEANDRE05F_SIGMODE
if( _currentMode == SIGMODE_MEANDRE || _currentMode == SIGMODE_MEANDRE05F ) {
#else
if( _currentMode == SIGMODE_MEANDRE ) {
#endif
lcd.write( 1 ); // meandre sign
lcd.print( _toggleMeandreOutVoltage ? F("5.0v") : F("3.3v") );
} else {
lcd.print( F(" ") ); // 5 x spaces
}
} // displayMeandreOutVolt()
//---------------------
void toggleMeandreVoltage( const bool _toggleOutVolt ) {
digitalWrite( TOGGLE_MEANDRE_VOUT, _toggleOutVolt ? HIGH : LOW ); // HIGH = 5v, 3.3v
}
//---------------------
unsigned long power( const uint8_t a, uint8_t b ) {
unsigned long res = 1;
if( b > 0 ) {
res = a; b--;
for( uint8_t i = 0; i < b; i++ ) res *= a;
}
return res;
}
//---------------------
void setADsignalMode( const sigmode_t _currentMode ) {
switch( _currentMode ) {
case SIGMODE_SINE: sigGen.setModeSD( MD_AD9833::MODE_SINE ); break;
case SIGMODE_TRIANGLE: sigGen.setModeSD( MD_AD9833::MODE_TRIANGLE ); break;
case SIGMODE_MEANDRE: sigGen.setModeSD( MD_AD9833::MODE_SQUARE1 ); break;
#ifdef ENABLE_MEANDRE05F_SIGMODE
case SIGMODE_MEANDRE05F: sigGen.setModeSD( MD_AD9833::MODE_SQUARE2 ); break;
#endif
}
} // setADsignalMode()
//---------------------
void setADfrequency( const bool _channel, const unsigned long _frequency ) {
sigGen.setFrequency( _channel ? MD_AD9833::CHAN_1 : MD_AD9833::CHAN_0, _frequency );
} // setADfrequency()
//---------------------
void setADchannel( const bool _channel ) {
if( _channel ) {
sigGen.setActiveFrequency( MD_AD9833::CHAN_1 );
frequency = settings.frequency[1];
//frequency = sigGen.getFrequency( MD_AD9833::CHAN_1 ); // alternative way is to read frequency value from AD9833 module
} else {
sigGen.setActiveFrequency( MD_AD9833::CHAN_0 );
frequency = settings.frequency[0];
}
} // setADchannel()
//---------------------
void blinkDisplayBacklight( const uint8_t nBlinks ) {
for( uint8_t i=0; i<nBlinks; i++ ) {
lcd.noBacklight(); _delay_ms(300);
lcd.backlight(); _delay_ms(300);
}