1 Core 2 Activities
One tiny spin program in 1 cog multi-tasks 2 simple and separate activities - blinking 2 LEDs. Blinking rate can be slow: half second on/off for one LED, 1 second on/off for the other. What is the smallest code example to do this?
Thanks in advance,
Humanoido
Thanks in advance,
Humanoido

Comments
But if the tasks are "cooperative" they can get along just fine.
My approach is:
1) Have one method per task.
2) Each method is called every repeatedly at some suitable iteration rate by a main loop.
3) Each task method maintains a "state variable" so that it does not forget what it was doing between calls.
4) A case statement can be used to direct activity within the task depending on the state variable.
4) Tasks may not hang up on repeat loops or waitxx.
This may seem very crude, but I have worked on Rolls-Royce jet engine management systems that were basically organized like this.
Below is a simple example for your flashing LEDs case. Only two states per task ON or OFF.
'Example of two tasks in Spin. ' CON _clkmode = xtal1 + pll16x _xinfreq = 6_553_600 OFF = 0 ON = 1 VAR 'Task state variables byte state0 byte state1 'Millisecond counter long ms PUB start 'Outputs, P0 for task0, P1 for task1 OUTA := %00 DIRA := %11 'Repeatedly run each tasks method every 1ms repeat task0 task1 waitcnt(clkfreq / 1000 + cnt) '1ms delay ms++ 'N.B. Tasks may not hang up for long periods in repeat loops ' Rather they would peform one iteration of a loop on each call ' remembering the task state and iteration count ready for the next call. 'Task 0 flashes LED 0 every second PRI task0 case state0 OFF: if (ms // 500) == 0 DIRA := %01 state0 := ON ON: if (ms // 500) == 0 DIRA := %00 state0 := OFF OTHER: 'WTF? 'Task 1 flashes LED 1 twice every second PRI task1 case state1 OFF: if (ms // 250) == 0 DIRA := %10 state1 := ON ON: if (ms // 250) == 0 DIRA := %00 state1 := OFF OTHER: 'WTF?Basically set each counter to control the output of one of the pins at the desired frequencies.
Humanoido
Edit: I think I see it now, it must be a faster crystal you're using.
Added this to CON but still not blinking:
_clkmode = xtal1 + pll16x ' Feedback and PLL multiplier
_xinfreq = 5_000_000 ' External oscillator = 5 MHz
The LEDs are controlled through dira which means for the demoboard outa has to be initialised to %11 (it may well work on his h/w). The other thing is that both tasks interfere with each others LED pins.
Change the dira setup to:
This then works for me on the demoboard (adjusted for 16/17).
Thank you very much for your assistance!
Humanoido
But I think you are looking for co-operative tasking in general.
The right way would be to use a greatest common denominator time cycle.
So create a state machine that is initiated 100 times a second.
A=A+1
B=B+1
IF A>50 LED0 turned on
IF A>100 LED0 turned OFF, A=0
IF B>100 LED1 turned on
IF B>200 LED1 turned OFF, B=0
Now, if the code in the tasks gets a bit complicated and the they are very different I would be inclined to put the task methods and their associated variables into their own separate Spin files as different objects. The top level "scheduler" loop would then be in a top level object by itself.
Of course if you do that you only need to write one LED task object in this case, create two instances of it with OBJ and pass the LED flash period in as a parameter.
One "deep" thing about this technique is that if you make the rule that there can be no loops (repeat etc) in the task methods and no waitxxx then it becomes possible for some code analysis program to find all the possible pathways through each task. Including any subroutine calls. For each pathway it could know which instructions are executed and how long they take to execute. This analysis can then tell you exactly how much of the "scheduler" time tick period each task takes in the worst case. That is to say, every time you change and recompile the code the analysis can tell you if you are running out of time. For those Rolls-Royce engine projects we had just such analysis built into the compiler.
Kuroneko, no confusion, it was your comments which sorted things out, thank you. Also good that you ran the program on a Demo Board too.
Humanoido
A=A+1
B=B+1
IF A>50 LED1 turned on
IF A>100 LED1 turned OFF, A=0
IF B>100 LED1 turned on
IF B>200 LED1 turned OFF, B=0
Tony P : Definitely looks like there's a place for both counters and a greatest common denominator time cycle from within the code! It's also a clean straightforward way of embedding state machines within code using a looping technique. It can also serve to create sub-processors. Thank you for pointing this out.Humanoido
Humanoido
Good idea - it would make writing and keeping track of more tasks easier.
Heater, this is another brilliant and logical idea - we must say - that Rolls-Royce jet engine project is having a positive effect on this forum thread.
Heater, as a next step to expand the program, what is involved in putting 8 LEDs in the program, instead of only 2? (I have LEDs on p0 through p7).. hint ..hint..
Humanoido
' ' Example of four tasks in Spin. ' controls 4 LEDs ' CON ' _clkmode = xtal1 + pll16x ' Feedback and PLL multiplier ' _xinfreq = 5_000_000 ' External oscillator = 5 MHz _clkmode = xtal1+pll16x ' Overclocked 6.25MHz crystal for 100MHz _xinfreq = 6_250_000 ' External oscillator = 6.25 MHz OFF = 0 ON = 1 VAR 'Task state variables byte state0 byte state1 long ms 'Millisecond counter PUB start 'Outputs, P0 for task0, P1 for task1 DIRA := %1111 repeat 'Repeatedly run each tasks method every 1ms task0 task1 task2 task3 waitcnt(clkfreq / 1000 + cnt) '1ms delay ms++ 'N.B. Tasks may not hang up for long periods in repeat loops ' Rather they would peform one iteration of a loop on each call ' remembering the task state and iteration count ready for the next call. PRI task0 'Task 0 flashes LED 0 every second case state0 OFF: if (ms // 500) == 0 outa |= %01 state0 := ON ON: if (ms // 500) == 0 outa ^= %01 state0 := OFF OTHER: PRI task1 'Task 1 flashes LED 1 twice every second case state1 OFF: if (ms // 250) == 0 outa |= %10 state1 := ON ON: if (ms // 250) == 0 outa ^= %10 state1 := OFF OTHER: PRI task2 'Task 2 flashes LED 2 every second case state0 OFF: if (ms // 500) == 0 outa |= %100 state0 := ON ON: if (ms // 500) == 0 outa ^= %100 state0 := OFF OTHER: PRI task3 'Task 3 flashes LED 3 twice every second case state1 OFF: if (ms // 250) == 0 outa |= %1000 state1 := ON ON: if (ms // 250) == 0 outa ^= %1000 state1 := OFF OTHER:Here is an 8 LED cooperative task scheduler example with only one actual LED flasher method with parameters to select which LED and period it should use. The flasher method is now in its own spin file as separate object. That save the confusuion over which vars belong to which task.
I have not run this. May wan to change the 1ms time tick to 10ms if Spin can't keep up.
'Example of eight cooperative tasks in Spin. ' CON _clkmode = xtal1 + pll16x _xinfreq = 5_000_000 OBJ 'Create 8 instances of the LED flasher task object task_led_0 : "task_led_flasher" task_led_1 : "task_led_flasher" task_led_2 : "task_led_flasher" task_led_3 : "task_led_flasher" task_led_4 : "task_led_flasher" task_led_5 : "task_led_flasher" task_led_6 : "task_led_flasher" task_led_7 : "task_led_flasher" PUB start 'Initialize 8 LED flasher tasks with different LED pins 'and flash periods. task_led_0.init(%00000000, 100) task_led_1.init(%00000010, 110) task_led_2.init(%00000100, 120) task_led_3.init(%00001000, 130) task_led_4.init(%00010000, 140) task_led_5.init(%00100000, 150) task_led_6.init(%01000000, 160) task_led_7.init(%10000000, 170) 'Repeatedly run each tasks run method every 1ms repeat task_led_0.run task_led_1.run task_led_2.run task_led_3.run task_led_4.run task_led_5.run task_led_6.run task_led_7.run waitcnt(clkfreq / 1000 + cnt) '1ms delay'LED flasher cooperative task. CON 'Possible task states. STATE_OFF = 0 STATE_ON = 1 VAR LONG state 'State of the task LONG ms 'Count milliseconds here LONG led 'Pin to use for a LED LONG period 'Flash period 'Call this first to initialise the task. PUB init(_led, _period) led := _led period := _period OUTA := 0 DIRA := led 'Call this every millisecond to "kick" the task along 'N.B. Tasks may not hang up for long periods in repeat loops ' Rather they would peform one iteration of a loop on each call ' remembering the task state and iteration count ready for the next call. PUB run ms++ case state STATE_OFF: if (ms // period) == 0 OUTA := led state := STATE_ON STATE_ON: if (ms // period) == 0 OUTA := 0 state := STATE_OFF OTHER: 'WTF?' Example of four tasks in Spin. ' controls 4 LEDs ' CON ' _clkmode = xtal1 + pll16x ' Feedback and PLL multiplier ' _xinfreq = 5_000_000 ' External oscillator = 5 MHz _clkmode = xtal1+pll16x ' Overclocked 6.25MHz crystal for 100MHz _xinfreq = 6_250_000 ' External oscillator = 6.25 MHz OFF = 0 ON = 1 VAR 'Task state variables byte state0 byte state1 long ms 'Millisecond counter PUB start 'Outputs, P0 for task0, P1 for task1 DIRA := %1111 repeat 'Repeatedly run each tasks method every 1ms task0 task1 task2 task3 waitcnt(clkfreq / 1000 + cnt) '1ms delay ms++ 'N.B. Tasks may not hang up for long periods in repeat loops ' Rather they would peform one iteration of a loop on each call ' remembering the task state and iteration count ready for the next call. PRI task0 'Task 0 flashes LED 0 every second case state0 OFF: if (ms // 500) == 0 outa |= %01 state0 := ON ON: if (ms // 500) == 0 outa ^= %01 state0 := OFF OTHER: PRI task1 'Task 1 flashes LED 1 twice every second case state1 OFF: if (ms // 250) == 0 outa |= %10 state1 := ON ON: if (ms // 250) == 0 outa ^= %10 state1 := OFF OTHER: PRI task2 'Task 2 flashes LED 2 every second case state2 OFF: if (ms // 500) == 0 outa |= %100 state2 := ON ON: if (ms // 500) == 0 outa ^= %100 state2 := OFF OTHER: PRI task3 'Task 3 flashes LED 3 twice every second case state3 OFF: if (ms // 250) == 0 outa |= %1000 state3 := ON ON: if (ms // 250) == 0 outa ^= %1000 state3 := OFF OTHER:Heater said "Here is an 8 LED cooperative task scheduler example with only one actual LED flasher method with parameters to select which LED and period it should use. The flasher method is now in its own spin file as separate object. That save the confusuion over which vars belong to which task. I have not run this. May wan to change the 1ms time tick to 10ms if Spin can't keep up."
I must say this code is remarkable! However, I ran it "as is" and nothing lights up. Next I will try the 10ms and let you know the results.
Humanoido
humanoido
Each task instance should be sure to only disturb it's own pins direction and state.
So remove the OUTA from the init method and change the DIRA to:
Change the OUTAs in the run method like so:
Do I have to solder some LEDs to my TriBlade Prop #3 ?
The init call for LED 0 does not have bit zero set in the LED parameter, should be:
"I can try another 8. What do you think?"
Now that you have that working it's easily extendable to 16 LEDs:)
I already have the original program working on 10 LEDs now. It goes from P7 on the header to P16 using the LEDs soldered on the demo board. It's a very nice arrangement. I'll continue adding LEDs until I get to 16. After that, I can harvest 4 more ports from the TV circuit for a total of 20 LEDs.
Heater, nice work, so if you were to speculate about how many (maximum) LED threads can run, with either program, what do you think? 128?
No idea. Of course you run out of memory at some point. Or you run out of time, perhaps 1ms is to fine grained.
Actually thinking about time, that 1ms timer tick will drift off depending on the execution time of all the thread run calls. so if one of our tasks was responsible for counting seconds, minutes, hours etc as a real time clock it would soon be wrong.
If we want more accurate timer ticks we should really do something like:
PUB start 'Initialsie 8 LED flasher tasks with different LED pins 'and flash periods. task_led_0.init(%00000001, 100) task_led_1.init(%00000010, 110) task_led_2.init(%00000100, 120) task_led_3.init(%00001000, 130) task_led_4.init(%00010000, 140) task_led_5.init(%00100000, 150) task_led_6.init(%01000000, 160) task_led_7.init(%10000000, 170) OneMS := clkfreq / 1000 'Calculate cycles per 1 millisecond TimeBase := cnt 'Get current count 'Repeatedly run each tasks run method every 1ms repeat waitcnt(TimeBase += OneMS) 'Wait to start of next millisecond task_led_0.run task_led_1.run task_led_2.run task_led_3.run task_led_4.run task_led_5.run task_led_6.run task_led_7.runAs shown in the propeller manual p74.
With that in place everything goes well until the total execution time of all the task calls exceeds 1ms then the waitcnt will hang up until CNT rolls over, 20 seconds or so.
But you've outgrown this method already. There are only two counters per cog, and you're way past two LEDs. The counters could provide a resettable counting value, but as you're progressing, they really don't offer anything over using the cnt register.
John R.
Humanoido
EDIT:I just remembered a way to double and triple the LEDs using the same number of ports. It's a technique used on BASIC Stamps. So an average of 25 displays can become 50 or 75. That gives some upward mobility..
Consider it done. The new code with a more accurate timebase is up and running!
I knew that would happen at some point. It needs a warning light, a red LED that lights steady to indicate an imminent rollover in the next cycle.
Humanoido
No, when we run out of RAM on the Prop we just virtualize it and continue. Same with LEDs:)
I'm confused...... based on your posts under "more cogs from one prop" and "cloning leds" , are you trying to see how many LED's you can light up with independent (deterministic?) timings, or are you trying to see how many independent threads can be supported under SPIN, and just using the LED's as indicators of that?
Cheers,
Peter (pjv)
Humanoido