Difference between revisions of "MC-Basic Programs"

From SoftMC-Wiki
Jump to: navigation, search
m (Subroutines)
(BackToTop button)
 
(18 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 
{{Out-of-date}}
 
{{Out-of-date}}
 +
<div id="BackToTop"  class="noprint" style="background-color:; position:fixed; bottom:32px; left:95%; z-index:9999; padding:0; margin:0;">
 +
<span style="color:blue; font-size:8pt; font-face:verdana,sans-serif; border:0.2em outset:#ceebf7; padding:0.1em; font-weight:bolder; -moz-border-radius:8px; ">
 +
[[Image:TOP2.png|50px|link=#top]] </span></div>
  
 
= Introduction =
 
= Introduction =
A project is all the software written for an application. The projects in MC-Basic are multi-tasking. They consist of many tasks running concurrently and independently of each other. The project is stored in multiple files, one file for each task. Tasks are routines that run simultaneously with many other tasks. Projects also include other files such as cam tables and record files. Projects are controlled from the BASIC Moves (BMDS) software. BMDS automatically sets up a separate directory for each project. For detailed information on any commands listed within this section, refer to the Reference Manual.
+
A project is all the software written for an application. The projects in MC-Basic are multitasking. They consist of many tasks running concurrently and independently of each other. The project is stored in multiple files, one file for each task. Tasks are routines that run simultaneously with many other tasks. Projects also include other files such as cam tables and record files. Projects are controlled from the ControlStudio software.
 +
<!-- For detailed information on any commands listed within this section, refer to the Reference Manual. -->
  
For clarity, a project is managed by BMDS and conveniently handles a group of logically coupled files (programs, cam tables, etc.), while the softMC deals with separate files (PRG, CAM, etc.). The Motion controller does not keep project files in the Flash and it is not aware of logical coupling of files and programs.
+
ControlStudio manages projects and handles a group of logically coupled files (programs, cam tables, etc.), while the softMC deals with separate files (PRG, CAM, etc.). The softMC does not keep project files in the flash memory and is not aware of logical coupling of files and programs.
 +
 
 +
ControlStudio automatically sets up a separate directory for each project.
  
 
= Project Structure =
 
= Project Structure =
Line 14: Line 20:
 
*An '''optional autoexec task''' (Autoexec.Prg) to automatically start the application on power-up.
 
*An '''optional autoexec task''' (Autoexec.Prg) to automatically start the application on power-up.
  
[[Image:Axystems;UserManual-31ProjectStructure.png|caption|600px]]
+
[[Image:Axystems;UserManual-31ProjectStructure.png|caption|400px]]
  
 
= Tasks =
 
= Tasks =
Line 41: Line 47:
 
The name of the configuration task is Config.Prg. The configuration task is used for declaration of number of axes, global variables and other constructs, such as cam tables, programmable limit switches (PLS), and group. The key to understanding the configuration task is that all data and constructs shared across tasks must be declared in the configuration task. Refer to the following figure.
 
The name of the configuration task is Config.Prg. The configuration task is used for declaration of number of axes, global variables and other constructs, such as cam tables, programmable limit switches (PLS), and group. The key to understanding the configuration task is that all data and constructs shared across tasks must be declared in the configuration task. Refer to the following figure.
  
[[Image:Axystems;UserManual-322ConfigurationTask.png|caption]]
+
[[Image:Axystems;UserManual-322ConfigurationTask.png|caption|400px]]
  
 
The configuration task can contain a program. Axes can be renamed here.
 
The configuration task can contain a program. Axes can be renamed here.
Line 52: Line 58:
 
Set the AutoExec task to run on power up. To do this, add the keyword Continue'' ''to the Program command. Do not include OnError or OnEvent sections in the AutoExec. Limit the AutoExec task to starting other tasks in the system. Refer to the next figure.
 
Set the AutoExec task to run on power up. To do this, add the keyword Continue'' ''to the Program command. Do not include OnError or OnEvent sections in the AutoExec. Limit the AutoExec task to starting other tasks in the system. Refer to the next figure.
  
[[Image:Axystems;UserManual-323AutoExecTask.png|caption]]
+
[[Image:Axystems;UserManual-323AutoExecTask.png|caption|400px]]
  
 
= Program Declarations =
 
= Program Declarations =
Line 89: Line 95:
 
'''Parentheses are not used in a subroutine if no parameters are passed.'''
 
'''Parentheses are not used in a subroutine if no parameters are passed.'''
  
MC-BASIC automatically checks the type of compliance between the subroutine declaration and the subroutine call. Any type mismatch causes an error during program loading. Automatic casting applies to numeric variables types. For example, suppose MySub is a subroutine that takes a Long parameter. In this case, the following scenario applies:
+
MC-Basic automatically checks the type of compliance between the subroutine declaration and the subroutine call. Any type mismatch causes an error during program loading. Automatic casting applies to numeric variables types. For example, suppose MySub is a subroutine that takes a Long parameter. In this case, the following scenario applies:
 
<pre>
 
<pre>
 
CALL MySub(3.3432)
 
CALL MySub(3.3432)
Line 134: Line 140:
 
PRINT “Mean Value Is “, Mean
 
PRINT “Mean Value Is “, Mean
 
END SUB
 
END SUB
 
 
CALL CalculateMean(XArray, TheMeanArray) ‘ Pass entire array by reference
 
CALL CalculateMean(XArray, TheMeanArray) ‘ Pass entire array by reference
 
 
CALL<nowiki> PrintMean(TheMeanArray[1]) </nowiki>‘ Pass a single array element by value
 
CALL<nowiki> PrintMean(TheMeanArray[1]) </nowiki>‘ Pass a single array element by value
 
</pre>
 
</pre>
Line 150: Line 154:
 
Parentheses are not used in subroutine '''CALL''' if no parameters are passed.
 
Parentheses are not used in subroutine '''CALL''' if no parameters are passed.
  
MC-BASIC automatically checks the type of compliance between the subroutine declaration and the subroutine call. Any mismatch (in number of parameters or parameters types) causes an error during program loading. Automatic type casting applies only for “by value” long and double parameters.
+
MC-Basic automatically checks the type of compliance between the subroutine declaration and the subroutine call. Any mismatch (in number of parameters or parameters types) causes an error during program loading. Automatic type casting applies only for “by value” long and double parameters.
 
<pre>
 
<pre>
 
SUB MySub(RefPar as long, byval ValPar as double)
 
SUB MySub(RefPar as long, byval ValPar as double)
Line 162: Line 166:
  
 
== User-Defined Functions ==
 
== User-Defined Functions ==
MC BASIC allows the definition of user functions to be used in programs in the same manner as using BASIC's pre-defined functions. User-defined functions are composed with multiple lines and may be recursive (can call itself). Unlike BASIC's system functions, the scope of user-defined functions is limited to the task in which it is defined.
+
MC-Basic allows the definition of user functions to be used in programs in the same manner as using BASIC pre-defined functions. User-defined functions are composed with multiple lines and may be recursive (can call itself). Unlike BASIC system functions, the scope of user-defined functions is limited to the task in which it is defined.
  
 
Functions are different from subroutines in one respect. Functions always return a value to the task that called the function. Otherwise, functions and subroutines use the same syntax and follow the same rules of application and behavior.
 
Functions are different from subroutines in one respect. Functions always return a value to the task that called the function. Otherwise, functions and subroutines use the same syntax and follow the same rules of application and behavior.
Line 182: Line 186:
 
There is no explicit limit on the number of functions allowed in a task. All functions must be located following the main program and must be contained wholly outside of the main program.
 
There is no explicit limit on the number of functions allowed in a task. All functions must be located following the main program and must be contained wholly outside of the main program.
  
MC-BASIC automatically checks the type of compliance between the function declaration and the function call. Any mismatch (in number of parameters, in parameters and returned value types) causes an error during program loading. Automatic type casting applies only for long and double returned values and “by value” parameters. For example:
+
MC-Basic automatically checks the type of compliance between the function declaration and the function call. Any mismatch (in number of parameters, in parameters and returned value types) causes an error during program loading. Automatic type casting applies only for long and double returned values and “by value” parameters. For example:
 
<pre>
 
<pre>
 
Function LongReturnFunc(…) As Long
 
Function LongReturnFunc(…) As Long
Line 242: Line 246:
 
</pre>
 
</pre>
  
= Multi-tasking =
+
= Multitasking =
The softMC supports multi-tasking. You can have multiple tasks running independently, sharing a single computer. A task is a section of code that runs in its own context. Microsoft Windows<sup></sup> is a multi-tasking system. If you open Explorer and Word at the same time, they run nearly independently of each another.
+
The softMC supports multitasking. You can have multiple tasks running independently, sharing a single computer. A task is a section of code that runs in its own context. Microsoft Windows<sup></sup> is a multitasking system. If you open Explorer and Word at the same time, they run nearly independently of each another.
  
 
In this case, both Explorer and Word have their own contexts. They share one computer, but run as if the other were not present. There is inter-task communication. If you double-click on a document in the file manager, it launches Word to edit the file you clicked.
 
In this case, both Explorer and Word have their own contexts. They share one computer, but run as if the other were not present. There is inter-task communication. If you double-click on a document in the file manager, it launches Word to edit the file you clicked.
  
With MC-BASIC, you can use different tasks to control different operational modes: one for power up, one for set-up, one for normal operation, and another for when problems occur. Like Windows, each task can run independently of the others, and you can prescribe interactions between tasks.
+
With MC-Basic, you can use different tasks to control different operational modes: one for power up, one for set-up, one for normal operation, and another for when problems occur. Like Windows, each task can run independently of the others, and you can prescribe interactions between tasks.
  
Multi-tasking is used when you want multiple processes to run largely independent of each other. For example, if you are using the softMC to interface to the operator, you will usually use a separate task to execute the interface code. Another example is when two parts of a machine are largely independent of each other. There is usually some control required between tasks as one task may start or stop another.
+
Multitasking is used when you want multiple processes to run largely independent of each other. For example, if you are using the softMC to interface to the operator, you will usually use a separate task to execute the interface code. Another example is when two parts of a machine are largely independent of each other. There is usually some control required between tasks as one task may start or stop another.
  
If a machine is simple to control, you should try to keep the entire program in one task (in addition to Config.Prg). If you do need to use multi-tasking, you should keep a highly structured architecture. <s>Kollmorgen</s> recommends that you limit use of the main task for axis and group set up, machine initialization, and controlling the other tasks. Normal machine operation should be programmed in other tasks. For example, Main.Prg might be limited to setting up axes, and then starting Pump.Prg,'' ''Conveyor.Prg'','' and Operator.Prg.
+
If a machine is simple to control, you should try to keep the entire program in one task (in addition to Config.Prg). If you do need to use multitasking, you should keep a highly structured architecture. It is recommended that you limit use of the main task for axis and group set up, machine initialization, and controlling the other tasks. Normal machine operation should be programmed in other tasks. For example, Main.Prg might be limited to setting up axes, and then starting Pump.Prg,'' ''Conveyor.Prg'','' and Operator.Prg.
  
 
Do not split control of an axis or group across tasks. You can put control for multiple axes in one task. Ideally, you should use multiple tasks for machines where different sections operate more or less independently. You can also use tasks to implement different operational modes.
 
Do not split control of an axis or group across tasks. You can put control for multiple axes in one task. Ideally, you should use multiple tasks for machines where different sections operate more or less independently. You can also use tasks to implement different operational modes.
  
Multi-tasking is a powerful tool, but it carries a cost. It is easy to make errors that are difficult to find. When multiple tasks are running concurrently, complex interaction is difficult to understand and recreate. Limit the use of tasks to situations where they are needed.
+
Multitasking is a powerful tool, but it carries a cost. It is easy to make errors that are difficult to find. When multiple tasks are running concurrently, complex interaction is difficult to understand and recreate. Limit the use of tasks to situations where they are needed.
  
Do not create a task as a substitute for an event handler. Events and tasks are not the same. MC-BASIC supports event handlers to respond to realtime events. Events are similar to interrupts in microprocessor systems. They normally run at higher priorities than the programs that contain them. They are ideal for quick responses to realtime events. Add the event handler to an existing task to respond to an event.
+
Do not create a task as a substitute for an event handler. Events and tasks are not the same. MC-Basic supports event handlers to respond to realtime events. Events are similar to interrupts in microprocessor systems. They normally run at higher priorities than the programs that contain them. They are ideal for quick responses to realtime events. Add the event handler to an existing task to respond to an event.
  
 
Do not use tasks in place of subroutines. Remember that when you start a task, the original task continues to run. When you call a subroutine, you expect the calling program to suspend execution until the subroutine is complete. The behavior of tasks where the two routines continue to execute can cause complex problems.
 
Do not use tasks in place of subroutines. Remember that when you start a task, the original task continues to run. When you call a subroutine, you expect the calling program to suspend execution until the subroutine is complete. The behavior of tasks where the two routines continue to execute can cause complex problems.
  
Knowing when to use multi-tasking and when to avoid it requires some experience. If you are new to multi-tasking, you may want to start slow until you are familiar with how it affects program structure. When you start a new project, BASIC Moves creates the main task as part of opening a new project. After that process is complete, you can add a new task to your project by selecting File, New. You can also press the new task button on the BASIC Moves tool bar.
+
Knowing when to use multitasking and when to avoid it requires some experience. If you are new to multitasking, you may want to start slow until you are familiar with how it affects program structure. When you start a new project, ControlStudio creates the main task as part of opening a new project. After that process is complete, you can add a new task to your project by selecting File, New. You can also press the new task button on the ControlStudio tool bar.
  
 
== Loading the Program ==
 
== Loading the Program ==
BASIC Moves automatically loads all tasks in your project when you select Run Project. You can select Run Project by selecting it from the Debug menu, by pressing the F5 key, or by pressing the “Load Task”and “Run Task” buttons on the tool bar. By default, tasks are loaded from the host PC to the softMC at a low priority (Priority = 16).
+
ControlStudio automatically loads all tasks in your project when you select Run Project. You can select Run Project by selecting it from the Debug menu, by pressing the F5 key, or by pressing the “Load Task”and “Run Task” buttons on the tool bar. By default, tasks are loaded from the host PC to the softMC at a low priority (Priority = 16).
  
 
When you select Run Task, the project’s main task is started at the lowest priority (Priority = 16). You can change the priority of the main task by selecting View-> Project Manager->Options and then changing the priority in the bottom of the window. If you structure your software so that the main program loads all other tasks, the Run Project button starts your machine.
 
When you select Run Task, the project’s main task is started at the lowest priority (Priority = 16). You can change the priority of the main task by selecting View-> Project Manager->Options and then changing the priority in the bottom of the window. If you structure your software so that the main program loads all other tasks, the Run Project button starts your machine.
  
== Preemptive Multi-tasking & Priority Levels ==
+
== Preemptive Multitasking & Priority Levels ==
 
Because many tasks share one processor, you must carefully design your system so tasks get processing resources when they need them. You do not want the operator interface taking all the resources when you need fast response to a realtime event. The operating system provides system resources based on two criteria: task priority level and time slice.
 
Because many tasks share one processor, you must carefully design your system so tasks get processing resources when they need them. You do not want the operator interface taking all the resources when you need fast response to a realtime event. The operating system provides system resources based on two criteria: task priority level and time slice.
  
When you create a program, you must select the task '''priority level'''. MC-BASIC allows you to specify 16 levels of priority. The task with the highest priority takes all the system resources it can use. In fact, no task of a lower priority receives any resources until all tasks of higher priority relinquish them. Most systems have one main task that runs at a medium priority and perhaps a background task that runs at a low priority, with a few high priority tasks. At every time slice, the softMC reevaluates which task has the highest priority and assigns resources to it.
+
When you create a program, you must select the task '''priority level'''. MC-Basic allows you to specify 16 levels of priority. The task with the highest priority takes all the system resources it can use. In fact, no task of a lower priority receives any resources until all tasks of higher priority relinquish them. Most systems have one main task that runs at a medium priority and perhaps a background task that runs at a low priority, with a few high priority tasks. At every time slice, the softMC reevaluates which task has the highest priority and assigns resources to it.
  
The BASIC Moves terminal window relies on the softMC command line task, which runs at priority 2. If you start a task with priority 1, the terminal will not be available until the task is complete or idle. Consequently, you will not be able to communicate with the softMC and you may have to power-down the system to recover the terminal window. You can optionally set the priority of a task when you start it. For example:
+
The ControlStudio terminal window relies on the softMC command line task, which runs at priority 2. If you start a task with priority 1, the terminal will not be available until the task is complete or idle. Consequently, you will not be able to communicate with the softMC and you may have to power-down the system to recover the terminal window. You can optionally set the priority of a task when you start it. For example:
  
 
<pre>StartTask Aux.Prg Priority=6</pre>
 
<pre>StartTask Aux.Prg Priority=6</pre>
Line 284: Line 288:
 
Tasks can control one-another. In fact, any task can start, continue, idle, or kill any other task, regardless of which task has the higher priority. For detailed information on these commands, refer to the Reference Manual.
 
Tasks can control one-another. In fact, any task can start, continue, idle, or kill any other task, regardless of which task has the higher priority. For detailed information on these commands, refer to the Reference Manual.
  
'''StartTask '''starts tasks from the main task. For testing, you can use '''STARTTASK''' from the terminal window. <s>DanaherMotion</s> recommends you do not use '''STARTTASK''' in AutoExec.Prg. The syntax of '''STARTTASK''' is:
+
'''StartTask '''starts tasks from the main task. For testing, you can use '''STARTTASK''' from the terminal window. Do not use '''STARTTASK''' in AutoExec.Prg. The syntax of '''STARTTASK''' is:
  
 
<nowiki>StartTask <</nowiki>''TaskName''<nowiki>> {Priority = <</nowiki>''Level''<nowiki>>}{NumberOfLoops = <</nowiki>''Loop Count''>}
 
<nowiki>StartTask <</nowiki>''TaskName''<nowiki>> {Priority = <</nowiki>''Level''<nowiki>>}{NumberOfLoops = <</nowiki>''Loop Count''>}
Line 464: Line 468:
 
Example of a mutually exclusive semaphore:
 
Example of a mutually exclusive semaphore:
 
<pre>
 
<pre>
common shared comMutex as semaphore
+
' common shared comMutex as semaphore
 
+
' defined in config.prg
defined in config.prg
 
 
 
 
Program
 
Program
 
 
Open COM2 BaudRate=9600 Parity=0 DataBits=8 StopBit=1 As #1
 
Open COM2 BaudRate=9600 Parity=0 DataBits=8 StopBit=1 As #1
 
 
While 1
 
While 1
 
+
' take semaphore lock serial port
take semaphore lock serial port
 
 
 
 
If SemTake(comMutex,5000) = 1 Then
 
If SemTake(comMutex,5000) = 1 Then
 
 
Print #1,”Hello ”;
 
Print #1,”Hello ”;
 
 
Print #1,”world”
 
Print #1,”world”
 
 
SemGive(comMutex) ‘unlock serial port
 
SemGive(comMutex) ‘unlock serial port
 
 
End if
 
End if
 
 
End While
 
End While
 
 
End program
 
End program
 
</pre>
 
</pre>
 +
 
== Synchronization Semaphores ==
 
== Synchronization Semaphores ==
 
Synchronization semaphores are essential in producer-consumer applications where task A prepares some data, while task B consumes it. In this case, the semaphore may eliminate constant polling for ready data and save considerable CPU resources.
 
Synchronization semaphores are essential in producer-consumer applications where task A prepares some data, while task B consumes it. In this case, the semaphore may eliminate constant polling for ready data and save considerable CPU resources.
Line 495: Line 488:
 
Example of Producer:
 
Example of Producer:
 
<pre>
 
<pre>
common shared syncSemaphore as semaphore
+
' common shared syncSemaphore as semaphore
defined in config.prg
+
' defined in config.prg
common shared globalA as long defined in config.prg
+
' common shared globalA as long ' defined in config.prg
 
Program
 
Program
 
Dim dummy as long
 
Dim dummy as long
Semaphore is created as “full” - flush it before first use
+
' Semaphore is created as “full” - flush it before first use
dummy=semTake(syncSemaphore) no waiting
+
dummy=semTake(syncSemaphore) ' no waiting
 
While 1
 
While 1
globalA=globalA+1 produce some data
+
globalA=globalA+1 ' produce some data
 
semGive(syncSemaphore)
 
semGive(syncSemaphore)
 
sleep (100)
 
sleep (100)
Line 525: Line 518:
  
  
[[Category:Axystems:MC-Basic|Programs]]
+
[[Category:MC-Basic|Programs]]

Latest revision as of 09:04, 8 August 2017

IMPORTANT.svgIMPORTANT
This entry is outdated and requires revision.

TOP2.png

Introduction

A project is all the software written for an application. The projects in MC-Basic are multitasking. They consist of many tasks running concurrently and independently of each other. The project is stored in multiple files, one file for each task. Tasks are routines that run simultaneously with many other tasks. Projects also include other files such as cam tables and record files. Projects are controlled from the ControlStudio software.

ControlStudio manages projects and handles a group of logically coupled files (programs, cam tables, etc.), while the softMC deals with separate files (PRG, CAM, etc.). The softMC does not keep project files in the flash memory and is not aware of logical coupling of files and programs.

ControlStudio automatically sets up a separate directory for each project.

Project Structure

Project files include tasks and cam tables.

Each project can contain three types of tasks:

  • General-purpose task
  • An optional configuration task (Config.Prg) to declare groups, programmable limit switches (PLS), cams, global variables and load of global libraries.
  • An optional autoexec task (Autoexec.Prg) to automatically start the application on power-up.

caption

Tasks

The three types of tasks are general-purpose tasks, configuration tasks, and autoexec tasks. Each type of task is outlined below.

General Purpose Tasks

General-purpose tasks are the workhorse of the softMC language. They implement the basic logic of your application. The great majority of your programming is implemented with general-purpose tasks. Each general-purpose task is divided into three sections: a task-variable section, a main program, and subroutines. The main program is further divided into three sections: a Start-up section, an OnError section, and an OnEvent section.

caption

A task-variable definition sectionThe task-variable definition section, where all task variables are declared with the Dim…Shared command.

The main programMost programming is done in the main programming section. The main programming section falls between the Program…End Program keywords. The main program itself has three sub-sections:

The Start-up sectionThe start-up section immediately follows the Program keyword. This is where task execution begins when you start a task.

OnError sectionThe OnError section responds to errors generated by the task, allowing your program to automatically respond to error conditions and (where possible), gracefully recover and restart the machine. There is (at most) one OnError section for each task and it is normally written just before the OnEvent section.

OnEvent sectionThis section contains optional blocks of code that respond to realtime changes, such as a motor position changing or an input switch turning on. The main program can contain code to automatically respond to events. This reduces the effort required to make tasks respond quickly and easily to realtime events.

Event handlers begin with the OnEvent and end with End OnEvent keywords. One OnEvent…End OnEven keyword combination is required for each realtime event. Event handlers must be contained wholly within the main program.

Optional subroutinesEach task can have any number of subroutines. Subroutines are component parts of the task, and consequently, they can only be called from within the task. If you want to call the same subroutine from two tasks, place one copy in each task.

Configuration Task

The name of the configuration task is Config.Prg. The configuration task is used for declaration of number of axes, global variables and other constructs, such as cam tables, programmable limit switches (PLS), and group. The key to understanding the configuration task is that all data and constructs shared across tasks must be declared in the configuration task. Refer to the following figure.

caption

The configuration task can contain a program. Axes can be renamed here.

AutoExec Task

The AutoExec task (AutoExec.Prg) is executed once on power up, just after the Configuration Task. Use AutoExec to start power-up logic. For example, you might want to use AutoExec to start a task that sets the outputs to the desired starting values. That way, the outputs are set immediately after the softMC boots, usually sooner than the PC.

For safety considerations we do not recommend starting of motion from the AutoExec task. Motion should be started by explicit operator’s request either by I/O or communication link from host PC.

Set the AutoExec task to run on power up. To do this, add the keyword Continue to the Program command. Do not include OnError or OnEvent sections in the AutoExec. Limit the AutoExec task to starting other tasks in the system. Refer to the next figure.

caption

Program Declarations

You must declare the start of programs and subroutines. For programs, use the Program…End Program keywords. Use Sub…End Sub keywords for subroutines.

The Program…End Program keywords mark the boundary between the variable declaration section and the main program. Each task must have only one Program keyword and end with the End Program keyword.

Program ‘Standard form of program command

 <code for program>

End Program

The AutoExec task, which must be loaded and run automatically at power-up must have CONTINUE following Program.

You pass parameters (either scalar or array) to the subroutine, which can then be used in the code of the subroutine. In the declaration line for the subroutine (SUB<name>), you declare the variable names and types of the parameters to pass. Parameters are passed either by reference or by value (ByVal). The default method is to pass parameters by reference. Arrays are passed only by reference. When you pass a variable (whether the variable is local to the task or is global) by reference, you pass the address of the variable to the subroutine, which changes the value of the variable (if the code of the subroutine is written to do this). When you pass a variable by value (ByVal) a copy of the value of the local variable is passed to the subroutine. The subroutine cannot change the value of the local variable. The syntax for defining a subroutine is:

SUB <name> ({{ByVal} <p_1> as <type_1> }…{, {ByVal} <p_n> as type_n>})

{ local variable declaration }

{ subroutine code }

END SUB

There is no explicit limit on the number of subroutines allowed in a task. All subroutines must be located following the main program, and must be contained wholly outside the main program. Subroutines can only be called from within the task where they reside. Subroutines may be recursive (call itself).

Use CALL to execute a subroutine:

CALL <subroutine>({<p_1>...<p_n>})

where:

<subroutine> is the name of the subroutine<p_1>...<p_n> are the subroutine parameters

Parentheses are not used in a subroutine if no parameters are passed.

MC-Basic automatically checks the type of compliance between the subroutine declaration and the subroutine call. Any type mismatch causes an error during program loading. Automatic casting applies to numeric variables types. For example, suppose MySub is a subroutine that takes a Long parameter. In this case, the following scenario applies:

CALL MySub(3.3432)
'OK: The double value 3.3432 is demoted to the Long value, 3
CALL MySub(a)
'Error: a is a string, there is a type mismatch
Call MySub(3,4)
'Error: The number of parameters is not correct

See the Subroutine Example in Appendix A for further details.

Subroutines

Parameters (either scalar or array) passed to the subroutine are used within the code of the subroutine. The declaration line for the subroutine (SUB<name>) is used to declare names and types of the parameters to pass.

Parameters are passed either by reference or by value (ByVal). The default method is to pass parameters by reference. Whole arrays are passed only by reference. Trying to pass a whole array by value results in a translation error. However, array elements can be passed both by reference and by value. The syntax for a subroutine is:

SUB <name> ({<par_1>([*])+ as <type_1>}…{, <par_n>([*])+ as<type_n>})

END SUB

<par_l>: name of array variable

<par_n>: name of array variable

[*]: dimension of an array without specifying the bounds

+: means one or more [*]


SUB CalculateMean(x[*][*] as DOUBLE, TheMean[*] as LONG)
DIM sum as DOUBLE
DIM I as LONG
FOR i = 1 to 100
sum = sum + x[i][1]
NEXT i
TheMean[1] = sum/100
END SUB

SUB PrintMean(ByVal Mean as LONG)
PRINT “Mean Value Is “, Mean
END SUB
CALL CalculateMean(XArray, TheMeanArray) ‘ Pass entire array by reference
CALL PrintMean(TheMeanArray[1]) ‘ Pass a single array element by value

When a variable is passed by reference (whether the variable is local to the task or global), the address of the variable is passed to the subroutine, which changes the value of the original variable (if the code of the subroutine is written to do this).

When a variable is passed by value (ByVal) a local copy of the value of the variable is passed to the subroutine, and the subroutine cannot change the value of the original variable.

There is no explicit limit on the number of subroutines allowed in a task. All subroutines must be located following the main program and must be contained wholly outside the main program. Subroutines can only be called from within the task where they reside. Subroutines may be recursive (can call itself). Use CALL to execute a subroutine with the following syntax:

CALL <subroutine_name>{(<par_1>{, …<par_n>})}

Parentheses are not used in subroutine CALL if no parameters are passed.

MC-Basic automatically checks the type of compliance between the subroutine declaration and the subroutine call. Any mismatch (in number of parameters or parameters types) causes an error during program loading. Automatic type casting applies only for “by value” long and double parameters.

SUB MySub(RefPar as long, byval ValPar as double)
…
END SUB
CALL MySub(LongVar, “String”) -> type mismatch in second parameter
CALL MySub(LongVar) -> wrong number of parameters
CALL MySub(LongVar, 2) -> a valid type casting for a by-value parameter
CALL MySub(DoubleVar, 2.2) -> invalid type casting for a by-ref parameter

User-Defined Functions

MC-Basic allows the definition of user functions to be used in programs in the same manner as using BASIC pre-defined functions. User-defined functions are composed with multiple lines and may be recursive (can call itself). Unlike BASIC system functions, the scope of user-defined functions is limited to the task in which it is defined.

Functions are different from subroutines in one respect. Functions always return a value to the task that called the function. Otherwise, functions and subroutines use the same syntax and follow the same rules of application and behavior.

Because functions return a value, function calls should be treated as expressions. Therefore, function called can be combined within print commands, assignment statements, mathematical operations and conditions of flow control statements. They can also be passed as by-value parameters of system or user-defined functions and subroutines.

PRINT <function_name>{(<par_1>{, …<par_n>})}
<variable_name> = <function_name>{(<par_1>{, …<par_n>})}
IF <function_name>{(<par_1>{, …<par_n>})} > 10 THEN
? LOG( <function_name>{(<par_1>{, …<par_n>})} )

Parentheses are not used in function CALL if no parameters are passed. Parameters (either scalar or array) passed to the function are used within the code of the function. Declare variable names and types of parameters in the declaration line of the function. Parameters can be passed by reference or by value. The default is by reference.

Arrays can only be passed by reference. Trying to pass a whole array by value results in a translation error. On the other hand, array elements can be passed by reference and by value.

To set up the return value, assign the return value to a virtual local variable with the same name as the function somewhere within the code of the function. This local variable is declared automatically during function declaration and the function uses it to obtain the return value.

There is no explicit limit on the number of functions allowed in a task. All functions must be located following the main program and must be contained wholly outside of the main program.

MC-Basic automatically checks the type of compliance between the function declaration and the function call. Any mismatch (in number of parameters, in parameters and returned value types) causes an error during program loading. Automatic type casting applies only for long and double returned values and “by value” parameters. For example:

Function LongReturnFunc(…) As Long
LongReturnFunc = “String” -> type mismatch in returned value
End Function

Function LongReturnFunc(…) As Long
LongReturnFunc = 43.7 -> valid type casting in returned value
End Function

A function can be recursive (can call itself). The following example defines a recursive function to calculate the value of N:

FUNCTION Factorial (ByVal N As Long) As Double
'By declaring N to be long, this truncates floating point numbers
'to integers
'The function returns a Double value
If N < 3 then 'This statement stops the recursion
Factorial = N '0!=0; 1!=1' 2!=2
Else
Factorial=N * Factorial(N-1) 'Recursive statement
End If
END FUNCTION

When writing a recursive function, you must have an IF statement to force the function to return without the recursive call being executed. Otherwise, the function never returns once it is called.

Arrays

Arrays can only be passed by reference. If a user tries to pass a whole array by value, the translator gives an error. Array syntax is:

SUB <name> ({<p_1>([*])+ as <type_1>}…{, <p_n>([*])+ as <type_n>})

{ local variable declaration }

{ subroutine code }

END SUB

where

<p_l> : name of array variable

<p_n> : name of array variable

[*] : dimension of array without specifying the bounds

+ : means one or more

Syntax example:

SUB mean(x[*][*] as DOUBLE, TheMean[*] as LONG)
DIM sum as DOUBLE
DIM I as LONG
FOR i = 1 to 100
sum = sum + x[i][1]
NEXT i
TheMean[1] = sum/100
END SUB

Multitasking

The softMC supports multitasking. You can have multiple tasks running independently, sharing a single computer. A task is a section of code that runs in its own context. Microsoft Windows is a multitasking system. If you open Explorer and Word at the same time, they run nearly independently of each another.

In this case, both Explorer and Word have their own contexts. They share one computer, but run as if the other were not present. There is inter-task communication. If you double-click on a document in the file manager, it launches Word to edit the file you clicked.

With MC-Basic, you can use different tasks to control different operational modes: one for power up, one for set-up, one for normal operation, and another for when problems occur. Like Windows, each task can run independently of the others, and you can prescribe interactions between tasks.

Multitasking is used when you want multiple processes to run largely independent of each other. For example, if you are using the softMC to interface to the operator, you will usually use a separate task to execute the interface code. Another example is when two parts of a machine are largely independent of each other. There is usually some control required between tasks as one task may start or stop another.

If a machine is simple to control, you should try to keep the entire program in one task (in addition to Config.Prg). If you do need to use multitasking, you should keep a highly structured architecture. It is recommended that you limit use of the main task for axis and group set up, machine initialization, and controlling the other tasks. Normal machine operation should be programmed in other tasks. For example, Main.Prg might be limited to setting up axes, and then starting Pump.Prg, Conveyor.Prg, and Operator.Prg.

Do not split control of an axis or group across tasks. You can put control for multiple axes in one task. Ideally, you should use multiple tasks for machines where different sections operate more or less independently. You can also use tasks to implement different operational modes.

Multitasking is a powerful tool, but it carries a cost. It is easy to make errors that are difficult to find. When multiple tasks are running concurrently, complex interaction is difficult to understand and recreate. Limit the use of tasks to situations where they are needed.

Do not create a task as a substitute for an event handler. Events and tasks are not the same. MC-Basic supports event handlers to respond to realtime events. Events are similar to interrupts in microprocessor systems. They normally run at higher priorities than the programs that contain them. They are ideal for quick responses to realtime events. Add the event handler to an existing task to respond to an event.

Do not use tasks in place of subroutines. Remember that when you start a task, the original task continues to run. When you call a subroutine, you expect the calling program to suspend execution until the subroutine is complete. The behavior of tasks where the two routines continue to execute can cause complex problems.

Knowing when to use multitasking and when to avoid it requires some experience. If you are new to multitasking, you may want to start slow until you are familiar with how it affects program structure. When you start a new project, ControlStudio creates the main task as part of opening a new project. After that process is complete, you can add a new task to your project by selecting File, New. You can also press the new task button on the ControlStudio tool bar.

Loading the Program

ControlStudio automatically loads all tasks in your project when you select Run Project. You can select Run Project by selecting it from the Debug menu, by pressing the F5 key, or by pressing the “Load Task”and “Run Task” buttons on the tool bar. By default, tasks are loaded from the host PC to the softMC at a low priority (Priority = 16).

When you select Run Task, the project’s main task is started at the lowest priority (Priority = 16). You can change the priority of the main task by selecting View-> Project Manager->Options and then changing the priority in the bottom of the window. If you structure your software so that the main program loads all other tasks, the Run Project button starts your machine.

Preemptive Multitasking & Priority Levels

Because many tasks share one processor, you must carefully design your system so tasks get processing resources when they need them. You do not want the operator interface taking all the resources when you need fast response to a realtime event. The operating system provides system resources based on two criteria: task priority level and time slice.

When you create a program, you must select the task priority level. MC-Basic allows you to specify 16 levels of priority. The task with the highest priority takes all the system resources it can use. In fact, no task of a lower priority receives any resources until all tasks of higher priority relinquish them. Most systems have one main task that runs at a medium priority and perhaps a background task that runs at a low priority, with a few high priority tasks. At every time slice, the softMC reevaluates which task has the highest priority and assigns resources to it.

The ControlStudio terminal window relies on the softMC command line task, which runs at priority 2. If you start a task with priority 1, the terminal will not be available until the task is complete or idle. Consequently, you will not be able to communicate with the softMC and you may have to power-down the system to recover the terminal window. You can optionally set the priority of a task when you start it. For example:

StartTask Aux.Prg Priority=6

The default priority of events is 1 (highest) and the default priority of programs is 16.

Time Slice is a method by which the operating system divides up resources when multiple tasks share the same priority level. The softMC provides the first task with one time slice, the next time slice goes to the second, the next to the third, and so on. The time slice is currently one millisecond duration. This method is sometimes called round robin scheduling.

Inter-Task Communications and Control

Tasks can control one-another. In fact, any task can start, continue, idle, or kill any other task, regardless of which task has the higher priority. For detailed information on these commands, refer to the Reference Manual.

StartTask starts tasks from the main task. For testing, you can use STARTTASK from the terminal window. Do not use STARTTASK in AutoExec.Prg. The syntax of STARTTASK is:

StartTask <TaskName> {Priority = <Level>}{NumberOfLoops = <Loop Count>}

NOTE-Info.svgNOTE
NOL is a short form for NumberOfLoops.

where:

<Level> is a long with value between 1 and 16. If <Level> is not entered, it defaults to 16, the lowest priority. Priority = 1 is the highest priority.

<Loop Count> is either -1 (indicating unlimited number of loops) or between 1 and 32768 indicating the number of times the task is executed. If <Loop Count> is not entered, it defaults to 1.

For example:

StartTask Task1.Prg Priority=8 NumberOfLoops = -1 'Run Task1 forever
StartTask Main.Prg NOL=1 'Run Main once

IdleTask stops the task at the end of the line currently being executed and idles all its events. An idled task can be continued (using CONTINUETASK) or terminated (using KILLTASK). IDLETASK does not stop motion currently being executed. This is significant because all the events are idled and cannot respond to an axis' motion. Tasks can be idled explicitly by other tasks, but cannot idel itself. This command is issued from a task or the terminal window. The syntax of IdleTask is:

IdleTask <TaskName>

For example:

IdleTask TASK1.PRG

Tasks that have been idled with IDLETASK are restarted only with CONTINUETASK. The command continues an idled task from the point at which it was stopped, or continues task execution after a break point has been reached. It is frequently used to restart tasks from the ONERROR error handler. If a run time error has occured, CONTINUETASK retries the line which caused the error. The error must be corrected before the task continues. This command is issued from any task or the terminal window. The syntax of ContinueTask is:

ContinueTask <TaskName>

For example:

ContinueTask TASK1.PRG

KillTask aborts the execution of a task. The program pointer is left on the line at which the task was stopped. The first action of KILLTASK is to kill and delete all events associated with the task. This is done to ensure that no event initiates an action after KILLTASK was executed. KILLTASK is issued from the terminal window. The syntax of the KillTask is:

KillTask <TaskName>

For example:

KillTask TASK1.PRG

Monitoring Tasks From the Terminal

For detailed information on these commands, refer to the Reference Manual.

TASK.STATUS provides the current state of any task. You can query TASK.STATE from the terminal window. You cannot use TASK.STATUS from within a program. The syntax for TASK.STATUS is:

? <TaskName>.Status

For example:

? TASK1.PRG.Status

TASKLIST returns the state and priority of all tasks loaded in the system. You can query TASKLIST only from the terminal window.

For example, if you type:

? TaskList

A typical result is:

TaskName = TASK1.PRG, Status = sstep, Priority=16
TaskName = DO.PRG, Status = suspend, Priority=4

Relinquishing Resources

When tasks of different priorities compete for processor time, the highest priority task always takes all the resources it needs. However, tasks of high priority can relinquish computer resources under some conditions. In these cases, tasks of lower priority run until the high priority tasks again demand the resources. There are three conditions under which a task relinquishes resources: when the task is terminated, when the task is suspended, or when the task is idled. For detailed information on these commands, refer to the Reference Manual.

A task terminates when it is finished executing. If a task starts with NUMBEROFLOOPS greater than zero, the task executes the specified number of times and terminates. The task relinquishes all resources. Terminated tasks remain loaded in the system and can be restarted.

One task can terminate another task by issuing KILLTASK. A task relinquishes all resources after the kill command. Killed tasks remain in the system and can be restarted.

Tasks relinquish processing resources temporarily when they are suspended. A task is suspended when it is waiting for a resource or is delayed. Suspended tasks still monitor events as long as the event priority is higher than the task priority. Never run a task at a higher priority level than any of its events.

Use SLEEP to delay a task for a specific period of time. This command can only be issued from within the task. One task cannot issue a SLEEP for another task. SLEEP causes the task to relinquish resources until the sleep time has expired.

Idled tasks relinquish resources. In this case, resources are relinquished until another task revokes the idle by issuing a CONTINUETASK.

Delete Task/Library deletes a file from the Flash Disk. Only filesnot loaded into RAM can be deleted.Files that are protected by a password may not be deleted. For example:

Delete FILE1.PRG

Event Handler

The main program can contain sections which automatically handle events. This reduces the programming effort required to make tasks respond quickly and easily to realtime events. Event handlers begin with OnEvent and end with End OnEvent and occur just after the Program keyword.

After OnEvent is loaded, turn the event On with EventOn just after the End OnEvent keyword (enable immediately). However, you can enable and disable OnEvent at any time using EventOn and EventOff. Multiple OnEvents can be written sequentially. The softMC system can support up to 64 events. The number of OnEvent(s) in a single task is not restricted, so a task may have from 0 to 64 OnEvent(s).

It is important to understand that OnEvents are different from ordinary tasks. OnEvents are preemptive within the task. That is, an OnEvent runs until complete and the program execution returns to the main program. While an OnEvent is executing, it does not release CPU time to the parent task or any other task. In this sense, OnEvents are similar to interrupt calls. They run to completion before execution returns to the main program. An OnEvent must have a higher priority than its parent task to ensure that when an event occurs, it interrupts its parent task and runs to completion. The rules are valid for a single process, (that is, the parent task and its events), while events of the respective tasks in the system share the CPU among themselves.

OnEvent

The syntax of OnEvent is:

OnEvent [EventName] [Condition] {Priority=EventPriority}{ScanTime=time}

where

EventName is any legal name that is otherwise not used in the task.

Condition is any logical expression such as System.Dout.1 = 1. The event fires on transitions of the condition from false to true.

Priority is an integer from 1 (highest) to 16 (lowest). If not entered, priority defaults to 1. The priority should always be higher than the priority of the task or the event never runs.

Time is an integer indicating the number of cycles between each scan. Time defaults to 1.

In this example, an event is set up to move axis "X-axis" to 10000 counts each time an input goes high:

OnEvent MOVE_ON_TRIGGER System.Din.1=ON
Move X-axis 10000
End OnEvent

Normally, event handlers run at a high priority so that once the event occurs, they run to completion. In most cases, this code should be very short as it usually takes all resources until it is complete.

ScanTime is in cycles of the SERCOS update rate. This is normally 2 or 4 milliseconds. Setting ScanTime to 5 configures the system to check the event condition every 10 or 20 milliseconds, depending on your update rate. For example:

OnEvent System.Din.2 = ON ScanTime = 5

Events can either be controlled from within the task in which they reside, or from the terminal. The main program or any subroutine can issue EventOn (to enable the OnEvent command) or EventOff (to disable it). OnEvents cannot be controlled from other tasks.

EventOn

EventOn enables OnEvent. The syntax of EventOn is:

EventOn [Event Name]

EventOn must come after the definition of the OnEvent.

EventOff

EventOff disables OnEvent. The syntax of EventOff is:

EventOff [Event Name]

Refer to the MC Reference Manual for information additional information about OnEvent, EventOn, and EventOff.

EventList

EventList provides a complete list of all events in all tasks with their name, the task name, the priority, whether the event is armed, and current status. EventList is valid only from the terminal window. For example:

? EventList

the result is something like the following line for each task:

Name = IOEvent Owner=Task1 Edge=1 Status=1 Scan=1 Priority=5 Action = Stop

where:

edge=1 indicates the event is armed (that is, the condition is false so that the condition becoming true will fire the OnEvent)

status=1 means the event is enabled

EventDelete

Deletes the specified event. The event does not respond to the specified condition until the task is executed again and the event is enabled.

EventDelete EVENT1

Events at Start-up

Events are normally triggered by the OnEvent condition changing from false to true. So a single transition in the condition is required to run OnEvent. One exception to this is start-up. At start-up, if the condition is true, OnEvent executes once, even if there has not been a transition.

Program Flow and OnEvent

You can use GoTo commands within an OnEvent block of code. However, because OnEvent interrupts the main program, you cannot use GoTo to branch out of the event handler or into the event handler. You cannot place OnEvent…End OnEvent in the middle of program flow commands (e.g., For…Next, If…Then, etc.). You cannot declare or use local variables inside an OnEvent block.

Semaphores

Semaphores are the basis for synchronization and mutual exclusion. The difference is that the mutual exclusion semaphore is created as “full” or “1”, while the synchronization semaphore is empty “0”. If the semaphore is used for protecting mutual resources, it is taken before accessing the resource and releases at the end. A synchronization semaphore is given by the producer and taken (consumed) by the consumer.

NOTE-Info.svgNOTE
A semaphore is created as “full.”

Global semaphore are defined with COMMON SHARED in CONFIG.PRG or from the command line. Since a semaphore's purpose is to protect data among tasks, there is no meaning to local semaphores.

A semaphore is given/released by SEMAPHOREGIVE and taken/consumed by SEMAPHORETAKE. It is possible to specify a time out of up to 5000 ms. SEMAPHORETAKE acquires a semaphore and returns before timeout or does not acquire a semaphore and returns after timeout.

NOTE-Info.svgNOTE
Mutual exclusion semaphores are taken and given by the same task. Synchronization semaphores are given by one task and taken by another task.

Mutual Exclusion Semaphores

Mutual exclusion semaphores lock resources by taking a semaphore. Another task(s) competing for the same resource is blocked until the semaphore is released.

Example of a mutually exclusive semaphore:

' common shared comMutex as semaphore
' defined in config.prg
Program
Open COM2 BaudRate=9600 Parity=0 DataBits=8 StopBit=1 As #1
While 1
' take semaphore lock serial port
If SemTake(comMutex,5000) = 1 Then
Print #1,”Hello ”;
Print #1,”world”
SemGive(comMutex) ‘unlock serial port
End if
End While
End program

Synchronization Semaphores

Synchronization semaphores are essential in producer-consumer applications where task A prepares some data, while task B consumes it. In this case, the semaphore may eliminate constant polling for ready data and save considerable CPU resources.

Example of Producer:

' common shared syncSemaphore as semaphore
' defined in config.prg
' common shared globalA as long ' defined in config.prg
Program
Dim dummy as long
' Semaphore is created as “full” - flush it before first use
dummy=semTake(syncSemaphore) ' no waiting
While 1
globalA=globalA+1 ' produce some data
semGive(syncSemaphore)
sleep (100)
End While
End program

Example of Consumer:

' common shared syncSemaphore as semaphore
' defined in config.prg
' common shared globalA as long ' defined in config.prg
Program
While 1
If SemTake(syncSemaphore,5000) = 1 Then
Print "A is "; globalA
End if
End While
End program