18M2 Multitasking

BeanieBots

Moderator
Just been having a quick play with the AXE181 touch sensor demo board.
I'm a bit confused by the behaviour of the following code:-

Code:
#picaxe 18m2

start0:
do
toggle B.4
pause 300
loop


start1:
do
toggle b.5
pause 290
loop

start2:
do
toggle b.6
pause 280
loop

start3:
do
toggle b.7
pause 270
loop
I was expecting to have four LEDs flashing with a slight variation in flash rate.
Outputs B.4 and B.7 do indeed toggle as expected but B.5 and B.6 are perfectly synchronised with each other:confused:

A variation in toggle rate is only observed when the difference in pause values gets quite a bit larger.

I'm sure this is related to multi-tasking overhead but surely not > 10mS?
 

MPep

Senior Member
I tried simulating this but found that there is no "picaxe18m2.exe" file in the compiler folder. Am using PE v5.2.11.
Any ideas?
 

BeanieBots

Moderator
You need version 5.3.0 to use the 18M2.

Interestingly, in simulation the behaviour is quite different to the silicon but still not what is expected. Also, only thread 0 appears to run (indicated by highlighted lines) yet all outputs toggle albeit in a very synchronised sequence:confused:
 

MartinM57

Moderator
Interesting...

A quick 4-channel scope check shows exactly what you observe:
B.4 - 300ms pause
B.5 - 280ms pause
B.6 - 280ms pause
B.7 - 260ms pause
..and B.5 and B.6 are in perfect synchronisation at all times, with B.4 and B.7 changing relative to each other all the time.

FYI, with the pauses taken out, the leading edges of B.4 to B.7 are all delayed by 172uS each, the toggle pulses are 1.75ms on/off - and B.5 and B.6 are NOT synchronised
 

hippy

Ex-Staff (retired)
You missed a note under the PAUSE command -

During M2 part multi task programs the accuracy of pause is reduced due to the parallel processing. The minimum resolution is around 20ms in multi task programs. For greater accuracy use single task mode.

So what's happening is that B.5 (290) has rounded down to the same as B.6 (280).

To watch simulations of particular tasks - uses "#SIMTASK <task>", and to really confuse yourself ... "#SIMTASK ALL" :)
 

MPep

Senior Member
I did not see any mention of v5.3.0 being released.
I see it is now up to v5.3.1
Will download.
 

BeanieBots

Moderator
Thanks for the quick reply Hippy.
I had missed (or rather forgotten) the bit about pause though at the time I'd taken it to read as "less accurate" which sounded fair and reasonable at the time rather than 'reduced resolution' which appears to be the case.

Using pause values with larger differences (typ 30) does indeed result in the expected sequences.

The "#SIMTASK ALL" directive does make the simulator show all tasks running but they do not 'quite' follow the same sequence as the silicon.
eg pause values of 300,280,260 & 240.
 

MPep

Senior Member
@Technical,

Perhaps a mention that v5.3.0 and v5.3.1 were out should have been made seperately, as opposed to being part of the 18M2 release.

@BeanieBots,

I would not have known about 5.3.1 if you had not mentioned it!
That's why I like this forum. Everybody tends to try and help, and learn from, each other, without egos getting in the way.

MPep.
 

hippy

Ex-Staff (retired)
The "#SIMTASK ALL" directive does make the simulator show all tasks running but they do not 'quite' follow the same sequence as the silicon.
eg pause values of 300,280,260 & 240.
That's likely to be case.

A rough overview of the hardware would be that the scheduler is round-robin and is entered after every every basic command, the internal clock is checked to see if 20ms has elapsed, task pause values ( if any ) are updated, the next available runnable task is selected, one of its basic command is executed.

For the simulator there isn't the fine granularity for timings to replicate exactly what real hardware would do so it's a close approximation but not a perfect emulation.

"#SIMTASK ALL" isn't very use in most debugging where you'd normally want to watch one task but is useful if there is a dead-lock and similar cases, and to give an illustration of what's happening when it's firing on all four cylinders.
 

Haku

Senior Member
I've been meaning to ask and this seems the appropriate thread;

Is there a list of commands/functions that are impaired by or impair the multitasking capabilities of the 18m2?

It would be handy to know those commands in case I run into a situation like BeanieBots discovered where the chip isn't behaving how you expect it to.
 

MPep

Senior Member
Resume command, from manual
Code:
start0:
high B.0 ; B.0 high
pause 100 ; wait for 0.1 second
low B.0 ; B.0 low
pause 100 ; wait for 0.1 second
goto start0 ; loop
start1:
pause 5000 ; wait 5 seconds
suspend 0 ; suspend task 0
pause 5000 ; wait 5 seconds
resume 0 ; [U][B]suspend[/B][/U] task 0
goto start1 ; loop
I take it that the bold suspend is incorrect and that resume should have been placed here.
 

hippy

Ex-Staff (retired)
@ Haku : I'm not sure if there's an explicit list but I believe it's only SETFREQ and PAUSE affected plus commands which depend on chip operating frequency ( eg PWM ).

@ MPep : Yes, the comment is wrong. I've made a note of that.
 

Technical

Technical Support
Staff member
The biggest issue with simulating small delays is that the ProgEdit simulator, by default, has to introduce a delay between each command so that the highlighted line can actually be seen. In fact the user can alter this time delay via View>Options.

With multi tasking this effect is amplified as you get a delay between each command in each task, so the simulation is always going to be a best attempt. Although extremely useful it will never be exactly the same as a real life chip.
 

Technical

Technical Support
Staff member
I've been meaning to ask and this seems the appropriate thread;

Is there a list of commands/functions that are impaired by or impair the multitasking capabilities of the 18m2?

It would be handy to know those commands in case I run into a situation like BeanieBots discovered where the chip isn't behaving how you expect it to.
See page 60 of the part1 of the latest release manual (7.0), but here is a summary:

pause, 20ms resolution
setfreq, not available
pwmout, not recommended due to possible frequency shifts
servo, may be a bit more twitchy but still operates
frequency dependant commands (e.g. count) all operate at 4MHz
RAM bytes 128-255 (peek/poke, @bptr) reserved for tasks
 

techElder

Well-known member
Just to get the juices flowing, can we get some simple (or complex) examples where the multitasking would be useful?
 

hippy

Ex-Staff (retired)
One thing for which multi-tasking is useful for is "I'm Alive!" indication such as a flashing LED. With multi-tasking it's a simple case of adding an extra task such as -

Start3:
Do
Toggle LED
Pause 500
Loop

It's also useful if you have two buttons which being pushed do two different things. While it's often easy enough to put both in a single loop of a single task it can make it easier to deal with separate tasks, one per button, each doing their own thing.

Likewise we could adopt our earlier LED flashing task such that the pause time was controllable by a 'flashSpeed' variable instead of a fixed pause time. Other tasks can then simply set 'flashSpeed' as required and the LED handling task will automatically pick it up.

It's often noted that RANDOM is improved if the command is called repeatedly within a loop. We can add a task which does that automatically; put "RANDOM randomNumber" in a looping task and other tasks can simply use the 'randomNumber' variable.

It can also be useful in 'timeout applications' one task can simply reset a timeout variable ( or the 'time' variable ) while another simply increments it ( or just watches if 'time' ) and when it exceeds a certain value it can take some action or indicate the timeout.

In many cases it will be about moving code 'off to somewhere else where it can be forgotten about' while developing the rest of the program.
 
Last edited:

hippy

Ex-Staff (retired)
Here's a simple "Lost" - have to enter the numbers or the world explodes - application. If you don't press the button on Input Pin C.0 within 108 seconds of the last push - ">KABOOM<" ...

Code:
#Picaxe 18M2

Start0:
  Do
    If pinC.0 = 1 Then
      time = 0
    End If
  Loop

Start1:
  Do
    If time > 108 Then
      SerTxd( ">KABOOM<", CR, LF )
    End If
    Pause 1000
  Loop
Part of the benefit of multi-tasking is that you can examine how each task works and understand that, even without knowing the other task.

Want give an audible warning that you must push the button and the world's about to end ? Just add ...

Code:
Start2:
  Do
    If time > 98 And Time < 108 Then
      Sound B.0, ( 100,10 )
    End If
    Pause 500
  Loop
Don't want to use the internal 'time' variable then use another, say 'myTicker'. But then you have to increment that every second or nothing happens, time doesn't elapse. One way would be ...

Code:
Start3:
  Do
    Pause 1000
    Inc myTicker
  Loop
 
Last edited:

boriz

Senior Member
Also good for anything an interrupt is good for only better, coz your primary code continues to run. And you can have three of them running concurrently.

Hey Hippy, how does it effect Interrupts? Can each thread have its own interrupt running? Could that be manipulated for effectively 7 interrupts? (One real interrupt per thread + 3 extra ‘manual’ interrupts using basic LOOP:TEST code in slots 1 to 3)
 

Technical

Technical Support
Staff member
end before start1: is useful, but not needed if you never get there (as with loop in Hippy's example)

There is one interrupt, which stops all tasks, executes, and then restarts all tasks.
 

hippy

Ex-Staff (retired)
Best to call them "tasks" or you'll get confusion with X2's which do have "slots" and are something else :)

What happens is that Task 0 will start running Task 1 code ...

Start0:
SerTxd( "Whoopy ")
Start1:
Do
SerTxd( "Doo " )
Pause 1000
Loop

And you'll get "Whoopy Doo Doo <pause> Doo Doo <pause>" etc.

This can get more problematic controlling I/O ...

Start0:
Start1:
Do
Toggle LED
Pause 1000
Loop

0 : Toggle ( LED becomes 1 )
1 : Toggle ( LED becomes 0 )
0 : Pause
1 : Pause

You will see a brief blip on the LED rather than the toggling you expect
 

boriz

Senior Member
I see. Thanks.

Since the Start0: directive is always implied, I wonder how long it’ll be before people start asking for help because their program does strange things and begins with Start1: ?
 

BeanieBots

Moderator
Since the Start0: directive is always implied, I wonder how long it&#8217;ll be before people start asking for help because their program does strange things and begins with Start1: ?
Oh, I think we're in for a lot of fun with this one:rolleyes:

As for uses, I think Hippy has hit the nail on the head. One of the tricky issues with non multitasking is the monitoring of variables and activation of sounders/LEDs that need to flash yet not upset regular control functions. Hence, alarms and similar will be made much simpler functions to develop.

A recent example for me was a PID contoller which had to monitor an alarm temperture and a count-down timer. If a timeout, the alarm had to sound five times every minute for ten minutes and be cancelled by any push button. If over-temp, the alarm had to sound every second. Meanwhile, the PID loop had to carry on doing its thing. With hindsight, this would have been much easier with an 08M doing the alarm sounds, triggered by a simple high on an input.
With an 18M2, it would be very simple.
 

boriz

Senior Member
Hey, you could do something like this:

Code:
Start0:
Do
    Stuff
    Stuff
    If short alarm required, set ALARM = 5
    If long alarm required, set ALARM = 20
Loop

Start1:
Do
    If ALARM>0 then
        Make sound
        Pause 300
        Dec ALARM
    Endif
Loop
Hey. I’m starting to get some cool ideas now. This multitasking lark is going to be a hoot.
 

hippy

Ex-Staff (retired)
That's the idea.

Imagine you have a PICAXE alarm clock program. You've written it so it pulses a LED when the alarm goes off but then you want to add an audible alarm plus an "Alarm Stop" and a "Snooze" button. It can be quite complicated to get all that 'mashed up' with an existing main loop and keep everything clean and simple - and this is remarkably similar to BeanieBots' PID alarm.

In the 'good old days' it might be best to use another PICAXE which looks for a pulse and handles the alarm buzzer and the two buttons, it gets triggered, when all is done waits for the next trigger. With multi-tasking you can just write the 'clock task' and an 'alarm task' and each does only what it's meant to do.

Let's assume it's an important alarm, you need to get someone there as soon as possible - you've got one alarm handler with stop and snooze. Why not add another task and you can have independent alarms in two or more zones, each working entirely independently when triggered.

Of course there's still the 'blocking issue' which comes from being single core so some things won't always suit themselves to multi-tasking and may need multiple PICAXE but there's now the possibility of multiple PICAXE and some of those PICAXE can be multi-tasking as well !
 

BeanieBots

Moderator
Of course there's still the 'blocking issue' which comes from being single core so some things won't always suit themselves to multi-tasking and may need multiple PICAXE but there's now the possibility of multiple PICAXE and some of those PICAXE can be multi-tasking as well !
An 18M2 coordinating several X2's conjurs up an image of something really quite powerful. However, I think where it will really score for the larger educational audience is the often asked question of "how can I make it do this and flash an LED at the same time". How many times have we had to explain how to break down long pauses into smaller segments to check for button presses and the like? Now it will be explaining how to set up flags so that tasks can interract with each other.
 

Yessir

Member
end before start1: is useful, but not needed if you never get there (as with loop in Hippy's example)

There is one interrupt, which stops all tasks, executes, and then restarts all tasks.
Two questions if I may:-

I was thinking of using an interrupt which is triggered in one task to suspend or restart a second task. The above quote suggests that this might not work because all tasks are suspended during an interrupt code and then restarted. Is this likely to be the case?

Secondly, the manual states that the setfreq command is not available in multitasking mode. What happens to the frequency if I have 2 tasks running and then suspend one of those tasks. Will the Picaxe frequency effectively increase because it is no longer processing two sets of instructions, or will it remain at 4MHz? Do all setfreq commands just get ignored, or will the Picaxe try to operate faster is I set to 8MHz with one tasks suspended?
 

hippy

Ex-Staff (retired)
On the first question, interrupts - When an interrupt occurs, all runnable tasks ( those not suspended ) are stopped, the interrupt executes and finishes, all previously runnable tasks are again available to be run, all previously suspended tasks will remain suspended unless they have been resumed within the interrupt in which case they become runnable.

On the second, execution speed - SETFREQ is not available. Each task will run as fast as it can with execution time equally divided between them while running. So for four tasks each will execute as if they were each running on a 4MHz PICAXE. Any execution time not used by a task when suspended or in PAUSE will be shared equally between the tasks which are running.

So the general case for programs with only HIGH, LOW and GOTO ..

four tasks running - each runs at approx 4MHz
three tasks running - each runs at approx 5MHz
two tasks running - each runs at approx 8MHz
one task running - that task runs at approx 16MHz

The firmware adjusts operating speed so commands which require specific timing are executed at 4MHz no matter what speed the task could run at so the overall execution speed of any task depends on what it and other tasks are doing.
 

centrex

Senior Member
I have been trying to follow the thread on multitasking and I can see the devices being used to flash leds in the background.
But what about something like measuring temperature with its calculations and the time it takes to do a reading (750ms).
Is it possible to have a number of similar complex programs running in the background feeding data to the main program
Regards
Centrex
 
Last edited:

hippy

Ex-Staff (retired)
It is possible to use multitasking to take multiple temperature readings and feed them into a main program, and using multi-tasking can simplify the program. In this case Task 0 and Task 1 read a particular temperature which will then be used by Task 2. Calculations have been simplified by just moving the raw reading taken into the result variable ...

Code:
#Picaxe 18M2
#No_Data
#Terminal 4800

Symbol houseTempRaw  = w0
Symbol houseTemp     = w1
Symbol gardenTempRaw = w2
Symbol gardenTemp    = w3

Start0:
  Do
    ReadTemp C.0, houseTempRaw
    houseTemp = houseTempRaw
  Loop

Start1:
  Do
    ReadTemp C.1, gardenTempRaw
    gardenTemp = gardenTempRaw
  Loop

Start2:
  Pause 20
  Do
    SerTxd( "House  = ", #houseTemp,  " "    )
    SerTxd( "Garden = ", #gardenTemp, CR, LF )
  Loop
Note though, because it's a single core processor, there will be the 750ms delay for each of the READTEMP commands, so the minimum update period for both temperatures sent will be 1.5 seconds.
 

MartinM57

Moderator
The way I see it is that he multitasking is such that after each complete statement in a task, the scheduler looks for another runnable task and exceutes the next statement there...and so on. Whether that applies to an IF statement (i.e. the IF statement is evaluated and then the task is switched before either the first THEN command or the first ELSE command are run)....I don't know.

So there is no benefit in the multi-tasking for 'hiding' for example the 750mS elapsed time of a READTEMP command
 

hippy

Ex-Staff (retired)
For IF the switch to another task occurs before the THEN. You have to break the high-level block command down into the lowest level commands as the PICAXE implements them ...

If b0 = b1 Then
b2 = 1
Else
b2 = 2
End If

becomes ...

If b0 <> b1 THEN Label1
b2 = 1
Goto Label2
Label1:
b2 = 2
Label2:

the whole of that IF-THEN-Label command is executed and execution will have passed to the next command ( ignoring labels ) so the PICAXE is poised to execute "b2=1" or "b2=2" but will switch to another task if there is another.

Similar things happen within CASE and in BINTOASCII etc but most times it's not something that has to be worried about.
 
Top