A Pinewood Derby Timer

copyright, Peter H. Anderson, Baltimore, MD, Oct, '99


Although, this discussion is set in the framework of a timer for a Pinewood Derby competition, it is applicable to a general class of problems where it is desired to know the ordering of events and when each event occurred.

Each February, the fathers of Cub Scouts compete in a competition. I say that with tongue in cheek. It's billed as a Cub competition, but my experience is its often a father competition or "my father is better than your father" type thing. When I was a Cub Master many years ago, we used human judges, but there was always a helpful someone who brought a Polaroid. In case their child was "wronged", they could produce a "photo finish".

For the past several years, in December and January, I have received a number of requests for electronics to do the timing and I always think back to pulling that racetrack out of our basement and I get tired, and can't imagine folks wiring up photocell arrangements, crystal oscillators, timer circuits and LED displays.

But, they do, and the problem is interesting in that it is simply stated; display the order of a sequence of events and the time that each event occurred, and yet it tends to defy a simple solution with a minimum of components.

The BasicX with its on-board timer capability may provide a simple solution.

Detailed Discussion

In the program PineWood.Bas, an input GO is brought low to start the race. This input may be derived from whatever actually starts the race, or an output from the BasicX might be used to start the cars when the GO button is depressed.

I have assumed four lanes and that when a car passes the finish line, some optical circuitry provides a momentary logic zero to the BasicX on the lower four bits of PortA.

The BasicX continually reads the lanes and if there is a change involving a lane (or lanes) which has not finished, the time is fetched and the lane (or lanes) are identified and the "place" and time are stored in array elements associated that lanes (or lanes).

On timeout which I have set to 10.0 seconds or when all cars have finished, the results are displayed. This might be a PC or serial LCD (or huge VFD). If a finish was not detected in a lane, question marks are displayed.

Note that in a single fetch of the status of the lanes, there may be multiple finishes. If so, all cars are assigned the same place.

In this routine, no amazinging output was provided when a car crosses the finish line. However, one might output NotFinished on PortC to turn on a lane indicator when a car finishes.

A question arises as to whether a car might be missed. That is, the car manages to completely cross the finish line between samples by the BasicX. Yes. If the maximum execution time from taking one sample to taking the next is greater than the time the signal from a lane is low, the event will be missed. A solution might be to set a latch (to 0) when a car crosses the finish.

Please note that my intent in developing this routine was simply to tinker and it was tested using push button switches. It seems practical for Pinewood Derbies, but I may be wrong.

' PineWood.Bas
' See text.
' copyright, Peter H. Anderson, Baltimore, MD, Oct, '99

Sub Main()

   Dim Lane as Integer	' Lanes 1 through 4
   Dim NewLanes as Byte	' current state of the four lanes
   Dim OldLanes as Byte	' previous state of the four lanes
   Dim Change as Byte	' difference of New and OldLanes

   Dim FinishDetected as Boolean	' used as a flag	

   Dim NotFinished as Byte	' those lanes that have not finished
   Dim NewChange as Byte	' Change and not finished

   Dim Tm as Single		' current time
   Dim TimeOut as Single	' time to wait until race finished

   Dim Place as Byte	' first, second, etc

   Dim PlaceLane(1 to 4) as Byte	' place and time associated
   Dim PlaceTime(1 to 4) as Single	' with each lane

   Dim space as String *1
   Dim question_mark as String *1

   Dim NotGo as Byte

   space = " "
   question_mark = "?"

   Call OpenSerialPort(2, 9600)

   TimeOut = 10.0
   NotFinished = &H0f	' a one in a bit position indicates lane has
			' not finished
   OldLanes = &H0f
   Place = 1		' first place

   Do			' loop until GO button pressed
      Register.DDRA = &H00
      Register.PORTA = &Hff
      NotGo = Register.PinA AND bx00010000     
   Loop Until (NotGo = 0)

   Call PutTime (0, 0, 0.0)	' initialize on board clock

      Register.DDRA = &H00	' PortA all inputs
      Register.PORTA = &Hff	' with Pull up resistor
      NewLanes = Register.PINA AND &H0F   ' lower four bits
      ' Call PutB(NewLanes)
      ' Call NewLine()

      Change = (OldLanes XOR NewLanes) AND &H0f
            ' a logic one in bit positions where there is a new change
      NewChange = (Change AND NotFinished) AND &H0f
	    ' a logic one where was a change and not finished

      OldLanes = NewLanes
      FinishDetected = FALSE

      If (NewChange <> 0) then
         ' Call PutStr(question_mark)
         Tm = Timer()		' fetch the time       
         For Lane = 1 To 4
            If (GetByteBit(NewChange, CByte(Lane-1)) = 1) then	
                            ' if finished
               NotFinished = PutByteBit(NotFinished, CByte(Lane-1), 0)
      	           ' save that it finished
               PlaceLane(Lane) = Place		
               PlaceTime(Lane) = Tm
               FinishDetected = TRUE
            End If           
      End If

      If (FinishDetected) then
         FinishDetected = FALSE
         Place = Place+1
      End If
      Tm = Timer()
      ' Call PutB(NotFinished)
      ' Call PutStr(space)
      ' Call PutS(Tm)
      ' Call NewLine()
    ' loop until timeout or all cars have finished
    Loop Until ((Tm > 10.0) OR (NotFinished = 0))	' race over

    ' now report the results

    For Lane = 1 to 4	' for each lane
       Call PutI(Lane)
       Call PutStr(space)
       If (GetByteBit(NotFinished,  CByte(Lane - 1)) = 1) then 
                   ' didn't finish
          Call PutStr(question_mark)
          Call PutStr(space)
          Call PutStr(question_mark)
          Call NewLine()
          Call PutB(PlaceLane(Lane))
          Call PutStr(space)
          Call PutS(PlaceTime(Lane))
          Call NewLine()
       End If


End Sub

Function PutByteBit(ByVal v as Byte, ByVal pos as Byte, _
                    ByVal state as Byte) as Byte

' sample usage  
'             x = PutByteBit(x, 3, 0)	' make bit 3 a zero

   Dim N as Byte, Mask as Byte
   Mask = bx00000001

   For N = 1 To Pos    
     Mask = Mask * 2

   If (state = 1) then
      v = v OR Mask
      v = v AND (Mask XOR &Hff)
   End If

   PutByteBit = v

End Function 

Function GetByteBit(ByVal v as Byte, ByVal pos as Byte) as Byte

' sample usage
'  y = GetByteBit(x, 3)	' get the value of bit 3

   Dim N as Byte, Mask as Byte, RetVal as Byte
   Mask = bx00000001

   For N = 1 To Pos
     Mask = Mask * 2

   v = v AND Mask

   If (v <>0) Then
     RetVal = 1
     RetVal = 0
   End If

   GetByteBit = RetVal

End Function