📄 counter.asm
字号:
; bank0 to access EEDATA is rubbish (DS40300B page 93 example 13-1).
EEPROM_ReadByte: ; read ONE byte from the PIC's data EEPROM
movwf bTemp ; save W
bcf INTCON, GIE ; disable INTs
errorlevel -302 ; Turn off banking message for the next few instructions..
bsf STATUS, RP0 ; Bank1 for ***ALL*** EEPROM registers in 16F628 (!)
movwf EEADR ;! write into EEPROM address register
bsf EECON1, RD ;! set "Read"-Flag for EEPROM
; why is EECON1.RD not cleared in MPLAB-sim ?!?
movf EEDATA, w ;! read byte from EEPROM latch
bcf STATUS, RP0 ;! normal access to Bank0
errorlevel +302 ; Enable banking message again
; bsf INTCON, GIE ; re-enable interrupts ? NOT IN THIS APPLICATION !
movwf INDF ; place result in *FSR
movfw bTemp ; restore W
return ; back to caller
; end EEPROM_ReadByte
EEPROM_Read4Byte: ; read FOUR bytes from the PIC's data EEPROM.
; Input parameters:
; w contains EEPROM address offset (i.e. "source index")
; will *NOT* be modified to simplify block-read .
; FSR points to the memory location where the byte shall be placed.
call EEPROM_ReadByte ; *FSR = EEPROM[w] (usually bits 31..24)
addlw 1 ; next source address
incf FSR , f ; next destination address
call EEPROM_ReadByte ; *FSR = EEPROM[w] (usually bits 23..16)
addlw 1 ; next source address
incf FSR , f ; next destination address
call EEPROM_ReadByte ; *FSR = EEPROM[w] (usually bits 15..8)
addlw 1 ; next source address
incf FSR , f ; next destination address
goto EEPROM_ReadByte ; *FSR = EEPROM[w] (usually bits 7..0)
; end EEPROM_Read4Byte
;--------------------------------------------------------------------------
; Count pulses, fed with the number of loop iterations for the gate time .
; WHILE counting, the multiplexed LED display is updated .
; Watchdog is fed in this loop !
; Input: Count of gate-time-loops in 'gatecnt_hi'+'gatecnt_lo' (16 bit).
; Returns: The number of pulses in 'freq' (clock cycles in [])
;--------------------------------------------------------------------------
count_pulses:
clrf freq_hi ; clear pulse counter (bits 31..24)
clrf freq_mh ; bits 23..16
clrf freq_ml ; bits 16..8
clrf freq_lo ; bits 7..0
clrf timer0_old ; 'old' value of timer0 to detect toggling MSB
clrf TMR0 ; timer register (PIC's hardware timer, 8 bit)
nop ; 2 instruction cycle delay
nop ; after writing to TMR0 (MPLAB-SIM: set breakpoint + clear stopwatch here)
; --------------- start of critial timing loop >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; The following timing loop must take a well-defined time in total per
; iteration, usually 50 (or 20) microseconds, which can be precisely achieved
; with a 4-MHz-crystal (or 20 MHz for variant 2+3) .
; This gives a basic delay for the frequency counter's gate time .
; The frequency at the input of TIMER 0 (not the prescaler)
; can not exceed f_crystal / 4,
; and every HIGH->LOW transition of bit7 in TIMER0 must be polled here.
; This is safe because ..
; Variant 1: With a 4-MHz-crystal, Timer0 can count up to 1 MHz input,
; MSB toggles every (128/1MHz) = 128 us, polled every 50us -> ok.
; Variant 2: With a 20-MHz-crystal, Timer0 can count up to 4 (not 5?!) MHz input,
; MSB toggles every (128/4MHz) = 32 us, polled every 20us -> ok.
; The numbers in square brackets below are the INSTRUCTION NUMBER within the loop.
; (not the count of oscillator cycles for a single command, which is always 4).
; These values can be checked with the "Stopwatch" function in MPLAB-SIM.
; The goal is to let this loop take EXACTLY <TIME> microseconds (50us or 20us).
count1 movfw disp_index ; [1] get the current digit number (disp_index = 0..4)
call Digit2MuxValue ; [2,3,4,5,6,7] display (6 commands including call+retlw)
movwf bTemp ; [8] save the bit pattern for the multiplexer port
movlw display0 ; [9] get the LED display data for the current digit...
addwf disp_index,w ; [10] add current digit number to address of LED data
movwf FSR ; [11] move address into the PIC's poor 'data pointer'
movfw INDF ; [12] w := *(FSR) use indirection register to read from table
movwf LEDS_PORT ; [13] set the LED segments
movfw bTemp ; [14] get the mupliplexer pattern (hurry, hurry !)
movwf ENABLE_PORT ; [15] set the LED multiplexer
incf disp_timer,f ; [16] increment display-multiplex timer
btfsc disp_timer,6 ; [17] (6-bit prescaler)
incf disp_index,f ; [18] next display if rolled over
bcf disp_timer,6 ; [19] limit disp_timer to 6 bits (!)
movfw disp_index ; [20] limit display index to 0...4
sublw .4 ; [21] subtract #4 - W register -> C=0(!) if result negative (W>4)
btfss STATUS,C ; [22] skip next instruction if C=1 (#4-W >= 0)
clrf disp_index ; [23] if C=0 (disp_index>4) then disp_index=0
; the following fragments of code always take the same number of clock
; cycles to execute, irrespective of whether the skips take place or not .
; Here still in 'count_pulses'.
movfw TMR0 ; [24] read least significant byte of
movwf freq_lo ; [25] pulse counter (bits 7..0)
movlw 1 ; [26] determine if timer 0 has rolled
btfss timer0_old,7 ; [27] over (rolled over if msb was
clrw ; [28] previously set and now isn't)
btfsc freq_lo,7 ; [29]
clrw ; [30]
addwf freq_ml,f ; [31] increment high bytes of pulse counter
skpnc ; [32] if low byte rolled over
incf freq_mh,f ; [33] (mh = "medium high byte" of counter)
; NOTE: we are not modifying freq_hi here !
; Bits 31..24 may be used later when multiplying with some factor
; (2^n) to compensate for the ASYNCHRON PRESCALER !
btfsc freq_mh,7 ; [34] overflow (freq > 7fffffh) ?
goto count3 ; [35] branch if yes
movfw freq_lo ; [36] save previous value from timer 0
movwf timer0_old ; [37]
tstf gatecnt_lo ; [38] check inner gate-time counter, LOW byte
skpnz ; [39] only decrement h-byte if l-byte zero
decf gatecnt_hi,f ; [40] decrement gate-time counter, HIGH byte
decf gatecnt_lo,f ; [41] always decrement gate-time counter, LOW byte
#if (DISP_VARIANT==1) ; only 50 instruction cycles per loop in DISPLAY VARIANT 1 (f_xtal=4 MHz, t_loop=50us)
; Got some instruction cycles left ? Insert a few NOPs to bring to total loop time to 50us.
clrwdt ; [42] (ex: nop, but since 2006-05-28 the dog must be fed !)
nop ; [43]
nop ; [44]
nop ; [45] ugh, what a waste of precious CPU power ;-)
movfw gatecnt_hi ; [46] counter = 0 ?
iorwf gatecnt_lo,w ; [47]
skpz ; [48]
goto count1 ; [49,50] goto always takes TWO instruction cycles
#else ; For VARIANTS 2+3 : 100 instruction cycles per loop
; (f_xtal=20 MHz, t_loop=20us, t_instr=4/20MHz=0.2us)
; Some time may be used for a nice software-based PULSE WIDTH MODULATION
; of the display intensity ... or other goodies/gimmicks one fine day !
clrwdt ; [42] (ex: nop, but since 2006-05-28 the dog must be fed !)
movlw .12 ; [43] load additional delay loops (X=12, see below) into W
WasteT1: addlw 0xFF ; [44, 48, .. ]
btfss STATUS, Z ; [45, 49, .. ] eats 4(!) INSTRUCTION CYCLES per loop
goto WasteT1 ; [46+47,50+51, .. ]
; Check this with MPLAB-SIM: here, after loop: [43 + 4*X], with X=12: [91]
nop ; [91]
nop ; [92]
nop ; [93]
nop ; [94]
nop ; [95]
movfw gatecnt_hi ; [96] counter = 0 ?
iorwf gatecnt_lo,w ; [97]
skpz ; [98]
goto count1 ; [99,50] goto always takes TWO instruction cycles
#endif ; variant 1 or variant 2/3 ?
; <<<<<<<<<<<<<<<<<<<<<<<< end of timing loop -----------------------------
movfw TMR0 ; get final value from timer 0
movwf freq_lo
movlw 1 ; determine if timer 0 has rolled
btfss timer0_old,7 ; over (rolled over if msb was
clrw ; previously set and now isn't)
btfsc freq_lo,7
clrw
addwf freq_ml,f ; increment high bytes of pulse
skpnc ; counter if low byte rolled
incf freq_mh,f ; over
count3 retlw 0
; end of routine 'count_pulses'. Result now in freq_lo..freq_hi.
;--------------------------------------------------------------------------
; Convert *FSR (32 bit) into BCD and show it on the display .
; Input : INDF = *FSR, 32-bit integer.
; Bad side effect : CONTENTS OF <freq> will be lost !!
;--------------------------------------------------------------------------
ShowInt32_FSR ; Convert <*FSR> (32 bit integer) to 8 BCD-digits ...
movfw INDF ; W := *FSR , load LOW byte
incf FSR , f ; FSR := FSR + 1
movwf freq ; freq.hi := W
movfw INDF ; W := *FSR , load MIDDLE LOW byte
incf FSR , f ; FSR := FSR + 1
movwf freq+1 ; freq.mh := W
movfw INDF ; W := *FSR , load MIDDLE HIGH byte
incf FSR , f ; FSR := FSR + 1
movwf freq+2 ; freq.ml := W
movfw INDF ; W := *FSR , load HIGH byte
incf FSR , f ; FSR := FSR + 1
movwf freq+3 ; freq.lo := W
; continue with CvtAndDisplayFreq !
;--------------------------------------------------------------------------
; Convert <freq> into BCD and show it on the display .
; Input : freq, 32-bit integer. CONTENTS OF <freq> will be lost !!
;--------------------------------------------------------------------------
CvtAndDisplayFreq ; Convert <freq>(32 bit integer) to 8 BCD-digits ...
clrf tens_index ; initialise the table index
movlw digits ; initialise the indirection register
movwf FSR ; ( FSR="pointer"; *FSR=INDF)
conv1 ; Loop for ALL POWERS OF TEN in the lookup table..
clrwdt ; feed the watchdog (may stay a bit longer)
movfw tens_index ; fetch the next power of ten
call TensTable ; (32 bits) from the lookup table
movwf divi+0 ; and store in divi
incf tens_index , f ; this was the HIGH byte
movfw tens_index
call TensTable
movwf divi+1
incf tens_index , f ; this was the MIDDLE-HIGH byte
movfw tens_index
call TensTable
movwf divi+2
incf tens_index , f ; this was the MIDDLE-LOW byte
movfw tens_index
call TensTable
movwf divi+3
incf tens_index , f ; and this was the LOW-byte of a power of ten
; ex: clrf 0 ; clear the decimal digit .. but address ZERO is called 'INDF' these days !
clrf INDF ; *FSR = 0
conv2 ; Loop to repeatedly subtract divi from freq (32-bit subtract)
; until underflow while incrementing the decimal digit.
sub32 freq,divi ; freq := freq - divi (with divi = 10 power N)
bnc conv3 ;
incf INDF , f ; The RESULT will be written back to freq,
goto conv2 ; in other words 'freq' will be lost !
conv3 add32 freq,divi ; freq := freq+divi; ready for next digit
incf FSR , f ; step to next decimal digit
movlw 8*4 ; 8 x 4-byte entries in TensTable
subwf tens_index,w
bnz conv1
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -