Quantcast
Channel: JeeLabs
Viewing all articles
Browse latest Browse all 265

Let's try out the multi-tasker

$
0
0

There’s been a multi-tasker hiding in the Embello repository for some time now. It’s a small variation of the one provided as part of the Mecrisp distribution, also on GitHub.

The multi-tasker lets us do multiple things at once, or more precisely: it can quickly switch between tasks, each with their own data and return stacks, and their own program counter. The result is that you can write a thread of code as if nothing else is of interest… as we’ll see.

But this is not quite the multi-tasking you might expect from an RTOS, which usually does everything based on interrupts from timers and other hardware peripherals. The multi-tasking traditionally used in Forth is cooperative, not pre-emptive: instead of using interrupts to force tasks to relinquish control, cooperative multi-tasking relies on tasks voluntarily passing control to other tasks from time to time. If everyone plays nice, everyone will get a fair deal.

This has some major implications:

  • cooperatve multi-tasking is much simpler (under 100 lines of Forth)
  • there is no risk of being interrupted, your code always runs to completion
  • the only time when a task loses control is when it calls pause
  • it is upon the programmer to make sure that this happens “often enough”
  • tasks are either “active” (running every so often) or “idle” (not running)
  • interrupts can be used to change the active/idle state of any task
  • new tasks can be dynamically added or removed from the list of all tasks
  • lastly, the multi-tasker can be started and stopped at any time

There’s a lot to take in when it comes to even just this simple form of multi-tasking, but as you will see, it’s very effective and an excellent match for Forth’s interactive nature. Let’s dive in!

Here’s a small example to blink an LED on pin PA12 as a background task:

PA12 constant LED1
OMODE-PP LED1 io-mode!

task: blinker

: blink& ( -- )
  blinker activate
  begin
    LED1 iox!   \ toggle LED1
    200 ms      \ wait 200 ms
  again ;

So far, nothing has been activated: this merely sets up the LED, defines a task called blinker, and defines a word which becomes the “handler” for this task when it’s running. So blinker is the task descriptor, while blink& is the actual task code and logic.

There is always one boot task, as we can see by entering the tasks command:

Task @ 20004AC0 Next: 20004AC0 State: FFFFFFFF Stack: 00000000 Handler: 00000000

To insert the blinker task into the task list, we need to call blink&, then tasks will show:

Task @ 20004AC0 Next: 20000BA8 State: FFFFFFFF Stack: 20000278 Handler: 00000000
Task @ 20000BA8 Next: 20004AC0 State: FFFFFFFF Stack: 20000CB8 Handler: 00000000

Note how the “Next” chain is a circular list. But… wait a minute… the LED is not blinking!

Oops, we need to enable multi-tasking: Forth is listening for commands and executing them, but multi-tasking dispatch has not been enabled yet. To start it up, type multitask - the LED starts blinking and our command prompt is still responsive. Forth is now dual-tasking!

To disable the multi-tasker, enter singletask. This is a very useful feature: when developing, you often need to stop all the background activity - especially if it’s generating its own output.

For a larger example, see g6s/ex/tasks.fs, which starts up 4 tasks in the background, each controlling its own LED and at a different rate.

There’s a stop word, which causes the current task to make itself inactive (idle). If you type stop at the command prompt, you’ll have deactivated the command prompt, but the LED(s) will keep on blinking. There are ways to get the prompt back, but it’s usually not a good idea.

Recall that this is collaborative multi-tasking, which only switches tasks when pause is called - so how come it’s switching tasks, even though there are no calls to pause in our code?

Well, the calls are in there, in a few places in the embello source code so far, in fact:

  • when calling “ms”, i.e. when requesting millisecond delays (but not in “us”)
  • when calling “key?” (or “key”, which calls key? internally)

Knowing exactly where these pause calls are made is essential if you need to stay in control in your code. The above two cases are fairly logical ones, since they both indicate that a task has nothing else to do, at least for a little while. Eventually, it’ll be given control again.

And that’s really the gist of it: with just the above very basic introduction to the Forth multi-tasker, you can start to write code which does a lot of things at “more or less” the same time. Each of the tasks can have loops and perform its work as if nothing else needs to happen - as long as you make sure that pause gets called regularly (within a few milliseconds is usually fine, but it really depends on the application).


Viewing all articles
Browse latest Browse all 265

Trending Articles