Hello,
I've played around with the code a bit, got it up and running on my EasyPIC7 development board using a 18F45K22 device. It counts, it times, it calculates and it displays (during and after measurement).

Name:  DSCF2326[1].jpg
Views: 3442
Size:  215.7 KB

The code I post here are what's running on my device, if you plan to use another device you must:
A) Replace the CONFIG with correct ones for your device
B) Make sure the PIC runs at 8MHz using whatever oscillator (external x-tal is probably best for timing accuracy)
C) Change the aliases to suit the hardware
D) Change the hardware setup section (TRIS, ANSEL, OSCCON, T1CON etc)

Code:
'--- Device configuration - replace with config for selected target ------------
#CONFIG
    CONFIG FOSC = HSMP	          ; HS oscillator (medium power 4-16 MHz)
    CONFIG  PLLCFG = OFF          ; Oscillator used directly
    CONFIG  PRICLKEN = OFF        ; Primary clock can be disabled by software
    CONFIG  FCMEN = OFF           ; Fail-Safe Clock Monitor disabled
    CONFIG  IESO = OFF            ; Oscillator Switchover mode disabled
    CONFIG  PWRTEN = OFF          ; Power up timer disabled
    CONFIG  BOREN = SBORDIS       ; Brown-out Reset enabled in hardware only (SBOREN is disabled)
    CONFIG  BORV = 190            ; VBOR set to 1.90 V nominal
    CONFIG  WDTEN = ON            ; WDT is always enabled. SWDTEN bit has no effect
    CONFIG  WDTPS = 32768         ; 1:32768
    CONFIG  CCP2MX = PORTC1       ; CCP2 input/output is multiplexed with RC1
    CONFIG  PBADEN = OFF          ; PORTB<5:0> pins are configured as digital I/O on Reset
    CONFIG  CCP3MX = PORTB5       ; P3A/CCP3 input/output is multiplexed with RB5
    CONFIG  HFOFST = ON           ; HFINTOSC output and ready status are not delayed by the oscillator stable status
    CONFIG  T3CMX = PORTC0        ; T3CKI is on RC0
    CONFIG  P2BMX = PORTD2        ; P2B is on RD2
    CONFIG  MCLRE = EXTMCLR       ; MCLR pin enabled, RE3 input pin disabled
    CONFIG  STVREN = ON           ; Stack full/underflow will cause Reset
    CONFIG  LVP = OFF             ; Single-Supply ICSP disabled
    CONFIG  XINST = OFF           ; Instruction set extension and Indexed Addressing mode disabled (Legacy mode)
    CONFIG  DEBUG = OFF           ; Disabled
    CONFIG  CP0 = OFF             ; Block 0 (000800-001FFFh) not code-protected
    CONFIG  CP1 = OFF             ; Block 1 (002000-003FFFh) not code-protected
    CONFIG  CP2 = OFF             ; Block 2 (004000-005FFFh) not code-protected
    CONFIG  CP3 = OFF             ; Block 3 (006000-007FFFh) not code-protected
    CONFIG  CPB = OFF             ; Boot block (000000-0007FFh) not code-protected
    CONFIG  CPD = OFF             ; Data EEPROM not code-protected
    CONFIG  WRT0 = OFF            ; Block 0 (000800-001FFFh) not write-protected
    CONFIG  WRT1 = OFF            ; Block 1 (002000-003FFFh) not write-protected
    CONFIG  WRT2 = OFF            ; Block 2 (004000-005FFFh) not write-protected
    CONFIG  WRT3 = OFF            ; Block 3 (006000-007FFFh) not write-protected
    CONFIG  WRTC = OFF            ; Configuration registers (300000-3000FFh) not write-protected
    CONFIG  WRTB = OFF            ; Boot Block (000000-0007FFh) not write-protected
    CONFIG  WRTD = OFF            ; Data EEPROM not write-protected
    CONFIG  EBTR0 = OFF           ; Block 0 (000800-001FFFh) not protected from table reads executed in other blocks
    CONFIG  EBTR1 = OFF           ; Block 1 (002000-003FFFh) not protected from table reads executed in other blocks
    CONFIG  EBTR2 = OFF           ; Block 2 (004000-005FFFh) not protected from table reads executed in other blocks
    CONFIG  EBTR3 = OFF           ; Block 3 (006000-007FFFh) not protected from table reads executed in other blocks
    CONFIG  EBTRB = OFF           ; Boot Block (000000-0007FFh) not protected from table reads executed in other blocks
#ENDCONFIG

DEFINE OSC 8                                ' We're running at 8MHz. The code is based on that fact.

DEFINE LCD_DREG PORTB                       ' Set LCD Data port
DEFINE LCD_DBIT 0                           ' Set starting Data bit (0 or 4) if 4-bit bus
DEFINE LCD_RSREG PORTB                      ' Set LCD Register Select port
DEFINE LCD_RSBIT 4                          ' Set LCD Register Select bit
DEFINE LCD_EREG PORTB                       ' Set LCD Enable port
DEFINE LCD_EBIT 5                           ' Set LCD Enable bit
DEFINE LCD_BITS 4                           ' Set LCD bus size (4 or 8 bits)
DEFINE LCD_LINES 2                          ' Set number of lines on LCD
DEFINE LCD_COMMANDUS 1500                   ' Set command delay time in us
DEFINE LCD_DATAUS 40                        ' Set data delay time in us


'---------- Variables ------------
Hours           VAR BYTE                    ' Elapsed time, hours
Minutes         VAR BYTE                    ' Elapsed time, minutes
Seconds         VAR WORD                    ' Elapsed time, seconds
ms              VAR WORD                    ' Elapsed time, milliseconds
State           VAR BYTE                    ' State machine variable, keeps track of what we're doing
Distance        VAR WORD                    ' Distance traveled in metres, calculated based on pulsecount
AverageSpeed    VAR WORD                    ' Final result of measurement, in units of 0.1km/h (123=12.3km/h)
Temp            var WORD                    ' Used during calculation
DebounceCount   VAR BYTE                    ' Used to debounce the start/stop button
LCDSequence     VAR BYTE                    ' Used to update the display step-by-step
TimeOverflow    VAR BIT                     ' Gets set if the seconds variable grows too big.
UpdateDisplay   VAR BIT                     ' Flag that is set when it't time to update the LCD

'----------------- Constants ----------------------
Idle            CON 0
Starting        CON 1
Counting        CON 2
Stopping        CON 3
Done            CON 4
Error           CON 5

'------------------- Aliases - change to suit hardware -------------------------
StartStopButton VAR PORTB.6                 ' Low when pressed
ReadyIndicator  VAR LATA.0                  ' High when idle/ready
RunIndicator    VAR LATA.1                  ' High when counting/timing
ErrorIndicator  VAR LATA.2                  ' High on count or time overflow
TMR1ON          VAR T1CON.0                 ' Run/stop bit for TMR1
TMR2ON          VAR T2CON.2                 ' Run/stop bit for TMR2
TMR1IF          VAR PIR1.0                  ' TMR1 interrupt flag
TMR2IF          VAR PIR1.1                  ' TMR2 interrupt flag

'--------------- Target setup - change to suit target device -------------------
ANSELA = 0                                  ' All pins as digital
ANSELB = 0
ANSELC = 0
ANSELD = 0
ANSELE = 0

TRISA  = %11111000                      ' Status LEDs on RC0-RC2
LATA   = %00000000

TRISB  = %01000000                      ' LCD on RB0-RB5, Start/Stop on RB6                     
LATB   = %00000000                      ' All pins low


'OSCCON = %01110010                      ' For 18F1320, Internal oscillator, 8MHz
'T1CON  = %00000010                      ' For 18F1320, External clock on RB6, timer OFF for now.
T1CON = %10000000                       ' For 18F45K22
T2CON  = %00000010                      ' 1:16 prescaler, TMR2 off for now
PR2    = 249                            ' T2 interrupt flag set every 250*16 tick

PAUSE 500

Start:
    State = Idle
    ReadyIndicator = 1
    RunIndicator = 0
    ErrorIndicator = 0
    LCDOUT $FE, 1,   "     Ready"
    LCDOUT $FE, $C0, "Press start/stop"
    
Main:
    Select CASE State
        
        Case Idle
            If StartStopButton = 0 THEN             ' If the button is pressed we
                'LCDOUT $FE, 1                       ' Clear the screen
                Hours = 0
                Minutes = 0
                Seconds = 0
                ms = 0
                TMR1H = 0                           ' reset the pulse count
                TMR1L = 0
                TMR2 = 0                            ' reset the timebase
                TMR1IF = 0                          ' clear the overflow flag
                TMR2IF = 0                          ' clear the timebase tick flag
                TMR1ON = 1                          ' enable the counter to count pulses from the driveshaft
                TMR2ON = 1                          ' enable the 2ms timer tick
                ReadyIndicator = 0                  ' update status LEDs
                RunIndicator = 1
                ErrorIndicator = 0
                TimeOverflow = 0                    ' clear overflow flag
                State = Starting                    ' and move to the next state.
            ENDIF
'---------------------------------------------------------------------------------------------------            
        CASE Starting
            IF TMR2IF THEN                          ' TMR2IF gets set every 2ms
                TMR2IF = 0                          ' reset flag
                GOSUB Update                        ' update our time registers
                
                ' Wait for the button to be released before transitioning to the next state.
                If StartStopButton = 1 THEN         ' Button released?
                    DebounceCount = DebounceCount + 1
                ELSE
                    DebounceCount = 0               ' Button must be released 10 ticks in a row.
                ENDIF
                
                IF DebounceCount = 9 THEN           ' Button has been released for 10 ticks
                    DebounceCount = 0
                    State = Counting                ' Transition to next state.
                ENDIF
            ENDIF
'---------------------------------------------------------------------------------------------------             
        
        Case Counting
            IF TMR2IF THEN                          ' 2ms tick?
                TMR2IF = 0                          ' clear flag and
                GOSUB Update                        ' update our time registers
                
                If StartStopButton = 0 THEN         ' Button pressed?
                    RunIndicator = 0
                    TMR1ON = 0                      ' Disable pulse counter
                    TMR2ON = 0                      ' Disable timer
                    State = Stopping
                ENDIF
             ENDIF
             
             IF TMR1IF THEN                         ' Pulse counter overflow
                TMR1ON = 0                          ' Disable pulse counter
                TMR2ON = 0                          ' Disable timebase
                RunIndicator = 0
                ErrorIndicator = 1
                State = Error             
             ENDIF
             
             ' Here we can do anything else that needs doing while the
             ' system is counting but it's very important that it does
             ' not take longer than 2ms or we will lose time.                    
'---------------------------------------------------------------------------------------------------              
        CASE Stopping
            ' Wait for the button to released before transitioning to the next state,
            If StartStopButton = 1 THEN             ' Is button released?
                DebounceCount = DebounceCount + 1
            ELSE
                DebounceCount = 0
            ENDIF
            
            IF DebounceCount = 10 THEN              ' Button released for duration of debounde time?
                DebounceCount = 0
                State = Done                        ' Transition to next state.
            ENDIF
            
            ' The timer is no longer running so we pause here for the same duration.
            PAUSE 2            
'---------------------------------------------------------------------------------------------------  
        
        CASE Done
            ' This is where we perform the calculations and present the result.
           
            ' Each count from the driveshaft equals 0.6m of car movement.
            ' Lets say the pulse count is 12345 and the seconds count is 503.
            ' We've traveled 12345*0.6=7407 metres in 503 seconds, 7407/503*3.6=53.0km/h
            ' Using the ** operator allows us to effectively multiply by fractions of 65535 so:
            '
            ' 12345 ** 39322 = 7407
            ' 7407 * 36 = 266652
            ' 266652 / 503 = 530 which we interpret as 53.0km/h
            '
            ' 53203 pulses in 1412 seconds,
            ' 53203*0.6/1412*3.6=81.39km/h:
            ' 53202 ** 39322 = 31922
            ' 31922 * 36 = 1149192
            ' 1149192 / 1412 = 813 which we interpret as 81.3km/h
            
            Distance.HIGHBYTE = TMR1H
            Distance.lowbyte = TMR1L
            Distance = Distance ** 39322        ' Multiply by 0.6, Distance is now in metres
            
            Temp = Distance * 36
            AverageSpeed = DIV32 Seconds        ' 215 for 21.5km/h
            
            LCDOUT $FE, 1, DEC Distance/1000, ".", DEC3 Distance//1000, "km in ", DEC2 Minutes, ":", DEC2 Seconds
            LCDOUT $FE, $C0, "Speed: ", DEC AverageSpeed/10,".", DEC AverageSpeed//10, "km/h"
            
            ReadyIndicator = 1
            State = Idle 

    
 '------------------------------------------------------------------------------   
        CASE Error
            If TMR1IF THEN                      ' Interrupt flag is set if counter has rolled over
                LCDOUT $FE, 1, "ERROR Pulse cnt"
            ENDIF
            
            IF TimeOverflow THEN                ' Flag set if seconds variable has rolled over
                LCDOUT $FE, 1, "ERROR Timer"
            ENDIF     

            LCDOUT $FE, $C0, "Restart to clear"
            
            State = Idle
    END SELECT

    
Goto Main

'-------------------------------------- Subroutines ------------------------------------------------
Update:
    ' Our timebase is 500Hz.
    ms = ms + 2
    If ms = 1000 then
        ms = 0
        seconds = seconds + 1
        
        IF Seconds // 60 = 0 THEN
            Minutes = Minutes + 1
            IF Minutes = 60 THEN
                Minutes = 0
                Hours = Hours + 1
            ENDIF
        ENDIF

        UpdateDisplay = 1                   ' We're triggering a display update every second         
       
        Distance.HIGHBYTE = TMR1H
        Distance.lowbyte = TMR1L
        Distance = Distance ** 39322        ' Multiply by 0.6, Distance is now in metres
            
        Temp = Distance * 36
        AverageSpeed = DIV32 Seconds        ' 215 for 21.5km/h
        
        ' We're using DIV32 to calculate the average speed. It does not allow the divisor
        ' to be larger than 32767 (which is 9 hours and some minutes so it should be fine)
        ' but since we're good programmers we're going to check for it anyway.
        If Seconds > 32767 THEN
            TimeOverflow = 1
            RunIndicator = 0
            ErrorIndicator = 1
            State = Error
        ENDIF

    ENDIF

    ' Since we have a 2ms time tick and we're polling the that tick with software
    ' we can not allow ourselves to spend too much time doing anything else.
    ' For this reason we've divided the while display update process in sections
    ' so we do it little by little while still going back checking the timer tick
    ' so that we don't lose track of time.
    IF UpdateDisplay THEN
        Select Case LCDSequence
            Case 1
                LCDOUT $FE, 128             ' Cursor at first character, first line        
            CASE 2
                LCDOUT DEC2 Minutes, ":"
            CASE 3
                LCDOUT DEC2 Seconds//60
            CASE 4
                LCDOUT "   "
                IF Distance < 10000 THEN
                    LCDOUT " "
                ENDIF   
            CASE 5
                LCDOUT DEC Distance/1000, ".", DEC3 Distance//1000
            CASE 6
                LCDOUT "km  "
            CASE 7
                LCDOUT $FE, $C0             ' Cursor at second line
            CASE 8
                LCDOUT DEC AverageSpeed/10,".", DEC AverageSpeed//10, "km/h  "
            CASE 9
                LCDOUT "       "
        END SELECT      
    
        LCDSequence = LCDSequence + 1
        IF LCDSequence = 10 THEN
            LCDSequence = 0
            UpdateDisplay = 0
        ENDIF
    ENDIF

RETURN
/Henrik.