timers
in Propeller 1
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
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
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.
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
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 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.
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.
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.
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