⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 counter.asm

📁 PIC16F628A芯片制作的频率计
💻 ASM
📖 第 1 页 / 共 5 页
字号:
        ;          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 + -