-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.c
1319 lines (1229 loc) · 47.9 KB
/
main.c
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
/****************************************************************************************
****************** COPYRIGHT (c) 2019 by Joseph Haas (DBA FF Systems) *****************
*
* File name: main.c
*
* Module: Control
*
* Summary: This is the main code file for the HM133 DTMF application
* License and other legal stuff:
* This software, comprised of all files contained in the original distribution archive,
* are protected by US Copyright laws. The files may be used and modified by the person
* receiving them under the following terms and conditions:
* 1) The software, or any protion thereof may not be distributed to any 3rd party by
* the recipient or any agent or assign of the recipient.
* 2) The recipient assumes all risks for the use (or mis-use) of this software.
*
*
* Project scope revision history:
* *** REV 03 ***
* 03-10-20 jmh: Modified pulse() to accept U16 pulse timer (modified pulse timer also). Rig array
* still stores U8, so it must be cast when the rig type is read on power-up.
* Modified ICOM mode to use MR(TUNER) as scan up, and F-1(V/M) as scan down. These
* keys instantiate an 800ms pulse on up or dn to start the scan mode.
* Modified ICOM pulse period to 45 ms (was 40).
* Added named rig types for Kenwood and generic 100 and 125.
* Fixed several code snippets for "VERS == 1" build case.
* *** REV 02 ***
* 08-28-19 jmh: Debug of HM151 mode. Tested on IC-7000. Found that the first key is ignored if no
* other keys are detected after. This means that the F-2 key needn't be set to a
* null command (can't do this anyway). Also verified that HM-151 keys are active
* during TX.
* Modified code to trap leading and falling edge of 'G' key. If entering pass-thru
* mode, delay DMUTE switch until release of 'G', else switch DMUTE immediately.
* Revamped init code to make sure DMUTE is not set to pass-thru until just before the
* start of the MPL.
* Added timer clear statements to init code to cover warm start case.
* 08-19-19 jmh: Added pulse delay variable to radio_fbtn[][] array. Kenwood needs at least 80ms,
* ICOM can take 40ms. Other delays up to 125 are also included.
* HW MOD: changed comparator voltage ref to a voltage divider driven from M8V to
* follow what the radios do.
* HW MOD: changed how 5V is wired to reset circuit. Debug adapter now works at
* least as well as with the 3.3V bypass Vreg.
* 08-18-19 jmh: VERS 2 testing continues...
* Tested HM151 mode & corrected LED functions. *** Need to test with IC-7000.
* Abandoned SPR as a strap, made Ro3 3rd strap.
* *** need to test option straps.
* 08-17-19 jmh: VERS 2 initial bring-up complete. Issues:
* 1) The dual FET footprint was wrong. Had to re-map 4094 bitmap to correct.
* LED and DMUTE had to be re-wired, so no SW change for those signals.
* PCB rev 003 update complete. Added Ro3 "gimmick" resistor to make P0.6
* the 3rd strap option. *** Need to see if this works. ***
* 2) Voltage reg either broke or some configuration issue.
* Flaky debug and operation modes - will look again at this on 2nd build.
* 1st article has a 3V regulator piggy-backed on the 5V Vreg with a jumper to
* U2 Vdd net (Vregi pin is lifted). U5 Vhi is about 2.9V...not sure this
* can be counted on to work all the time.
* Made some code tweaks to get DTMFs to work correctly. Not sure why they broke going
* to rev 002 HW. Also, cleaned up some commented-out code (still some left).
* Added "mic present" mirror. If strap selects HM151, DN0 = MIC_DET_N any time MIC_DET_N
* changes state. In addition to mirror, this code kills PTT and tone related registers
* and clears DMUTE if the mic is removed.
* Added restart capability to re-initialize system when mic is removed. Code will
* continually restart until mic is re-connected.
* Resticted all keys but "M" in HM151 mode. DTMFs will generate if PTT pressed & DMUTE
* is active.
* 08-15-19 jmh: HM133 DTMF now works (mostly). PTT can NOT be pressed if HM133 DTMF-S is active.
* May not need HM133/HM151 strap-opt.
* Strap support for revA *mostly* working.
* 1st key logic now traps "1st" 1st key and ignores following until button released.
* 08-12-19 jmh: Added support for straps. radio_fbtn[][] array holds transistor pulse patterns that
* correspond to key/rig combinations.
* Added support for extended buttons (MC-44/MH-36) in the button operations switch{} and
* in the support Fns.
* Changed pulse_up/pulse_dn to a single funtion which now supports all of the transistor
* outputs of the CD4094 SR port expander.
* VERS 1 now support using DMUTE for a single expansion button (since it won't do the
* HM151 FT option correctly).
* Added support for HM151 FT mode. Fn LED = off if in FT mode, else, on for DTMF enabled mode.
* 08-09-19 jmh: Added #if directives to support PCB version 2. Re-mapped PTT, UP, and DN bit controls
* to use a central Fn to set/clear bits. Paves the way for port extender to be used
* in version 2 PCB. bbSPI code is in place for port expander. Waveforms verified on
* o'scope, but not tested. Port update uses TIMER0 ISR to clock 8 bits and strobe
* CD4094 port expander I.C.. XFR plus STB takes about 45us.
* 08-07-19 jmh: Added feature to interrupt U/D macros on any keypress
* Added feature to change Fn LED brightness. If no PTT, "*" = dim, "#" = brt.
*
* 08-04-19 jmh: basic functionality working with HM-151. HM-133 DTMFs need work.
* added a DTMF activity timer for HM-151 so that the MIC is unmuted
* after an interval of no DTMF key being pressed.
* The "Fn LED" now depicts mic mute status during tone cycles. Off =
* muted (or macro digit pressed), ON = unmuted. Blink = up/dn pulse.
*
* *** REV 01 ***
* 08-03-19 jmh: project baselined at rev 01
* 07-20-19 jmh: Project origin, copied from Orion PLL
*
***************************************************************************************/
/****************************************************************************************
* File scope revision history:
* 04-09-16 jmh: creation date
*
***************************************************************************************/
//--------------------------------------------------------------------------------------
// main.c
//
// The following resources of the F531 are used:
// 24.500 MHz internal osc
//
// UART: n/u
//
// Timer0: n/u
// Timer1: DDS sample driver
// Timer2: Application timer (1ms/tic)
//
// ADC: n/u
//
// PCA:
// CEX0 = ICOM mic data decode ISR
// CEX1 = DDS PWM ISR
// CEX2 = Aux LED PWM out (Fixed PWM ratio, no ISR)
//
// SYSTEM NOTES:
//
// This project adapts an HM-133 (or HM-151) microphone so that it can produce DTMF signals.
// The PCB features the F531 MCU, a 3.3V regulator, open-drain drivers for up/dn and PTT, and
// switching circuits for the microphone/DTMF signals. The small form-factor board is intended
// to be an in-line module between the microphone and the transciever. Additional features
// such as up/down toggle commands are also possible allowing a specific number of pulses (up or
// dn) to be issued to the connected radio. DTMF memories are also possible using spare FLASH
// to hold the sequences.
//
// DTMF signals are generated using DDS code running the PCA CEX1 output in 8-bit PWM mode. If
// a keypress is recieved while the PTT is active (from either the PTT input discrete or the PTT
// keycode), the system will turn on the appropriate DTMF pair until the key is released, or PTT
// goes inactive. Since PCA can't do an 8-bit regular interrupt, Timer1 is used to drive the sample
// clock. Thus, the DDS ISR code lives inside Timer1, and updates the PWM register. Timer1 needs to
// run at the same rate as the PCA to preserve update alignment.
//
// The DDS is currently being driven at FSAMP = 23,925 Hz to ease the filtering requirements. This
// increases the Fstep to 0.365Hz, but this is still well below the worst-case 10.4Hz ETSI requirement
// for DTMF.
//
// PCA CEX2 is used to drive an "AUX" LED on the HM-133 (typically blue, and is user installed near
// the u/d buttons on the HM-133/151). It is a fixed PWM output to provide a fine adjustment to the
// LED brightness. LED on/off/brightness can be used to convey status (power on, DTMF output, PGM
// mode enabled, etc...).
//
// The software also interprets HM-133 key codes to drive the PTT and U/D open-drain discretes.
// U/D also features a tiered pulse ramp whereby the initial keypress sends one pulse, then after
// 1 second of holding, the system begins pulsing at 5 Hz. After 3 seconds of keypress, the
// system begins pulsing at 10 Hz. Pulsing stops imeediately upon release of the key.
//
//--------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------
// compile defines
#include "init.h"
#include "typedef.h"
#include "c8051F520.h"
//-----------------------------------------------------------------------------
// Definitions
//-----------------------------------------------------------------------------
#define VERS 2 // PCB vers 2 uses 4094 port expander, else VERS = 1
//#define IS_SIM // enable this define if using simulator
// see init.h for #defines
//-----------------------------------------------------------------------------
// External Variables
//-----------------------------------------------------------------------------
extern code const U8 SINE[];
volatile U16 hmd_timer; // HM151 data idle timeout timer
//-----------------------------------------------------------------------------
// Main Variables
//-----------------------------------------------------------------------------
// port assignments
// PCB version 1 bit defines
#if VERS == 1
sbit MDATA = P1^7; // (i) mic data in
sbit MMUTE_N = P1^6; // (o) mic mute out (act low)
sbit PTTb = P1^5; // (o) PTT out to rig (act hi)
sbit DNb = P1^4; // (o) mic DN button out to rig (act hi)
sbit UPb = P1^3; // (o) mic UP button out to rig (act hi)
sbit PTTM_N = P1^1; // (i) mic PTT in (act low)
sbit DMUTE = P1^0; // (o) rig data mute out (act hi)
#endif
// PCB version 2 bbSPI defines
#if VERS == 2
sbit MDATA = P1^7; // (a) comparator (-) input (mic serial data)
sbit STB = P1^6; // (o) port expander serial strobe
sbit MIC_DET_N = P1^5; // (i) HM133 mic present detect
sbit SCLK = P1^4; // (o) port expander bbSPI SCLK
sbit MOSI = P1^3; // (o) port expander bbSPI MOSI
sbit CMP_IN = P1^2; // (a) comparator (+) input (VREF with hysteresis)
sbit PTTM_N = P1^1; // (i) mic PTT in (act low)
sbit DMUTE = P1^0; // (o) rig data mute out (act hi)
sbit Ro2 = P1^0; // (o) strap option 2 (bit 1)
sbit LED_PWM = P0^7; // (o) PWM out for LED
sbit Ro3 = P0^6; // (o) strap option 3 (bit 2)
sbit MMUTE_N = P0^5; // (o) mic mute out (act low)
sbit Ro1 = P0^5; // (o) strap option 1 (bit 0)
sbit SPR = P0^4; // (i) SW option bit input
sbit PWMDAC = P0^3; // (o) DDS PWMDAC output
sbit MDATA_IN = P0^2; // (i) limited serial data input (connected to comparator out)
sbit CMPOUT = P0^1; // (o) comparator putput
sbit VREF = P0^0; // (a) ext VREF in (NOT USED FOR GPIO IN THIS DESIGN)
#endif
//-----------------------------------------------------------------------------
// Local variables
//-----------------------------------------------------------------------------
#define HM_START 0x01
#define HM_ERROR 0x02
#define HM_BUFMAX 3 // HM-133/151 buffers and working regs
volatile U8 hm_hptr; // save ptr in case we overflow
volatile U8 hm_tptr; // HM-133/151 key buffer tail ptr
volatile U32 hm_buf[HM_BUFMAX];
volatile U8 hm_status_buf[HM_BUFMAX];
volatile U8 hm_ctptr; // capture buf tail ptr
volatile U8 hm_chptr; // capture buf head ptr
volatile U32 sys_error_flags; // system error flags
volatile char curr_key;
volatile U8 ipldds; // dds ISR init flag
volatile U16 delF1; // phase (tone) register for tone 1
volatile U16 delF2; // phase (tone) register for tone 2
volatile U16 hmkey_timer; // key timer
volatile U8 waittimer; // wait() function timer
volatile U8 dbounceHM_tmr;
volatile U8 iplTMR; // timer IPL init flag
volatile U16 press_timer; // key press timer and flag
volatile U8 press_flag; // key-press timeout flag
U16 pulse_delay; // pulse delay value
#define UD_PERIOD 10 // up/dn pulse period
volatile U16 ud_timer;
volatile U8 xport; // expansion port data register
// bbSPI registers
volatile U8 spdr;
volatile U8 spmask;
// HM133/151 ISR data decode vars
volatile U32 dmask; // data mask
volatile U32 hm_data; // data register
volatile U8 hm_status; // status register
volatile U16 last_edge; // last edge capture (sign extended to 32 bits)
volatile U8 bit_count; // count of # bits rcvd
// HM-133/151 code LUT arrays:
#define MAX_KEY 25 // # keys on HM-151
#define MAX_KEY_133 23 // # keys on HM-133
// HM-133: there are actually 25 keys, but two of them act as modifiers
// (func and dtmf) and do not send a key code. The keys have different
// labeling, but each key is in roughly the same position with the same
// code. However, many keys are labeled differently from the HM-151.
// HM-133 applications should use the following key map:
// Key code Function (key) HM-151 function (key)
// M F2 MR
// V F1 V/M
// X Band change XFC
// T MR (and MW with persistent press) TUNER/CALL
// L VFO SPCH/LOCK
// F n/a F1
// G n/a F2
//
// HM-133 function key return set. '%' and '!' are placekeepers to keep
// alignment with the key_addr[] array. These codes should never be sent
// by an HM-133. HM-151 doesn't have a FUNC mode, so it will never send
// any of these codes.
// !! These codes don't mean anything to the adapter, so they are included as comments
// for completeness. !!
/*code char fnkey_code[] = { 'p', 'o', 'n', 'k', 'm', 'l', 'j', '%',
'!', 'a', 'b', 'c', 'q', 'd', 'e', 'f',
'r', 'g', 'h', 'i', 's', '+', '`', '$',
't', '\0' };*/
// Normal mode keycodes for HM-151 and HM-133 (see above for HM-133 notes)
code char key_code[] = { 'L', 'T', 'X', '/', 'V', 'M', '\\', 'F',
'G', '1', '2', '3', 'A', '4', '5', '6',
'B', '7', '8', '9', 'C', '*', '0', '#',
'D', '\0' };
#define DTMF_OFFS (9) // offset to subtract from key_code index to become dtmf index
// this offset is dependent on the placement of keycodes in the
// "_code" tables above. SO...don't re-arrange the key_code[] table!
// DTMF tone lookup tables for row and column
code U16 dtmf_row[] = { ROW1_TONE, ROW1_TONE, ROW1_TONE, ROW1_TONE,
ROW2_TONE, ROW2_TONE, ROW2_TONE, ROW2_TONE,
ROW3_TONE, ROW3_TONE, ROW3_TONE, ROW3_TONE,
ROW4_TONE, ROW4_TONE, ROW4_TONE, ROW4_TONE,
};
code U16 dtmf_col[] = { COL1_TONE, COL2_TONE, COL3_TONE, COL4_TONE,
COL1_TONE, COL2_TONE, COL3_TONE, COL4_TONE,
COL1_TONE, COL2_TONE, COL3_TONE, COL4_TONE,
COL1_TONE, COL2_TONE, COL3_TONE, COL4_TONE,
};
// serial key codes less the func/dtmf/1stkey modifier nybble.
code U16 key_addr[] = {0x0b02,0x1302,0x2302,0x2202,0x0a02,0x1202,0x2002,0x1002,
0x0802,0x0b82,0x1382,0x2382,0x4382,0x0982,0x1182,0x2182,
0x4182,0x0a82,0x1282,0x2282,0x4282,0x0882,0x1082,0x2082,
0x4082,0x0000};
// The first index selects the radio type, the second index selects the button.
// The array return is written to the port expansion SR to select the desired Fn.
code U8 radio_fbtn[][7] = {
// HM133 button --> UP F1 VFO/LOCK DN MR/CALL BAND/OPT Pulse Delay
// | | | | | | |
{ UP, UP1, UP2, DN, DN1, DN2, MS45}, // 0 generic (ICOM)
{ UP, MH36_ACC, MH36_P1, DN, MH36_DMR, MH36_P2, MS100}, // 1 Yaesu MH36
{ UP, UP1, UP2, DN, DN1, DN2, MS80}, // 2 Kenwood
{ UP, UP1, UP2, DN, DN1, DN2, MS100}, // 3 generic (ICOM/KW) <- placekeeper for new rig type
{ UP, UP1, UP2, DN, DN1, DN2, MS125}, // 4 generic (ICOM/KW) <- placekeeper for new rig type
{ UP, UP1, UP2, DN, DN1, DN2, MS125}, // 5 generic (ICOM/KW) <- placekeeper for new rig type
{ UP, UP1, UP2, DN, DN1, DN2, MS125}, // 6 generic (ICOM/KW) <- placekeeper for new rig type
{ 0, 0, 0, 0, 0, 0, 0} // 7 HM-151 (IC-7000) -- no up/dn or other outputs supported
}; // for this mic
code U8 swopt_pattern_lut[] = {
// "a" = 00000001, mask = 00000010
0x01,
// "b" = 00001000, mask = 00001000
0x08,
// "c" = 00001010, mask = 00001000
0x0a,
// "d" = 00000100, mask = 00000100
0x04,
// "e" = 00000000, mask = 00000001
0x00,
// "f" = 00000010, mask = 00001000
0x02,
// "g" = 00000110, mask = 00000100
0x06,
// "h" = 00000000, mask = 00001000
0x00 };
code U8 swopt_mask_lut[] = {
// "a" = 00000001, mask = 00000010
0x02,
// "b" = 00000001, mask = 00001000
0x08,
// "c" = 00000101, mask = 00001000
0x08,
// "d" = 00000100, mask = 00000100
0x04,
// "e" = 00000000, mask = 00000001
0x01,
// "f" = 00000010, mask = 00001000
0x08,
// "g" = 00000110, mask = 00000100
0x04,
// "h" = 00000000, mask = 00001000
0x08 };
//-----------------------------------------------------------------------------
// Local Prototypes
//-----------------------------------------------------------------------------
void process_hm(U8 flag);
void flash_swvers(U8 pattern, U8 mask);
void pulse(U8 discr, U8 pcount, U16 pdly);
void outbit(U8 bitmap, U8 on);
void send8(U8 sdata);
U8 get_opt(void);
void wait(U8 wvalue);
U8 got_hmd(void);
S32 get_hmd(void);
char* get_hmcode(U32 keym);
//******************************************************************************
// main()
// The main function inits I/O and process I/O.
//
//******************************************************************************
void main(void) //using 0
{
volatile U8 i; // temp uchar
volatile U8 j;
U8 q; // DTMF-S temp
U8 t; // DTMF-S temp
U8 mdet_edge; // mic det edge reg
U8 new_events;
U8 dmute_mem; // reg to hold initial dmute status when "G" key detected
volatile char d; // temp char
char* dp; // pointer to key code array
#define MAX_ACCUM 99
volatile U8 accum; // u/d pulse-count accumulator
U32 si; // temp 32
static U8 hm_count; // hm key repeat counter
static U8 keyh_mem; // current key (used to determine 1st keypress)
static U8 keyh_edg; // 1st key edge
static U8 ptt_edge; // edge discrete
static U8 aflag; // accum repeat inhib flag
static U8 softptt; // soft ptt detected
volatile U8 swopt; // SW ipl strap options
volatile U8 run; // warm restart trigger
// start of main
while(1){ // outer loop is for soft-restart capability
PCA0MD = 0x00; // disable watchdog
EA = 0;
run = 1; // enable run
// init MCU system
Init_Device(); // init MCU
EA = 1;
xport = 0; // init expansion port
#if VERS == 1
// VERS 1 port init
PTTb = 0; // (o) PTT out to rig (act hi)
DNb = 0; // (o) mic DN button out to rig (act hi)
UPb = 0; // (o) mic UP button out to rig (act hi)
swopt = 0x0; // set version 1 SW strap
DMUTE = 0; // (o) rig data mute out (act hi)
#endif
#if VERS == 2
// VERS 2 port init
swopt = get_opt(); // fetch SW option straps
send8(xport);
P0MDOUT = 0xAE; // update GPIO config, post option read
P1MDOUT = 0x59;
DMUTE = 0;
#endif
swopt = swopt & 0x07; // isolate rig options
// swopt = STRAP_HM151; // force HM151 debug
pulse_delay = (U16)radio_fbtn[swopt][IDX_PLSDLY]; // fetch pulse delay
MDATA = 1; // (i) mic data in
MMUTE_N = UNMUTE_BIT; // (o) mic mute out (act low)
PTTM_N = 1; // (i) mic PTT in (act low)
DMUTE = 1; // (o) rig data mute out (act hi)
#if VERS == 2
MIC_DET_N = 1; // (i) init mic det (act low)
#endif
// init module vars
iplTMR = TMRIPL; // timer IPL init flag
PCA0CPM1 &= 0xfe; // disable CCF1
dmask = 0x01; // reset data regs
hm_data = 0;
bit_count = 0;
hm_status = 0;
last_edge = 0;
press_flag = 0;
keyh_edg = 0;
delF1 = 0; // cler the DDS tone regs
delF2 = 0;
hmkey_timer = 0;
waittimer = 0;
dbounceHM_tmr = 0;
press_timer = 0;
press_flag = 0;
ud_timer = 0;
ipldds = 1; // ipl the DDS
// process IPL init
keyh_mem = KEY_NULL;
hm_count = 0;
aflag = 0;
softptt = 0; // init soft PTT
#if VERS == 2
mdet_edge = ~MIC_DET_N; // init mic det edge to trap on IPL
#else
mdet_edge = 1;
#endif
#ifndef IS_SIM // skip DDS wait for sim
while(ipldds); // wait for DDS to init
#endif
#ifdef IS_SIM // allows range-checking of big constants in simulator
delF1 = HMT_SYNC; // place constant expressions here for a quick check
delF1 = COL4_TONE;
#endif
// flash version and radio select code:
// "3" = 00000011 = 0x03
// mask = 00010000 = 0x10
flash_swvers(0x03, 0x10); // Version 3
// radio select code: use LUT for pattern and mask
flash_swvers(swopt_pattern_lut[swopt], swopt_mask_lut[swopt]);
if(swopt == STRAP_HM151){ // if HM151 FT mode...
DMUTE = 0; // pass-thru MDATA
PCA0CPM2 &= ~0x40; // fn led off (ECOM);
}else{
DMUTE = 1; // inhibit pass-thru
PCA0CPM2 |= 0x40; // fn led on (ECOM);
}
ptt_edge = ~PTTM_N;
// main loop
while(run){ // inner-loop runs the main application
new_events = 0;
d = PTTM_N;
if(d != ptt_edge){ // process manual PTT input
ptt_edge = d;
if(d){ // PTT input is ground true
MMUTE_N = MUTE_BIT; // PTT released
outbit(PTT, 0);
delF1 = 0;
delF2 = 0;
if(swopt != STRAP_HM151){ // if NOT HM151 FT mode...
PCA0CPM2 |= 0x40; // fn led on (ECOM);
}else{
if(DMUTE){
PCA0CPM2 |= 0x40; // fn led on (ECOM);
}
}
// turn off PWMO ISR
PCA0CPM1 &= 0xfe;
}else{
outbit(PTT, 1); // PTT pressed
MMUTE_N = UNMUTE_BIT;
}
}
d = '\0';
if(got_hmd()){ // trap keys
new_events = KEYHM_CHNG;
dbounceHM_tmr = 150;
}
if(dbounceHM_tmr == 0){
if(keyh_mem != KEY_NULL){ // trap debounce timeouts
new_events |= KEYHM_CHNG;
}
}
if(delF1){
press_timer = MS1650; // reset DTMF timer anytime a tone is being generated
}
if(press_flag){ // keypress timeout, enable mic
press_flag = 0;
if(swopt != STRAP_HM151){ // if NOT HM151 FT mode...
PCA0CPM2 |= 0x40; // fn led back on (ECOM);
}else{
if(DMUTE){
PCA0CPM2 |= 0x40; // fn led back on (ECOM);
}
}
MMUTE_N = UNMUTE_BIT; // unmute mic
}
#if VERS == 2
t = MIC_DET_N; // grab the GPIO
#else
t = 0;
#endif
if(mdet_edge != t){ // take action if there has been a change
mdet_edge = t;
if(t){ // if mic removed, abort PTT and tones
outbit(PTT, 0);
if(run == 2){ // This is an interlock to prevent continuous restarts while the mic is disconnected
run = 0; // restart
}else{
run = 2; // set first pass interlock
}
}
if(swopt == STRAP_HM151){ // if HM151 FT mode... update mirror
t = ~t & 0x01; // need to invert to account for NFET inversion
outbit(DN, t);
}
}
// this branch handles key-driven events
if(new_events & KEYHM_CHNG){ // process trapped events
if(!softptt){
if(dbounceHM_tmr == 0){ // process no-key from HM-133/151
keyh_edg = 0;
if(keyh_mem != KEY_NULL){
// release key
if(!delF1){
if(swopt != STRAP_HM151){
PCA0CPM2 |= 0x40; // led back on if no tone (ECOM);
}else{
if(DMUTE){
PCA0CPM2 |= 0x40; // led back on if no tone (ECOM);
}
}
}
aflag = 0; // release accum inhibit
i = keyh_mem | 0x80; // set release keypress
}
keyh_mem = KEY_NULL; // if no key, clear current key mem.
}
}
si = get_hmd(); // get hm-133/151 data word
dp = get_hmcode(si); // do key-code lookup, pointer is set to keycode array
d = *(dp + HM_IDX_CODE);
if(*(dp + HM_IDX_STAT) & HM_1STKEY){
if(keyh_edg == 0){
keyh_edg = 1;
}else{
*(dp + HM_IDX_STAT) &= ~HM_1STKEY; // only allow 1 1stkey in a row
}
}else{
keyh_edg = 0;
}
if(d != KEY_NULL){ // trap first press, and prevent false repeats
if(d == PTT_CODE){
i = d;
keyh_mem = KEY_NULL;
}else{
dbounceHM_tmr = 150; // reset loss-of-key detect timer
if(d && (d != keyh_mem)){ // if mic data is not null AND key mem is null:
keyh_mem = d; // save key to mem (first key)
i = d;
hm_count = HMKEY_HOLD_CNT; // set key-hold counter
}else{
if(hm_count != 0){ // processes key-hold counter (not used at the moment)
if(--hm_count == 0){
// Process key "HOLD"
if((keyh_mem == '/') || (keyh_mem == '\\')){
*(dp + HM_IDX_STAT) |= HM_1STKEY; // recycle key
i = keyh_mem;
}
hm_count = 2;
}
}
}
}
}else{
keyh_edg = 0; // save null to key mem if debounce == 0
}
if(swopt == STRAP_HM151){ // if HM151 FT mode...
if(i == ('G'|0x80)){
if(dmute_mem){
DMUTE = 0;
PCA0CPM2 &= ~0x40; // fn led off (ECOM);
dmute_mem = 0;
}
}
if(*(dp + HM_IDX_STAT) & HM_1STKEY){
if(i == 'G'){ // toggle DMUTE when HM151-F2 key encountered
dmute_mem = DMUTE; // remember initial state of DMUTE
if(!DMUTE){ // update Fn LED
DMUTE = 1;
PCA0CPM2 |= 0x40; // fn led on (ECOM);
}
}
}
if(!DMUTE){ // ignore keypresses if data is NOT muted
i = '\0';
}
}
if(i & 0x80){
delF1 = 0;
delF2 = 0;
}
// process DTMF keys if PTT == 1, or into accum if PTT == 0
if(((i>='0') && (i<='9')) || ((i>='A') && (i<='D')) || (i =='*') || (i == '#')){
t = 1;
}else{
t = 0;
}
j = *(dp + HM_IDX_STAT) & HM_1STKEY;
if(*(dp + HM_IDX_STAT) & (HM_PTT | HM_DTMF) == (HM_PTT | HM_DTMF) && !t){
// DTMF-S PTT hold
q = 1;
delF1 = 0;
delF2 = 0;
}
/* if(((*(dp + HM_IDX_STAT) & HM_PTT) == HM_PTT) && q){
// DTMF-S release
q = 0;
MMUTE_N = UNMUTE_BIT; // (o) mic unmute out (act low)
}*/
if(t){
if(*(dp + HM_IDX_STAT) & HM_DTMF){
press_timer = MS1650; // reset DTMF timer
}
if(*(dp + HM_IDX_STAT) & HM_PTT){
outbit(PTT, 1);
}
if(xport & PTT){
if(j){
PCA0CPM2 &= ~0x40; // fn led off if dtmf (ECOM);
MMUTE_N = MUTE_BIT; // (o) mic mute out (act low)
delF1 = dtmf_row[*(dp+HM_IDX_DTMF)];
delF2 = dtmf_col[*(dp+HM_IDX_DTMF)];
}
}else{
if((i >= '0') && (i <= '9')){ // enter numbers into accumulator
if(swopt != STRAP_HM151){ // only if NOT in HM151 FT mode...
PCA0CPM2 &= ~0x40; //ECOM;
if(!aflag){
aflag = 1; // inhibit repeats
j = i - '0';
accum *= 10;
accum += j;
if(accum > MAX_ACCUM){
accum = j;
}
}
}
}else{
accum = 0; // non-numbers in DTMF space clear accum
if(i == '*'){
PCA0CPH2 = LED_DIM; // set dim LED
PCA0CPL2 = 0;
}
if(i == '#'){
PCA0CPH2 = LED_BRT; // set brt LED
PCA0CPL2 = 0;
}
}
}
}else{
if(swopt == STRAP_HM151){ // if HM151 FT mode...
if(i != 'M'){ // at this point, only "M" key is allowed in HM151 mode (other allowed
i = '\0'; // ... keys have already been processed) all others are ignored
}
}
switch(i){
case '/': // up key
if(*(dp + HM_IDX_STAT) & HM_1STKEY){
pulse(radio_fbtn[swopt][IDX_UP], accum, pulse_delay);
accum = 0;
}
break;
case '\\': // down key
if(*(dp + HM_IDX_STAT) & HM_1STKEY){
pulse(radio_fbtn[swopt][IDX_DN], accum, pulse_delay);
accum = 0;
}
break;
case PTT_CODE:
if(*(dp + HM_IDX_STAT) & HM_PTT){ // is PTT pressed keycode
if(*(dp + HM_IDX_STAT) & HM_DTMF){ // if DTMF active, time to turn off tones if we see this code
MMUTE_N = MUTE_BIT; // stay muted...
delF1 = 0;
delF2 = 0;
}else{
if(!press_timer){
MMUTE_N = UNMUTE_BIT; // unmute mic
}
}
outbit(PTT, 1); // activate PTT out
softptt = 1; // set soft PTT active
}else{
MMUTE_N = MUTE_BIT;
outbit(PTT, 0); // de-activate PTT out
delF1 = 0;
delF2 = 0;
if(swopt != STRAP_HM151){ // if NOT HM151 FT mode...
PCA0CPM2 |= 0x40; // fn led on (ECOM);
}else{
if(DMUTE){
PCA0CPM2 |= 0x40; // fn led on (ECOM);
}
}
softptt = 0; // set soft PTT inactive
}
break;
case 'L': // VFO/LOCK (HM151 = SPCH/LOCK)
if(*(dp + HM_IDX_STAT) & HM_1STKEY){
pulse(radio_fbtn[swopt][IDX_VFO], 1, pulse_delay);
}
break;
case 'T': // MR/CALL (HM151 = TUNER/CALL)
if(swopt == STRAP_ICOM){ // if ICOM mode...long up pulse to start scan
if(*(dp + HM_IDX_STAT) & HM_1STKEY){
pulse(radio_fbtn[swopt][IDX_UP], 1, MS800);
}
}else{
if(*(dp + HM_IDX_STAT) & HM_1STKEY){
pulse(radio_fbtn[swopt][IDX_MR], 1, pulse_delay);
}
}
break;
case 'X': // BND/OPT (HM151 = XFC)
// toggle DMUTE on v001 hardware
if(*(dp + HM_IDX_STAT) & HM_1STKEY){
pulse(radio_fbtn[swopt][IDX_BAND], 1, pulse_delay);
}
break;
case 'V': // F1 (HM151 = V/M)
if(swopt == STRAP_ICOM){ // if ICOM mode...long dn pulse to start scan
if(*(dp + HM_IDX_STAT) & HM_1STKEY){
pulse(radio_fbtn[swopt][IDX_DN], 1, MS800);
}
}else{
if(*(dp + HM_IDX_STAT) & HM_1STKEY){
pulse(radio_fbtn[swopt][IDX_F1], 1, pulse_delay);
}
}
break;
case 'M': // Toggle test tone
if(*(dp + HM_IDX_STAT) & HM_1STKEY){
if(delF1){
MMUTE_N = UNMUTE_BIT; // (o) mic mute out (act low)
delF1 = 0;
delF2 = 0;
if(swopt != STRAP_HM151){ // if NOT HM151 FT mode...
PCA0CPM2 |= 0x40; // fn led on if no tone (ECOM);
}else{
if(DMUTE){
PCA0CPM2 |= 0x40; // fn led on if no tone (ECOM);
}
}
}else{
MMUTE_N = MUTE_BIT; // (o) mic mute out (act low)
delF1 = TONE_1000;
delF2 = 0;
PCA0CPM2 &= ~0x40; // fn led off if tone (ECOM);
}
}
break;
default:
i += 1;
break;
} // end key processing switch()
} // end "key processing" branch
} // end "new events"
} // end while(run)
} // end outer while()
} // end main()
// *********************************************
// *************** SUBROUTINES ***************
// *********************************************
//-----------------------------------------------------------------------------
// flash_swvers() uses ms timer to pulse out Morse data on LED
// "pattern" is dit(0) or dah(1). 1st element is pointed to by "mask"
// subsequent elements are toward the LSb of pattern (mask is right shifted
// to reach next element). When mask == 0, character is done. There is
// a DAH delay on entry and exit.
//-----------------------------------------------------------------------------
#define MORSE_PER MS100 // dit period
void flash_swvers(U8 pattern, U8 mask){
U8 j = 0; // temp vars
volatile U8 k = mask; // vers pattern temps
PCA0CPM2 &= ~0x40; // led dim (ECOM)
ud_timer = (3 * MORSE_PER);
while(ud_timer != 0L);
do{
PCA0CPM2 &= ~0x40; // led dim (ECOM)
ud_timer = MS100;
while(ud_timer != 0L);
PCA0CPM2 |= 0x40; // led brt (ECOM)
if(pattern & k){
ud_timer = (3 * MORSE_PER);
}else{
ud_timer = MORSE_PER;
}
k = k >> 1;
while(ud_timer != 0L);
j = got_hmd();
j |= ~PTTM_N;
}while((j == 0) && k);
if(j == 0){ // if there was no keypress or ptt, delay
PCA0CPM2 &= ~0x40; // led dim (ECOM)
ud_timer = (3 * MORSE_PER);
while(ud_timer != 0L);
}
PCA0CPM2 |= 0x40; // led brt (ECOM)
return;
}
//-----------------------------------------------------------------------------
// pulse() uses ms timer to pulse discretes pcount pulses
//-----------------------------------------------------------------------------
void pulse(U8 discr, U8 pcount, U16 pdly){
U8 i; // temp vars
U8 j;
if(pcount == 0){
j = 1;
}else{
j = pcount;
press_timer = 100; // set some delay
do{
i = got_hmd();
if(i){
get_hmd(); // clear data word buffer
press_timer = 80;
}
}while(press_timer);
}
for(i=0; i<j; i++){
PCA0CPM2 &= ~0x40; // led dim (ECOM)
outbit(discr, 1);
ud_timer = pdly;
while(ud_timer);
PCA0CPM2 |= 0x40; // led brt (ECOM)
outbit(discr, 0);
ud_timer = pdly;
while(ud_timer);
if(got_hmd()){
i = j; // a keypress detected, abort
}
}
return;
}
//************************************************************************
// outbit() consolodates OC output port updates into a single function
//************************************************************************
void outbit(U8 bitmap, U8 on){
#if VERS == 1
// This is the version 1 routine. Only PTT, UP, DN, & F2 (MW) are supported
switch(bitmap){
case PTT:
if(on){
PTTb = 1;
xport |= bitmap;
}else{
PTTb = 0;
xport &= ~bitmap;
}
break;
case UP:
if(on) UPb = 1;
else UPb = 0;
break;
case DN:
if(on) DNb = 1;
else DNb = 0;
break;
case DN2:
if(on) DMUTE = 1;
else DMUTE = 0;
break;
default:
break;
}
#endif
#if VERS == 2
// This is the version 2 routine. PTT, UP, UP1, UP2, DN, DN1, & DN2 are supported
if(on){ // set/clear bitmap
xport |= bitmap;
}else{
xport &= ~bitmap;
}
send8(xport); // transfer to port expander
#endif
return;
}
#if VERS == 2
//************************************************************************
// send8() does a bit-bang SPI into a CD4094 to produce a port expansion
// output. Uses T0 to clock data
//************************************************************************
void send8(U8 sdata){
spmask = 0x80; // init mask
spdr = sdata; // store to bbSPI data reg
TR0 = 1;
while(TR0); // loop until xfr complete
STB = 1; // toggle strobe with delay
TR0 = 1;
while(TR0); // loop until xfr complete
STB = 0;
// since there will be several ms applied between send8 xfers, no further delay is needed
// but beware...this must be enforced by the programmer!!
return;
}
//************************************************************************
// get_opt() sets strap GPIOs for inputs (assumes they are already configured
// as OD outputs), reads the strap inputs, calculates the resulting option
// value, then returns that value to the caller.
//************************************************************************
U8 get_opt(void){
U8 rtn = 0;
Ro3 = 1; // set the option GPIOs for input
Ro2 = 1;
Ro1 = 1;
wait(50); // let the inputs settle
rtn = ((U8)Ro3 & 0x01) << 2; // collect the option inputs
rtn |= ((U8)Ro2 & 0x01) << 1;
rtn |= ((U8)Ro1 & 0x01);