Shop OBEX P1 Docs P2 Docs Learn Events
timers — Parallax Forums

timers

have been away from the prop for several years. I need 3 separate timers running
minutes to hours, to flag my main program (that will be busy doing other things).
Such as I want to set timer 1 to run x minutes and then set a flag. Timer 2 and 3, similar tasks.
while main program is running and checks for the flags..Should be a simple thing... but ???


' blink...test floor heat control wiring... and timing
' THIS IS WORKING AS IS... chek waits to be called
'*************************************************************************************************
CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
K = 2
High = 1
Low = 0

VAR
byte W
long stack1 [40]
byte Q
Pub main

DirA[0..7] := $FF '0-7 is out
dira[8..15] := $FF 'port 8-15 dir is out
dira[16..23] := $FF 'port 16-23 is out
W := 10
cognew (chek (W), @stack1)

Q := 0

repeat while Q == 0 'blink p-6 until times up

waitcnt(10_000_000 + cnt)
outa[6] := 1
waitcnt(10_000_000 + cnt)
outa[6] := 0
Q := 0
chek (5)
repeat while Q == 0 'this never runs
waitcnt(10_000_000 + cnt)
outa[4] := 1
waitcnt(10_000_000 + cnt)
outa[4] := 0


Pub chek (R) 'time routine R times
DirA[0..7] := $FF '0-7 is out
dira[8..15] := $FF 'port 8-15 dir is out


repeat R
waitcnt(70_000_000 + cnt) 'delay 1 sec
outa[1] := 1 'blink p 1
waitcnt(10_000_000 + cnt)
outa[1] := 0

Q := 1 'flag to show times up
return

Comments

  • Cluso99Cluso99 Posts: 18,066
    You are in the P2 section. Do you mean to be in the P1 Propeller section?
    Spin is not ready for P2 yet, and we do counter thing differently, as well as setting the clock. P2 has a 64 bit clock counter too.
  • Mod Edit: Moved to P1.
  • Sorry, yep I am working with a P1.
  • Francis BauerFrancis Bauer Posts: 353
    edited 2019-11-26 01:30
    10gigbill,

    Please use the 'C' code forum attribute in order to preserve the spacing/indent of your code. As it stands right now it is hard to know what you have inside loops or not.

    I've posted your code below using the 'C' code forum attribute. See if I have it correctly indented. In its current form it does appear to blink LEDs as you expect. The reason why the p4 LED never blinks is that you invoked the 'chek' routine again after the first loop finishes. The 'chek' loop sets Q equal 1, so the p4 loop never runs because Q is 1.

    Note: The first invocation of 'chek' by cognew only runs the specified number of times then stops. The second invocation runs in the same cog as your main code. I don't know if that is what you wanted or not...
    ' blink...test floor heat control wiring... and timing
    ' THIS IS WORKING AS IS... chek waits to be called
    '*************************************************************************************************
    CON
    _clkmode = xtal1 + pll16x
    _xinfreq = 5_000_000
    K = 2
    High = 1
    Low = 0
    
    VAR
    byte W
    long stack1 [40]
    byte Q
    Pub main
    
    dira[0..7] := $FF '0-7 is out
    dira[8..15] := $FF 'port 8-15 dir is out
    dira[16..23] := $FF 'port 16-23 is out
    W := 10
    cognew (chek (W), @stack1)
    
    Q := 0
    repeat while Q == 0 'blink p-6 until times up  
      waitcnt(10_000_000 + cnt)
      outa[6] := 1
      waitcnt(10_000_000 + cnt)
      outa[6] := 0
    
    Q := 0
    chek (5)
    repeat while Q == 0 'this never runs
      waitcnt(10_000_000 + cnt)
      outa[4] := 1
      waitcnt(10_000_000 + cnt)
      outa[4] := 0
    
    
    Pub chek (R) 'time routine R times
      dira[0..7] := $FF '0-7 is out
      dira[8..15] := $FF 'port 8-15 dir is out
    
      repeat R
        waitcnt(70_000_000 + cnt) 'delay 1 sec
        outa[1] := 1 'blink p 1
        waitcnt(10_000_000 + cnt)
        outa[1] := 0
      Q := 1 'flag to show times up
    return
    
  • Thanks Francis
    I am using the prop to control 2 pumps in a floor heat system. I would like to have 4 timers
    running in 4 cogs, so they can all run independently. If a thermostat calls for heat in zone 1
    then that pump will run for x time and not be allowed to run again for y time.. same for pump 2.
    I would like the main program to run, looking at thermostat inputs and time switches, and
    call the timers for the different tasks. I just want to start a timer like an alarm clock, and
    have it set a flag when time is up... all causing my hair to turn gray !
  • What is your time "tick" for all of the timers? Typically, for an application like this, it would be one second or one tenth of a second or something similar. A timer would be just a long word that holds the "time to go" in "tick"s. The routine that decrements these timers will only decrement a non-zero value, so a zero value means the time is up ... you don't need a separate flag. One cog simply counts down these timers.
  • kwinnkwinn Posts: 8,697
    10gigbill wrote: »
    Thanks Francis
    I am using the prop to control 2 pumps in a floor heat system. I would like to have 4 timers
    running in 4 cogs, so they can all run independently. If a thermostat calls for heat in zone 1
    then that pump will run for x time and not be allowed to run again for y time.. same for pump 2.
    I would like the main program to run, looking at thermostat inputs and time switches, and
    call the timers for the different tasks. I just want to start a timer like an alarm clock, and
    have it set a flag when time is up... all causing my hair to turn gray !

    Since you are talking about a heating system where changes happen relatively slowly you could do that very simply as Mike suggests and have the code that sets the time for each of the counters set a flag that indicates if the pump is allowed or not allowed to pump.
  • AJLAJL Posts: 512
    edited 2019-11-27 03:47
    kwinn wrote: »
    10gigbill wrote: »
    Thanks Francis
    I am using the prop to control 2 pumps in a floor heat system. I would like to have 4 timers
    running in 4 cogs, so they can all run independently. If a thermostat calls for heat in zone 1
    then that pump will run for x time and not be allowed to run again for y time.. same for pump 2.
    I would like the main program to run, looking at thermostat inputs and time switches, and
    call the timers for the different tasks. I just want to start a timer like an alarm clock, and
    have it set a flag when time is up... all causing my hair to turn gray !

    Since you are talking about a heating system where changes happen relatively slowly you could do that very simply as Mike suggests and have the code that sets the time for each of the counters set a flag that indicates if the pump is allowed or not allowed to pump.

    Given that at any given time you would only have 0, 1, or 2 timers running, it sounds like you could just launch 2 cogs, each having it's own thermostat input, 'run' time, 'no-run' time, and output pin.

    Each pump cog starts with a WAITPEQ (or WAITPNE) to wait for the thermostat trigger, then:
    starts the pump
    waits for 'run' time to expire
    stops the pump
    waits for 'no-run' time to expire
    then loops back to the idle state (WAITPEQ or WAITPNE)

    Each pump cog takes responsibility for its own 'run', 'no-run', and 'idle' state, leaving the main program free to do other things. If you use global variables to hold the 'run' and 'no-run' durations then the main program can adjust them without restarting the pump cog program.

    If your main program wants to know if a pump is running it can monitor the relevant pump control pin.
    Does your main program need to know if the pump cog is in the 'no-run' window?
    If so, then you can have the pump cogs set flags in HubRAM, or you could use locks set and cleared by each pump cog to indicate its 'no-run' state, with the main program releasing any lock immediately if it manages to set it.

    If the main program just needs to know if the pump cog is in the timing phase ('run' or 'no-run' state), then it would be sufficient to have the pump cog clear the flag or wait until it secures the lock, then run the whole timing cycle before setting the flag or releasing the lock.

    You can even have the main program override the pump cog without restarting it, just by including a checking loop before the pump starts. Then any time the main program wants to prevent an idle pump from running it simply sets the flag or lock and holds it until conditions change.

    So, for this last approach with locks your pump cog core routine would look something like:
    WAITPEQ (THERMO, MASK, 0)  'wait for thermostat trigger
    REPEAT UNTIL NOT LOCKSET (ID) 'loop here if main program has disabled this pump
    OUTA[PUMP] := 1 'start pump
    REPEAT RUN
       WAITCNT (SECOND + CNT) 'wait for RUN seconds
    OUTA[PUMP] := 0 'stop pump
    REPEAT NORUN
       WAITCNT (SECOND + CNT) 'wait for NORUN seconds
    LOCKCLR (ID)   'indicate end of timing cycle
    

    To disable a pump during the timing cycle the main program would need to stop the pump cog, set the lock, and start the pump cog again.
  • I live in LA and my other profession is acting, hence I tend to take a very simple approach to things. That said, nearly every commercial Propeller application I've written runs a background cog that uses fixed timing (1ms) -- this is a convenient base for event timing. By writing a Spin cog, the top level code can access the "background" variables directly. In my case, when a timer is enabled it counts up. What if you want to count down from a specific time? To do this I set the timer to a negative value and then check for 0. Easy peasy.

    I wrote a simple demo that runs four timers in one cog -- no need to waste cogs with such a simple requirement. The Propeller can do a lot of work in one millisecond, so you can easily add more. This is the essential guts of the timer code:
    var
    
      long  bcog                                                    ' cog id of background process
      long  bstack[16]                                              ' stack for background Spin cog
    
      long  t0enable                                                ' timer enables
      long  t1enable
      long  t2enable 
      long  t3enable 
    
      long  t0millis                                                ' timers
      long  t1millis   
      long  t2millis 
      long  t3millis
    
    
    pri background | t
    
      t := cnt                                                      ' sync with system clock
      repeat
        waitcnt(t += MS_001)                                        ' run loop every millisecond
    
        if (t0enable)
          ++t0millis
    
        if (t1enable)
          ++t1millis
    
        if (t2enable)
          ++t2millis
    
        if (t3enable)
          ++t3millis
    
    Yes, it's that easy.

    The demo displays each of the timers in clock format (will milliseconds) and lets you toggle the enable control vars; by timer, or you can stop or start all at once.

    In your program each of the elements will have a state variable that determines the use of its timer.

  • JonnyMacJonnyMac Posts: 8,912
    edited 2019-11-27 17:29
    Here's another option which is probably better suited to your requirements. In your application you seem to want a run time, followed my a minimum off time before an output can be restarted. This is where signed values are handy; in this case a negative timer value means it's running. If it's 0 or positive, we check against a minimum hold-off time. Here is the guts of the timer code and support methods.
    con { timers }
    
      #0, IDLE, RUNNING, HOLD_OFF
    
    
    pub set_timer(ch, runtime, offtime)
    
      if (get_timer_state(ch) == IDLE)                              ' okay to restart?
        timer0[ch] := -runtime                                      ' counts up to 0 when running
        holdoff0[ch] := offtime                                     ' set new minimum off time before idle
    
    
    pub get_timer_state(ch)
    
      if (timer0[ch] < 0)
        return RUNNING
      elseif (timer0[ch] => holdoff0[ch])
        return IDLE
      else
        return HOLD_OFF
    
    
    var
    
      long  tcog
      long  tstack[16]
    
      long  timer0                                                  ' timers
      long  timer1
      long  timer2
      long  timer3
    
      long  holdoff0                                                ' hold-off before next run
      long  holdoff1
      long  holdoff2
      long  holdoff3
    
    
    pri run_timers | t
    
      t := cnt
      repeat
        waitcnt(t += CLK_FREQ)
        ++timer0
        ++timer1
        ++timer2
        ++timer3
    
    Mind you, the simplicity of this code means your timer resolution is +/- one second. That's easily fixed by setting a timing constant which affects the timer loop delay, and scaling/descaling your timer values in and out.

    It seems like you're controlling simple IO. Here's a little method that refreshes an output pin based on the state of the timer.
    pub refresh_pin(pin, ch)
    
      if (timer0[ch] < 0)                                           ' negative is running
        io.high(pin)
      else
        io.low(pin)
    

    Okay... I've been coding since 6AM and it's past 10PM -- that's enough fun for me. I hope this stuff is useful.
  • You guys have been very helpful. I have been away from Spin for several years and I was
    reading my original instruction book propeller manual v 1.1 page 78 about cognew.
    It did not mention that when the when the cognew spin method finished, it would
    release the cog. So I thought it would stay in the cog. And if I called cognew more
    thatn 6 times I would be out of cogs.... Then I read your comments, then I read the
    later official Guide page 34, and it sez when method is done it frees up cog...
    I am a 76 year old retired engineer, and I am absolutely convinced that instruction manuals
    should be written by people who have learned the hard way, and NOT by the guys
    that invented the device and know it inside out … This is a Great forum, and you
    guys have been very helpful. Thanks.
  • JonnyMacJonnyMac Posts: 8,912
    edited 2019-11-27 21:07
    It did not mention that when the when the cognew spin method finished, it would release the cog

    Indeed, it does. In many of my commercial apps I use a "background" cog that calls other routines; the background cog runs an infinite loop so that it doesn't stop itself.

    Here's a real-world example from my HC-8+ controller template; this keeps track of a global milliseconds value (for timing), manages an RG LED to give red, green, and yellow -- with flashing of any two colors -- and process inputs through a couple of shift register. I like keeping my actual "background" method very tidy.
    con
    
      { ----------------------------- }
      {  B A C K G R O U N D   C O G  }
      {  - global milliseconds        }
      {  - R/G LED                    }
      {  - TTL/DMX inputs scanning    }
      { ----------------------------- }
      
    
    var  
    
      long  bcog                                                    ' background cog #
      long  bcstack[32]                                             ' stack for background cog
                                                                     
      long  millis                                                  ' global milliseconds register
                                                                     
                                                                     
    pri background | t                                              ' launch with cognew()
                                                                     
      io.low(R_LED)                                                 ' setup R/G LED pins
      io.low(G_LED)                                                  
                                                                     
      io.high(LD_165)                                               ' setup x165 io pins
      io.low(CLK_165)                                                
      io.input(DO_165)                                               
                                                                     
      millis := 0                                                    
                                                                     
      t := cnt                                                      ' sync loop timer
      repeat                                                         
        waitcnt(t += MS_001)                                        ' run loop every millisecond
        ++millis                                                     
        refresh_rg_led                                              ' update the R/G LED
        scan_ttl_ins                                                ' re-scan TTL & DMX inputs
                                                                     
                                                                     
    var                                                              
                                                                     
      long  rgycolor[2]                                             ' led phase colors
      long  rgytime[2]                                              ' led phase timing (ms)
      long  rgphase                                                 ' current phase (red or green chip)
      long  phasetimer                                              ' time in phase
      long  rgtimer                                                 ' timer for rg process
                                                                     
                                                                     
    pri refresh_rg_led                                              ' call only from support()
                                                                     
      if (++phasetimer => rgytime[rgphase])                         ' done with this phase?
        phasetimer := 0                                             ' yes, reset timer
        rgphase := 1 - rgphase                                      '  and invert phase
                                                                     
      if (++rgtimer == 16)                                           
        rgtimer := 0                                                 
                                                                     
      case rgycolor[rgphase]                                        ' set led to color for phase
        OFF:                                                         
          outa[R_LED..G_LED] := %00                                  
                                                                     
        GRN:
          outa[R_LED..G_LED] := %01  
                                                                     
        RED:
          outa[R_LED..G_LED] := %10 
                                                                       
        YEL:
          if (rgtimer < 2)                                                                       
            outa[R_LED..G_LED] := %10                               ' 1 red                        
          else                                                                                   
            outa[R_LED..G_LED] := %01                               ' 15                  
                                                              
    
    var
    
      long  ttlpins
      long  dmxaddr                                                  
    
                                                                    
    pri scan_ttl_ins : tempin                                         
                                                                     
    '' Scan TTL and DMX address inputs                               
                                                                     
      outa[LD_165] := 0                                             ' blip Shift/Load line    
      outa[LD_165] := 1
    
      tempin := ina[DMX_A8]                                         ' bit16                                           
    
      ' unrolled for best speed
    
      tempin := (tempin << 1) | ina[DO_165]                         ' bit15
      outa[CLK_165] := 1                                            ' blip clock  
      outa[CLK_165] := 0                                                        
      tempin := (tempin << 1) | ina[DO_165]
      outa[CLK_165] := 1                   
      outa[CLK_165] := 0                   
      tempin := (tempin << 1) | ina[DO_165]
      outa[CLK_165] := 1                   
      outa[CLK_165] := 0                   
      tempin := (tempin << 1) | ina[DO_165]
      outa[CLK_165] := 1                   
      outa[CLK_165] := 0
      tempin := (tempin << 1) | ina[DO_165] 
      outa[CLK_165] := 1                    
      outa[CLK_165] := 0                    
      tempin := (tempin << 1) | ina[DO_165] 
      outa[CLK_165] := 1                    
      outa[CLK_165] := 0                    
      tempin := (tempin << 1) | ina[DO_165] 
      outa[CLK_165] := 1                    
      outa[CLK_165] := 0
      tempin := (tempin << 1) | ina[DO_165] 
      outa[CLK_165] := 1                    
      outa[CLK_165] := 0
      
      tempin := (tempin << 1) | ina[DO_165]                         ' bit7
      outa[CLK_165] := 1                    
      outa[CLK_165] := 0                    
      tempin := (tempin << 1) | ina[DO_165] 
      outa[CLK_165] := 1                    
      outa[CLK_165] := 0                    
      tempin := (tempin << 1) | ina[DO_165] 
      outa[CLK_165] := 1                    
      outa[CLK_165] := 0                    
      tempin := (tempin << 1) | ina[DO_165] 
      outa[CLK_165] := 1                    
      outa[CLK_165] := 0
      tempin := (tempin << 1) | ina[DO_165] 
      outa[CLK_165] := 1                    
      outa[CLK_165] := 0                    
      tempin := (tempin << 1) | ina[DO_165] 
      outa[CLK_165] := 1                    
      outa[CLK_165] := 0                    
      tempin := (tempin << 1) | ina[DO_165] 
      outa[CLK_165] := 1                    
      outa[CLK_165] := 0                                       
      tempin := (tempin << 1) | ina[DO_165]                         ' bit0                    
                                                             
      ttlpins := tempin.byte[0]                                     ' update global vars
      dmxaddr := tempin >> 8
    
Sign In or Register to comment.