Quozl's Linux Speedometer, Odometer, and Trip Computer

| quozl@us.netrek.org | up |


Implemented on a 386/25 8Mb DECpc laptop, this Linux application attaches to a PIC 16F84 microcontroller which is then attached to a vehicle speed sensor. The program displays current speed, distance travelled, and allows the marking of waypoints and the calculation of distance to the next town.




Sensor Assembly

This was already part of Quozl's chariot, all he had to do was find the right wires leading into the aftermarket cruise control speed regulator. The documentation for the unit gave all that was required.

Microcontroller Sampler

The microcontroller program operates by measuring time in milliseconds against revolutions of the sensor assembly. It keeps a 32-bit counter for each value and dumps them out via RS-232 every half second. The serial output is buffered so that the delivery of data is not corrupted by the rapidly changing input data. A baud rate of 4800 baud was chosen to match the crystal clock rate such that the same interrupt task manages both serial data transmission, updating the millisecond counter, and detecting revolutions.

The data stream from the microcontroller to the laptop consists of an initialisation marker on power up "a\r\n", followed by regular lines of text containing ten digit ASCII values for the counters, separated by a space. The time in milliseconds is output first, followed by the revolution counter. For example:

Sampler Hardware

(click on the images for a magnified 1024x768 pixel image)

Also available is an annotated circuit board image. Where it says 890k, read it as 3.9k.

Schematic courtesy of John, though with the same 890k vs 3.9k error. My fault.

Theory of Operation - Hardware

The positive DTR signal from the laptop passes through a 1N4003 diode to supply voltage to the 78L05 regulator, with a 330uF capacitor. The laptop thus controls power to the circuit. Note that if the level of the DTR signal is too low, there may not be sufficient current to power the circuit. Substitute a 12V source in place of DTR, or try a low drop regulator.

The two 22pF capacitors and crystal form the precision timing source for the microcontroller. The 3.9k resistor is a pull-up for the MCLR reset input of the PIC.

Input filtering of the oscillating 12V VSS input is via a series 22k resistor and a 1M pull-up. The PIC input port clamping diodes are used to restrain the voltage at RB0/INT, which is configured as an input.

The RB4 pin is configured for output and generates an pre-inverted TXD serial data signal to the laptop. An optional LED on the prototype was used to indicate transmissions.

No other pins on the microcontroller are used. Clearly, there is ample room for an LCD display and keypad.

Theory of Operation - Software

During initialisation, the microcontroller is configured to deliver a TMR0 interrupt every 208 microseconds. This is done by setting TMR0 to OSC/4 and then loading the TMR0 counter on each interrupt. This rate was chosen to give 4808Hz, a match to the 4800 baud serial rate.

The interrupt service routine (ISR) is therefore called at 4808Hz. The ISR performs these actions;

  1. reloads the TMR0 register to schedule the next interrupt,
  2. accumulates the 208 microsecond elapsed time,
  3. periodically increments a 32-bit millisecond counter,
  4. shifts the next serial bit throught the RB4 output pin,
  5. detects changes in the state of RB0/INT input pin,
  6. ignores changes that have occurred in the last 4ms,
These actions are described below.

At the start of the ISR, the TMR0 register is preloaded to a value calculated based on the number of instructions executed by the processor since the ISR began. The calculation is hand-crafted and depends on the instruction stream. This allows TMR0 to be operated as a 208 microsecond repeating interrupt source, rather than using the counter in a free-run mode.

Because it is known that each interrupt occurs 208 microseconds from the last one, a 16-bit counter is used to accumulate time since the last whole millisecond. The ISR adds 208 microseconds to this counter. When the counter reaches a number above 1000, one millisecond has passed. The counter is reduced by 1000 microseconds, leaving the remainder. Thus a portion of the ISR is executed for every one millisecond, but the actual instant of execution drifts by up to 208 microseconds.

The ISR fragment that is executed every millisecond updates a 32-bit millisecond counter, and sets a flag so that the non-interrupt level code can be woken.

Next, the remainder of the ISR is executed, still at 208 microsecond intervals. It checks to see if serial data is being transmitted, and if it is, the next bit is shifted out to the RB4 output pin. Finally, the input pin RB0/INT is checked to see if it has changed state. If it has turned on since it was off (208 microseconds ago), then a 32-bit counter of cycles is incremented, the main code is woken, and a 4ms debounce counter is set. Changes to the pin state are ignored until the debounce counter reaches zero after around 19 more interrupts.

The main code performs these actions:

  1. initialises the microcontroller,
  2. enables the TMR0 interrupt,
  3. reports the protocol version to the serial port,
  4. waits for the ISR to roll the millisecond clock,
  5. reads the current clock and cycle counters,
  6. sends the counters in decimal to the serial port,
  7. waits until the transmit buffer is empty,
  8. waits for the next half second,
  9. repeats from step 4 above.

Linux Laptop

The curses based program on the laptop accepts the serial data stream and performs calculations to derive speed. This requires calibration; calibrated kilometer marks on certain roads are good for this.

The program also allows entry of waypoints, or marks, whereby a name can be associated with a particular odometer value. The first keystroke of the name is used to read the odometer, and the return key writes the entry to the log. As an additional feature, if the mark name is a number, it is assumed to be distance to a destination, and the program calculates and displays average speed since the mark, remaining distance to the destination, and expected arrival time.

Initial Calibration

In the file warner.c is a calibration constant mm. This is millimetres travelled between each sensor pulse, and varies between each vehicle. It also varies according to the sensor, tyre radius, tyre wear, cornering rate, ambient temperature, and road surface type.

Initial calibration was by dangling a plumb bob or pointer from the left passenger door, such that it was a few millimetres off the concrete. The vehicle was allowed to move (by gravity) until the VSS output changed state (e.g. off to on). A mark was placed on the concrete, and the vehicle rolled again until the VSS output changed state back, and then full cycle (e.g. on to off, delay, then off to on again). Another mark was placed. The distance between the two marks was then measured, using a millimetre ruler.

Subsequent calibration was done against both the vehicle speedometer and a GPS. The value of mm was changed in real-time in an older version of the code, by creating a file calibrate.dat with a new value. Once we'd finished calibration, we disabled that section of the code.

Higher Frequency

The design as it stands is limited by the sampling rate and the debounce logic to a maximum signal frequency of about 125Hz. One correspondent wanted to use this design for frequencies of up to 1700Hz while monitoring a power meter. To enhance the code for higher frequency, remove or reduce the debounce logic. In theory this will allow measurement up to 2403Hz.

Code to be removed:

L96:            ignore                  ; cycle input debounce timeout (1ms)

L154:   decfsz  ignore,f                ; decrement and test debouncer timer
        goto    isr_clock_end

        incf    ignore,f                ; restore to unity

L195:   decfsz  ignore,w                ; test the ignore timer
        goto    isr_cycle_end           ; if running, continue to ignore
        movlw   d'4'                    ; set 4ms debounce ignore
        movwf   ignore

L208:   decfsz  ignore,w                ; test the ignore timer
        goto    isr_cycle_end           ; if running, continue to ignore
        movlw   d'4'                    ; set 4ms debounce ignore
        movwf   ignore


Page Change Log

2005-06-25Initial calibration described.
2003-08-18Change of name and web site link for a contributor.
2003-05-23Fixed Makefile for more recent versions of gpasm, thanks to Craig Franklin.
2001-10-19Wrote up adjustment for higher sampling rates, for Gerald, whose return mail address bounced.
2001-04-11Add schematic provided by John, and wrote up the theory of operation
2001-04-08Add close-up annotated photograph of prototype
1999-11-01Add photographs of prototype
1999-07-07Initial page

| quozl@us.netrek.org | up |