-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcounter.s
2333 lines (2037 loc) · 108 KB
/
counter.s
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
;**************************************************************************
; FILE: counter.s *
; CONTENTS: Simple low-cost digital frequency meter using a PIC 16F628A *
; AUTHOR: Wolfgang Buescher, DL4YHF *
; (based on a work by James Hutchby, MadLab, 1996) *
; ex MPLAB 8 project (MPASM Assembly compilator) *
; ported to MPLAB X v6 project (pic-as Assembly compiler) *
; by Aniello Di Nardo (StarNiell) (IU8NQI) *
; REVISIONS: (latest entry first) *
; 2022-11-27 - Aniello Di Nardo (IU8NQI): porting to MPLAB X 6 (pic-as) *
; remember to set "-Wl,-PresetVec=0x0" in pic-as global *
; options (Additional Options) of Project Properties *
; Added 10695000 into IF Table *
; 2021-02-23 - Ho-Ro: 1 Hz resolution up to 99999 Hz (range < 101760 Hz) *
; 2021-02-18 - Ho-Ro: display underflow as " 0" (DISPLAY_VARIANT_2/3) *
; 2020-08-25 - Ho-Ro: Modified for Linux gpsam (produces identical hex) *
; 2017-09-10 - Nigel Kendrick (nigel-dot-kendrickatgeemail.com *
; Added option to specify that you are using an external *
; crystal oscillator on OSC1, which frees OSC2/RA6/PB6 *
; as an output pin that can then be used to control display *
; digit 5 instead of the diode/transistor NAND gate. *
; 2006-05-31 - Added the 'power-save' option which temporarily puts the *
; PIC to sleep (with only the watchdog-oscillator running) *
; 2006-05-15 - New entry in the preconfigured frequency table for 4-MHz *
; IF filters (like "Miss Mosquita" [Moskita] by DK1HE) *
; 2005-08-24 - Cured a bug in the COMMON ANODE decimal point setting . *
; (the "^0xFF" for the AND-mask was missing in macro 'conv') *
; 2005-03-21 - Added a few conditionals to use the same sourcecode *
; to drive a COMMON ANODE display ( DISPLAY_VARIANT_3 ) *
; 2004-03-14 - Fixed a range-switching bug around 8 MHz . *
; - Support TWO different display variants now, *
; optimized for different board layouts, and different clock *
; frequencies (4 MHz for variant 1, 20 MHz for variant 2). *
; 2004-03-05 - Added the feature to add or subtract a frequency offset . *
; 2004-02-18 - Migration to a PIC16F628 with 4 MHz crystal (el Cheapo) *
; - Changed the LED patterns '6' and '9' because they looked *
; like '0b and 'q' in the old counter version . *
; - Added the auto-ranging feature *
; - Stepped from 24-bit to 32-bit integer arithmetic, to be *
; able to count 50 MHz with 1-second gate time, *
; or (at least) adjust ANY github dl4yhfresult for the ANY *
; prescaler *
; division ratio, which may give pretty large numbers . *
; - A PIC16F628 worked up to 63 MHz with this firmware . *
;**************************************************************************
PROCESSOR 16F628A
RADIX DEC
; processor specific definitions
#include <xc.inc>
; DEBUG=1 for simulation, DEBUG=0 for real hardware
#define DEBUG 0
; Selection of LED display control bits... since 2005, three different variants.
; Select ONE OF THESE in MPLAB under "Project".."Build Options".."Macro Definitions"!
; DISP_VARIANT=1 : first prototype, PIC on left side of display
; DISP_VARIANT=2 : second prototype, separated PIC and display board
; DISP_VARIANT=3 : similar as (2), but for COMMON CATHODE display
; Unfortunately it seems impossible to assign a NUMERIC VALUE to a macro
; in MPLAB (not in MPASM!) ....
#ifdef DISPLAY_VARIANT_1
; very first (old) prototype by DL4YHF
#define DISP_VARIANT 1
#define COMMON_ANODE 0
#define COMMON_CATHODE 1
#else
#ifdef DISPLAY_VARIANT_2
; 5 digits, new layout, COMMON CATHODE
#define DISP_VARIANT 2
#define COMMON_ANODE 0
#define COMMON_CATHODE 1
#else
; added 2005-03-21 :
#ifdef DISPLAY_VARIANT_3
; similar as (2), but for COMMON ANODE display
#define DISP_VARIANT 3
#define COMMON_ANODE 1
#define COMMON_CATHODE 0
#else
; default: 5 digits, new layout, COMMON CATHODE
#define DISP_VARIANT 2
#define COMMON_ANODE 0
#define COMMON_CATHODE 1
MESSG "Using default: 5 digits, new pin layout, common cathode LEDs"
#endif
#endif
#endif
;NK Define EXT_CLOCK to state that you are using an external oscillator on
;OSC1 pin (16F628 pin 16), so OSC2 pin (15) is not needed and can be used
;as RA6 to control display digit 5 instead of the transistor/diode NAND gate.
#ifdef EXT_CLOCK
#define EXTERNAL_CLOCK 1
#else
#define EXTERNAL_CLOCK 0
#endif
;**************************************************************************
; *
; Summary *
; *
;**************************************************************************
; The software functions as a frequency meter with an input signal
; range of 1 Hz to ~ 50 MHz and with an accuracy of +/- 1Hz
; if the oscillator crystal is properly trimmed .
; Signal pulses are counted over a fixed time interval of 1/4 second to
; 1 second (gate time). High frequency pulses are counted over 1/4 s
; to make the meter more responsive with no loss of displayed accuracy.
; Pulses are counted using Timer 0 of the PIC,
; which is set to increment on rising edges on the TMR0 pin. The 8-bit
; hardware register is extended by software into a 32-bit pulse counter.
; If timer 0 rolls over (msb 1 -> 0) between successive polls then the
; high two bytes of the pulse counter are incremented.
; Timer 0 is unable to count more than one pulse per instruction cycle
; (per 4 clock cycles) so the prescaler is used at frequencies above
; 1MHz (4MHz clock / 4) and also to ensure that pulses are not lost
; between polls of timer 0 (which would happen if more than 128 pulses were
; received). Fortunately the prescaler is an asynchronous counter
; which works up to a few ten MHz (sometimes as far as 60 MHz) .
; Timing is based on a software loop of known execution period . The loop
; time is 50 or 20 us which gives integer counts to time 1 s and 1/4 s .
; During this timing loop, the multiplexed LED display is updated .
; The frequency in binary is converted to decimal using a powers-of-ten
; lookup table. The binary powers of ten are repeatedly subtracted from
; the frequency to determine the individual decimal digits. The decimal
; digits are stored at the 8 bytes at 'digits'. Leading zeroes are then
; suppressed and the 4 (or 5) significant digits are converted to LED data
; for the 7-segment displays using a lookup table.
; The signal frequency is displayed on four (or five) 7-segment displays.
; The displays are multiplexed which means that only one display is enabled
; at any one time. The variable 'disp_index' contains the index of the currently
; enabled display. Each display is enabled in turn at a sufficient frequency
; that no flicker is discernable. A prescaler ('disp_timer') is used
; to set the multiplexing frequency to a few hundred Hz.
; The display shows the signal frequency in KHz or MHz, according to the
; following table:
; --------------------------
; | Frequency | Display |
; --------------------------
; | < 1Hz | 0 |
; | 1Hz | 0.001[0] | Note: kHz-dot is flashing (blinking)
; | 10Hz | 0.010[0] |
; | 100Hz | 0.100[0] |
; | 1.000KHz | 1.000[0] |
; | 10.00KHz | 10.00[0] |
; | 100.0KHz | 100.0[0] |
; | 1.000MHz | 1.000[0] | Note: MHz-dot is steady (not blinking)
; | 10.00MHz | 10.00[0] |
; --------------------------
; If there is no signal at all, a single zero is displayed in the 4th(!) digit.
; Overflows are not displayed because they cannot be detected !
;**************************************************************************
; *
; PIC config definitions *
; *
;**************************************************************************
; CONFIG directive is used to embed configuration data within .s file.
; The lable following the directive is located in the xc.inc file.
; See respective data sheet for additional information on configuration word.
; Since 2006-05-28, the watchdog must be ENABLE in the config word
; because of its wakeup-from-sleep function (see 'Sleep100ms') .
; EX(16F84:)
; CONFIG FOSC = RC
; CONFIG WDTE = ON
; CONFIG PWRTE = ON
; CONFIG CP = OFF
;NK If using an external clock source, use the EXTERNAL_CLOCK definition.
#if (EXTERNAL_CLOCK==0)
; display variant 1 : clocked with 4 MHz (low power, "XT" )
#if (DISP_VARIANT==1)
CONFIG FOSC = XT
; display variants 2+3 : clocked with 20 MHz (needs "HS" oscillator)
#else
CONFIG FOSC = HS
#endif
#else
CONFIG FOSC = EXTRC_CLKOUT
#endif
; Other CONFIG directives valid for each display variants
CONFIG WDTE = ON
CONFIG PWRTE = ON
CONFIG MCLRE = OFF
CONFIG BOREN = OFF
CONFIG LVP = OFF
CONFIG CPD = OFF
CONFIG CP = OFF
; TODO: Eliminare *************************************************************
; '__IDLOCS' directive may be used to set the 4 * 4(?!?) ID Location Bits .
; These shall be placed in the HEX file at addresses 0x2000...0x2003 .
;__IDLOCS H'1234'
;CONFIG IDLOC0 = 0x15
; *************************************************************
;**************************************************************************
; *
; Port assignments *
; *
;**************************************************************************
PORT_A_IO equ 0b0000 ; port A I/O mode (all output)
PORT_B_IO equ 0b00000000 ; port B I/O mode (all output)
LEDS_PORT equ PORTB ; 7-segment LEDs port
ENABLE_PORT equ PORTA ; display enable port
; Bitmasks to control the digit outputs have been moved to enable_table .
; YHF: Note that 'display #0 is the MOST SIGNIFICANT digit !
#define IOP_PROG_MODE PORTA,5 ; digital input signal, LOW enters programming mode
;**************************************************************************
; *
; Constants and timings *
; *
;**************************************************************************
; processor clock frequency in Hz (4MHz)
; display variant 1 : clocked with 4 MHz (low power consumption)
#if (DISP_VARIANT==1)
CLOCK equ 4000000
; display variants 2+3 : clocked with 20 MHz (higher resolution)
#else
CLOCK equ 20000000
#endif
; microseconds per timing loop
; display variant 1 : clocked with 4 MHz
#if (DISP_VARIANT==1)
; 20 microseconds is impossible with 4-MHz-Crystal, so use 50 us instead !
; Make sure all gate times can be divided by this interval without remainder :
; 1 second / 50 us = 20000 (ok)
; 1/4 second / 50 us = 5000 (ok)
; 1/8 second / 50 us = 2500 (ok)
TIME equ 50
; display variants 2+3 : clocked with 20 MHz
#else
; 20 microseconds is impossible with 4-MHz-Crystal, so use 50 us instead !
; Make sure all gate times can be divided by this interval without remainder :
; 1 second / 20 us = 50000 (ok)
; 1/4 second / 20 us = 12500 (ok)
; 1/8 second / 50 us = 6250 (ok)
TIME equ 20
; variant 1 or 2+3 ?
#endif
; Clock cycles per timing loop. See subroutine count_pulses .
; Usually CYCLES=200 (for 4 MHz crystal, 50 usec - loop)
; or 400 (for 20 MHz crystal, 20 usec - loop)
CYCLES equ TIME*CLOCK/1000000
GATE_TIME_LOOPS equ CLOCK/CYCLES ; number of gate-time loops for ONE SECOND gate time
LAMPTEST_LOOPS equ CLOCK/(2*CYCLES) ; number of loops for a 0.5 SECOND lamp test after power-on
PROGMODE_LOOPS equ CLOCK/(10*CYCLES) ; number of delay loops for display in PROGRAMMING MODE (0.1 sec)
; Configuration of power-saving mode :
#if( DEBUG )
PSAVE_DELAY_TIME equ 10 ; number of 0.25-sec-intervals before turning off (shorter for debugging)
#else
PSAVE_DELAY_TIME equ 60 ; number of 0.25-sec-intervals before turning off (some dozen seconds)
#endif
PSAVE_FLASHUP_TIME equ 14 ; number of 0.7(!)-second-intervals between two flashes in power-saving mode
PSAVE_MAX_DIFF equ 10 ; maximum frequency difference (range-dependent, see below)
; Unit: N times "frequency-resolution", see frequency-range table .
; Example: PSAVE_MAX_DIFF=10 means 10*4Hz in Range 1 (1..3.4 MHz) .
; Menu Indices ... must match the jump table PMDisplay + PMExecute !
MI_QUIT equ 0 ; exit from menu
MI_PSAVE equ 1 ; turn "power save"-option on and off
MI_ADD equ 2 ; save frequency offset to ADD it from now on
MI_SUB equ 3 ; save frequency offset to SUBTRACT it from now on
MI_ZERO equ 4 ; set the frequency offset to ZERO and leave menu
MI_STD_IF equ 5 ; jump into the STANDARD INTERMEDIATE FREQUENCY table..
MI_INDEX_MAX equ 5 ; normal menu indices up to MI_INDEX_MAX .
MI_IF_1 equ 6 ; show the 1st standard IF
MI_IF_2 equ 7 ; show the 2nd standard IF
MI_IF_3 equ 8 ; show the 3rd standard IF
MI_IF_4 equ 9 ; show the 4th standard IF
MI_IF_5 equ 0x0A ; show the 5th standard IF
MI_IF_6 equ 0x0B ; show the 6th standard IF
MI_IF_QT equ 0x0C ; exit standard IF menu without changing anything
MI_IF_SUBMENU_MAX equ 0x0B
;MI_IF_5 equ 0x0A ; show the 4th standard IF
;MI_IF_QT equ 0x0B ; exit standard IF menu without changing anything
;MI_IF_SUBMENU_MAX equ 0x0A
;**************************************************************************
; *
; File register usage *
; *
;**************************************************************************
; RAM memory (general purpose registers, unfortunately not the same for PIC16F84 & PIC16F628)
; in PIC16F628: RAM from 0x20..0x7F (96 bytes, 0x20.. only accessable in Bank0)
; 0xA0..0xEF (another 80 bytes in Bank1)
; 0x120..0x14F (another 48 bytes in Bank2)
; 0x0F0..0x0FF, 0x170..0x17F , 0x1F0..0x1FF are mapped to 0x70..0x7F (same in all banks)
; So use 0x70..0x7F for context saving in the PIC16F628 and forget 0x0F0.. 0xNNN !
;
; Note on the 32-bit integer arithmetics as used in this code:
; - They begin with MOST SIGNIFICANT BYTE in memory, but...
; - Every byte location has its own label here, which makes debugging
; with Microchip's simulator much easier (point the mouse on the name
; of a variable to see what I mean !)
;
tens_index equ 0x27 ; index into the powers-of-ten table
divi equ 0x28 ; power of ten (32 bits)
divi_hi equ 0x28 ; same as 'divi' : HIGH byte
divi_mh equ 0x29 ; MEDIUM HIGH byte
divi_ml equ 0x2A ; MEDIUM LOW byte
divi_lo equ 0x2B ; LOW byte
timer0_old equ 0x2C ; previous reading from timer0 register
gatecnt_hi equ 0x2D ; 16-bit counter (msb first)
gatecnt_lo equ 0x2E ; 16-bit counter (lsb last)
bTemp equ 0x2F ; temporary 8-bit register,
; may be overwritten in ALL subroutines
freq equ 0x30 ; frequency in binary (32 bits)....
freq_hi equ 0x30 ; same location, begins with HIGH byte
freq_mh equ 0x31 ; ... medium high byte
freq_ml equ 0x32 ; ... medium low byte
freq_lo equ 0x33 ; ... low byte
freq2 equ 0x34 ; frequency too, copied for programming mode
freq2_hi equ 0x34 ; same location, begins with HIGH byte
freq2_mh equ 0x35 ; ... medium high byte
freq2_ml equ 0x36 ; ... medium low byte
freq2_lo equ 0x37 ; ... low byte
foffs equ 0x38 ; frequency too, copied for programming mode
foffs_hi equ 0x38 ; same location, begins with HIGH byte
foffs_mh equ 0x39 ; ... medium high byte
foffs_ml equ 0x3A ; ... medium low byte
foffs_lo equ 0x3B ; ... low byte
menu_index equ 0x3C ; menu item for programming mode
menu_timer equ 0x3D ; used to detect how long a key was pressed
digits equ 0x40 ; frequency as decimal digits (8 bytes)...
digit_0 equ 0x40 ; same location as MOST SIGNIFICANT digit, 10-MHz
digit_1 equ 0x41 ; usually the 1-MHz-digit
digit_2 equ 0x42 ; usually the 100-kHz-digit
digit_3 equ 0x43 ; usually the 10-kHz-digit
digit_4 equ 0x44 ; usually the 1-kHz-digit
digit_5 equ 0x45 ; usually the 100-Hz-digit
digit_6 equ 0x46 ; usually the 10-Hz-digit
digit_7 equ 0x47 ; usually the 1-Hz-digit
digit_8 equ 0x48 ; must contain a blank character (or trailing zero)
display0 equ 0x49 ; display #0 data
display1 equ 0x4A ; display #1 data
display2 equ 0x4B ; display #2 data
display3 equ 0x4C ; display #3 data
display4 equ 0x4D ; display #4 data
disp_index equ 0x4E ; index of the enabled display (0 to 4 for 5-digit display)
disp_timer equ 0x4F ; display multiplex timer (5 bits)
adjust_shifts equ 0x50 ; count of 'left shifts' to compensate prescaler+gate time
blinker equ 0x51 ; prescaler for the flashing 1-kHz-dot
psave_timer equ 0x52 ; timer for power-save mode (incremented every 0.25 seconds)
psave_freq_lo equ 0x53 ; low-byte of frequency to detect changes for power-save mode
psave_flags equ 0x54 ; power-saving flags with the following bits:
; clear:normal mode, set:power-saving in action (display blanked)
#define PSFLAG_ACTIVE psave_flags,0
options equ 0x55 ; display options with the following flag-bits:
; clear:normal mode, set:power-saving mode enabled
#define OPT_PWRSAVE options,0
;**************************************************************************
; *
; Macros (1) *
; *
;**************************************************************************
eep_dw MACRO value ; a DOUBLEWORD split into 4 bytes in the PIC's DATA EEPROM
db (value>>24),(value>>16) and 0xFF,(value>>8) and 0xFF,value and 0xFF
ENDM
;**************************************************************************
; *
; EEPROM memory definitions *
; *
;**************************************************************************
; for PIC16F84: 0x00..0x3F were valid EEPROM locations (64 byte)
; for PIC16F628: 0x00..0x7F are valid EEPROM locations (128 byte)
; EEPROM location for frequency offset
#define EEPROM_ADR_FREQ_OFFSET 0x00
; EEPROM location for standard IF table (4*4 byte)
#define EEPROM_ADR_STD_IF_TABLE 0x04
; EEPROM location for "options" (flags)
#define EEPROM_ADR_OPTIONS 0x20
; Initial contents of DATA EEPROM:
; PSECT edata set the current position to first EEPROM byte position
PSECT edata
; CALL eep_dw (32 bit write macro: 4 byte for each call)
; start from EEPROM_ADR_FREQ_OFFSET
eep_dw 0 ; [00..03] initial frequency offset = ZERO
; start from EEPROM_ADR_STD_IF_TABLE
eep_dw 455000 ; [04..07] frequently used in old AM radios
eep_dw 3999000 ; [08..0B] used in "Miss Mosquita" (DK1HE / DL QRP AG)
eep_dw 4194304 ; [0C..0F] used in other homebrew amateur radio receivers
eep_dw 4433619 ; [10..13] sometimes used in homebrew amateur radio receivers
eep_dw 10695000 ; [14..17] common CB Radio IF (Galaxy Pluto, President Jackson, ecc...)
eep_dw 10700000 ; [18..1F] frequently used in old FM radios
; start from EEPROM_ADR_OPTIONS
db 0 ; [20] "options" (flags), cleared by default
;**************************************************************************
; *
; Ex OPCODE MPLAB 8 Macros *
; *
;**************************************************************************
; skpnc
skpnc macro
btfsc CARRY
endm
; skpc
skpc macro
btfss CARRY
endm
; skpz
skpz macro
btfss ZERO
endm
; skpnz
skpnz macro
btfsc ZERO
endm
; tstf
tstf macro addr
movf addr
endm
; clrc
clrc macro
bcf STATUS,STATUS_C_POSITION
endm
; bc
bc macro addr
btfsc STATUS,STATUS_C_POSITION
goto addr
endm
; bnc
bnc macro addr
btfss STATUS,STATUS_C_POSITION
goto addr
endm
; bnz
bnz macro addr
btfss STATUS,STATUS_Z_POSITION
goto addr
endm
; setc
setc macro
bsf STATUS,STATUS_C_POSITION
endm
; bz
bz macro addr
btfsc STATUS,STATUS_Z_POSITION
goto addr
endm
;**************************************************************************
; *
; More Macros *
; *
;**************************************************************************
;--------------------------------------------------------------------------
; macros to implement lookup tables - these macros hide the PIC syntax
; used and make the source code more readable
; (YHF: CAUTION - BUT THESE MACROS HIDE SOME VERY NASTY PITFALLS .
; TABLE MUST NOT CROSS PAGE BORDER DUE TO 'ADDWF PCL, f' ! )
;--------------------------------------------------------------------------
cquad macro value
retlw value>>24 ; high byte
retlw (value>>16) and 0xFF ; middle-high byte
retlw (value>>8) and 0xFF ; middle-low byte
retlw value and 0xFF ; low byte
endm
table macro label ; define lookup table
label addwf PCL,f ; caution: this is 'PCL' only, cannot add to the full 'PC' in a PIC !
endm
;--------------------------------------------------------------------------
; add with carry - adds the w register and the carry flag to the file
; register reg, returns the result in <reg> with the carry flag set if overflow
;--------------------------------------------------------------------------
addcwf macro reg
local add1,add2
bnc add1 ; branch if no carry set
addwf reg,f ; add byte
incf reg,f ; add carry
skpnz
setc
goto add2
add1:
addwf reg,f ; add byte
add2:
endm
;--------------------------------------------------------------------------
; subtract with "no-carry" - subtracts the w register and the no-carry flag
; from the file register reg, returns the result in reg with the no carry flag
; set if underflow
;--------------------------------------------------------------------------
subncwf macro reg
local sub1,sub2
bc sub1 ; branch if carry set
subwf reg,f ; subtract byte
skpnz ; subtract no carry
clrc
decf reg,f
goto sub2
sub1:
subwf reg,f ; subtract byte
sub2:
endm
;--------------------------------------------------------------------------
; MACRO to perform 32-bit addition - adds the four bytes at op2 to the
; three bytes at op1 (most significant bytes first), returns the result in
; op1 with op2 unchanged and the carry flag set if overflow
;--------------------------------------------------------------------------
add32 macro op1,op2 ; op1 := op1 + op2
movf op2+3,w ; add low byte (bits 7...0)
addwf op1+3,f
movf op2+2,w ; add middle-low byte (bits 15..8)
addcwf op1+2
movf op2+1,w ; add middle-high byte (bits 23...16)
addcwf op1+1
movf op2+0,w ; add high byte (bits 31...24)
addcwf op1+0
endm
;--------------------------------------------------------------------------
; MACRO to perform 32-bit subtraction - subtracts the four bytes at op2
; from the four bytes at op1 (most significant bytes first), returns the
; result in op1 with op2 unchanged and the no carry flag set if underflow
;--------------------------------------------------------------------------
sub32 macro op1,op2 ; op1 := op1 - op2
movf op2+3,w ; subtract low byte
subwf op1+3,f
movf op2+2,w ; subtract middle low byte
subncwf op1+2
movf op2+1,w ; subtract middle high byte
subncwf op1+1
movf op2+0,w ; subtract high byte
subncwf op1+0
endm
;--------------------------------------------------------------------------
; MACRO to negate a 32-bit value ( op := 0 - op ) .
;--------------------------------------------------------------------------
neg32 macro op ; op1 := 0 - op2
local neg_done
comf op, f ; invert all 8 bits in high byte
comf op+1, f ; invert all 8 bits in middle high byte
comf op+2, f ; invert all 8 bits in middle low byte
comf op+3, f ; invert all 8 bits in low byte
; Note at this point 0x000000 would have turned into 0xFFFFFFF .
; Must add ONE to complete the TWO's COMPLIMENT calculation ( -0 = 0 ).
; Note that "incf" affects only the Z flag but not the C flag .
incfsz op+3, f ; increment low byte (bits 7...0)
goto neg_done ; if incremented result NOT zero, we're through !
incfsz op+2, f ; increment middle low byte (bits 15...8)
goto neg_done ; if incremented result NOT zero, ...
incfsz op+1, f ; increment middle high byte (bits 23...16)
goto neg_done ; if ...
incfsz op+0, f ; increment high byte (bits 31...24)
goto neg_done ;
neg_done:
endm
;**********************************************************************
; Start Program (entry point) resetVec (reset vector) in 0x00
; ex ORG 0x00
; Remember to set "-Wl,-PresetVec=0x0" in the pic-as global options
; (Additional Options) of Project Properties
PSECT resetVec,class=CODE,delta=2
; resetVec label
resetVec:
goto MainInit ; go to beginning of program
; (begin of ROM is too precious to waste for ordinary code, see below...)
;**************************************************************************
; *
; Lookup tables *
; Must be at the start of the code memory to avoid crossing pages !! *
; *
;**************************************************************************
;--------------------------------------------------------------------------
; 7-segment LED data table
;--------------------------------------------------------------------------
; Index 0..9 used for decimal numbers, all other indices defined below :
CHAR_A equ 10 ; Letters A..F = HEX digits, index 10..15
CHAR_b equ 11 ;
CHAR_c equ 12 ;
CHAR_d equ 13 ;
CHAR_E equ 14 ;
CHAR_F equ 15 ;
CHAR_G equ 16 ; Other letters used in "programming" mode
CHAR_H equ 17 ;
CHAR_i equ 18 ;
BLANK equ 19 ; blank display
TEST equ 20 ; power-on display test
CHAR_P equ 21 ; A few other letters for programming mode...
CHAR_r equ 22 ;
CHAR_o equ 23 ; "Prog"
CHAR_Q equ 24 ; "Quit"
CHAR_u equ 25 ;
CHAR_t equ 26 ;
CHAR_S equ 27 ; "Sub"
CHAR_Z equ 28 ; "ZEro"
CHAR_I equ 29 ; large "I" (left aligned!) for "IF"
CHAR_J equ 30 ;
CHAR_k equ 31 ;
CHAR_L equ 32 ;
CHAR_N equ 33 ;
CHAR_V equ 34 ;
CHAR_EQ equ 35 ; "="
#if (DISP_VARIANT==1)
DPPOINT_BIT equ 4 ; decimal point bit (same for all digits)
#define _A 0x01 ; bitmask for segment A , etc ..
#define _B 0x02
#define _C 0x20
#define _D 0x08
#define _E 0x04
#define _F 0x40
#define _G 0x80
#define _DP 0x10
; DISPLAY VARIANT #1
#endif
#if (DISP_VARIANT==2) || (DISP_VARIANT==3)
DPPOINT_BIT equ 1 ; decimal point bit (same for all digits)
; bitmask for segment A , etc ..
#define _A 0x40
#define _B 0x80
#define _C 0x04
#define _D 0x01
#define _E 0x08
#define _F 0x10
#define _G 0x20
#define _DP 0x02
; DISPLAY VARIANT #2 + #3
#endif
BLANK_PATTERN equ 0b00000000 ; blank display pattern (7-segment code)
;-----------------------------------------------------------------------------
; Table to convert a decimal digit or a special character into 7-segment-code
; Note: In DL4YHF's PIC counter, all digits have the same segment connections,
; so we do not need individual conversion tables for all segments.
;
; AAAA
; F B
; F B
; GGGG
; E C
; E C
; DDDD DP
;
;-----------------------------------------------------------------------------
Digit2SevenSeg:
#if (COMMON_ANODE)
; since 2005-03-21 ... never tested by the author !
#define SSEG_XORMASK 0xFF
#else
; for COMMON CATHODE: No bitwise EXOR to the pattern
#define SSEG_XORMASK 0x00
#endif
addwf PCL,F ; caution: this is 'PCL' only, not 'PC'. Beware of page borders.
; A = 0, B = 1, C = 5, D = 3, E = 2, F = 6, G = 7, DP = 4
retlw (_A+_B+_C+_D+_E+_F )^SSEG_XORMASK ; ABCDEF. = '0' ( # 0 )
retlw ( _B+_C )^SSEG_XORMASK ; .BC.... = '1' ( # 1 )
retlw (_A+_B +_D+_E +_G)^SSEG_XORMASK ; AB.DE.G = '2' ( # 2 )
retlw (_A+_B+_C+_D +_G)^SSEG_XORMASK ; ABCD..G = '3' ( # 3 )
retlw ( _B+_C +_F+_G)^SSEG_XORMASK ; .BC..FG = '4' ( # 4 )
retlw (_A +_C+_D +_F+_G)^SSEG_XORMASK ; A.CD.FG = '5' ( # 5 )
retlw (_A +_C+_D+_E+_F+_G)^SSEG_XORMASK ; A.CDEFG = '6' ( # 6 )
retlw (_A+_B+_C )^SSEG_XORMASK ; ABC.... = '7' ( # 7 )
retlw (_A+_B+_C+_D+_E+_F+_G)^SSEG_XORMASK ; ABCDEFG = '8' ( # 8 )
retlw (_A+_B+_C+_D +_F+_G)^SSEG_XORMASK ; ABCD.FG = '9' ( # 9 )
retlw (_A+_B+_C +_E+_F+_G)^SSEG_XORMASK ; ABC.EFG = 'A' ( # 10 )
retlw ( _C+_D+_E+_F+_G)^SSEG_XORMASK ; ..CDEFG = '0b ( # 11 )
retlw ( _D+_E +_G)^SSEG_XORMASK ; ...DE.G = 'c' ( # 12 )
retlw ( _B+_C+_D+_E +_G)^SSEG_XORMASK ; .BCDE.G = 'd' ( # 13 )
retlw (_A +_D+_E+_F+_G)^SSEG_XORMASK ; A..DEFG = 'E' ( # 14 )
retlw (_A +_E+_F+_G)^SSEG_XORMASK ; A...EFG = 'F' ( # 15 )
retlw (_A +_C+_D+_E+_F )^SSEG_XORMASK ; A.CDEF. = 'G' ( # 16 )
retlw ( _B+_C +_E+_F+_G)^SSEG_XORMASK ; .BC.EFG = 'H' ( # 17 )
retlw ( _E )^SSEG_XORMASK ; ....E.. = 'i' ( # 18 )
retlw (BLANK_PATTERN )^SSEG_XORMASK ; ....... = ' ' ( # 19 )
retlw (0b11111111 )^SSEG_XORMASK ; all segments on ( # 20 )
; A few more letters for programming mode :
retlw (_A+_B +_E+_F+_G)^SSEG_XORMASK ; AB..EFG = 'P' ( # 21 )
retlw ( _E +_G)^SSEG_XORMASK ; ....E.G = 'r' ( # 22 )
retlw ( _C+_D+_E +_G)^SSEG_XORMASK ; ..CDE.G = 'o' ( # 23 )
retlw (_A+_B+_C +_F+_G)^SSEG_XORMASK ; ABC..FG = 'Q' ( # 24 )
retlw ( _C+_D+_E )^SSEG_XORMASK ; ..CDE.. = 'u' ( # 25 )
retlw ( _D+_E+_F+_G)^SSEG_XORMASK ; ...DEFG = 't' ( # 26 )
retlw (_A +_C+_D +_F+_G)^SSEG_XORMASK ; A.CD.FG = 'S' ( # 27 )
retlw (_A+_B +_D+_E +_G)^SSEG_XORMASK ; AB.DE.G = 'Z' ( # 28 )
retlw ( _E+_F )^SSEG_XORMASK ; ....EF. = 'I' ( # 29 )
retlw ( _B+_C+_D )^SSEG_XORMASK ; .BCD.. = 'J' ( # 30 )
retlw ( _D+_E+_F+_G)^SSEG_XORMASK ; ...DEFG = 'k' ( # 31 )
retlw ( _D+_E+_F )^SSEG_XORMASK ; ...DEF. = 'L' ( # 32 )
retlw (_A+_B+_C +_E+_F )^SSEG_XORMASK ; ABC.EF. = 'N' ( # 33 )
retlw ( _C+_D+_E+_F )^SSEG_XORMASK ; ..CDEF. = 'V' ( # 34 )
retlw ( _D +_G)^SSEG_XORMASK ; ...D..G = '=' ( # 35 )
;--------------------------------------------------------------------------
; Table to control which 7-segment display is enabled. Displays are usually
; COMMON CATHODE (variants 1+2) so pulled low to enable.
; For DISP_VARIANT=3 (COMMON ANODE), the digit-driving pattern is inverted.
; Input: W = 0 means the MOST SIGNIFICANT DIGIT (the leftmost one), etc.
; Result: VALUE to be written to ENABLE_PORT to activate the digit
;
; NK Modified to use PA6 (RA6) if an external clock module is being used.
;--------------------------------------------------------------------------
Digit2MuxValue:
addwf PCL,f ; caution: this is 'PCL' only, not 'PC'
; Note: If the program counter is affected, a command requires to instruction cycles (=8 osc cycles)
; muliplexer values for DISPLAY VARIANT #1 :
#if (DISP_VARIANT==1)
retlw 0b11110111 ; most significant digit is on PA3 (!)
retlw 0b11111110 ; next less significant dig. on PA0 (!)
retlw 0b11111011 ; next less significant dig. on PA2 (!)
retlw 0b11111101 ; 4th (sometimes the last) digit PA1 (!)
#if (EXTERNAL_CLOCK==0)
retlw 0b11111111 ; 5th (OPTIONAL) least significant digit = NOT (PA3+PA2+PA1+PA0)
#else ; We're using an external oscillator so PA6 is used to drive 5th digit
retlw 0b10111111 ; 5th (OPTIONAL) least significant digit PA6
#endif
; DISPLAY VARIANT #1
#endif
; muliplexer values for DISPLAY VARIANT #2 (5 digits, COMMON CATHODE) :
#if (DISP_VARIANT==2)
retlw 0b11110111 ; most significant digit is on PA3 (!)
retlw 0b11111011 ; next less significant dig. on PA2 (!!)
retlw 0b11111110 ; next less significant dig. on PA0 (!!)
retlw 0b11111101 ; 4th (sometimes the last) digit PA1 (!)
#if (EXTERNAL_CLOCK==0)
retlw 0b11111111 ; 5th (OPTIONAL) least significant digit = NOT (PA3+PA2+PA1+PA0)
; We're using an external oscillator so PA6 is used to drive 5th digit
#else
retlw 0b10111111 ; 5th (OPTIONAL) least significant digit PA6
#endif
; DISPLAY VARIANT #2
#endif
; muliplexer values for DISPLAY VARIANT #3 (5 digits, COMMON ANODE) :
#if (DISP_VARIANT==3)
; Modified to cater for when PA6 is driving display digit 5.
#if (EXTERNAL_CLOCK==0)
retlw 0b11111000 ; most significant digit is on PA3 (!)
retlw 0b11110100 ; next less significant dig. on PA2 (!!)
retlw 0b11110001 ; next less significant dig. on PA0 (!!)
retlw 0b11110010 ; 4th (sometimes the last) digit PA1 (!)
retlw 0b11110000 ; 5th (OPTIONAL) least significant digit = NOT (PA3+PA2+PA1+PA0)
; We're using an external oscillator so PA6 is used to drive 5th digit
#else
retlw 0b10111000 ; most significant digit is on PA3 (!)
retlw 0b10110100 ; next less significant dig. on PA2 (!!)
retlw 0b10110001 ; next less significant dig. on PA0 (!!)
retlw 0b10110010 ; 4th (sometimes the last) digit PA1 (!)
retlw 0b11110000 ; 5th (OPTIONAL) least significant digit PA6
#endif
; DISPLAY VARIANT #3
#endif
;--------------------------------------------------------------------------
; Powers-of-ten table (32 bits, most significant byte first)
; Based on an idea by James Hutchby (MadLab, 1996) .
; Modified for 32-bit arithmetic by Wolfgang Buescher (2004).
;--------------------------------------------------------------------------
TensTable:
addwf PCL,f
cquad 10000000 ; 10 million is sufficient for the counter itself
cquad 1000000
cquad 100000
cquad 10000
cquad 1000
cquad 100
cquad 10
cquad 1
;--------------------------------------------------------------------------
; DISPLAY jump table for programming mode .
; Loads the display-strings like "quit" etc into the display latches.
; Input parameter: menu_index (0 .. MI_INDEX_MAX)
; Output placed in display0..display3
;
;--------------------------------------------------------------------------
PMDisplay:
movf menu_index, w ; load menu index into W register
addwf PCL, f ; add W to lower part of program counter (computed jump)
goto PmDisp_Quit ; show "quit" (quit programming mode)
goto PmDisp_PSave ; show "PSave"(power-saving mode on/off)
goto PmDisp_Add ; show "add " (add frequency offset)
goto PmDisp_Sub ; show "sub " (subtract frequency offset)
goto PmDisp_Zero ; show "Zero" (set frequency offset to zero)
goto PmDisp_StIF ; show "StdIF" (select standard IF from table)
goto PmDisp_IF_1 ; show 1st standard IF from table
goto PmDisp_IF_2 ; show 2nd standard IF from table
goto PmDisp_IF_3 ; show 3rd standard IF from table
goto PmDisp_IF_4 ; show 4th standard IF from table
goto PmDisp_IF_5 ; show 5th standard IF from table
goto PmDisp_IF_6 ; show 6th standard IF from table
goto PmDisp_Quit ; show "quit" (quit STANDARD IF menu)
; Add more display strings here if needed !
;--------------------------------------------------------------------------
; EXECUTION jump table for programming mode .
; Executes the commands "quit", "psave", "add", "sub", "zero", etc.
; Input parameter: menu_index (0 .. MI_INDEX_MAX)
;--------------------------------------------------------------------------
PMExecute:
; Execute the function belonging to menu_index
movf menu_index, w ; load menu index into W register
addwf PCL, f ; add W to lower part of program counter (computed jump)
goto PmExec_Quit ; quit programming mode
goto PmExec_PSave ; turn power-saving mode on/off
goto PmExec_Add ; add frequency offset from now on
goto PmExec_Sub ; subtract frequency offset from now on
goto PmExec_Zero ; set frequency offset to zero
goto PmExec_StIF ; switch to "Standard IF selection mode"
goto PmExec_SelIF ; select 1st standard IF from table
goto PmExec_SelIF ; select 2nd standard IF from table
goto PmExec_SelIF ; select 3rd standard IF from table
goto PmExec_SelIF ; select 4th standard IF from table
goto PmExec_SelIF ; select 5th standard IF from table
goto PmExec_Quit ; quit STANDARD IF menu
; Add more jumps here if needed !
;**************************************************************************
; *
; Procedures *
; *
;**************************************************************************
;--------------------------------------------------------------------------
; Configure the prescaler for TIMER 0 in the PIC's OPTION register .
;--------------------------------------------------------------------------
; Description of the OPTION register, from the PIC16F628 data sheet:
; bit 7: RBPU: PORTB Pull-up Enable bit
; 1 = PORTB pull-ups are disabled
; 0 = PORTB pull-ups are enabled by individual port latch values
; bit 6: INTEDG: Interrupt Edge Select bit
; 1 = Interrupt on rising edge of RB0/INT pin
; 0 = Interrupt on falling edge of RB0/INT pin
; bit 5: T0CS: TMR0 Clock Source Select bit
; 1 = Transition on RA4/T0CKI pin
; 0 = Internal instruction cycle clock (CLKOUT)
; bit 4: T0SE: TMR0 Source Edge Select bit
; 1 = Increment on high-to-low transition on RA4/T0CKI pin
; 0 = Increment on low-to-high transition on RA4/T0CKI pin
; bit 3: PSA: Prescaler Assignment bit
; 1 = Prescaler is assigned to the WDT
; 0 = Prescaler is assigned to the Timer0 module
; bit 2-0: PS2:PS0: Prescaler Rate Select bits, here shown for TMR0 :
; 000 = 1 : 2
; ... 111 = 1 : 256
; Note: to count EVERY pulse (1 : 1) with TMR0, the prescaler
; must be assigned to the WATCHDOG TIMER (WDT) !
; Some examples (for the OPTION register, parameter in W for SetPrescaler):
PSC_DIV_BY_2 equ 0b00100000 ; let prescaler divide TMR0 by two
PSC_DIV_BY_4 equ 0b00100001 ; let prescaler divide TMR0 by 4
PSC_DIV_BY_8 equ 0b00100010 ; let prescaler divide TMR0 by 8
PSC_DIV_BY_16 equ 0b00100011 ; let prescaler divide TMR0 by 16
PSC_DIV_BY_32 equ 0b00100100 ; let prescaler divide TMR0 by 32
PSC_DIV_BY_64 equ 0b00100101 ; let prescaler divide TMR0 by 64
PSC_DIV_BY_128 equ 0b00100110 ; let prescaler divide TMR0 by 128
PSC_DIV_BY_256 equ 0b00100111 ; let prescaler divide TMR0 by 256
SetPrescaler:
; copy W into OPTION register, avoid watchdog trouble
clrwdt ; recommended by Microchip ("switching prescaler assignment")
bsf STATUS, STATUS_RP0_POSITION ;! setting STATUS_RP0_POSITION enables access to OPTION reg
; option register is in bank1. i know. thanks for the warning.
movwf OPTION_REG ;! ex: "option" command (yucc)
bcf STATUS, STATUS_RP0_POSITION ;! clearing STATUS_RP0_POSITION for normal register access
retlw 0
PrescalerOff:
; turn the prescaler for TMR0 "off"
; (actually done by assigning the prescaler to the watchdog timer)
clrwdt ; clear watchdog timer
clrf TMR0 ; clear timer 0 AND PRESCALER(!)
bsf STATUS, STATUS_RP0_POSITION ;! setting STATUS_RP0_POSITION enables access to OPTION reg
; option register is in bank1. i know. thanks for the warning.
movlw 0b00100111 ;! recommended by Microchip when
;! changing prescaler assignment from TMR0 to WDT
movwf OPTION_REG ;! ex: "option" command (yucc)
clrwdt ;! clear watchdog again
movlw 0b00101111 ;! bit 3 set means PS assigned to WDT now
movwf OPTION_REG ;! ex: "option" command (yucc)
bcf STATUS, STATUS_RP0_POSITION ;! clearing STATUS_RP0_POSITION for normal register access
retlw 0
;--------------------------------------------------------------------------
; Power-saving subroutine: Puts the PIC to sleep for ROUGHLY 100 milliseconds .