| Description: |
What is Multitasking?
A huge advantage the BX-24 has over other microcontrollers is its
ability to perform multiple tasks simultaneously. This is known as
multitasking, and in our 21st-century world, it is something we
take for granted. After all, we have come to expect that with our desktop computers
we are able to
simultaneously play a CD, browse the Internet, and write to our friends
in on-line chat rooms.
However, in today's world of microcontrollers, most processors are not able
to multitask, giving those who program NetMedia's BX-24 a significant advantage
over those who don't. Time and time again, people tell me this is one of
the main reasons they chose the BX-24 over Parallax's BasicStamp and other
similar processors.1
Why is Multitasking Beneficial?
Multitasking is useful anytime you need to run two or more tasks at the same time.
This is especially handy when dealing with robotic motion, for example. Imagine
that you want your robot to move forward while, at the same time, it's turret head
scans left and right for obstacles, or while a gripper's jaws are opening and closing.
If you were to program the robot to do these tasks
without using multitasking techniques, the motion of the robot would be choppy and slow.
The video links in the table below illustrate how multitasking techniques to
greatly enhance a robot equipped with a gripper assembly.
(The robot and the gripper shown in the videos are available from
Robodyssey Systems.)
The code for each program is also given.
| Robot Motion with
Gripper Action |
|
Motion With Multitasking Techniques
Using the multitasking techniques, the motion of both the robot and the
gripper is smooth and uniform.
|
Motion Without Multitasking Techniques
When multitasking techniques are not used, the motion of both the robot and the
gripper is rough and choppy.
|
Multitasking with the BX-24
What I hope to demonstrate with this tutorial is the BX-24's ability to execute two
"tasks" simultaneously. With the following code, I'll show that the
BX-24 can quickly print the first 500 integers to the screen while
the onboard red LED slowly blinks. (Try this with a BasicStamp!)
How NOT to multitask
If we simply use top-down programming to do this,
the numbers being printed be will choppy because they will have to wait for the LED to blink on and
off once before displaying the next number in the sequence. Consider the following program, which
flashes the red LED on for 0.2s and off for 0.5s and then displays an integer to the screen.
(There's no need to enter this code, just study it.)
' ////// This is NOT how to multitask! /////
Public Sub Main()
Dim i as Integer
i = 0
Do Until (i > 500)
i = i + 1 ' Increment i
Debug.Print CStr(i) ' Print i
Call PutPin(25,0) ' Turn Red LED on
Call Delay(0.2) ' pause for 0.2s
Call PutPin(25,1) ' Turn Red LED off
Call Delay(0.5) ' pause for 0.5s
Loop
End Sub
How to multitask
However, if we use multitasking techniques, we'll be able
to seamlessly and simultaneously execute both tasks. Let me show you how.
First, create a BasicX application in its own folder and name it "Multitasking.bas".
Next, within the Main subprogram, create a simple For-To-Next loop that will count to 500.
Enter the code below and execute the program, paying close attention to how long it
takes for the 500 numbers to be printed. (You may wish to use a stopwatch.)
Public Sub Main()
Dim i as Integer
For i = 1 to 500
Debug.Print CStr(i) ' Print i
Next
End Sub
Now, let's create the task that will cause the red LED to blink off and on. Ultimately, this will be
the task that will run simultaneously, side-by-side with the Main subprogram.
The task (or tasks) that you want to run
must be entered as a subprogram. Let's name our subprogram "RedLEDTask" and enter it
below the Main subprogram of our program. There's nothing fancy here -- just your everyday,
run-of-the-mill subprogram.
' ////// RedLEDTask (This will run in the simultaneously.) /////
Public Sub RedLEDTask()
Do
Call PutPin(25,0) ' Turn Red LED on
Call Delay(0.2) ' pause for 0.2s
Call PutPin(25,1) ' Turn Red LED off
Call Delay(0.5) ' pause for 0.5s
Loop
End Sub
Now, here's where the multitasking comes in. What we need to do is get the RedLEDTask subprogram
to run from the RAM stack. To do this, we'll need to allocate a chunk of memory that
will be dedicated to running RedLEDTask. This memory space is set aside using a global variable array, which is
defined by a Dim statement and placed above the the Main subprogram (below Option Explicit):
' ////// Allocate space for RedLEDTask /////
Dim RedLEDStack(1 to 50) as Byte ' Set aside 50 bytes for RedLEDTask
For my program, I created a variable array named RedLEDStack, which will be used to reserve space in RAM for the
RedLEDTask program. (Note the difference between RedLEDStack and RedLEDTask!)
I decided to reserve 50 Bytes of space in RAM for this task; therefore, I dimensionalized my variable array
with the argument: (1 to 50).
So, why did I choose to reserve 50 Bytes? There's not an easy
answer to this question. For now, just assume that the decision to set aside 50 Bytes is a good one.
For a more detailed explanation, see the section below entitled Determining the
Task Stack Size.
There's one final step and then we're done! Somehow, we have to call the RedLEDTask subprogram.
We do this with the CallTask command, the syntax for which is:
CallTask "TaskName", StackVariableName
Because our task uses an infinite Do-Loop, Once called it will never
stop running until you tell it to do so. While stopping a task is not well documented, it is not difficult
to do. See the section entitled Halting the Execution of a Task to learn how to
halt a task.
While we can call a task from anywhere within a program, we should
invoke our RedLEDTask from within the Main subprogram before the For-To-Next loop is executed
in order to see the effect of multitasking! This is done easily enough with the statement:
' ////// Call the RedLEDTask task /////
CallTask "RedLEDTask", RedLEDStack
So, to putting it all together, our "Multitasking.bas" program is written:
Option Explicit
' ////// Allocate space for RedLEDTask /////
Dim RedLEDStack(1 to 50) as Byte ' Set aside 50 bytes for RedLEDTask
' ////// Main /////
Public Sub Main()
Dim i as Integer
CallTask "RedLEDTask", RedLEDStack ' Start the task
For i = 1 to 500
Debug.Print CStr(i) ' Print i
Next
End Sub
' ////// RedLEDTask (This will run simultaneously.) /////
Public Sub RedLEDTask()
Do
Call PutPin(25,0) ' Turn Red LED on
Call Delay(0.2) ' pause for 0.2s
Call PutPin(25,1) ' Turn Red LED off
Call Delay(0.5) ' pause for 0.5s
Loop
End Sub
Run the program, and with any luck, you'll see a bunch of numbers scrolling up your
computer monitor while the BX-24's onboard red LED blinks indefinitely. The important thing
to note, is that the two tasks (i.e., the counting and the blinking) are running simultaneously
and independently.
How cool is that?!
Halting the Execution of a Task 2
Stopping a task once it has started running is not at all well documented and can therefore
appear complicated. Below I show two ways to halt a task.
The first method involves some simple programming logic and event trapping within the task. See my
"AdvancedMultiTasking.bas" program
for an illustration of this method. The main idea here is that I set a Boolean variable
(named TurnOnGreen) to False if I wish the GreenLEDTask task to stop. (See the bold line below.)
Sub GreenLEDTask()
Do While TurnOnGreen
Call PutPin(25,0) ' Turn Red LED on
Call Delay(0.2) ' pause for 0.2s
Call PutPin(25,1) ' Turn Red LED off
Call Delay(0.5) ' pause for 0.5s
Loop
End Sub
The second method is brought to us by Don Kinzer. Don has shown that it is quite easy to halt a
task -- once you've done a lot of research into the matter, that is! With this method,
we can halt a task by manipulating a single byte of the task's stack. You see, The first few bytes of the
task's stack are used to store information about the task. It turns out, the
first byte of the stack can be used to halt the task. By setting the first byte of the stack equal to
bxTaskExit (a built-in BasicX constant), we can halt the execution of any task.
This method is illustrated with my
"StopMultiTasking.bas" program,
which behaves exactly as my "AdvancedMultiTasking" program does, but it manipulates the stack to halt the task.
In my "StopMultiTasking" program,
I stop the execution of a task called GreenLEDTask, whose stack is allocated with the array GreenLEDStack.
To stop the GreenLEDTask, all I have to write is,
GreenLEDStack(1) = bxTaskExit
Pretty easy, huh? Then, to start the task running again all we have to do is enter the line,
CallTask "GreenLEDTask", GreenLEDStack
as usual. Advanced users may be interested in reading Don Kinzer's postings on this matter on
Yahoo's BasicX discussion group. I've placed the relevant links to Don's very useful discovery here:
Determining the Task Stack Size 2
Determining how much memory to set aside for a task is difficult to do. In the above program, I set
aside 50 Bytes of stack space for the RedLEDTask. So, why, did I choose to reserve this
amount of memory? Let's look at it from a number of perspectives.
The BX-24 has only 400 Bytes of RAM, so that is the stack upper limit. (Unfortunately, it is not
possible to add more memory to the BX-24.)
The lower limit is more difficult to define. Basically, you want to reserve as little space as possible
without causing your program to misbehave. If space is not an issue, you won't hurt anything if you
set aside a lot of space, say 200 Bytes, for this subprogram.
As a first-order approximation to the stack space needed, we can use our project's MAP file.
Every time a program is compiled, a MAP file (*.mpp) is created. The MAP file for
the "Multitasking.bas" program will be named
"Multitasking.mpp".
Open this MAP file with some text editor, like Notepad. With the MAP file opened,
scroll down until you find a reference to the RedLEDTask subprogram. Here's what my MAP file
displayed:
Note that the size of the RedLEDTask subprogram ("multitasking.redledtask") is 28 Bytes. (Your
task may eat up a a bit more or a bit less than mine, but it should be close to 28 Bytes.)
This simply means that the actual code takes up 28 Bytes. You should know that the stack memory required
to execute the task is not the same thing as the number of bytes of code given by the MAP file, but it
does give us a ball-park figure to work with. Since the RedLEDTask's code consumed 28 Bytes, I reserved
50 Bytes to act as a cushion.
In actuality, the calculation for stack usage is a complicated one. If space is an issue and you need
to determine the exact amount of memory consumed by the task, let me direct you to the
excellent work of Don Kinzer and Mike Perks. The duo's
BasicX disassembler
and analyzer (bxDism) program
is an excellent tool for anyone interested in dissecting their code and pinpointing memory usage.
Their application, which is available for free at the above link, is a command-line program. (To
invoke the command window, click on the Start button then Run. In the textbox that opens type "cmd"
and hit enter.) Mike has authored a nice user's guide to explain the program's operation and how to run it
from the command window.
When allocating stack space, keep in mind that BasicX needs 15 bytes of the stack
space for housekeeping details. Remember, also, that if you add code to your task,
you should increase the memory allotment for that task!
1 The three most common reasons people choose the
BX-24 over the BasicStamp are:
- The BX-24 has eight built-in A-to-D pins. (The BasicStamp has none.)
- The BX-24 can perform floating point math. (The BasicStamp can't.)
- The BX-24 can multitask. (The BasicStamp can't.)
2 Thanks to Don Kinzer for his valued assistance with this section!
|