-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathuBitX_Teensy.ino
2526 lines (2139 loc) · 96.2 KB
/
uBitX_Teensy.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
/*
* uBitX_Teensy
*
* Using a Teensy 3.2 for control and USB audio for soundcard modes ( Teensy can appear in Windows as a 16 bit sound card )
*
* First the ILI9341 will need to be converted to 3.3 volt operation. Currently all on 5 volts which apparently works but runs hot.
* On the ILI9341 remove the solder jumper at J1. Install an 82 ohm or 100 ohm resistor from pin 1 ( vcc ) to pin 8 (LED) on top
* of the connector pins.
* On the raduino board remove the 7805. Cut the etch to pin 4 ( reset ) on the outside of the connector near the edge of the board.
* There is not much room here. This removes +5 from the reset pin. Remove the 22 ohm surface mount resistor near pin 8. This
* removes the LED pin from +5 volts. The LED was wired as above and now the etch run from the 22 ohm resistor is on the net
* for pin 4. Wire the via or surface mount pad on this etch run to 3.3 volts on the nano socket to put 3.3 volts on the reset pin.
* Install the 7805 without the heatsink on the opposite side from where it was. Bend the leads up at 90 degrees. Make sure it is
* installed correctly - the back of the metal tab will be up with the body of the 7805 about covering the via where 3.3 volts was
* wired. Have the 7805 a little off the board so it doesn't heat the wire we just put on side 2. I pop riveted a small piece of
* aluminium to the 7805 for some extra heat sinking.
* Remove R2, a pullup to 5 volts on signal KEYER.
* Mark the board for 3.3 volt operation. The Nano can no longer be used with this Raduino. Test with a current limiting supply, a
* worn out 9 volt battery from a smoke detector works. Power up with no processor, no screen. Short out the 3.3 volt run to protect
* the Si5351 in case the 7805 is in backwards. It should draw about 3 ma. Test that the 5 volt net has 5 volts. Insert the screen
* and power up. It should draw about 60 ma on the depleted battery and the screen should light.
*
*
* A plan outline:
*
* The easy idea:
* Use the signals on the Audio connector. Very little modifications to the main board. ( need CW key jack mods )
* Remove the LM386 from the socket. Sample audio on VOL-M. Volume control works like an RF gain control adjusting level into the DSP.
* Connect the DAC to MIC via capacitor, pot for level control, capacitor. USB audio for DIGI mode transmit uses the DAC.
* Receive Audio to the speaker/headphones using PWM to the SPK on the audio connector. Suspect speaker volume will be low, headphone
* volume will be loud. Receive audio will also be sent to USB for DIGI modes.
*
* CW key jack: replace the DIT series resistor R3 with 10 ohm. Wire PTT from the MIC jack over to the DAH contact on the key jack.
* Remove the series resistor R2. Keyer works with digital signals, KEYER and PTT as DIT and DAH.
*
*
* Values from standard radio, the PLL remains fixed.
* Clock 0 : BFO 11,056,400
* Clock 1 : LSB 33,948,600
* Clock 1 : USB 56,061,400
* Clock 2 : VFO 48 meg to 75 meg
* 1st IF : 45,005,000
* XTAL Base : 25,000,000
* PLL divider 35
* PLL : 875,000,000
* CAL : 189,000
* PLL for calc 875,189,000
* Actual XTAL 25,005,400 if CAL was zero ( 189000/35 )
*
* Will break out the VFO from this scheme so that it uses the unused PLL and even dividers for maybe lower jitter.
* Maybe can run a divider of 12. 900/12 = 75. 600/12 = 50. 48*12 = 575 - so 80 meters runs PLL slightly out of spec.
* The existing Si5351 scheme seems to work ok, may not implement this idea.
*
* ATU board. An ATU-100 mini clone from uSDX SOTA radio.
* With a 18F2220, communication will be serial instead of the original plan of I2C.
* May or may not use a level converter. For a Teensy 4.1 project would need a level converter.
* Mounting will be tight. Maybe just mount using a bnc on the back panel.
* A connector exists to pick up RF on the main board.
* Poll during TX to display the FWD and REF power. Could display the L and C also.
* ATU commands using serial.
Z - Zero relays, keep current data. Bypass mode.
O - On, apply relays
S - Set relay data Cval Lval ( wait for O command to apply )
R - Report relay values Cval Lval
P - Report Fwd and Rev Power. Fwd, Rev, in 16 bit. Dynamic values, a new fwd, rev measurement is made.
W - Report sWr. Static value of the result of the last tune command.
T - Tune ( return T swr )
C D Inc or Dec Cval
L M Inc or Dec Lval
H - Toggle Cap to Low Z or High Z side of L
*
* Could construct a Teensy 4.1 version ( make sure all pullups to 5 volts removed )
* Use the Teensy audio shield. Ordered a Softrock with K2 IF, about 4.914 mhz. Can receive using the IQ DSP and transmit using
* the built in SSB filter at 11 mhz.
* This would be a totally different project. Could order a different v6 next year.
* Could put Softrock IQ on a jack and run HDSDR or a Teensy remote head.
* Would need to know the Audio IF in use to make this work as transceiver.
*/
/* Change log
*
* Implemented basic control of the receiving functions, mode, band switching.
* Added 160 meters to the band stack. Can listen there, would need an external filter to try transmitting.
* Added a USA license class indicator to show when in a ham band and what class license is needed to transmit.
* Added some frequency presets to the 60 meter menu - 630 meters, some ssb aircraft frequencies.
* Added rudimentary CAT control using Argonaut V protocol. Can fix any inaccuracies as needed.
* Added TR switching.
* Added a keyer with TR sequencing. Avoids sending while the relays are switching. No sidetone yet.
* Added a keyer mode submenu. Mode A, Mode B, Straight, Ultimatic
* Added some touch targets to the hidden menu. Touch area is much larger than the targets. Hidden menu no longer hidden.
* Added a multi function menu where values are changed with the encoder. ( keyer speed for example )
* Added a toogles menu where values are turned on and off ( swap keyer Dit and Dah for example )
* Tried the idea of different cw power levels using the Si5351 drive levels 2,4,6,8, ma. No change in output power so removed.
* Re-wired some pins on the Teensy adapter making Teensy pins 3,4 available for use with the PWM library object.
* Wiring changes for the key jack, R2 removed, R3 replaced with 10 ohm, PTT jumpered to the Key jack for DAH.
* Jumper pin PTT on MIC jack to same pin on the Key Jack. PTT is the etch run that goes directly to pin 2 on connector.
* Added audio library elements. Compile for USB type Serial,Midi,Audio.
* Removed the LM386 and converted to Digital audio. USB receive audio works.
* Enabled TX USB audio for DIGI mode in tx() function. It seems I wired the tx level pot backwards, CCW increases the tx drive.
* Added a fine tune clarify to help with frequency drift due to temperature changes. Changes both transmit and receive frequency.
* This is not a RIT control.
* Added PWM filter for headphone audio, 10n and 4.7mh.
* A/D may be quieter without connecting Analog ground to Digital ground.
* Added some TX/RX sequencing to the PTT function. Relays and bi-directional amps take somewhere between 32 and 42 ms to switch.
* Added a tune toggle for the to be installed auto tuner.
* Re-wrote all the tx/rx sequencing to use a common function. CW is now 64 ms behind sidetone.
* Note: Library object PWM only works when cpu speed is 48 or 96. For the T3.2 that is underclocked or overclocked.
* Added cw sidetone. And audio muting during TX.
* Added a more complicated audio library model for current and planned features.
* Implemented an AM detector. AM is tuned off frequency and the recified.
* It is followed with a highpass to restore the dc level and a lowpass to try to filter out the carrier.
* Ended up with tuning the BFO off frequency to pass the AM signal at an audio IF of 3.5 to 6.5 or thereabouts.
* Removed R2 on the Raduino. I think all signals are now 3.3 volts and safe for a Teensy 4.1 for future experiments.
* Wired a jumper for analog ground. Can see if signals are better with or without connecting the grounds.
* Added DIGI drive level per band. Set for output of 5 watts at 12.5 volts. Set tune power to 25% approximately 1 watt.
* Power out is lower on 15, 12 and 10 meters.
* Wired up the ATU. PIC16F1938 out of stock for 1 year. PIC18F2220 has I2C on different pins, other pins look ok.
* Wired a Pickit 2 header to the I/O interface provided, Power, Ground, Data, Clock did not line up in any useful way.
* Wired the Pickit interface such that Data and Clock line up which put Vpp, Vcc and Ground on the I/O lines of
* RB0-RB2. So this method lost the 3 I/O lines and required leaving the one pin where Vpp connected out of the socket.
* Added an S meter using an image converted with 565 online image converter by Rinky Dink Electronics.
* Added a way to override 5 watt DIGI power and allow full power if desired, so a non-qrp DIGI mode.
* 15 12 and 10 will still be < 5 watts.
* Added AGC. Changed volume to range to 0.99, no gain. Think following BiQuad distorts on loud signals if volume is 1.0 or above.
* Gain boosted with agc_gain now.
* Re-wired two connections to free up Teensy pins 0 and 1 for the ATU serial port. Used pins A6 and A7.
* Added a connector for the ATU with connections, NC +5 Gnd Rx Tx to go to the ATU Pickit connector vpp +5 gnd data clock.
* Added a 1000uf cap +5 to Gnd under the Teensy 3.1.
* ATU working. Saving tuning solutions in eeprom, 10 per band on 100khz steps.
* (Data sheet example eeprom write code fails to clear bit 6 in eecon1 register.)
* (Enabled brown out on PIC18F2220 as another step in trying to get the eeprom writes to work. Part errata workaround.)
* Displaying the atu relay data on the screen.
*
* To do.
* Review power levels again. I twiddled the MIC gain pot a bit to make sure it was in a linear range.
* Think I should set this for 5 watts on 17 meters instead of 15 meters as I did before.
* Review PWM audio. Is the 88k suppressed enough. Would it sound better using the DAC and an analog mux to allow dual use
* for both transmit and receive? A Teensy 3.5 would allow easier wiring with 2 DAC's.
* Noticed some spurs on SSB transmit, think a 11 meg IF suckout trap on TP13 or TP14 may be useful. Not much room there.
* Farhan says move L5 and L7 to side two - signal feedback to the 45 meg filter. This would be a simple fix.
* Scope FFT on TP13 and TP14 to see if have any 11 meg signal there. Could use Scope FFT to see what stage the spurs appear.
* They seem to be 11 meg mixed with the transmit frequency. Didn't measure how high they are.
* From Groups IO, maybe the mixing is in the final IRF510's.
* Didn't see any on 80 meters.
* 20 meters seem to be a multi mix with spurs at +- 2.4.
* 30 meters +- 0.9
* 40 meters +- 3.8
* 15 meters one at 10.0
* 10 meters one at 17.8
* Terminal Mode.
* The touch calibration is from a previous project, seems to be working ok but could be reviewed.
* CW decoder. Hell decoder.
* Audio scope, audio FFT displays.
* Amp for the internal speaker. Function lost when removed the LM386. Voltage gain not needed, just power I think.
* Band scope by scanning? ( mute, scan one freq, return vfo, unmute ) THIS idea didn't work well.
* Noise reduction, auto notch.
* CW filters. SSB high cut filter for QRM reduction.
* Transmit timout timer, in case miss the CAT command to return to RX. Maximum tx on time.
* Some pictures for documentation.
*/
// Wiring ( not easy with Nano upside down ) Teensy mounted with USB on the other side for the shortest wiring to the display.
// Teensy mounted upside right.
// Special wiring will be A2, and maybe A3 for audio input. A2 wired to VOL-M via the circuit shown in Audio Library for A/D input.
// The DAC pin wired to MIC via some caps and a pot for level. DAC-1k-cap-500 ohm pot-cap-1k-MIC. Caps 6.8 or 10 uf.
// Pins D3, D4 wired to SPK via the circuit shown in the Audio Library for PWM output. LM386 removed from the socket.
// 4.7 mh added in series with the SPK for better filtering. 60 inches of wire on FT37-43. ( a little over 100 turns )
// The Teensy audio library will control these 5 pins.
// ILI9341 wired for standard SPI as outlined on Teensy web site and it matches the Nano wiring pin for pin
// Uses pins 8 to 13 wired to Nano pins 8 to 13
// I2C uses the standard A4 A5, so they are wired to Nano A4 A5
// The keyer will make use of the PTT pin as either DIT or DAH - digital keyer inputs instead of one analog pin
// Teensy free pins, A3.
#define TR 7 // to nano pin 7
// nano pin 6 CW_TONE not wired
#define LP_A 6 // to nano pin 5
#define LP_B 5 // to nano pin 4
#define LP_C A9 // to nano pin 3
#define CW_KEY A8 // to nano pin 2
#define DIT_pin A1 // to nano pin A6 - was KEYER, short a resistor on main board, remove pullup to 5 volts.
#define DAH_pin A0 // to nano pin A3
#define PTT A0 // same pin as DAH_pin, ptt wired over to the key jack, remove a resistor on the main board
#define ENC_B A7 // to nano A0
#define ENC_A A6 // to nano A1
#define ENC_SW 2 // to nano A2
// Teensy pins 3 and 4 are audio library pwm output pins. A14 is the DAC.
// Teensy pins 0 and 1 are the RX and TX for Serial1. Connected to the built in ATU. If using a Teensy 4.1 would need a level converter.
/* button states */
#define IDLE_ 0
#define ARM 1
#define DTDELAY 2
#define DONE 3
#define TAP 4
#define DTAP 5
#define LONGPRESS 6
#define DBOUNCE 50
#include <ILI9341_t3.h>
#include <XPT2046_Touchscreen.h>
//#include <Wire.h>
#include <i2c_t3.h> // non-blocking wire library
// There are control files in the audio library where include Wire.h will need to be replaced with include i2c_t3.h. ( 7 total ? )
// Or maybe Wire.h will work ok with this program instead of i2c_t3 if desired.
#include <SPI.h>
#include "led_fonts.h"
#include "smeter5.h"
#define BFO 11059590L // this puts the bfo on the high side of the filter, sharper cutoff?
#define IF 45000000L // what is the actual center of the IF. Testing says 45k even is about right.
// 4bpp pallett 0-3, 4-7, 8-11, 12-15
const uint16_t EGA[] = {
ILI9341_BLACK, ILI9341_NAVY, ILI9341_DARKGREEN, ILI9341_DARKCYAN,
ILI9341_MAROON, ILI9341_PURPLE, ILI9341_OLIVE, ILI9341_LIGHTGREY,
ILI9341_DARKGREY, ILI9341_BLUE, ILI9341_GREEN, ILI9341_CYAN,
ILI9341_RED, ILI9341_MAGENTA, ILI9341_YELLOW, ILI9341_WHITE
};
#define TFT_DC 9
#define TFT_CS 10
ILI9341_t3 tft = ILI9341_t3(TFT_CS, TFT_DC);
XPT2046_Touchscreen ts(8);
#define ATU Serial1
/***************************** Audio Library **********************************/
#include <Audio.h>
// Audio processing model with AGC, AM detector, CW detector and other dummy objects for NR Notch and Decoders
// An issue with this model is the AGC loop is outside of the bandwidth object.
// If outside the bandwidth object, strong signals outside of the desired bandwidth will cause the desired signal to decrease in volume.
// If AGC loop includes the bandwidth object, strong signals outside of the desired bandwidth could overload the earlier stages.
// Solution is two separate agc loops which is perhaps a bit complicated for this radio.
// 1st agc loop prevents overload, 2nd sets the desired listening volume.
// The chosen implementation will only have an issue with narrow bandwidth modes like CW on a very busy band.
// GUItool: begin automatically generated code
AudioInputAnalog VOL_M; //xy=223.30557250976562,286.52777099609375
AudioAnalyzePeak peak1; //xy=347.2500190734863,161.13887405395508
AudioOutputUSB usb2; //xy=348.13885498046875,207.36110305786133
AudioAmplifier AGC; //xy=397.77777099609375,286.3888854980469
AudioMixer4 NR_Notch; //xy=450,357.2221984863281
AudioInputUSB usb1; //xy=477.52777099609375,192.5555419921875
AudioAnalyzeRMS rms1; //xy=479.16668701171875,234.72219848632812
AudioSynthWaveformSine SideTone; //xy=481.11114501953125,147.5
AudioEffectRectifier AM_Det; //xy=538.75,411.25
AudioMixer4 RX_SEL; //xy=629.9999389648438,307.6666564941406
AudioMixer4 TX_SEL; //xy=691.27783203125,181.61109924316406
AudioAmplifier amp2; //xy=778.75,410
AudioFilterBiquad BandWidth; //xy=801.666748046875,307.08331298828125
AudioMixer4 Decoders; //xy=862.5,240.138916015625
AudioAnalyzeToneDetect CW_Det; //xy=917.5,410
AudioOutputAnalog MIC; //xy=994.166748046875,181.361083984375
AudioOutputPWM SPK; //xy=994.444580078125,306.3055419921875
AudioConnection patchCord1(VOL_M, 0, usb2, 0);
AudioConnection patchCord2(VOL_M, 0, usb2, 1);
AudioConnection patchCord3(VOL_M, peak1);
AudioConnection patchCord4(VOL_M, AGC);
AudioConnection patchCord5(AGC, 0, NR_Notch, 0);
AudioConnection patchCord6(AGC, 0, RX_SEL, 0);
AudioConnection patchCord7(AGC, AM_Det);
AudioConnection patchCord8(AGC, rms1);
AudioConnection patchCord9(NR_Notch, 0, RX_SEL, 2);
AudioConnection patchCord10(usb1, 0, TX_SEL, 1);
AudioConnection patchCord11(SideTone, 0, RX_SEL, 1);
AudioConnection patchCord12(SideTone, 0, TX_SEL, 0);
AudioConnection patchCord13(AM_Det, 0, RX_SEL, 3);
AudioConnection patchCord14(RX_SEL, BandWidth);
AudioConnection patchCord15(TX_SEL, MIC);
AudioConnection patchCord16(amp2, CW_Det);
AudioConnection patchCord17(BandWidth, amp2);
AudioConnection patchCord18(BandWidth, 0, Decoders, 0);
AudioConnection patchCord19(BandWidth, SPK);
// GUItool: end automatically generated code
/*
// Added peak object for signal level
// GUItool: begin automatically generated code
AudioSynthWaveformSine SideTone; //xy=175,170
AudioInputUSB usb1; //xy=176,257
AudioInputAnalog VOL_M; //xy=179,110
AudioMixer4 RX_SEL; //xy=340,176
AudioMixer4 TX_SEL; //xy=341,258
AudioAnalyzePeak peak1; //xy=351,51
AudioOutputPWM SPK; //xy=490,177
AudioOutputAnalog MIC; //xy=490,259
AudioOutputUSB usb2; //xy=493,105
AudioConnection patchCord1(SideTone, 0, RX_SEL, 1);
AudioConnection patchCord2(SideTone, 0, TX_SEL, 0);
AudioConnection patchCord3(usb1, 0, TX_SEL, 1);
AudioConnection patchCord4(VOL_M, 0, usb2, 0);
AudioConnection patchCord5(VOL_M, 0, usb2, 1);
AudioConnection patchCord6(VOL_M, 0, RX_SEL, 0);
AudioConnection patchCord7(VOL_M, peak1);
AudioConnection patchCord8(RX_SEL, SPK);
AudioConnection patchCord9(TX_SEL, MIC);
// GUItool: end automatically generated code
*/
/*
#include <Audio.h>
//#include <Wire.h>
//#include <SPI.h>
//#include <SD.h>
//#include <SerialFlash.h>
// Digital Audio with no processing. A/D VOL_M to usb, A/D VOL_M to PWM SPK, usb to DAC MIC. Sidetone to SPK and/or MIC.
// GUItool: begin automatically generated code
AudioSynthWaveformSine SideTone; //xy=175,170
AudioInputUSB usb1; //xy=176,257
AudioInputAnalog VOL_M; //xy=179,110
AudioMixer4 RX_SEL; //xy=340,176
AudioMixer4 TX_SEL; //xy=341,258
AudioOutputPWM SPK; //xy=490,177
AudioOutputAnalog MIC; //xy=490,259
AudioOutputUSB usb2; //xy=493,105
AudioConnection patchCord1(SideTone, 0, RX_SEL, 1);
AudioConnection patchCord2(SideTone, 0, TX_SEL, 0);
AudioConnection patchCord3(usb1, 0, TX_SEL, 1);
AudioConnection patchCord4(VOL_M, 0, usb2, 0);
AudioConnection patchCord5(VOL_M, 0, usb2, 1);
AudioConnection patchCord6(VOL_M, 0, RX_SEL, 0);
AudioConnection patchCord7(RX_SEL, SPK);
AudioConnection patchCord8(TX_SEL, MIC);
// GUItool: end automatically generated code
*/
/*******************************************************************************/
// globals
// Implementing a simplistic split tuning model.
// VFO A is the receive vfo, VFO B is the transmit vfo, split is used for RIT
// Can listen on VFO B, if split is also selected.
// VFO B follows VFO A unless split is enabled.
// B --> A exit split with VFO B selected. A --> B exit spit with VFO A selected.
// encoder switch tap = enable split A selected (RIT), tap then toogles B or A vfo, long press B to A or A to B as above.
// radio defines for vfo_mode.
#define VFO_A 1
#define VFO_B 2
#define VFO_SPLIT 4
#define VFO_DIGI 8
#define VFO_CW 16
#define VFO_LSB 32
#define VFO_USB 64
#define VFO_AM 128
// radio variables, set reasonable for 1st band change
// can start a different band in setup, but will save this data to the 40 meter band_stack
// ECARS usually has some activity to listen to
int32_t vfo_a = 7255000, vfo_b = 7255000;
uint8_t vfo_mode = VFO_A + VFO_LSB;
int band = 3; // 40 meters
int transmitting; // status of TR
int cw_tr_delay = 65; // semi breakin time, in multi fun, actual value is *10 ms.
int fast_tune = 1; // double tap encoder to toggle
uint8_t cat_tx; // a flag that CAT is in control of the transmitter
#define STRAIGHT 0 // CW keyer modes
#define ULTIMATIC 1
#define MODE_A 2
#define MODE_B 3
#define PRACTICE_T 4 // last options toggle practice, and swap
#define KEY_SWAP 5
uint8_t key_mode = ULTIMATIC;
int cw_practice = 1; // toggle and multi fun variables must be int, have value 0 to 99.
int key_swap = 0;
int wpm = 14;
int volume_ = 98; // actual * 0.01
int clari = 50; // adjustment for temperature changes, maps to +-50 hz
int tuning; // low power tune via sidetone object
int side_vol = 10; // sidetone volume
uint8_t oob; // out of band flag, don't save vfo's to the bandstack. Don't transmit.
int digi5w = 1; // use the digi power levels in the bandstack for digi power
int dis_info; // display some information in Smeter area
int lock; // lock vfo, allow CAT freq changes
int agc_gain = 25; // agc gain 0 to 9.9 with 10 == 1.0
float sig_rms; // global so can print
// float agc_sig = 0.20; // glabal so can print, init at floor
float agc_print;
uint8_t t_swr; // swr value returned from ATU
// Touch menu's
void (* menu_dispatch )( int32_t ); // pointer to function that is processing screen touches
struct menu {
char title[20];
const char *menu_item[10]; // array of pointers to strings, (even number needed for 2 wide menu)
int32_t param[10];
int y_size; // x size will be half the screen, two items on a line for now
int color;
int current;
};
// mode menu items
const char m_vfoa[] = " VFO A";
const char m_vfob[] = " VFO B";
const char m_split[] = " Split";
const char m_cw[] = " CW";
const char m_usb[] = " USB";
const char m_lsb[] = " LSB";
const char m_am[] = " AM";
const char m_digi[] = " DIGI";
struct menu mode_menu_data = {
{ " VFO Mode (keyer)" },
{ m_vfoa,m_vfob,m_split,m_cw,m_usb,m_lsb,m_am,m_digi },
{0,1,2,3,4,5,6,7,-1,-1},
48,
EGA[1],
5
};
const char m_keys[] = " Straight";
const char m_keyu[] = " Ultimatic";
const char m_keya[] = " Mode A";
const char m_keyb[] = " Mode B";
const char m_keyp[] = " Practice";
const char m_keyw[] = " Swap";
struct menu keyer_menu_data = {
{ " Keyer Mode" },
{ m_keys,m_keyu,m_keya,m_keyb,m_keyp,m_keyw },
{ 0,1,2,3,4,5,-1,-1,-1,-1 },
40,
EGA[2],
1
};
// band menu
const char b_160[] = " 160m rx";
const char b_80[] = " 80m";
const char b_40[] = " 40m";
const char b_30[] = " 30m";
const char b_20[] = " 20m";
const char b_17[] = " 17m";
const char b_15[] = " 15m";
const char b_12[] = " 12m";
const char b_10[] = " 10m";
struct menu band_menu_data = {
{ " BAND (60m)" }, // 60 meters in a submenu
{ b_160, b_80, b_40, b_30, b_20, b_17, b_15, b_12, b_10 },
{ 0,1,3,4,5,6,7,8,9,-1 },
40,
EGA[1],
2
};
const char c_1[] = " Ch 1";
const char c_2[] = " Ch 2";
const char c_3[] = " Ch 3";
const char c_4[] = " Ch 4";
const char c_5[] = " Ch 5";
const char c_6[] = " 630 m";
const char c_7[] = " Wx Fax";
const char c_8[] = " Av NY A";
const char c_9[] = " Av NY E";
const char c_10[]= " Av Car";
struct menu band_60_menu_data = { // 60 meter channels with some other presets
{ " Channel" },
{ c_1, c_2, c_3, c_4, c_5, c_6, c_7, c_8, c_9, c_10 },
{ 5330500, 5346500, 5357000, 5371500, 5403500, 474200, 6338600, 5598000, 6628000, 5550000 },
40,
EGA[2],
2
};
struct BAND_STACK {
uint32_t vfoa;
uint32_t vfob;
uint8_t mode;
uint8_t relay;
float digi_pwr; // TX_SEL gain for 5 watts out at 12.5 volts VCC, 15,12,10 lower power.
};
// don't set up any splits here
struct BAND_STACK band_stack[10] = {
{ 1875000, 1875000, VFO_A+VFO_LSB, 4, 0.00 }, // can listen on 160 meters, tx has harmonics and spur
{ 3928000, 3928000, VFO_A+VFO_LSB, 4, 0.60 }, // relay C for 80 and 60 meters
{ 6000000, 6000000, VFO_A+VFO_USB, 4, 0.70 },
{ 7168000, 7168000, VFO_A+VFO_LSB, 2, 0.85 }, // relay B
{ 10106000, 10106000, VFO_A+VFO_USB, 2, 0.80 },
{ 14200000, 14200000, VFO_A+VFO_USB, 1, 0.75 }, // relay A
{ 18100000, 18100000, VFO_A+VFO_DIGI, 1, 0.80 },
{ 21100000, 21100000, VFO_A+VFO_CW, 0, 0.99 }, // default lowpass
{ 24900000, 24900000, VFO_A+VFO_USB, 0, 0.99 },
{ 28250000, 28250000, VFO_A+VFO_USB, 0, 0.99 }
};
struct multi { // all multi variables type int, functions that use them to map to floats if needed
char title[20]; // value 0 to 99 - functions to map as needed
int num; // now many of the 10 slots are actually in use
const char *menu_item[10]; // variable names
int *val[10]; // variable pointers
int current; // adjusting this one now
};
const char mf_vl[] = " Volume";
const char mf_bf[] = " Clarify"; // +- 50 hz for drift due to temperature
const char mf_ks[] = " Key Spd";
const char mf_tr[] = " cwDelay"; // 0-99 maps to 0 to 990 ms, min value is 100
const char mf_sv[] = " SideVol"; // sidetone volume
const char mf_ag[] = " AGC Vol"; // agc gain
// PWM audio volume
struct multi multi_fun_data = {
" Multi Adj (exit)",
6,
{ mf_vl, mf_ag, mf_bf, mf_ks, mf_tr, mf_sv },
{ &volume_, &agc_gain, &clari, &wpm, &cw_tr_delay, &side_vol },
0
};
const char tt_ks[] = "Key Swap";
const char tt_kp[] = "Practice";
const char tt_ft[] = "Fast Vfo";
const char tt_tt[] = "ATU TUNE";
const char tt_d5[] = "Digi 5W";
const char tt_di[] = "Info Dis";
const char tt_lk[] = "Lock VFO";
struct multi toggles_data = {
" Toggle Values",
7,
{ tt_ks, tt_kp, tt_ft, tt_tt, tt_d5, tt_di, tt_lk },
{ &key_swap, &cw_practice, &fast_tune, &tuning, &digi5w, &dis_info, &lock },
2
};
// screen owners
#define DECODE 0
#define MULTI 1
#define MENUS 2
uint8_t screen_owner = DECODE;
extern void initOscillators();
extern void si5351bx_setfreq(uint8_t clknum, uint32_t fout);
//extern void si5351_set_calibration( int32_t cal );
/*****************************************************************************************/
void setup() {
pinMode( TR, OUTPUT ); // set a default radio state, receive mode
pinMode( LP_A, OUTPUT);
pinMode( LP_B, OUTPUT);
pinMode( LP_C, OUTPUT);
pinMode( CW_KEY, OUTPUT);
digitalWriteFast( TR, LOW );
digitalWriteFast( LP_A, LOW ); // LP relays should be set up for the starting band. Done below.
digitalWriteFast( LP_B, LOW ); // they don't switch on until TR goes high
digitalWriteFast( LP_C, LOW );
digitalWriteFast( CW_KEY, LOW );
Serial.begin(1200); // 2 meg usb serial, baud rate makes no difference. Argonaut V baud rate.
ATU.begin(1200); // ATU
pinMode( ENC_A, INPUT_PULLUP );
pinMode( ENC_B, INPUT_PULLUP );
pinMode( ENC_SW, INPUT_PULLUP );
pinMode( DIT_pin, INPUT_PULLUP ); // R2 removed, R3 replaced with 10 ohm ( zero to 100 works )
//pinMode( DAH_Pin, INPUT_PULLUP ); // connected to PTT
pinMode( PTT, INPUT_PULLUP ); // DAH and PTT connected together with wire on side 2
tft.begin();
tft.fillScreen(ILI9341_BLACK);
tft.setRotation(1);
ts.begin(); // touchscreen
ts.setRotation(3); // ? should have been 1, same as print rotation. This screen is different.
tft.setTextSize(2); // sign on message
tft.setTextColor(ILI9341_WHITE); // or foreground/background for non-transparent text
tft.setCursor(10,200);
tft.print("K1URC uBitX w/ Teensy 3.2"); // about 25 characters per line with text size 2
tft.setTextColor(ILI9341_CYAN);
tft.setTextSize(1);
tft.setCursor(14,230); tft.print("Mic");
tft.setCursor(150,230); tft.print("Spkr");
tft.setCursor(319-24,230); tft.print("Key");
Wire.begin(); // I2C_OP_MODE_DMA, I2C_OP_MODE_ISR possible options.
Wire.setClock(400000);
initOscillators(); // sets bfo
si5351bx_setfreq( 0, BFO ); // set new bfo if changed from the default
// si5351bx_setfreq( 1, 33948600 ); // set LSB mode
// si5351bx_setfreq( 1, 56061400 ); // set LSB mode
si5351bx_setfreq( 1, IF + BFO ); // set LSB mode
vfo_freq_disp();
vfo_mode_disp();
set_relay( band_stack[band].relay );
// band_change(3); // can start with a different band if desired
mf_bar_disp();
menu_dispatch = &hidden_menu; // function pointer for screen touch
// audio library setup
AudioNoInterrupts();
AudioMemory(40);
SideTone.frequency(600);
SideTone.amplitude( 0.0 );
RX_SEL.gain(0,1.0); // listen to Receive audio
RX_SEL.gain(1,0.0); // sidetone muted
RX_SEL.gain(2,0.0); // NR Notch
RX_SEL.gain(3,0.0); // AM mode
TX_SEL.gain(0,0.0); // low power tuning mode using sidetone
TX_SEL.gain(1,0.0); // USB audio tx for DIGI mode
TX_SEL.gain(2,0.0); // unused
TX_SEL.gain(3,0.0); // unused
MIC.analogReference(INTERNAL); // 1.2 volt p-p
AGC.gain( (float)(agc_gain)/10.0 ); // default signal pass through until agc loop takes over
BandWidth.setHighpass( 0, 200, 0.707 );
BandWidth.setLowpass( 1, 3200, 0.707 );
AudioInterrupts();
ATU.write('Z');
// atu_band(); done from vfo_freq_disp
}
void loop() {
static uint32_t tm;
uint32_t t;
int t2;
t2 = encoder();
if( t2 ){
if( screen_owner == DECODE && lock == 0 ) freq_update( t2 );
if( screen_owner == MULTI ) multi_fun_encoder( t2 );
}
t = touch();
if( t ) (*menu_dispatch)(t); // off to whoever owns the touchscreen
if( rms1.available() ){ // agc
sig_rms = rms1.read();
agc_process( sig_rms);
}
// One ms processing
t = millis() - tm;
tm += t;
if( t > 5 ) t = 5; // first time or out of processing power
while( t-- ){ // repeat for any missed time ticks
t2 = button_state(0);
if( t2 > DONE ) button_process(t2);
if( vfo_mode & VFO_CW ) keyer();
if( tuning ) tune(); // Antenna tuning
if( transmitting ) tx_rx_seq(); // 48 ms delays for TX RX switching
if( tuning == 0 && transmitting > 48 ) read_atu_pwr(); // send a power command to the ATU, process with atu_response
// test_1st_IF(); // find the center of the 45mhz filter. Seems 45k about right.
}
radio_control(); // CAT
check_ptt();
if( screen_owner == DECODE ) info_corner();
if( tuning == 0 && ATU.available() ) atu_response();
}
void read_atu_pwr(){ // called once per ms, send commands to the ATU when transmitting
static int tm;
if( ++tm < 120 ) return; // 1200 baud at 60 bits ==> lowest number here would be 50 and that would max out the 1200 baud link.
ATU.write('P'); // send the get power command, atu_response will process the return information
tm = 0;
}
void atu_response(){ // do something with the data returned by the ATU
static int st;
static uint8_t cmd[8];
cmd[st++] = ATU.read();
if( st == 8 ){ // unknown command, out of sync
st = 0;
return;
}
switch( cmd[0] ){
case 'R': // tuning solution
if( st == 3 ){
disp_tune(cmd);
st = 0;
}
break;
case 'P': // fwd and rev power
if( st == 5 ){
tx_fwd_rev(cmd);
st = 0;
}
break;
default: st = 0; break; // invalid command response from the atu
}
}
// display fwd and rev power on the s-meter. Scale rev power as if fwd was full scale to get swr reading.
void tx_fwd_rev( uint8_t *p ){
int fwd, rev;
float ffwd, frev;
fwd = p[1] * 256 + p[2];
rev = p[3] * 256 + p[4];
fwd = map(fwd,0,800,0,1000); // !!! need correct conversion factor for the swr bridge
ffwd = (float)fwd/1000.0;
rev = map(rev,0,800,0,1000);
frev = (float)rev/1000.0;
frev = frev/ffwd;
sig_pwr_meter(ffwd, frev);
}
void disp_tune( uint8_t * p ){
static unsigned int count;
if( ++count == 1 ) return; // inhibit 1st time on power on, power on message stays on the screen
tft.setTextSize(2);
tft.setCursor(10,210);
if( p[2] == 0xff ) tft.setTextColor(EGA[12],0);
else tft.setTextColor(EGA[10],0);
tft.write( p[0] ); tft.write(' ');
print_bin( p[1] ); tft.write(' '); tft.write(' '); // print binary with leading zero's
print_bin( p[2] );
}
// tft print binary with leading zero's
void print_bin( uint8_t val ){
int i;
for( i = 128; i > 0; i >>= 1 ){
if( val & i ) tft.write('1');
else tft.write('0');
}
}
#define AGC_FLOOR 0.20 // 0.15 0.05
#define AGC_SLOPE 9 // 6
#define AGC_HANG 500 // hang == ms time
void agc_process( float reading ){
static float agc_sig = AGC_FLOOR;
static int hang;
float g, g2;
int ch; // flag change needed
static int again;
ch = 0;
if( again != agc_gain ) again = agc_gain, ch = 1; // implement gain changes when signal is below floor
if( reading > agc_sig && reading > AGC_FLOOR ){ // attack 0.001
agc_sig += 0.009, hang = 0, ch = 1;
}
else if( agc_sig > AGC_FLOOR && hang++ > AGC_HANG/3 ){ // decay
agc_sig -= 0.0001, ch = 1;
}
if( ch ){ // change needed
g2 = (float)agc_gain / 10.0;
g = agc_sig - AGC_FLOOR;
g *= AGC_SLOPE;
g = g2 - g; // agc action reduces gain from the set value agc_gain
if( g <= 0 ) g = 0.1;
set_agc_gain(g);
}
}
void set_agc_gain(float g ){
//g = g * (float)agc_gain / 10.0;
AudioNoInterrupts();
AGC.gain(g);
AudioInterrupts();
agc_print = g;
}
#define TUNE_OFF 15000 // 10000 is 10 seconds
void tune(){ // enable a low power tune signal via DAC and SideTone object. Sequence and timeout.
// called once per millisecond
static uint8_t c1;
uint8_t c;
++tuning;
if( tuning < 0 ) tuning = TUNE_OFF;
switch( tuning ){
case 2:
tx();
c1 = 0;
t_swr = 0; // 0.0 for when no tuner installed
break;
case 50: // enable the sidetone after relays have switched
// si5351bx_setfreq( 2, vfo_b + IF + ( clari - 50 )); handled in tx sequencer
SideTone.frequency(1500);
SideTone.amplitude(0.99);
TX_SEL.gain(0,0.25); // mic level pot was set on 17 meters, 15m - 10m will be lower than other bands
ATU.write('T');
break;
//case 100: // debug get some info from the ATU.
// ATU.write('P'); // get fwd rev for passthrough
// ATU.write('O'); // enable current solution
// ATU.write('P'); // get fwd rev for current solution
// ATU.write('T'); // start tune later
//break;
case TUNE_OFF: // timeout at nn seconds
tuning = 0;
rx();
break;
default: // check if have response from ATU and end early
if( ATU.available() ){
c = ATU.read();
if( c1 == 0 && c == 'T' ) c1 = c;
else if( c1 == 'T' ) t_swr = c, tuning = -5; // atu done, returned 'T swr' response
// Serial.print(c,HEX); // just debug prints
// Serial.write(' ');
}
break;
}
if( tuning < 0 ){
//Serial.println();
ATU.write('R'); // get solution
}
}
void info_corner(){
static uint32_t tm; // slow down the display
float val;
float val2;
static float val3; // save peak for info as now calling the smeter more frequently
const int ls = 11; // vertical spacing of lines from line 80
static int mod;
if( millis() - tm < 25 ) return;
if( peak1.available() == 0 ) return;
tm = millis();
val2 = peak1.read();
if( val2 > val3 ) val3 = val2;
if( ++mod >= 40 && dis_info ){
mod = 0;
tft.setTextSize( 1 );
tft.setTextColor(EGA[10], EGA[0] );
tft.setCursor( 254, 80+ 1*ls );
tft.print("cpu ");
tft.setCursor( 280, 80+ 1*ls );
val = (float)AudioProcessorUsage() / 100.0 ;
tft.print( val );
tft.setCursor( 254, 80 + 2*ls ); // agc amp gain. needs to be < 1.0 on strong signals
tft.print("agc "); // adjust agc_gain in multi fun or the volume pot for signal into DSP
tft.setCursor( 280, 80 + 2*ls );
tft.print( agc_print );
tft.setCursor( 254, 80 + 3*ls ); // ATU errors reported as swr values
tft.print("swr "); // 25.0 - REV > FWD
tft.setCursor( 280, 80 + 3*ls ); // 25.1 - (divide by zero) REV = FWD
val = (float)t_swr/10.0; // 25.2 - swr > 25.5 ( divide result more than 8 bits )
if( val < 10.0 ) tft.print(val,2); // 25.3 - timeout. No signal detected.
else tft.print(val,1);
tft.setCursor( 254, 80 ); // displaying top line last for color changes
tft.print("Sig ");
tft.setCursor( 280, 80 );
if( val3 > 0.95 ) tft.setTextColor( EGA[12], EGA[0] );
else if( val3 > 0.85 ) tft.setTextColor( EGA[14], EGA[0] );
tft.print( val3 );
val3 = 0.0;
}
val2 = map( val2,0.0,1.0,1.0,10.0 ); // log scale on meter, convert 0.0 - 1.0 to a value where the log
val2 = log10(val2); // has a range of 0.0 - 1.0 ( log of 1 is zero, log of 10 is 1.0 )
if( transmitting == 0 ) sig_pwr_meter( val2,0.0 ); // is this correct? or should a straight value be plotted and the
} // meter markings provide the log function. Doing log here does expand lower
// signals to cover more of the meter.
void band_change( int to_band ){
int val;
if( oob == 0 ){ // only save the vfo's if we are inside an amateur band.
band_stack[band].vfoa = vfo_a;
band_stack[band].vfob = vfo_b;
band_stack[band].mode = vfo_mode;
}
band = to_band;
vfo_a = band_stack[band].vfoa;
vfo_b = band_stack[band].vfob;
vfo_mode = band_stack[band].mode;
set_relay( band_stack[band].relay );
vfo_freq_disp();
vfo_mode_disp();
// update the mode data struct as we just changed vfo_mode with the band change
if( vfo_mode & VFO_CW ) val = 3;
if( vfo_mode & VFO_USB ) val = 4;
if( vfo_mode & VFO_LSB ) val = 5;
if( vfo_mode & VFO_AM ) val = 6;
if( vfo_mode & VFO_DIGI ) val = 7;
mode_menu_data.current = val;
atu_band();
}
void set_relay( int num ){
digitalWriteFast( LP_A, LOW ); // clear relays
digitalWriteFast( LP_B, LOW );
digitalWriteFast( LP_C, LOW );
if( num & 1 ) digitalWriteFast( LP_A, HIGH ); // relays will actually switch during TX
if( num & 2 ) digitalWriteFast( LP_B, HIGH );
if( num & 4 ) digitalWriteFast( LP_C, HIGH );
}
// touch the screen top,middle,bottom to bring up different menus. Assign menu_dispatch.
// This is default touch processing.
void hidden_menu( int32_t t ){
int32_t yt, xt;
screen_owner = MENUS;
yt = t & 0xff;
xt = t >> 8;
// Serial.print("X "); Serial.print(xt); Serial.print(" Y "); Serial.println(yt);
// touch rotation is different than the print rotation on this ILI9341
// check the y value of touch to see what menu area
if( yt < 50 ){
menu_display( &mode_menu_data,3 ); // pass the mode hack value 3
menu_dispatch = &mode_menu; // screen touch goes to mode_menu() now
}
else if( yt < 100 ){
menu_display( &band_menu_data,0 );
menu_dispatch = &band_menu;
}
else if( yt < 160 && xt < 50 ){ // left side of screen, multi menu
multi_display( &multi_fun_data, 1 );
menu_dispatch = &multi_fun_touch;
screen_owner = MULTI;
}
else if( yt < 160 && xt > 320-50 ){
multi_display( &toggles_data, 1 );
menu_dispatch = &toggles_touch;
}
/*
else if( yt > 190 && xt > 270 && (vfo_mode & VFO_CW) ){ // keyboard CW sending
menu_dispatch = &key_tx;
key_tx(0);
}
else if( yt > 140 ){
menu_display( &decode_menu_data );
menu_dispatch = &decode_menu;
}
*/
else menu_cleanup(); // not active part of the screen, return to normal op.
}
void band_60_menu( int32_t t ){ // setup for 60 meters
int selection; // pick the 80 meter filter
selection = touch_decode( t, band_60_menu_data.y_size );
if( selection != -1 && band_60_menu_data.param[selection] != -1 ){
// 630 meters special case in this menu
if( band_60_menu_data.param[selection] < 3000000 ) band_change(0); // 160 meters relays setting