Timing/Using a Propeller as a stopwatch

ryfitzger227ryfitzger227 Posts: 97
edited 2013-05-29 - 20:50:39 in Propeller 1
Hello.

I'm moving up from the BASIC Stamp for a project that I'm working on. I'm very new to this type of programming language and I'm looking for some help with getting to learn it.

My project is a stopwatch/timer that is accurate up to 1 microsecond (.000001 sec). This leads to my first question. Is the Propeller capable of doing that (I know the BS2 isn't!!)?

I think I understand what a cog is, but just to make sure I'll explain my plan for this. My stopwatch actually consist of 4 different timers.3 of them start at the same time. So here's how I've layed out my cogs.

Cog 1 - Main (controller). Get's I/O's status and starts/stops the necessary timers.
Cog 2 - Serial Communication. After the time is calculated (I'm not going to be doing any math on the Prop. The computer program will calculate the final time. I just need it to calculate how long - in Prop time - it took for me to press Button A and then Button B).
Cog 3 - Timer 1. The main timer. It will have the longest Elapsed Time.
Cog 4 - Timer 2. Starts at the same time as Timer 1, but will end with the shortest Elapsed Time. (Stops separately from Timer 1)
Cog 5 - Timer 3. Starts at the same time as Timer 1, and will have the "middle" Elapsed TIme. (Stops separately from Timer 1)
Cog 6 - Timer 4. Starts at a special time, but will end with Timer 1.

Can the Prop have 4 timers going at the same time and still keep an accurate resolution of 1 microsecond??

I don't see where this code will be very complex, but I could be wrong. All I'm doing is just calculating the ticks it was from the start of a timer to the stop. If I need to account for some overhead, or dividing by 10,000 to get a time in seconds, this will all be done in the program on the computer.

I read somewhere that I should use assembly rather than Spin because it's faster. Is that statement true?

Basically I'm just asking for some help/advice/resources on how to get started on timing in this resolution with the Prop.

If you have any questions, just ask.

Ryan

Comments

  • kwinnkwinn Posts: 8,472
    edited 2013-04-29 - 19:17:00
    Hello.

    I'm moving up from the BASIC Stamp for a project that I'm working on. I'm very new to this type of programming language and I'm looking for some help with getting to learn it.

    My project is a stopwatch/timer that is accurate up to 1 microsecond (.000001 sec). This leads to my first question. Is the Propeller capable of doing that (I know the BS2 isn't!!)?
    Yes, the prop can do that.
    I think I understand what a cog is, but just to make sure I'll explain my plan for this. My stopwatch actually consist of 4 different timers.3 of them start at the same time. So here's how I've layed out my cogs.

    Cog 1 - Main (controller). Get's I/O's status and starts/stops the necessary timers.
    Cog 2 - Serial Communication. After the time is calculated (I'm not going to be doing any math on the Prop. The computer program will calculate the final time. I just need it to calculate how long - in Prop time - it took for me to press Button A and then Button B).
    Cog 3 - Timer 1. The main timer. It will have the longest Elapsed Time.
    Cog 4 - Timer 2. Starts at the same time as Timer 1, but will end with the shortest Elapsed Time. (Stops separately from Timer 1)
    Cog 5 - Timer 3. Starts at the same time as Timer 1, and will have the "middle" Elapsed TIme. (Stops separately from Timer 1)
    Cog 6 - Timer 4. Starts at a special time, but will end with Timer 1.

    Can the Prop have 4 timers going at the same time and still keep an accurate resolution of 1 microsecond??
    Yes. The timers use the same clock.
    I don't see where this code will be very complex, but I could be wrong. All I'm doing is just calculating the ticks it was from the start of a timer to the stop. If I need to account for some overhead, or dividing by 10,000 to get a time in seconds, this will all be done in the program on the computer.
    This is fairly simple if the start/stop signals for the timers are clean (no debouncing required) and less than 56 seconds apart. If the elapsed time is more than that it is only a bit more complex. Then you need to take the counter rollover into account.
    I read somewhere that I should use assembly rather than Spin because it's faster. Is that statement true?
    Yes. Assembly is much faster than spin. Under some circumstances this might be do-able in spin, but may need assembly.
    Basically I'm just asking for some help/advice/resources on how to get started on timing in this resolution with the Prop.

    If you have any questions, just ask.

    Ryan

    Will the elapsed time between starting and stopping the timer be more than 56 seconds?

    What type of signal will be used to start and stop the counters?
  • JonnyMacJonnyMac Posts: 6,429
    edited 2013-04-29 - 19:46:49
    What kind of duration are looking to time? -- longest?
  • BeanBean Posts: 8,076
    edited 2013-04-30 - 04:34:16
    If you are going over 56 seconds, you always have the option of running the propeller at a slower clock.
    Running at 10MHz (PLLx2) will allow you almost 8 minutes before you have to worry about roll-over.

    Bean
  • Tracy AllenTracy Allen Posts: 6,422
    edited 2013-04-30 - 08:15:00
    I see from your previous post that you've been working for some time on a 0.4 clay oval racetrack with an infra-red beam breaker. Is that the same as you are asking about here? Anything less than milliseconds is probably pushing the physical limits.

    Execution times on the Prop even in Spin are much faster than the BASIC Stamp, and there are commands such as WAITPEQ that would allow the program to synchronize to the bream breaker or to the start signal. The advantage of the Prop there is that it can be timing the one interval at the same time that it is transmitting the previous interval over the serial port.

    Another option for timing on the Prop are the COG counters. They can measure pulses down to sub-microsecond resolution, and in the logic modes they can be started/stopped by the levels on a couple of input pins. A spin or pasm program would observe the levels in parallel and capture the intervals to send to your central processor.
  • ryfitzger227ryfitzger227 Posts: 97
    edited 2013-04-30 - 13:08:54
    I'm working on another Timing System, but this time for Drag Racing. The times will always be less than 30 seconds. I will be using an infrared beam to start/stop the timers. (Picture a pushbutton though).

    I learn better with code, and I haven't seen anything that has really showed me how to do anything.

    Can someone give me an example (in Assembly) how to create 3 cogs. 1 for serial port, 1 monitoring the pushbutton output - and starting/stopping the timer, and the last for the timer? (I just want to start off easy. This way I'll learn how to create cogs and time something.. Then I can expand that to more than 1 timer.) I hate asking people to write me code, but I seriously have no clue about this language. Remember. All I really want is clock cycles (I guess). Then I'll send that number to the computer program, which will convert those clock cycles into time. You don't have to worry about accounting for overhead, I'll do that.

    - Ryan
  • cavelambcavelamb Posts: 686
    edited 2013-04-30 - 14:13:17
    Hi Ryan,
    I've not bothered with a serial driver yet.
    Haven't had the need.
    So this won't be what you may have hoped for.
    But it might be useful as an example of a couple of things.
    Like How to start other cogs. How to pass information around.

    This runs on a QuickStart, so it uses the capacitive touch buttons instead of real switches.
    Those take quite a bit of fiddling with to make them work.
    Not a problem, but it takes clock cycles.
    This button driver is written in Spin, so it takes even more clock cycles.
    But it's more understandable for first time coders, I think.

    The Touchpad button driver runs in the second cog.

    I'm setting a flag in the first cog and watching for it in the third.

    Anyway, the main routine:
    CON { Flag_Demo.spin }
      _CLKMODE=XTAL2 
      _xinfreq = 5_000_000
      
    VAR
      LONG MS001, Buttons, Flag
      
    OBJ button: "QS_Buttons"    ' touchpad driver
    
    OBJ Fwatch: "Flag_Watch"             ' other cog method
    
    PUB ButtonDemo | B          ' local variables
      MS001 := CLKFREQ / 1_000  ' define 1 millisec 
      button.start( @Buttons )  ' send address of Buttons
      Fwatch.start(@flag)
    
    Repeat
         if Buttons & |< 4       ' button 4 pressed?
             Flag := 1           ' set Flag
             TurnON(4+16)        ' show the button pressed
             WaitMS(10)                
             TurnOFF(4+16)
         else
             Flag := 0            ' else clear Flag
         If Flag == 1            ' this cog can see Flag directly
             TurnON(1+16)        ' LED 1 ON
             TurnOFF(6+16)       ' LED 6 Off
         else
             TurnOFF(1+16)       ' LED 1 OFF
             TurnON (6+16)       ' LED 6 ON     
    
    PUB WaitMS(W)                ' wait for W milliseconds
      W := W*MS001                
      WaitCNT (W+cnt)
    
    PUB TurnON(pin)
        dira[pin] := 1
        outa[pin] := 1
    
    PUB TurnOFF(pin)
        dira[pin] := 1
        outa[pin] := 0
    

    The button driver:
    'CON { QS_ButtonScan.spin }
    '    { returns buttons packed bitwide is a byte}
    VAR                  
      LONG ButtonAdr, ButtonCog, MS001
      LONG Stack[ 16 ]                 ' define my stack
    
    PUB start( BAdr )                  ' BAdr = Button Address
      ButtonAdr := BAdr                ' save address of return byte
      if ButtonCog                     ' did the new cog start?
        cogstop(ButtonCog-1)           ' OOPS! no cog available
      ButtonCog := cognew(ButtonScan, @Stack) + 1
      MS001 := clkfreq/1000
    
    PUB ButtonScan | B, B1              ' local variables
      dira [0..7] := %11111111          ' all pad pins outputs    
      outa [0..7] := %11111111          ' all pad pins high   
      'main loop - scan the buttons   
      Repeat                            ' loop forever
         Repeat B from 0 to 7           ' QS LEDS are pins 0-7
           dira [B] := 1                ' make pin an output 
           dira [B] := 0                ' make pin an input
           WaitMS(4)                    ' short delay for some decay
           B1 := ina[B]                 ' read the pad
           if B1 == 0                   ' 0 here means pressed
              BYTE[ButtonAdr] |= |< B   ' set bit if pressed   
           else                    
              BYTE[ButtonAdr] &= !|< B  ' clear bit if not
    
    PUB WaitMS(W)                       'wait for W milliseconds
      W := W * 1000 'MS001                
      WaitCNT (W+cnt)
    

    And the flag monitor:
    CON { Flag_Watch.spin }
    VAR
      LONG Stack[ 16 ]                 ' define my stack
      LONG FlagA, AcogID
      
    PUB start( F )                      ' start this in a new cog
      FlagA := F                        ' save location of flag variable
      if ACogID
        cogstop(ACogID-1)               ' no cog available
      ACogID := cognew(Aproc, @Stack) + 1
      
    PUB Aproc 
      'main loop - scan the buttons   
      Repeat                            ' loop forever
        TurnON(19)                      ' LED 3 show's I'm alive 
        if  byte[FlagA] ==   1          'if I see a 1
           TurnON(16)                   '  show this LED 15
           TurnOFF(23)
        else                            'if I see a 0 
           TurnON(23)                   '  show that LED 23
           TurnOFF(16)
    
    PUB TurnON(pin)
        dira[pin] := 1
        outa[pin] := 1
    
    PUB TurnOFF(pin)
        dira[pin] := 1
        outa[pin] := 0  
    
  • JonnyMacJonnyMac Posts: 6,429
    edited 2013-04-30 - 15:47:56
    There are lots of ways to do this -- I've attached a simple demo that may get you started. The timing code is in PASM, though this might work fine in Spin, too (it could be translated). By doing it this way, though, it's a nice, neat object that you can load as many copies of as you like (and have cogs to support). The code is simplified by taking advantage of the internal timers; I'm using positive mode to handle timing while a control pin is high. This allows you to start/stop the timer internally by taking the control pin high/low from your master cog. Or, you can start by taking the control pin high from an external circuit. NOTE that you must put a 3.3K or higher resistor between 3.3v and the control pin as the object drives the control pin high and low.

    Again, this is just a start. Brush strokes, if you will. It's up to you to paint the masterpiece. :-)

    This is PASM portion of the object. Pretty easy.
    dat
    
                            org     0
    
    hrtimer                 mov     t1, par                         ' start of parameters
                            add     t1, #4                          ' skip over timing result
                            rdlong  t2, t1                          ' read sync pin
                            mov     syncmask, #1                    ' convert to mask
                            shl     syncmask, t2
    
                            mov     ctra, POS_DETECT                ' ctra measures high state
                            or      ctra, t2                        '  of sync pin
                            mov     frqa, #1
    
    hrmain                  waitpne syncmask, syncmask              ' wait for low on sync    
                            mov     phsa, #0                        ' reset timer   
    
                            waitpeq syncmask, syncmask              ' wait for high on sync
                            nop
                            waitpne syncmask, syncmask              ' wait for low on sync
    
                            mov     t1, phsa                        ' copy timing
                            wrlong  t1, par                         ' report to hub
    
                            jmp     #hrmain
    
    ' --------------------------------------------------------------------------------------------------
    
    POS_DETECT              long    %01000 << 26                    ' increment phsx on high
    
    syncmask                res     1                               ' mask for control pin
    
    t1                      res     1                               ' work vars
    t2                      res     1
    
                            fit     496
    
  • kwinnkwinn Posts: 8,472
    edited 2013-04-30 - 19:56:17
    @ryfitzger227

    A little more detail on how the timers will be started and stopped would be of help.

    Will there be a signal on one pin to start the timer and a signal on another pin to stop it, or will a pin go high (or low) to start the timer and low (or high) to stop it?

    Will the start/stop method be the same for all 4 timers? It sounds like this will be so based on post 1, so two start signals (timers 1, 2, 3) and (timer 4) and three stop signals (timers 1, 4), (timer 2), and (timer 3). Is this correct?

    The simplest approach may be to have a PASM program that monitors 2 pins (start and stop), starts a counter when the start signal is true, stops the counter when the stop signal is true, and writes the 32 bit counter value to a hub location. The program could then be loaded into 4 cogs with the pin numbers and hub locations passed as parameters. The main spin program would send the data to the PC.
  • Tracy AllenTracy Allen Posts: 6,422
    edited 2013-04-30 - 22:58:34
    Can someone give me an example (in Assembly) how to create 3 cogs. 1 for serial port, 1 monitoring the pushbutton output - and starting/stopping the timer, and the last for the timer? (I just want to start off easy. This way I'll learn how to create cogs and time something.. Then I can expand that to more than 1 timer.)

    Ryan, you're really biting off quite a lot with that request, as you move up from the BASIC Stamp.

    Here is a snippet that starts a serial port at 9600 baud, and then simply spits out the time between two presses of a button. It is all in Spin. Well, not quite. When it starts the serial port object (Parallax Serial Terminal), that in turn does start up another Cog that does the heavy lifting, but you don't have to know the details. In PBASIC, the serial commands are built in, but in Spin, the program has to initialize the serial port. Also in PBASIC, the Pause command is built in, but in Spin, it is a method that is written into your own program code by referring to the Prop's clock frequency and its global counter. You should understand thoroughly how the Pause method works.

    The Main program loop goes just as you might write the same thing in PBASIC. It waits for the button on p0 to go low, captures the global counter value, then waits for the button to go up and then down again. At that point it sends the difference between the current value of the counter minus the starting value, out as a decimal ascii number to the serial terminal. Then it waits for the button up again and repeats the whole process. Is all that already completely clear to you? If so, the next step may be to start timers running in parallel in different Cogs. However, I'd echo Kwinn, "A little more detail on how the timers will be started and stopped would be of help."
    [FONT=courier new][SIZE=1]CON
      _clkmode = xtal1 + pll16x                '
      _xinfreq = 5_000_000
    
    
    OBJ
      pst : "Parallax Serial Terminal"
    
    PUB Main : ticks
      pst.Start(9600)
      Pause(100)
    
      ticks := cnt
      repeat
        repeat while ina[0]
        ticks := cnt
        repeat until ina[0]
    
        repeat while ina[0]
        pst.dec(cnt - ticks)
        pst.newline
        repeat until ina[0]
    
    
    PRI Pause(ms)
      waitcnt(clkfreq/1000 * ms + cnt)
    [/SIZE][/FONT]
    
  • JonnyMacJonnyMac Posts: 6,429
    edited 2013-05-02 - 07:42:22
    The simplest approach may be to have a PASM program that monitors 2 pins (start and stop), starts a counter when the start signal is true, stops the counter when the stop signal is true, and writes the 32 bit counter value to a hub location.

    Super easy. Since you only need one long for the timing result I wrote the pasm such that the start and stop pins are passed in it -- put the start pin in byte0 and the stop pin in byte1; pass the address of your result variable in the cognew() call. Again, easy-peasy.

    dat
    
                            org     0
    
    tixtimer                rdlong  t1, par                         ' read pins
                            mov     t2, t1                          ' make copy
    
                            and     t1, #$3F                        ' isolate timer start pin
                            mov     startmask, #1                   ' create mask
                            shl     startmask, t1
    
                            shr     t2, #8                          ' isolate timer stop pin
                            and     t2, #$3F                        ' create mask
                            mov     stopmask, #1
                            shl     stopmask, t2
                            
                            mov     t1, #0
    report                  wrlong  t1, par                         ' we're ready
    
    runtimer                waitpne startmask, startmask            ' clear control inputs
                            waitpne stopmask, stopmask                   
    
                            waitpeq startmask, startmask            ' wait for start
                            neg     t1, cnt                         ' start timer
    
                            waitpeq stopmask, stopmask              ' wait for stop
                            add     t1, cnt                         ' update ticks
    
                            jmp     #report
    
    
    ' --------------------------------------------------------------------------------------------------
    
    startmask               res     1                               ' mask for start input
    stopmask                res     1                               ' mask for stop input
    
    t1                      res     1                               ' work vars
    t2                      res     1
    
                            fit     496
    
  • JonnyMacJonnyMac Posts: 6,429
    edited 2013-05-02 - 07:42:42
    The simplest approach may be to have a PASM program that monitors 2 pins (start and stop), starts a counter when the start signal is true, stops the counter when the stop signal is true, and writes the 32 bit counter value to a hub location.

    Super easy. Since you only need one long for the timing result I wrote the pasm such that the start and stop pins are passed in it -- put the start pin in byte0 and the stop pin in byte1; pass the address of your result variable in the cognew() call. Again, easy-peasy.

    dat
    
                            org     0
    
    tixtimer                rdlong  t1, par                         ' read pins
                            mov     t2, t1                          ' make copy
    
                            and     t1, #$3F                        ' isolate timer start pin
                            mov     startmask, #1                   ' create mask
                            shl     startmask, t1
    
                            shr     t2, #8                          ' isolate timer stop pin
                            and     t2, #$3F                        ' create mask
                            mov     stopmask, #1
                            shl     stopmask, t2
                            
                            mov     t1, #0
                            
    report                  wrlong  t1, par
    
                            waitpne startmask, startmask            ' clear control inputs
                            waitpne stopmask, stopmask                   
    
                            waitpeq startmask, startmask            ' wait for start
                            neg     t1, cnt                         ' start timer
    
                            waitpeq stopmask, stopmask              ' wait for stop
                            add     t1, cnt                         ' update ticks
    
                            jmp     #report
    
    
    ' --------------------------------------------------------------------------------------------------
    
    startmask               res     1                               ' mask for start input
    stopmask                res     1                               ' mask for stop input
    
    t1                      res     1                               ' work vars
    t2                      res     1
    
                            fit     496
    
  • ryfitzger227ryfitzger227 Posts: 97
    edited 2013-05-27 - 17:54:41
    Sorry for taking such a long time to respond back to this thread. I've been busy and had to take a break on this. I have a few questions to add.

    First off, before I get too far into PASM - if I use Spin how accurate will the timing be? For example. If I create a timing program in Spin, find out how long it takes for the commands, and add that back to the final time will that overhead be the same EVERY time? Or will it vary? If it does, then I guess I'll use PASM. (I'm assuming you would still have to account for overhead, but that it would have a static overhead when using PASM).

    I'm leaning towards using/learning PASM for this project because it seems so simple. I like the idea of the copying and pasting the same program and then passing the pin numbers as paramaters. But you mention that I would use the main program for serial port communication. Is it possible for me to write the main program in Spin and then the rest of the objects in PASM (timing)?

    This might seem kinda crazy, but I have no clue. How do you save PASM files? Will they still be .spin?

    Honestly, the starting and stopping is fairly complex. It consist of (mainly) 2 Props. Prop 1 for starting/reaction times and controlling the lights that tell the person when to go and getting their reaction time. Prop 2 will be timing that person until he gets to the end of the track (with multiple times at different feet intervals - ex. 60ft, 330ft, and finally 660ft.) I'm going to ignore Prop 1 for now because it's pretty complex. It involves a bunch of different timers starting/stopping at different times. Prop 2 is just (practically) one timer starting and stopping like a stopwatch. Remember Timer 1 (the main, or Elapsed Time, timer)? That timer is what every timer after it (except the last) goes off of by when it's time to start. Prop 1 will do it's timing and when the guy leaves, it will get his reaction time. Well at the same time it will set the base of a transistor that's connected to a pin high. The emitter/collector will be connected to a Pin on Prop 2. This will be the signal that Timer 1 starts by. Therefore, Timer 1, 2, & 3 all start off of that. This will be the only time a transistor is being used. The rest will be started/stopped by an infrared. View the table below for more in depth information.

    [TABLE="class: grid"]
    [TR]
    [TD]Starting Line[/TD]
    [TD]60 feet[/TD]
    [TD]330 feet[/TD]
    [TD]MPH Start[/TD]
    [TD]MPH/ET End (660 feet)[/TD]
    [/TR]
    [TR]
    [TD]Started by
    Transistor
    connected to
    Prop 1[/TD]
    [TD]Ended
    by
    infrared[/TD]
    [TD]Ended
    by
    infrared[/TD]
    [TD]Started
    by
    infrared[/TD]
    [TD]Ended
    by
    infrared[/TD]
    [/TR]
    [TR]
    [TD]Starts Timer 1,
    2, & 3[/TD]
    [TD]Ends
    Timer 2[/TD]
    [TD]Ends
    Timer 3[/TD]
    [TD]Starts Timer
    4[/TD]
    [TD]Ends Timer 1, 4[/TD]
    [/TR]
    [/TABLE]

    If you have anymore questions, just ask. I'm always open for suggestions. Thanks for everything so far!
  • jmgjmg Posts: 14,240
    edited 2013-05-27 - 18:28:40
    First off, before I get too far into PASM - if I use Spin how accurate will the timing be? For example. If I create a timing program in Spin, find out how long it takes for the commands, and add that back to the final time will that overhead be the same EVERY time? Or will it vary?

    The precision of a WAIT command is the same in Spin and PASM, what PASM allows is faster re-arm, and it could time between edges very close together.

    However, if you want to resolve to 1us, but never have edges closer than a few ms, then Spin could be quite ok.
    Human Reaction times, and running times, will be > 100ms, and you may need to worry more about the other end of the scale: timer roll-over.
    Slowing the Prop down a little from the 80MHz will probably do for that.
  • prof_brainoprof_braino Posts: 4,312
    edited 2013-05-28 - 07:33:42
    ... looking for some help with getting to learn it.

    My project is a stopwatch/timer that is accurate up to 1 microsecond (.000001 sec).
    JonnyMac wrote: »
    What kind of duration are looking to time? -- longest?

    If you are interested in a solution maybe a little more powerful than spin, and maybe a little easier than PASM:

    http://code.google.com/p/propforth/wiki/Logger1Simple

    This is the logging package from propforth. It has microsecond resolution and can comfortably log once per second (counter, date time stamp, and data). It requires no addition hardware besides the SD card for logging (you copuld log to EEprom instead). There is a one time calibration, once the drift setting is input, its nut dead on. If the temperature is stable, there is about 3-4 microsecond variance. It uses double math, and the time stamp won't roll over for just over seven thousand years.

    It's seems its fine for day to day logging, as the temperature changes, the clock drifts in then back out again, so over 24 hours, it averages out. If you are going to put it outside for an extended period of time of in varying temperature environment, you can use a temperature compensated crystal, but it hasn't been necessary yet for the testing performed.

    The current test is a torture test designed to wear out the SD card, but is has logged 110 gig of data once per second to the same file. The SD has not failed yet even though the head header is constantly being rewritten. To make off loading the data easier, the lgger was changes to create a new file when the Day rolls over, this should never fail the SD.

    The logger continues loggin even when the log is accessed or the time is set. If power is lost, the system restarts with default time of 2000, January 1, 12:00 AM; but after you set the correct time (manually) you can easily back fill the correct date stamps.

    If you don't use forth, you might be able to use the same method in the language(s) of your choice.
  • ryfitzger227ryfitzger227 Posts: 97
    edited 2013-05-28 - 13:51:36
    Thanks for the reply!

    Im new to this, so what happens after 56 seconds? Does the timer just stop, or does it become inaccurate? I shouldn't be timing for that long under any circumstance, but in case the stop infrared is missed I want to know what I should do.
  • jmgjmg Posts: 14,240
    edited 2013-05-28 - 15:08:49
    Thanks for the reply!

    Im new to this, so what happens after 56 seconds? Does the timer just stop, or does it become inaccurate? I shouldn't be timing for that long under any circumstance, but in case the stop infrared is missed I want to know what I should do.

    ~56 seconds( 2^32/80e6 = 53.6870912) is just the 32 bit number wrap-around, and that is for a 80MHz SysClock.

    80MHz gives a 12.5ns LSB, so you may decide to just drop the 80MHz, to move that 53 seconds.
    eg 10MHz SysClk gives 32 bits to 428 seconds, and still can edge sense to 100ns.

    Or, you could choose to drive a spare Pin as a Counter-Clock, and then you can freely scale to any time you want, but still have a faster CPU processing speed.
    The COG counters can be set to tick at that Pin-Rate using POSEDGE or NEGEDGE modes.

    Here, 80MHz divided by 80 to a 1us pin-rate, would overflow in 71 minutes, and you could easily change to 10us, or 100us.

    If you are serious about the 1us LSB, then you might want to also capture a GPS 1pps signal, and use that as a calibrate reference. ;)
  • prof_brainoprof_braino Posts: 4,312
    edited 2013-05-28 - 18:07:13
    jmg wrote: »
    ~56 seconds( 2^32/80e6 = 53.6870912) is just the 32 bit number wrap-around, and that is for a 80MHz SysClock.

    If you use 64 bit numbers (2^64 / 80e6 ) it rolls over after 7,311 years. The way the counters works, they just operate in the back ground and count time for free, and you can do other stuff with the cogs.

    Back to post 6, if you log when the beam was broken, and whether it was at the start or end of the track, you could log not only each race, but the intervals between races, and the interval between race days, for the entire season, and for multiple seasons, all on one SD.

    What are you going to use as a display? The prop can also run a VGA display, and a keyboard. And propforth can of course do this, with the logger running on on a separate cog.

    (I'm kind of excited about this logger stuff, can you tell?).
  • ryfitzger227ryfitzger227 Posts: 97
    edited 2013-05-29 - 15:52:39
    Ryan, you're really biting off quite a lot with that request, as you move up from the BASIC Stamp.

    Here is a snippet that starts a serial port at 9600 baud, and then simply spits out the time between two presses of a button. It is all in Spin. Well, not quite. When it starts the serial port object (Parallax Serial Terminal), that in turn does start up another Cog that does the heavy lifting, but you don't have to know the details. In PBASIC, the serial commands are built in, but in Spin, the program has to initialize the serial port. Also in PBASIC, the Pause command is built in, but in Spin, it is a method that is written into your own program code by referring to the Prop's clock frequency and its global counter. You should understand thoroughly how the Pause method works.

    The Main program loop goes just as you might write the same thing in PBASIC. It waits for the button on p0 to go low, captures the global counter value, then waits for the button to go up and then down again. At that point it sends the difference between the current value of the counter minus the starting value, out as a decimal ascii number to the serial terminal. Then it waits for the button up again and repeats the whole process. Is all that already completely clear to you? If so, the next step may be to start timers running in parallel in different Cogs. However, I'd echo Kwinn, "A little more detail on how the timers will be started and stopped would be of help."
    [FONT=courier new][SIZE=1]CON
      _clkmode = xtal1 + pll16x                '
      _xinfreq = 5_000_000
    
    
    OBJ
      pst : "Parallax Serial Terminal"
    
    PUB Main : ticks
      pst.Start(9600)
      Pause(100)
    
      ticks := cnt
      repeat
        repeat while ina[0]
        ticks := cnt
        repeat until ina[0]
    
        repeat while ina[0]
        pst.dec(cnt - ticks)
        pst.newline
        repeat until ina[0]
    
    
    PRI Pause(ms)
      waitcnt(clkfreq/1000 * ms + cnt)
    [/SIZE][/FONT]
    

    I kinda jumped ahead a little since cavelamb showed me how to create cogs, etc. But this code for the timing I just can't get to work. What am I doing wrong? For testing purposes I'm using a QuickStart board. Are the pushbuttons on it a little different?

    Here's the code.

    main.spin
    CON
    
            _clkmode = xtal1 + pll16x                                               
            _xinfreq = 5_000_000
    
    
    VAR
    
    
      LONG t1, t2, t3, t4
       
    OBJ pst : "Parallax Serial Terminal"
    OBJ timer : "timer"
    PUB main
    
    
      pst.start(9600)
      timer.start(@t1, @t2, @t3, @t4)
    
    
      repeat
        pause(250)
        pst.str(string(16, "Timer 1: "))
        pst.dec(t1)     
    
    
    PUB pause(ms)
      waitcnt(clkfreq/1000 * ms + cnt)
    

    timer.spin
    VAR
    
      long  stack[10]
    
    
    PUB start(t1a, t2a, t3a, t4a)
    
    
      cognew(main(t1a, 0), @stack)
    
    
    PUB main(tadr, tstart) : ticks
    
    
      ticks := cnt
      repeat
          repeat while ina[tstart]
          ticks := cnt
          repeat until ina[tstart]
    
    
          LONG[tadr] := cnt - ticks
    

    The Serial Terminal keeps saying "Timer 1: 0".
  • Tracy AllenTracy Allen Posts: 6,422
    edited 2013-05-29 - 20:50:39
    First advice would be not to jump ahead! Objects are nice, but you should have a good reason for going there. I think here the issue is the buttons themselves and how to read them.

    The type of pushbuttons does make a difference. The code I provided as well as quite a bit of the other code here has assumed that the signal is a solid ON or OFF as you would typically get from an industrial electronic timer or control. A mechanical button or the buttons on the quickstart are a different matter. They will bounce and the code will race through its states. You'll need to add code to debounce the buttons to be sure that they are solidly in one state or another, and the debouncing can be done either in line with the timer, or as a separate routine running in its own cog.
Sign In or Register to comment.