PIC Microcontoller IO Method

Pulse Counter

by Scott Dattalo


   cblock   0x20 ;or whatever your ram start is

     total_pulses          ; the number of pulses counted
     current_state         ; bit0 reflects the most recent sampled state
                           ; bit1reflects the most recent filtered state
                           ; bit2 indicates that the debounce filter is running
     steady_state_counter  ; keeps track of the number of consecutive steady
                           ; state samples

   endc


#define    IOPORT   PORTA
#define    IOBIT    0


   ;;--------------------------------------------------------------
   ;; pulse_counter - count pulses on an I/O pin
   ;;
   ;; This routine counts the number of occurances of high pulses
   ;; on the IOBIT of IOPORT (defined above).
   ;;
   ;;
   ;; Must be called at a constant rate and at least 4 times faster
   ;; than the narrowest pulse expected.
   ;; For example, if the narrowest pulse expected is 10 ms then
   ;; this routine should be called at least once every 2.5 ms. 1ms
   ;; would be a good interval.

pulse_counter:

; Define the number of consecutive samples required to measure either
;a high or a low:

#define SAMPLES_FOR_STEADY_STATE  4


    movlw  0                   ;Sample the input
    btfsc  IOPORT,IOBIT
     movlw 1

    xorwf  current_state,w     ;compare to the previous sample
    andlw  1                   ;Clear upper bits of current sample
    skpz                       ;If this sample is different than previous
     goto  detected_a_change   ; then handle below

  ;; no change in last two samples

  ;If we're not filtering, then we can return.
  ;If we are filtering, but the steady state counter hasn't
  ;terminated, then we can also return

    btfsc  current_state,2
     decfsz steady_state_counter,f
      return

  ; We sampled `SAMPLES_FOR_STEADY_STATE' consecutive samples

    bcf    current_state,2     ;We're no longer filtering

    rlf    current_state,w     ;Compare the current filtered state
    xorwf  current_state,w     ;with the previous filtered state
    andlw  2                   ;Just look at the differences
    xorwf  current_state,f     ;and save the current as the previous
    andlw  2                   ;again, look at the differences
    skpz                       ;If there are no differences
     btfss current_state,1     ;or the current filtered state is low
      return                   ;then we found a low pulse


  ; We found a high pulse. Count it!
    incf   pulse_counter,f

  ; This would be a good place to check the pulse counter...

    return

detected_a_change:
    xorwf  current_state,f     ;save this sample for next time

    movlw  SAMPLES_FOR_STEADY_STATE  ; Initialize the steady state counter
    movwf  steady_state_counter

    bsf    current_state,2     ; Set a flag so we know we're filtering

    return


This has not been tested...
  cblock

     sample    ; The most significant bit contains the most recently filtered
               ;state.  The other bits contain the last N samples, where N is
               ;the filter width. (and N must be less than 8).

     pulse_counter  ; each time a pulse is discovered, this is incremented


  endc


;--------------------------
; constants:
;

; Number of consecutive samples of the same state required to declare
;an input as filtered

#define STEADY_STATE_SAMPLES  4


; The FILTER_MASK is a constant with the least significant
;STEADY_STATE_SAMPLES bits set. e.g. if STEADY_STATE_SAMPLES is 4
;then the FILTER_MASK is (1<<4)-1 = 16-1 = 15 = 00001111b

#define FILTER_MASK           ( (1 << STEADY_STATE_SAMPLES) - 1)


count_pulses:
     clrc                       ;Copy the I/O pin state into the carry
     btfsc   IOPORT,IOBIT       ;First, it's assumed the io pin is low
      setc                      ;If it isn't then we set the carry bit
                                ;NOTE, if the I/O pin is either the most
                                ;or least significant bit, then a rlf
                                ;or rrf instruction accomplishes the
                                ;same thing in 2 fewer cycles.

   ; The next 4 instructions copy the new sample to the lsb and shifts
   ;the previous samples left one position. However, the msb (which
   ;contains the filtered state) is left unchanged.
   ; starting with sample == abcdefg and C=s (carry is equal to newest
   ;sample):

     rlf     sample,f           ; bcdefghs C=a
     rlf     sample,f           ; cdefghsa C=b
     rrf     sample,w           ; cdefghsa C=a (W contains bcdefghs)
     rrf     sample,f           ; acdefghs C=a

     andlw   FILTER_MASK        ;examine the last N consecutive samples
     skpnz                      ;If they're all zero
      bcf    sample,7           ; then the filtered state is low.

     xorlw   FILTER_MASK        ;But we're really interested in highs
                                ;If we complement all of the bits
                                ;and the result is zero, then that
                                ;means the last N samples were high.
     skpnz                      ;If any of the last N were low or if
      btfsc  sample,7           ; they're all high but the filter is
       return                   ;  already high, then we're done

   ;If we get here then a positive pulse has been detected

     incf   pulse_counter,f     ;count it
                                ;
     bsf     sample,7           ;Set the "filtered" flag

     return



See:

Questions: