![]() |
NetBurner 3.3.9
PDF Version |
The NetBurner Real-Time Operating System (NBRTOS) a full featured preemptive multitasking real-time operating system, supporting multiple tasks, semaphores, mail boxes, FIFOs, queues, and critical sections just to name a few. As part of the NetBurner development package, the RTOS is pre-configured and running. The task you will normally start with is named UserMain()
.
There are 255 priority levels (1 through 255). The lower the number, the higher the priority. Some of these tasks are reserved by the system, such as the idle task. You can specify a priority when the task is created, and change the priority later if you wish. A priority level can only be used by one task at a time. Be sure to check the return values when creating a task or changing a task priority to verify that the operation was successful.
In a preemptive operating system the highest priority task ready to run will always run. This is an extremely important point, so we will mention it again: the highest priority task ready to run will always run. This is different than a Windows or Unix system that employs a round-robin in which each task will run based on its time slice. If you create a high priority task that can always run and never blocks, then no lower priority tasks will ever run. Lower priority tasks can only run when a higher priority task blocks on a resource or blocking function call.
For example, there are two tasks A and B. Task A has priority 50 and Task B has priority 51. Since Task A is of higher priority, it will always run (and Task B will never run) unless it calls a blocking function. Task B will then run for as long as Task A blocks; this could be 1 second due to a call to OSTimeDly(TICKS_PER_SECOND)
, until a shared resource is available due to a call to OSSemPend(), or until data is available from a network connection due to a select()
or read()
. It both tasks are in a blocking state, then the RTOS idle task will run.
Generally any function that pends on a resource or creates a time delay:
Generally any function that does a read operation or pends on a file descriptor
select()
read()
, including variants with timeoutswrite()
, blocks until at least one char can be writtengets()
getchar()
fgets()
Generally calls that will pend on an incoming connection or received data
Generally functions that post to pending objects
The number and type of system tasks and priorities used by the system will depend on your platform and the system features used by your application. For example, if your application calls StartHTTP( ) to enable web services, then a system task will be created that handles web server requests. The system task priority levels are defined in \nburn\nbrtos\include\constants.h
.
Whether you use the Application Wizard in NBEclipse to create a new application, or start with one of the example programs, you will notice that the point at which your application takes control is a function named UserMain()
. UserMain()
is a task created by the system. The first few lines will consist of system initialization functions such as init()
and StartHttp()
.
Additional tasks are created with the OSTaskCreatewName()
or OSSimpleTaskCreatewName()
functions. The following is an example program that demonstrates the use of each function. When using OSTaskCreaewName()
you specify all parameters:
A significant benefit of the full function call is that it returns the status of the task creation process. For example, one possible error is that you cannot create a second task at the same priority as any other task.
The 'OSSimpleTaskCreatewName()' implementation lets you specify just the task function, priority, and name.
The following RTOS mechanisms can be used to protect shared data resources. They are listed in a decreasing order of severity as regarding system latency (all pend and post functions are at the same level).
OSSemPend() OSSemPost() | Protects an area of memory or resource. A task calls OSSemPend, which will block until the resource is available. OSSemPost releases the resource. |
OSMboxPend() OSMboxPost() | Same as semaphore, except a pointer variable is passed as the “message”. A task or ISR can post a message, but only a task can pend on a message. Both the posting task and pending task must agree on what the pointer points to. |
OSQPend() OSQPost() | A Queue is basically an array of mailboxes. It is used to post one or more messages. When you initialize a Queue, you must specify the maximum number of messages. The first message posted to the queue will be the first message extracted from the queue (FIFO). |
OSFifoPend() OSFifoPost() OSFifoPostFirst() OSFifoPendNoWait() | A FIFO is similar to a queue, but is specifically designed to pass pointers to OS_FIFO structures. The first parameter of the structure must be a (void *) element, which is used by the operating system to create a linked list of FIFOs. When initializing a FIFO, you do not specify the maximum number of entries as with a queue. Instead, your application has the ability (and responsibility) to allocate memory (static or dynamic) in which to store the structures. This can be done statically by declaring global variables, or dynamically by allocating memory from the heap. As with a queue, the first message posted to the FIFO will be the first message extracted from the queue. |
OSCritEnter OSCritExit OSCritObj | This is a counted critical section that restricts access to resources to one task at a time, sometimes called a “mutex”. For example, you have a linked list that is maintained by 3 separate tasks. If one task is manipulating the list, you could first call OSCirtEnter for that object (the list). If any other task tries to manipulate the list, it will block at the OSCritEnter until the task that previously called OSCritEnter, calls OSCritExit. Note that the number of enter calls must match number of exit calls. OSCritObj is a C++ implementation that uses scoping to automatically call the enter and exit functions. See example below. |
OSLock() OSUnlock() OSLockObj | Disables other tasks, but not interrupts. Increments for each OSLock, decrements for each OSUnlock. The C++ object OSLockObj was created to assist in making sure that an unlock is called for each lock. When an OSLockObj is created, the constructor calls OSLock( ). When the object goes out of scope, OSUnlock( ) is automatically called by the destructor. |
USER_ENTER_CRITICAL() USER_EXIT_CRITICAL() | Macro that disables other tasks and interrupts. Increments count on enter, decrements on exit. |
How do you decide which type of mechanism to use? Some guidelines are:
A semaphore is a protected variable that is used to control access to shared system resources (such as memory or serial ports), to signal the occurrence of events and task synchronization. A task can request a semaphore and wait until the resource or event takes place (called pending). A task can also post to a semaphore to indicate it no longer needs a resource, or to signal an event has taken place. To create a semaphore you declare one of type OS_SEM and initialize with OSSemInit()
: OS_SEM
MySemaphore; OSSemInit( &MySemaphore, 0 ); // set initial value to 0
Your application tasks can now use the post and pend functions on the semaphore: OSSemPost( &MySemaphore ); // post to a semaphore
OSSemPend( &MySemaphore, 0 ); // pend on a semaphore
The second parameter in the OSSemPend()
function specifies the number of time ticks to wait. A value of 0 waits forever. A good way to express a wait value is to use the TICKS_PER_SECOND definition provided by the RTOS: OSSemPend( &MySemaphore, TICKS_PER_SECOND * 5)
to wait 5 seconds.
A message queue is an object that enables tasks and interrupt service routines to pend and post pointer sized messages. The pointer values typically point to some type of object or structure that contains the actual message or data.
A FIFO is similar to a queue, but is specifically designed to pass pointers to OS_FIFO structures. The first parameter of the structure must be a (void *) element, which is used by the operating system to create a linked list of FIFOs. When initializing a FIFO, you do not specify the maximum number of entries as with a queue. Instead, your application has the ability (and responsibility) to allocate memory (static or dynamic) in which to store the structures. This can be done statically by declaring global variables, or dynamically by allocating memory from the heap. As with a queue, the first message posted to the FIFO will be the first message extracted from the queue.
OSCritEnter()
, OSCritExit()
and an OSCritObj
enable an application to use counted critical sections that restrict resource access to one task at a time (also called a “mutex”). For example, you have a linked list that is maintained by 3 separate tasks. If one task is manipulating the list, you call OSCritEnter()
before modifying the list. If any other task tries to access the list, the task will block at the OSCritEnter() call until the task that previously called OSCritEnter()
, calls OSCritExit()
. Since this is a counting critical section implementation, the number of enter calls must match number of exit calls for each task.
OSCritObj
is a C++ implementation that uses scoping to automatically call the enter and exit functions so you do not need to manually match each enter with an ext. In comparison with OsLock()
, OsCritEnter()
does not restrict task swapping unless two tasks want to access the same resource. OsLock()
prevents all task swapping.
OSFlags enables a function or task to pend on multiple flags or events, in contrast to a OSSemaphore which can pend on only a single event. The OSFlag implementation is essentially a 32-bit bitmap in which each bit position represents a “flag”. You create a OSFlag
object with OSFlagCreate()
, then set, clean and read the flags with the appropriate function. There are a number of functions used to monitor or pend on the flags, and provide the ability to pend on any one or more of the flags being set, or pending on all of flags being set at one time.
Flag Functions
OSFlagSet()
Set the bits asserted with bits_to_setOSFlagState()
Return the current value of the flagsOSFlagClear()
Clear the bits asserted in bits_to_clrOSFlagPendAll()
Wait until all of the flags indicated by mask are setOSFlagPendNoWait()
Check (but do not wait) if all of the flags indicated by the mask are setOSFlagPendAny()
Wait until any of the flags indicated by the bit mask are setOSFlagPendAnyNoWait()
Check (but do not wait) if any of the flags indicated by the mask are set