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.
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 Do 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 Next 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() Else Call PutB(PlaceLane(Lane)) Call PutStr(space) Call PutS(PlaceTime(Lane)) Call NewLine() End If Next 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 Next If (state = 1) then v = v OR Mask Else 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 Next v = v AND Mask If (v <>0) Then RetVal = 1 Else RetVal = 0 End If GetByteBit = RetVal End Function