You can use a timer interrupt. You can use TMR0 or just "piggyback" on Darrel's TMR1 interrupt in the SSPWM routine.

The only caveat is that the period of that interrupt must be shorter than the narrowest pulse (either high or low) that your speedometer sensor puts out.

I have written (and posted) a routine that would do what you want. Mine is written in asm, but could be written in PBP as well.

Below is a section. It measures the speed of 9 fans by watching the tachometers.

A couple of things to note -

It must be written as an ISR.
The interrupt time is determined by PRELOADH and PRELOADL.
The routine runs for an exact number of interrupts before stopping (FanClock is the counter) 1000 is the number here.
So if you interrupt at 1mSec intervals, this routine will run for 1 second before being restarted in the main loop.

To use:

Set your interrupt rate.
Determine how many counts you need to get the accuracy you want (which determines
how long you need to count and therefore, the maximum update rate of your speedometer).

If you get 1 cycle (1 high period and one low period) per foot, and you have a 50% duty cycle, then you will get 88 cycles per second with a high period of 5.6 mSec and a low period of 5.6 mSec. To account for jitter, your interrupt period would have to be 5 mSec or less. You would have 166 transitions (low->high + high -> low) in one second. If you set FanCounter to time out in .3614 seconds (say 100 counts with a 3.614 mSec update rate), the count would be in exact mph with a .3614 second update rate. Longer counting intervals and faster interrupts gives more accuracy and less jitter.


You need clear TPE before you start.
Check TPE in your main loop. If it is set, the routine has completed. Transfer the contents of the FanxCounters to another set of variables (call them FanYcounters, clear all the FanXcounters, Clear FanClock and Clear TPE. Now your main loop can read FanYcounters and display the mph, or RPM, or whatever.

You don't have to stop TMR0 on most chips to reload it. You do have to stop most of the other timers before reloading. Check your datasheet.
The routine runs for an exact number of interrupts before stopping. You need clear TPE before you start.

Code:
Asm                            
ReadTachs
         movff    PreloadH,TMR0H  ; Preload depends on clk speed
         movff    PreloadL,TMR0L
         infsnz  MasterClock
         incf    MasterClock + 1
        
         btfsc   TPE,0
         bra     DoneForNow
        
;CheckFans         
         infsnz  FanClock        
         incf    FanClock + 1
            
         movlw   0x03 - 1          ; Real value is 1000 = 0x3E8, but must have one less
         cpfsgt  FanClock + 1      ; to compare with greater than
         bra     FanRoutine
         movlw   0xE8 - 1          ; Likewise, subtract one here, too.
         cpfsgt  FanClock
         bra     FanRoutine
         bsf     TPE,0
         
         clrf    FanClock
         clrf    FanClock + 1
         bra     DoneForNow
                
FanRoutine
              
         movf    PORTB,0,0
         movwf   Temp,0            ; Save VAR so can't change between compare and save
         xorwf   OldPortB,0,0
         movwf   changedB,0
         movff   Temp,OldPortB
         
         movf    PORTC,0,0
         movwf   Temp,0
         xorwf   OldPortC,0,0
         movwf   changedC,0
         movff   Temp,OldPortC
         
         movf    PORTD,0,0
         movwf   Temp,0
         xorwf   OldPortD,0,0
         movwf   changedD,0
         movff   Temp,OldPortD
       
         
Fan1       
         btfss   changedB,0
         bra     Fan2
         infsnz  Fan1Counter
         incf    Fan1Counter+1
       
Fan2        
         btfss   changedB,1
         bra     Fan3
         infsnz  Fan2Counter
         incf    Fan2Counter+1
                
Fan3        
         btfss   changedB,2
         bra     Fan4
         infsnz  Fan3Counter
         incf    Fan3Counter+1
Fan4        
         btfss   changedB,3
         bra     Fan5
         infsnz  Fan4Counter
         incf    Fan4Counter+1
       
Fan5       
         btfss   changedB,4
         bra     Fan6
         infsnz  Fan5Counter
         incf    Fan5Counter+1        
       
Fan6       
         btfss   changedB,5
         bra     Fan7
         infsnz  Fan6Counter
         incf    Fan6Counter+1 
                 
Fan7        
         btfss   changedC,0
         bra     Fan8
         infsnz  Fan7Counter
         incf    Fan7Counter+1  
           
Fan8        
         btfss   changedD,5
         bra     Fan9
         infsnz  Fan8Counter
         incf    Fan8Counter+1               
              
Fan9        
         btfss   changedD,4
         bra     DoneForNow
         infsnz  Fan9Counter
         incf    Fan9Counter+1  
                     
DoneForNow
        
         bcf     INTCON,2   ; Clear the TIMER0 interrupt flag
       
        INT_RETURN	 		; WAS RETFIE FAST      Return from the interrupt
ENDASM