Shop OBEX P1 Docs P2 Docs Learn Events
Stopwatch? Timer/Counter? — Parallax Forums

Stopwatch? Timer/Counter?

rtowlerrtowler Posts: 29
edited 2007-02-15 18:41 in Propeller 1
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 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?

Comments

  • Mike GreenMike Green Posts: 23,101
    edited 2007-02-15 01:55
    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.
  • rtowlerrtowler Posts: 29
    edited 2007-02-15 06:07
    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.
  • Mike GreenMike Green Posts: 23,101
    edited 2007-02-15 06:43
    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
       return cognew(@entry,@pulseWidth)
    
    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
    
    DAT
    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
               sub         endTime,startTime
               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.
  • Chris SavageChris Savage Parallax Engineering Posts: 14,406
    edited 2007-02-15 15:36
    This thread is being moved from the BASIC Stamp Forum to the Propeller Forum.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Chris Savage
    Parallax Tech Support
  • rtowlerrtowler Posts: 29
    edited 2007-02-15 18:25
    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)

    start
    while running
      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
    endwhile
    
    



    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.

    -r
  • Mike GreenMike Green Posts: 23,101
    edited 2007-02-15 18:41
    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.
Sign In or Register to comment.