Code:
'****************************************************************
'* Name : Speedometer.pbp *
'* Author : Henrik Olsson *
'* Notice : Copyright (c) 2017 [select VIEW...EDITOR OPTIONS] *
'* : All Rights Reserved *
'* Date : 2017-09-13 *
'* Version : 1.0 *
'* Notes : Uses TMR1 configured as a counter to measure *
'* distance traveled and TMR2 as a 2ms timebase to *
'* measure real time. *
'* An active low start/stop button is used to start *
'* and stop the measurement. *
'* *
'* : Target is 18F1320 but running on internal 8MHz *
'* oscillator but should work on any device having *
'* a TMR1 and TMR2 with or without minor changes. *
'* : *
'****************************************************************
'-------------------- Device configuration ----------------------
#CONFIG
CONFIG OSC = INTIO2 ; Internal RC oscillator, port function on RA6 and port function on RA7
CONFIG FSCM = ON ; Fail-Safe Clock Monitor enabled
CONFIG IESO = ON ; Internal External Switchover mode enabled
CONFIG PWRT = OFF ; PWRT disabled
CONFIG BOR = ON ; Brown-out Reset enabled
CONFIG BORV = 27 ; VBOR set to 2.7V
CONFIG WDT = ON ; WDT enabled
CONFIG WDTPS = 512 ; 1:512
CONFIG MCLRE = ON ; MCLR pin enabled, RA5 input pin disabled
CONFIG STVR = ON ; Stack full/underflow will cause Reset
CONFIG LVP = OFF ; Low-Voltage ICSP disabled
CONFIG DEBUG = OFF ; Background debugger disabled, RB6 and RB7 configured as general purpose I/O pins
CONFIG CP0 = OFF ; Block 0 (00200-000FFFh) not code-protected
CONFIG CP1 = OFF ; Block 1 (001000-001FFFh) not code-protected
CONFIG CPB = OFF ; Boot Block (000000-0001FFh) not code-protected
CONFIG CPD = OFF ; Data EEPROM not code-protected
CONFIG WRT0 = OFF ; Block 0 (00200-000FFFh) not write-protected
CONFIG WRT1 = OFF ; Block 1 (001000-001FFFh) not write-protected
CONFIG WRTC = OFF ; Configuration registers (300000-3000FFh) not write-protected
CONFIG WRTB = OFF ; Boot Block (000000-0001FFh) not write-protected
CONFIG WRTD = OFF ; Data EEPROM not write-protected
CONFIG EBTR0 = OFF ; Block 0 (00200-000FFFh) not protected from table reads executed in other blocks
CONFIG EBTR1 = OFF ; Block 1 (001000-001FFFh) not protected from table reads executed in other blocks
CONFIG EBTRB = OFF ; Boot Block (000000-0001FFh) not protected from table reads executed in other blocks
#ENDCONFIG
'---------- Variables ------------
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
TimeOverflow VAR BIT ' Gets set if the seconds variable grows too big.
'---------- Constants -------------
Idle CON 0
Starting CON 1
Counting CON 2
Stopping CON 3
Done CON 4
'------------ Aliases --------------
StartStopButton VAR PortB.0 ' Low when pressed
ReadyIndicator VAR LATB.2 ' High when idle/ready
RunIndicator VAR LATB.3 ' High when counting/timing
ErrorIndicator VAR LATB.4 ' 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
'--------- Hardware setup -----------
ADCON1 = %11111111 ' All pins as digital
LATB = %00000000 ' All pins low
TRISB = %01000001 ' RB0=Button, RB1=TX, RB2=LED, RB3=LED, RB4=LED, RB6=Counter input
OSCCON = %01110010 ' Internal oscillator, 8MHz
T1CON = %00000010 ' External clock on RB6, timer OFF for now.
T2CON = %00000010 ' 1:16 prescaler, TMR2 off for now
PR2 = 249 ' T2 interrupt flag set every 250*16 tick
RCSTA = $90 ' Enable serial port & continuous receive
TXSTA = $20 ' Enable transmit, BRGH = 0
SPBRG = 12 ' 38400 Baud @ 8MHz, 0,16%
SPBRGH = 0
BAUDCTL.3 = 1 ' Enable 16 bit baudrate generator
PAUSE 500
Start:
HSEROUT["Program start",13]
For Temp = 0 to 9
ReadyIndicator = 1
Pause 250
ReadyIndicator = 0
Pause 250
NEXT
ReadyIndicator = 1
Main:
Select CASE State
Case Idle
If StartStopButton = 0 THEN ' If the button is pressed we
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 UpdateTime ' 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 UpdateTime ' 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 ' Counter overflow
ErrorIndicator = 1
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.
If TMR1IF THEN ' Interrupt flag is set if counter has rolled over
HSEROUT["ERROR: Pulse counter overflow, can't compute...", 13]
Goto Abort
ENDIF
IF TimeOverflow THEN ' Flag set if seconds variable has rolled over
HSEROUT["ERROR: Seconds counter overflow, can't compute...", 13]
Goto Abort
ENDIF
' 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 65536 so:
'
' 12345 ** 39322 = 7407
' 7407 * 36 = 266652
' 266652 / 503 = 530 which we interpret as 53.0km/h
'
' 6045 pulses in 312 seconds,
' 6045*0.6/312*3.6=41.85km/h:
' 6045 ** 39322 = 3627
' 3627 * 36 = 130572
' 130572 / 312 = 418 which we interpret at 41.8km/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
HSEROUT[DEC (TMR1H * 256 + TMR1L), " pulses",13]
HSEROUT[DEC Distance, "m", 13]
HSEROUT[DEC Seconds, ".", DEC3 ms, "s",13]
HSEROUT[DEC AverageSpeed/10,".", DEC AverageSpeed//10, "km/h",13]
Abort:
ReadyIndicator = 1
ErrorIndicator = 0
State = Idle
END SELECT
Goto Main
'-------------------------------------- Subroutines ------------------------------------------------
UpdateTime:
' Our timebase is 500Hz.
ms = ms + 2
If ms = 1000 then
ms = 0
seconds = seconds + 1
' 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
ErrorIndicator = 1
ENDIF
ENDIF
RETURN
Bookmarks