-
Notifications
You must be signed in to change notification settings - Fork 1
/
counter.asm
2125 lines (1831 loc) · 107 KB
/
counter.asm
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.asm *
; CONTENTS: Simple low-cost digital frequency meter using a PIC 16F628A *
; AUTHOR: Wolfgang Buescher, DL4YHF *
; (based on a work by James Hutchby, MadLab, 1996) *
; REVISIONS: (latest entry first) *
; 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 'b' 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 result for the ANY prescaler *
; division ratio, which may give pretty large numbers . *
; - A PIC16F628 worked up to 63 MHz with this firmware . *
;**************************************************************************
list P=16F628A
#include <p16f628a.inc> ; processor specific definitions
#define DEBUG 0 ; DEBUG=1 for simulation, DEBUG=0 for real hardware
; 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
#define DISP_VARIANT 1 ; very first (old) prototype by DL4YHF
#define COMMON_ANODE 0
#define COMMON_CATHODE 1
#else
#ifdef DISPLAY_VARIANT_2
#define DISP_VARIANT 2 ; 5 digits, new layout, COMMON CATHODE
#define COMMON_ANODE 0
#define COMMON_CATHODE 1
#else
#ifdef DISPLAY_VARIANT_3 ; added 2005-03-21 :
#define DISP_VARIANT 3 ; similar as (2), but for COMMON ANODE display
#define COMMON_ANODE 1
#define COMMON_CATHODE 0
#else
#define DISP_VARIANT 2 ; default: 5 digits, new layout, COMMON CATHODE
#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 .asm file.
; The lables following the directive are located in the respective .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 _CP_OFF & _WDT_ON & _PWRTE_ON & _RC_OSC
;NK If using an external clock source, use the _EXTCLK_OSC definition.
#if (EXTERNAL_CLOCK==0)
#if (DISP_VARIANT==1) ; display variant 1 : clocked with 4 MHz (low power, "XT" )
__CONFIG _CP_OFF & _WDT_ON & _PWRTE_ON & _XT_OSC & _LVP_OFF & _BODEN_OFF & _MCLRE_OFF
#else ; display variants 2+3 : clocked with 20 MHz (needs "HS" oscillator)
__CONFIG _CP_OFF & _WDT_ON & _PWRTE_ON & _HS_OSC & _LVP_OFF & _BODEN_OFF & _MCLRE_OFF
#endif
#else
#if (DISP_VARIANT==1) ; display variant 1 : clocked with 4 MHz external oscillator module
__CONFIG _CP_OFF & _WDT_ON & _PWRTE_ON & _EXTCLK_OSC & _LVP_OFF & _BODEN_OFF & _MCLRE_OFF
#else ; display variants 2+3 : clocked with 20 MHz external oscillator module
__CONFIG _CP_OFF & _WDT_ON & _PWRTE_ON & _EXTCLK_OSC & _LVP_OFF & _BODEN_OFF & _MCLRE_OFF
#endif
#endif
; '__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'
; (definitions of "file" registers removed. They are defined in a header file!)
;**************************************************************************
; *
; Port assignments *
; *
;**************************************************************************
PORT_A_IO equ b'0000' ; port A I/O mode (all output)
PORT_B_IO equ b'00000000' ; 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)
#if (DISP_VARIANT==1) ; display variant 1 : clocked with 4 MHz (low power consumption)
CLOCK equ .4000000
#else ; display variants 2+3 : clocked with 20 MHz (higher resolution)
CLOCK equ .20000000
#endif
; microseconds per timing loop
#if (DISP_VARIANT==1) ; display variant 1 : clocked with 4 MHz
; 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
#else ; display variants 2+3 : clocked with 20 MHz
; 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
#endif ; variant 1 or 2+3 ?
; 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 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:
#define PSFLAG_ACTIVE psave_flags,0 ; clear:normal mode, set:power-saving in action (display blanked)
options equ 0x55 ; display options with the following flag-bits:
#define OPT_PWRSAVE options,0 ; clear:normal mode, set:power-saving mode enabled
;**************************************************************************
; *
; Macros (1) *
; *
;**************************************************************************
eep_dw macro value ; a DOUBLEWORD split into 4 bytes in the PIC's DATA EEPROM
de (value>>.24), (value>>.16)&0xFF, (value>>8)&0xFF, value&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)
#define EEPROM_ADR_FREQ_OFFSET 0x00 ; EEPROM location for frequency offset
#define EEPROM_ADR_STD_IF_TABLE 0x04 ; EEPROM location for standard IF table (4*4 byte)
#define EEPROM_ADR_OPTIONS 0x20 ; EEPROM location for "options" (flags)
; Initial contents of DATA EEPROM:
org (0x2100 + 2*EEPROM_ADR_FREQ_OFFSET)
eep_dw .0 ; [00..03] initial frequency offset = ZERO
org (0x2100 + 2*EEPROM_ADR_STD_IF_TABLE) ; standard 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 .10700000 ; [14..17] frequently used in old FM radios
; [18..1F] reserved for other "preprogrammed" values
org (0x2100 + 2*EEPROM_ADR_OPTIONS)
de .0 ; [20] "options" (flags), cleared by default
;**************************************************************************
; *
; 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)&0xFF ; middle-high byte
retlw (value>>8)&0xFF ; middle-low byte
retlw value&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
movfw op2+3 ; add low byte (bits 7...0)
addwf op1+3,f
movfw op2+2 ; add middle-low byte (bits 15..8)
addcwf op1+2
movfw op2+1 ; add middle-high byte (bits 23...16)
addcwf op1+1
movfw op2+0 ; 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
movfw op2+3 ; subtract low byte
subwf op1+3 , f
movfw op2+2 ; subtract middle low byte
subncwf op1+2
movfw op2+1 ; subtract middle high byte
subncwf op1+1
movfw op2+0 ; 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
;**********************************************************************
ORG 0x000 ; processor reset vector
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
#endif ; DISPLAY VARIANT #1
#if (DISP_VARIANT==2) || (DISP_VARIANT==3)
DPPOINT_BIT equ 1 ; decimal point bit (same for all digits)
#define _A 0x40 ; bitmask for segment A , etc ..
#define _B 0x80
#define _C 0x04
#define _D 0x01
#define _E 0x08
#define _F 0x10
#define _G 0x20
#define _DP 0x02
#endif ; DISPLAY VARIANT #2 + #3
BLANK_PATTERN equ b'00000000' ; 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:
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
#if (COMMON_ANODE)
#define SSEG_XORMASK 0xFF ; since 2005-03-21 ... never tested by the author !
#else
#define SSEG_XORMASK 0x00 ; for COMMON CATHODE: No bitwise EXOR to the pattern
#endif
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 = 'b' ( # 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 (b'11111111' )^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)
#if (DISP_VARIANT==1) ; muliplexer values for DISPLAY VARIANT #1 :
retlw b'11110111' ; most significant digit is on PA3 (!)
retlw b'11111110' ; next less significant dig. on PA0 (!)
retlw b'11111011' ; next less significant dig. on PA2 (!)
retlw b'11111101' ; 4th (sometimes the last) digit PA1 (!)
#if (EXTERNAL_CLOCK==0)
retlw b'11111111' ; 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 b'10111111' ; 5th (OPTIONAL) least significant digit PA6
#endif
#endif ; DISPLAY VARIANT #1
#if (DISP_VARIANT==2) ; muliplexer values for DISPLAY VARIANT #2 (5 digits, COMMON CATHODE) :
retlw b'11110111' ; most significant digit is on PA3 (!)
retlw b'11111011' ; next less significant dig. on PA2 (!!)
retlw b'11111110' ; next less significant dig. on PA0 (!!)
retlw b'11111101' ; 4th (sometimes the last) digit PA1 (!)
#if (EXTERNAL_CLOCK==0)
retlw b'11111111' ; 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 b'10111111' ; 5th (OPTIONAL) least significant digit PA6
#endif
#endif ; DISPLAY VARIANT #2
#if (DISP_VARIANT==3) ; muliplexer values for DISPLAY VARIANT #3 (5 digits, COMMON ANODE) :
; Modified to cater for when PA6 is driving display digit 5.
#if (EXTERNAL_CLOCK==0)
retlw b'11111000' ; most significant digit is on PA3 (!)
retlw b'11110100' ; next less significant dig. on PA2 (!!)
retlw b'11110001' ; next less significant dig. on PA0 (!!)
retlw b'11110010' ; 4th (sometimes the last) digit PA1 (!)
retlw b'11110000' ; 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 b'10111000' ; most significant digit is on PA3 (!)
retlw b'10110100' ; next less significant dig. on PA2 (!!)
retlw b'10110001' ; next less significant dig. on PA0 (!!)
retlw b'10110010' ; 4th (sometimes the last) digit PA1 (!)
retlw b'11110000' ; 5th (OPTIONAL) least significant digit PA6
#endif
#endif ; DISPLAY VARIANT #3
;--------------------------------------------------------------------------
; 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:
movfw menu_index ; 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_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
movfw menu_index ; 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_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 b'00100000' ; let prescaler divide TMR0 by two
PSC_DIV_BY_4 equ b'00100001' ; let prescaler divide TMR0 by 4
PSC_DIV_BY_8 equ b'00100010' ; let prescaler divide TMR0 by 8
PSC_DIV_BY_16 equ b'00100011' ; let prescaler divide TMR0 by 16
PSC_DIV_BY_32 equ b'00100100' ; let prescaler divide TMR0 by 32
PSC_DIV_BY_64 equ b'00100101' ; let prescaler divide TMR0 by 64
PSC_DIV_BY_128 equ b'00100110' ; let prescaler divide TMR0 by 128
PSC_DIV_BY_256 equ b'00100111' ; let prescaler divide TMR0 by 256
SetPrescaler: ; copy W into OPTION register, avoid watchdog trouble
clrwdt ; recommended by Microchip ("switching prescaler assignment")
errorlevel -302 ; Turn off banking message for the next few instructions..
bsf STATUS, RP0 ;! setting RP0 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, RP0 ;! clearing RP0 for normal register access
errorlevel +302 ; Enable banking message again
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(!)
errorlevel -302 ; Turn off banking message for the next few instructions..
bsf STATUS, RP0 ;! setting RP0 enables access to OPTION reg
; option register is in bank1. i know. thanks for the warning.
movlw b'00100111' ;! recommended by Microchip when
;! changing prescaler assignment from TMR0 to WDT
movwf OPTION_REG ;! ex: "option" command (yucc)
clrwdt ;! clear watchdog again
movlw b'00101111' ;! bit 3 set means PS assigned to WDT now
movwf OPTION_REG ;! ex: "option" command (yucc)
bcf STATUS, RP0 ;! clearing RP0 for normal register access
errorlevel +302 ; Enable banking message again
retlw 0
;--------------------------------------------------------------------------
; Power-saving subroutine: Puts the PIC to sleep for ROUGHLY 100 milliseconds .
; - crystal oscillator turned OFF during this phase
; - only the internal RC-oscillator for the watchdog keeps running
; - expiration of watchdog during sleep does NOT reset the PIC,
; only wakes it up again so normal operation may resume
; - LED display will be off during this time
;--------------------------------------------------------------------------
Sleep150ms: ; go to sleep for approx. 150 milliseconds, and then RETURN (no reset)
; Details on the PIC's watchdog timer (from PIC16F628 datasheet) :
; > The WDT has a nominal timeout period of 18 ms (with
; > no prescaler). The timeout periods vary with temperature,
; > VDD and process variations from part to part (see
; > DC specs).
; > The Watchdog Timer is a free running on-chip RC oscillator which does
; > not require any external components. This RC oscillator is separate
; > from the ER oscillator of the CLKIN pin. That means that the WDT will run,
; > even if the clock on the OSC1 and OSC2 pins of the device has been stopped,
; > for example, by execution of a SLEEP instruction.
; > During normal operation, a WDT timeout generates a device RESET.
; > If the device is in SLEEP mode, a WDT timeout causes the device to wake-up
; > and continue with normal operation.
; > The WDT can be permanently disabled by programming the configuration bit
; > WDTE as clear .
; In other words, to use the watchdog-timer for "temporary sleep" here ,
; it must be ENABLED in the configuration word when programming the PIC.
; (because its not possible to turn it on via software if it's not on).
; But once the watchdog timer is ON, it must be FED periodically otherwise
; it will reset the PIC during normal operation !
; Here (in the frequency counter), the prescaler remains assigned to timer0
; so the watchdog interval is ~ 18 milliseconds (+/-, RC-oscillator) .
; > The CLRWDT and SLEEP instructions clear the WDT and the postscaler,
; > if assigned to the WDT, and prevent it from timing out and generating
; > a device RESET. The TO bit in the STATUS register will be cleared upon
; > a Watchdog Timer timeout.
#if(COMMON_CATHODE) ; display with COMMON CATHODE :
movlw 0x00 ; segment drivers LOW to turn off
#else ; not COMMON CATHODE but COMMON ANODE:
movlw 0xFF ; segment drivers HIGH to turn off
#endif
movwf LEDS_PORT ; turn LED segments off
; Note: The global interrupt-enable flag (GIE) is off in this application !
; To avoid unintended wake-up on 'interrupt' (port level change),
; disable all interrupt-SOURCES: Clear T0IE,INTE,RBIE,PEIE too :
clrf INTCON ; disable all interrupts during SLEEP mode
clrwdt ; clear watchdog timer
clrf TMR0 ; clear timer 0 AND PRESCALER(!)
errorlevel -302 ; Turn off banking message for the next few instructions..
bsf STATUS, RP0 ;! setting RP0 enables access to OPTION reg
; option register is in bank1. i know. thanks for the warning.
movlw b'00101011' ;! assign PS to WDT; divide by 8 FOR WDT(!)
movwf OPTION_REG ;! ex: "option" command (yucc)
bcf STATUS, RP0 ;! clearing RP0 for normal register access
errorlevel +302 ; Enable banking message again
sleep ; sleep for approx 18 ms (one watchdog interval)
; The SLEEP command clears the Watchdog Timer and stops the main oscillator.
; Only the internal watchdog timer keeps running.
; The WDT is is also cleared when the device wakes-up from SLEEP,
; regardless of the source of wake-up, so no need for 'clrwdt' here !
nop ; arrived here, slept for ~ 8 times 18 milliseconds
return ; end Sleep150ms
;--------------------------------------------------------------------------
; Convert a character into LEDs data for the 7-segment displays, fed with
; the character in w. Bit 7 set means 'decimal point AFTER this digit' .
;--------------------------------------------------------------------------
conv macro display ; macro for duplicate code
movwf display ; save decimal point bit (msb)
andlw 7fh ; mask bit
call Digit2SevenSeg ; convert digit into 7-segment-code via table
btfsc display,7 ; check bit 7 = decimal point ?
#if(COMMON_CATHODE)
iorlw 1<<DPPOINT_BIT ; include decimal point if bit 7 set (bitwise OR)
#else ; not COMMON CATHODE but COMMON ANODE: decimal point must be 'AND'ed to pattern:
andlw (1<<DPPOINT_BIT)^0xFF ; include decimal point if bit 7 set (bitwise AND)
#endif
movwf display ; set display data register
endm
conv_char0: ; display digit #0 (leftmost, or MOST SIGNIFICANT digit)
conv display0
retlw 0
conv_char1: ; display #1
conv display1
retlw 0
conv_char2: ; display #2
conv display2
retlw 0
conv_char3: ; display #3
conv display3
retlw 0
conv_char4: ; display #4 (rightmost, or LEAST SIGNIFICANT digit, "ones")
conv display4
retlw 0
;--------------------------------------------------------------------------
; Fill the 5-digit display latch with blank characters
;--------------------------------------------------------------------------
ClearDisplay:
movlw BLANK_PATTERN
movwf display0
movwf display1
movwf display2
movwf display3
movwf display4
retlw 0
;--------------------------------------------------------------------------
; Save a single Byte in the PIC's Data-EEPROM.
; Input parameters:
; INDF = *FSR contains byte to be written (was once EEDATA)
; w contains EEPROM address offset (i.e. "destination index")
;
;--------------------------------------------------------------------------
; write to EEPROM data memory as explained in the 16F628 data sheet.
; EEDATA and EEADR must have been set before calling this subroutine
; (optimized for the keyer-state-machine).
; CAUTION : What the lousy datasheet DS40300B wont tell you: