View Full Version : Stopwatch? Timer/Counter?
02-15-2007, 09:11 AM
I have a 4-wire RS-422 device that I'll drive using a TI SN65C1167 driver/receiver. It is a simple device where you send it a start pulse and then wait for the stop pulse. What I need to do is (very accurately) measure the time between the start and stop pulses. Ideally the resolution would be 0.1 usec or better with a minimum resolution of 0.3 usec within a range of ~90 usecs to ~4000 usecs. I think this pretty much excludes doing it entirely on the stamp (BS2p).
So my question is, does anyone know of an "easy to interface" IC that would act as a stopwatch timer working within the above specs? I've been googling for much of the day investigating this and have been looking at timers, counters, PITs... and I'm a bit surprised I haven't been able to find anything simple (by which I mean everything I need on a chip including SPI/I2C interface). I also haven't been able to figure out how to build something like this from separate components but that might just be because I am dense http://forums.parallax.com/images/smilies/tongue.gif. I've found the LS7366R which might be as good as I will find but was hoping for something a bit simpler. Not that I'm not up for a challenge but...
Anyone have any experience with this? Any ideas?
02-15-2007, 09:55 AM
How about a Propeller? It can do I2C with an existing library. It can do SPI trivially. It can do accurate timekeeping (to 12.5ns resolution). It can even drive a VGA or TV display with only a couple of passive components and handle a PS/2 keyboard and/or mouse all without "breaking a sweat". It is a 3.3V device, but does interface easily to 5V devices and it's supported by Parallax.
02-15-2007, 02:07 PM
That's an interesting suggestion. I have been following the propeller since it was announced and have wanted to start playing around with it but I do have a time constraint on this project... But then again, I already have had to change a component which will require a complete re-design of the PCB.
So given that I have a sensor that when connected to the driver/receiver basically has a start pin and stop pin. When a trigger pulse (~2ms low-high-low) is applied to the start pin and then anywhere from 90-4000 usecs later the stop pin goes low-high-low to indicate a stop. I need to calculate the time from when the start pulse is initiated to when the stop pulse is received.
So, in general, how would this be done with the propeller?
I see the app note on the counters. I guess I would use one for the timing?
And another cog would monitor the start/stop pins?
I guess I have a lot to think about.
02-15-2007, 02:43 PM
Given that a Spin operation takes maybe 10-20us, you need to do the timing in assembly language. The assembly routine would take only a few instructions. If the start pin is I/O pin 0 and the stop pin is I/O pin 1:
VAR long pulseWidth
PUB start ' Start a cog with the measurement routine
PUB getNext ' Return the width of the next pulse seen
pulseWidth := 0 ' Use zero to indicate no pulse seen yet
repeat until pulseWidth ' Wait until this changes to non-zero
return pulseWidth ' Return the new value
entry waitpeq zeroes,#%01 ' Make sure pin 0 is low
waitpne zeroes,#%01 ' Now wait for it to go high
mov startTime,cnt ' Save the system clock value
waitpne zeroes,#%10 ' Wait for pin 1 to go high
mov endTime,cnt ' Get the system clock value
wrlong endTime,PAR ' Return the elapsed time
jmp #entry ' Go wait for the next pulse
zeroes long 0 ' A constant zero
startTime res 1 ' Start pulse system clock
endTime res 1 ' Stop pulse system clock (and calculated difference)
When you call the start routine from the main program (that does the other work), it starts this assembly routine in another cog (processor). All this routine does is to wait for the start pin to be low, then waits for the leading edge and records the system clock time of this (to within about 65ns). It then waits for the leading edge of the stop pulse and records the system clock time of this. It calculates the difference and copies that to the address in main memory originally passed when the cog was started. The getNext routine sets the pulseWidth variable to zero which shouldn't normally occur. It then waits for the other cog (processor) to record the next pulse width and returns that as its value. It's in units of the system clock time (12.5ns for an 80MHz clock) and you can do any kind of normalization you want on the value.
02-15-2007, 11:36 PM
This thread is being moved from the BASIC Stamp Forum to the Propeller Forum.
Parallax Tech Support
02-16-2007, 02:25 AM
Well, I guess I should have made it clear that I was also still interested in any basic stamp solutions as well. More of a thread fork than a move. Oh well...
Mike, thank you very much for the code snippet.
I think I am starting to understand this... It's a bit of a jump from the basic stamp. The code you posted simply defines the functions I would need, right? The "PUB" sections define (public) subroutines? "DAT" defines the assembly function. PAR is a reserved word that is what? I see the result of the getNext assembly function is written to it, but what is it? A return register? Is there only one or can functions return multiple values? (pardon my ignorance)
To use your functions, I would add the main program below your code? Something like (in pseduo-code)
low 0 ' set start pin low
pulseout, 0, 2 ' 1.6 us pulse to start device
rawTime=getNext() ' get the elapsed time
time = rawTime * someFactor ' scale it to some workable unit
print time ' display the output
So when the pulseout (prop equivalent) is called, the timer running on another cog starts (because it is waiting for the start pin to go high). The call to getNext will wait until a value is returned which might not be a wait at all if the timer function executes faster than the spin code. It shouldn't (since the min value is ~90us), but I am just trying to understand the flow and how this whole parallel execution stuff works.
I'm off to read the manual. Again, thanks for the code and suggestions. The propeller is a very elegant way to tackle this problem (and project). I hope I can work it into the schedule.
02-16-2007, 02:41 AM
You'll see in the manual that all methods (routines) have an implicit return value (called 'result'). For convenience, you can change the name of the return value to something more meaningful. Like all parameters, the result is a 32-bit value. Functions that need to return multiple values usually do so by having the address of a return area passed to the function which stores the extra results at that address.
There are a couple of reasons for writing the routine the way I did. 1) The start pulse may still be high (given your description) after the stop pulse has come and gone. That's why it always waits for the start pin to be low. 2) The assembly routine just sits there waiting for the next start pulse. There's an initialization penalty for starting up a cog ... it's on the order of 100us. As a result, it's unusual to just start up a cog for a single action. Usually there's a repeated action. Often the cog routine waits for a variable to be set to a particular value that tells it what to do next. 3) The reason for 'getNext' to set the pulseWidth value to zero, then wait for the next value to be stored is that this guarantees that it's a new reading. If you're just tracking the sensor values and it doesn't matter whether you see the same value twice, you don't need any of that code, just use 'pulseWidth' in your calculations and it will reflect the most recent reading. Another thing you can do is to move the 'pulseWidth := 0' to the end of 'getNext'. This allows you to "double buffer" the readings. You're still using each reading only once, but can do something else while the assembly routine is handling the next reading.