JaeOS -- Just Another Embedded OS

Because the RTOS should be a commodity.
Because the RTOS should be a commodity.

JaeOS API Documentation

WARNING: This site is getting prettier but it is still under construction.

This document has been automatically converted from an MS Word document. Formatting may look different from normal web pages.

Table of Contents

Introduction

JaeOS API

Creating Tasks

RTOS_CreateTask

RTOS_CreateTimeShareTask

RTOS_SetTaskName

Operations on Tasks

RTOS_Delay

RTOS_DelayUntil

RTOS_WakeupTask

RTOS_ChangePriority

RTOS_KillSelf

RTOS_KillTask

RTOS_SuspendSelf

RTOS_SuspendTask

RTOS_ResumeTask

RTOS_YieldPriority

RTOS_YieldTimeSlice

RTOS_LockScheduler

RTOS_UnlockScheduler

Semaphores

RTOS_CreateSemaphore

RTOS_PeekSemaphore

RTOS_PostSemaphore

RTOS_GetSemaphore

"Naked" Events

RTOS_CreateEventHandle

RTOS_WaitForEvent

RTOS_SignalEvent

Critical Sections

Timeshare Utility Functions

RTOS_GetTimeSlice

RTOS_GetRemainingTicks

RTOS_SetTimeSlice

Miscellaneous Utility Functions and Macros

RTOS_GetCurrentTask

RTOS_IsInsideIsr

RTOS_GetTime

RTOS_StartMultitasking

Extras

Message Queues

RTOS_RegInt RTOS_CreateQueue

RTOS_DestroyQueue

RTOS_RegInt RTOS_Enqueue

RTOS_RegInt RTOS_PrependQueue

RTOS_RegInt RTOS_Dequeue

RTOS_PeekQueue

JaeOS Configuration

Macros, Types and Functions to be Defined by the Target Port

The header file rtos_types.h

The header file rtos_target.h

Macros, Types and Functions to be Defined by the Application

The header file rtos_config.h

 

 

Introduction

The name RTOS described in this document is called Just Another Embedded Operating System abbreviated as JaeOS (pronounced "jay-oh-es").

I wrote JaeOS out of frustration. In small embedded systems parallel execution of software functions is often desired, many projects however do not wish to deal with the complexity of having an OS. The primary goal was to implement a somewhat minimalist, but fully functional RTOS. Making the OS fully functional was considered to be important in JaeOS design, having a hand crafted system call for every situation was not.

What I mean by that is that e.g. a counting semaphore can be used as a binary semaphore, or as a mutex/lock, so providing a separate set of API calls for all those is unnecessary. Using what is provided in an appropriate manner for the situation is more important than having dozens of special use primitives with perhaps marginally better performance. JaeOS is intended to be a commodity OS enabling the use to run software packages such as lwIP that require some sort of OS under them. JaeOS does not aim to be a highly optimized solution. Nevertheless the performance should be adequate for many embedded projects especially ones that have some sort of custom hardware interacting with software, such as FPGA based project.

The secondary goal was to actually have an OS with system calls that I have wished I had when working on various embedded products.

JaeOS API

The JaeOS core is intended to provide multi-threaded software execution on somewhat resource constrained systems. Due to the nature of embedded applications there was no assumption that the system has any sort of dynamic memory allocation facilities. As such the operating system code does not use any dynamic memory allocation and will work with all data structures statically compiled in. The API has been kept to a minimum to provide bare essentials to execute multiple tasks in parallel. The scheduler is a priority based RTOS scheduler with some provisions to define a group of tasks that are executes in a time share fashion among each other.

Creating Tasks

The API functions to create and set up tasks are special in the sense that they will work even if the OS is not actually running yet so as to allow initial setup before multitasking can begin.

Task structures must be initialized before they can be used. The task creation operations fill out various data fields in the task structure and also create references to the task inside the OS so that the scheduler can later on select the task for execution.

All tasks have a unique priority (only one task per priority level is supported). Each task has its own stack. The main function of the stack supposed to have an endless loop, if this function ever returns the task is killed and its priority level is freed.

Task creation has two different variants, the first one creates a normal (real time) task. The second one creates a task that is part of the time sharing peer group. Peers in the time share have time slices and can get preempted by another peer if their time slice expires. This preemption on time slice expiry cycles through all peers in a Round Robin fashion.

RTOS_CreateTask

RTOS_RegInt RTOS_CreateTask(RTOS_Task *task, const char *name, RTOS_TaskPriority priority, void *sp0, unsigned long stackCapacity, void (*f)(void *), void *param);

Initialize a task structure and let the operating system know that it exists.

Parameters:

n Task is the address of the task structure being initialized.

n The name of the task.

n Priority is the task"s priority.

n Sp0 is the beginning of the stack area (lowest address).

n StackCapacity is the number of items that fit into the stack.

n F() is the tasks main function.

n Param is a pointer passed to f() when the task starts running.

Return Value:

n RTOS_OK task creation was successful.

n RTOS_ERROR_PRIORITY_IN_USE the priority is already used by another task.

n RTOS_ERROR_OPERATION_NOT_PERMITTED task is NULL.

RTOS_CreateTimeShareTask

RTOS_RegInt RTOS_CreateTimeShareTask(RTOS_Task *task, const char *name, RTOS_TaskPriority priority, void *sp0, unsigned long stackCapacity, void (*f)(), void *param, RTOS_Time slice);

Initialize a task structure and let the operating system know that it exists. This version creates a task that is member of the peer group of equally important tasks.

Parameters:

n Task is the address of the task structure being initialized.

n The name of the task.

n Priority is the task"s priority.

n Sp0 is the beginning of the stack area (lowest address).

n StackCapacity is the number of items that fit into the stack.

n F() is the tasks main function.

n Param is a pointer passed to f() when the task starts running.

n Slice is the length of the task"s time slice in ticks.

Return Value:

n RTOS_OK task creation was successful.

n RTOS_ERROR_PRIORITY_IN_USE the priority is already used by another task.

n RTOS_ERROR_OPERATION_NOT_PERMITTED task is NULL.

RTOS_SetTaskName

void RTOS_SetTaskName(RTOS_Task *task, const char *name);

Deprecated: RTOS_CreateTask() now takes the task name as a parameter, no need to separately set the task name.

This function sets a task"s name (which can be used e.g. for debugging purposes) if the OS is configured to support task names.

Parameters:

n Task is the address of the task structure whose name is being defined.

n Name is the task name.

Operations on Tasks

Once tasks have been created and the RTOS is up and running the following operations can be performed on tasks.

RTOS_Delay

RTOS_RegInt RTOS_Delay(RTOS_Time ticksToSleep);

Put the task to sleep for the given number of ticks.

Parameters:

n TicksToSleep the number of ticks to sleep. Ticks are counted as timer interrupts happen. So an RTOS_Delay(1); can sleep anywhere between no time at all to one interval between two timer interrupts depending when in between two interrupts the task starts to sleep. A ticksToSleep value of 0 returns immediately.

Return Value:

n RTOS_OK The sleep has finished normally.

n RTOS_ERROR_OPERATION_NOT_PERMITTED A delay operation is attempted from inside an ISR, or when the scheduler is locked or the system"s idle task is trying to sleep.

n RTOS_ABORTED The delay was terminated prematurely probably by RTOS_WakeupTask().

 

RTOS_DelayUntil

RTOS_RegInt RTOS_DelayUntil(RTOS_Time wakeUpTime);

This function is like RTOS_Delay() but specifies the end of the sleep as an absolute time on the system timer.

Example:

RTOS_Time t = RTOS_GetTime();

While(1)

{

DoSomeWorkHere();

t += 10; // 10 tick intervals.

RTOS_DelayUntil(t);

}

This example will execute at exactly 10 tick intervals unless the system is under really heavy load to cause extra delays. If we used RTOS_Delay(10) instead the time spent between two executions of this loop would depend on how long it takes to do the work.

Parameters:

n WakeupTime The time when the task is to be waken up.

Return Value:

n RTOS_OK The sleep has finished normally.

n RTOS_ERROR_OPERATION_NOT_PERMITTED A delay operation is attempted from inside an ISR, or when the scheduler is locked or the system"s idle task is trying to sleep.

n RTOS_ABORTED The delay was terminated prematurely probably by RTOS_WakeupTask().

 

RTOS_WakeupTask

RTOS_RegInt RTOS_WakeupTask(RTOS_Task *task);

Wake up a task that is either sleeping in one of the delay functions or waiting for a semaphore or other event.

Parameters:

n Task The address of the task structure of the task that should be woken up.

Return Value:

n RTOS_OK Success.

n RTOS_ERROR_OPERATION_NOT_PERMITTED The parameter task is NULL or the task is not actually sleeping or waiting.

RTOS_ChangePriority

RTOS_ChangePriority(RTOS_Task *task, RTOS_TaskPriority targetPriority);

Change the priority of atask. If, for whatever reason, a task needs to run at an elevated priority this function allows it to do so.

The same function can be used to restore the task"s original priority.

Parameters:

n Task The address of the task structure whose priority is to be changed.

n TargetPriority The priority the task is trying to change to.

Return Value:

n RTOS_OK Success.

n RTOS_ERROR_OPERATION_NOT_PERMITTED task is null.

n RTOS_ERROR_PRIORITY_IN_USE The target priority is already in use.

n RTOS_ERROR_FAILED Operation has failed because the requested priority level does not exist in the system or it is the idle priority.

 

RTOS_KillSelf

RTOS_RegInt RTOS_KillSelf(void);

Kill the calling task. This function must be called from task level. It is the function that is called if the task"s main function returns. This is supposed to be a simpler version of RTOS_KillTask() as killing the currently running task from itself requires fewer checks for special cases. Unlike RTOS_KillTask(), RTOS_KillSelf() is always present. The recommended way to kill a task is to exit its main loop and return. If the task needs to exit prematurely from an internal function it can do so by calling RTOS_KillSelf().

Killing a task from another task via RTOS_KillTask() is not recommended because there is no way to know what the other task is doing or if it will leave the system in a usable state.

 

RTOS_KillTask

RTOS_RegInt RTOS_KillTask(RTOS_Task *task);

Kill a task and release its priority. It is provided in case some software package that can be ported to JaeOS requires the ability to kill a task. Its use is not recommended. The proper way to finish a thread of operation is to just simply return from its main loop and let the OS release its priority, not killing it from another task.

Parameters:

n Task The task to kill.

Return Value:

n RTOS_OK Success.

n RTOS_ERROR_OPERATION_NOT_PERMITTED Task is NULL.

n RTOS_ERROR_FAILED Operation has failed. This will happen on SMP systems if the task is running on another CPU.

RTOS_SuspendSelf

RTOS_RegInt RTOS_SuspendSelf(void);

A simpler version of RTOS_SuspendTask(), RTOS_SuspendSelf() suspends the calling task. In almost any use case I could think of the only thing that makes sense is for a task to suspend itself and wait for another task or an ISR to wake it up. I could not think of a good reason how suspending another task would make sense since there is no way to know what it is doing.

RTOS_SuspendTask

RTOS_RegInt RTOS_SuspendTask(RTOS_Task *task);

Suspend a task.

The suspended task will go into a "suspended state". If the task was waiting for an event or sleeping using RTOS_Delay() the visible effect after resume will be as if the task was waken up by RTOS_WakeUpTask(), so handling possible aborted operations for event/semaphore operations and sleep becomes necessary.

Note: The above semantics was chosen from various possibilities, none of which are without flaws.

Furthermore I could not come up with any plausible use case where it makes sense to suspend another task (in general it is not possible to know what other tasks are doing, what resources they are holding, etc. so stopping them in the middle of whatever they are doing does not sound like a good idea).

A task suspending itself (not going to sleep for a period of time or waiting for a specific event) and some other piece of code making a specific task resume does make sense in certain circumstances.

It is strongly recommended that tasks should only suspend themselves, not other tasks, preferably using the simpler call RTOS_SuspendSelf().

Parameters:

n Task The task to suspend.

Return Value:

n RTOS_OK Success.

n RTOS_ERROR_OPERATION_NOT_PERMITTED Task is NULL.

n RTOS_ERROR_FAILED Operation has failed. This will happen on SMP systems if the task is running on another CPU.

 

 

RTOS_ResumeTask

RTOS_RegInt RTOS_ResumeTask(RTOS_Task *task);

Resume a task. The task must have been previously suspended.

Parameters:

n Task The task to resume.

Return Value:

n RTOS_OK Success.

n RTOS_ERROR_OPERATION_NOT_PERMITTED Task is NULL or it wasn"t suspended.

 

RTOS_YieldPriority

void RTOS_YieldPriority(void);

Yield to a task that is possibly of a lower priority than the current task.

E.g. If a task is polling for some hardware to change status (e.g. if the hardware cannot actually generate an interrupt), but it is not time critical, the task might not wish to burn all CPU cycles polling but allows someone else to run instead. If the yield is successful the next lower priority task will run if it is not the idle task (this function never yields to the idle task). If there is no ready to run task of a lower priority the highest priority task that is ready to run will be selected. This latter scenario is possible if e.g. the current task was selected to run by a yield from a higher priority task. This way a series of tasks can yield to each other and execution will just cycle through them.

A task selected by a yield will run till the next event that causes scheduling which can be another yield, an interrupt including the timer interrupt, trying to get a semaphore or go to sleep by calling one of the delay functions.

This operation is intended to be used in tasks with priority based (real time) scheduling if they wish to let another task make use of some CPU cycles instead of looping around waiting.

E.g.:

While(0 == ReadSomeHardwareBit())

{

RTOS_YieldPriority();

}

This function is usually not suitable for time sharing peers to yield to each other.

RTOS_YieldTimeSlice

void RTOS_YieldTimeSlice(void);

This operation is to be used from a task that is part of the time sharing peer group to give up the remaining ticks in its time slice and let one of its peers run instead. Other than the fact that yielding is a voluntary action on the task"s part the effect is the same as if the task"s time slice had run out and it was unscheduled by the system in favor of its peers.

RTOS_LockScheduler

void RTOS_LockScheduler(void);

Lock the scheduler. While the scheduler is locked it will not select another task to run.

RTOS_UnlockScheduler

void RTOS_UnlockScheduler(void);

Unlock the scheduler.

Internally the OS keeps track of how many times the scheduler has been locked and unlocked so lock-unlock pairs can be nested.

Semaphores

The primary synchronization primitives in the XNONAMEX RTOS are counting semaphores. They can be used to implement other inter process communication functionality, and in fact a counting semaphore with an initial count of 1 can function where one would use a mutex so only semaphores are provided in order to keep the OS core to a minimum.

RTOS_CreateSemaphore

RTOS_RegInt RTOS_CreateSemaphore(RTOS_Semaphore *semaphore, RTOS_SemaphoreCount initialCount);

Create a properly formed semaphore structure from raw memory. If the semaphore is a statically compiled variable the C run time system would already set every field in it to zeroes and it is ready to use as a semaphore. If however a different initial count is needed or if the semaphore is allocated e.g. in dynamic memory by malloc() initialization is needed before the semaphore can be used.

Parameters:

n Semaphore The semaphore to be initialized.

Return Value:

n RTOS_OK Success.

n RTOS_ERROR_OPERATION_NOT_PERMITTED Semaphore is NULL.

 

 

RTOS_PeekSemaphore

RTOS_SemaphoreCount RTOS_PeekSemaphore(RTOS_Semaphore *semaphore);

Return the value of the counting semaphore without changing it.

Parameters:

n Semaphore The semaphore.

Return Value:

n The count of the semaphore.

RTOS_PostSemaphore

RTOS_RegInt RTOS_PostSemaphore(RTOS_Semaphore *semaphore);

Perform the "semaphore up" operation. If there are no tasks waiting for this semaphore its counter is incremented. If there are tasks waiting one of them is woken up.

Parameters:

n Semaphore The semaphore.

Return Value:

n RTOS_OK Success.

n RTOS_ERROR_OPERATION_NOT_PERMITTED Semaphore is NULL.

 

RTOS_GetSemaphore

RTOS_RegInt RTOS_GetSemaphore(RTOS_Semaphore *semaphore, RTOS_Time timeout);

Perform the "semaphore down" operation. If the semaphore"s count is non-zero it is decremented and the function returns immediately with success. If the semaphore"s count is zero the task starts to wait for some other task or an ISR to post the semaphore. The return value then depends on if the semaphore was posted first or if the time out has expired before the semaphore was posted.

This function can be called from an ISR but only with a timeout of zero (do not wait for the semamhore to be posted).

Parameters:

n Semaphore The semaphore.

n Timeout The number of ticks to wait for the semaphore. If a timeout of 0 is specified there is no wait, if the semaphore"s count is already zero the call returns with a timeout. To wait for a semaphore for an unlimited time pass the value RTOS_TIMEOUT_FOREVER as the timeout parameter. JaeOS allows calling RTOS_GetSemaphore() from an ISR but only with timeout = 0.

Return Value:

n RTOS_OK Success.

n RTOS_ERROR_OPERATION_NOT_PERMITTED Semaphore is NULL or this function was called from an ISR or with the Scheduler locked and timeout is not set to zero.

n RTOS_TIMED_OUT Time out happened before the semaphore was posted.

n RTOS_ABORTED Waiting for the semaphore has been interrupted by RTOS_WakeupTask().

 

 

"Naked" Events

"Naked" events are somewhat unusual as they are not a common type synchronization primitive supported by operating systems. They are in fact part of the implementation of the semaphore mechanism but without a counter. A task cannot "acquire" an event, it can only wait for a future occurrence of an event. Since this naturally comes from the implementation and at various times I wished I had something like that when using other operating systems I have decided to expose the unadorned event mechanism through the API.

The idea is that a task can wait for an event that will happen in the future much the same was as it would wait for someone posting a semaphore. When the event is signaled one of the tasks waiting for it (decided by the scheduling mechanism) is woken up.

RTOS_CreateEventHandle

RTOS_RegInt RTOS_CreateEventHandle(RTOS_EventHandle *event);

Turn raw bits to an event handle. The explanation is similar to the one provided at the function RTOS_CreateSemaphore().

Parameters:

n Event The address of the event structure to initialize.

Return Value:

n RTOS_OK Success.

n RTOS_ERROR_OPERATION_NOT_PERMITTED Event is NULL

RTOS_WaitForEvent

RTOS_RegInt RTOS_WaitForEvent(RTOS_EventHandle *event, RTOS_Time timeout);

Wait for the event to happen.

Parameters:

n Event The address of the event structure.

n Timeout The number of ticks to wait for the event. To wait for an event for an unlimited time pass the value RTOS_TIMEOUT_FOREVER as the timeout parameter. If timeout = 0 this call does nothing.

n  

Return Value:

n RTOS_OK Success.

n RTOS_ERROR_OPERATION_NOT_PERMITTED Event is NULL or the function has been called from an ISR or with the scheduler locked.

n RTOS_TIMED_OUT Time out happened before the event was signaled.

n RTOS_ABORTED Waiting for the event has been interrupted by RTOS_WakeupTask().

 

 

 

RTOS_SignalEvent

RTOS_RegInt RTOS_SignalEvent(RTOS_EventHandle *event);

Signal that the event has happened to wake up a task that is waiting for the event.

Parameters:

n Event The address of the event structure.

Return Value:

n RTOS_OK Success.

n RTOS_ERROR_OPERATION_NOT_PERMITTED Event is NULL.

Critical Sections

Three macros are provided to enter and exit critical sections and to declare a variable to save the context so that upon exiting it can be restored.

RTOS_SavedCriticalState(saved_state)

RTOS_EnterCriticalSection(saved_state);

DoWorkThatMustNotBeInterruptedHere();

RTOS_ExitCriticalSection(saved_state);

 

Critical sections disable interrupts. Saved_state is a variable that can hold the state of how interrupts were enabled or disabled before using RTOS_EnterCriticalSection() and RTOS_ExitCriticalSection() restores the saved state. This is one of the common ways to implement enter/exit critical section and I believe that often the compiler produces more efficient code than e.g. for a solution where a counter is maintained of how many times the code tried to nest enter critical section calls. Of course nothing prevents the target port to choose an implementation with a counter and make RTOS_SavedCriticalSection() do nothing.

Timeshare Utility Functions

Utility functions to manipulate the time slice of a timeshare task on the fly.

RTOS_GetTimeSlice

RTOS_Time RTOS_GetTimeSlice(RTOS_Task *task);

Get the length of the time slice of a task. If the task parameter is NULL this function returns zero.

RTOS_GetRemainingTicks

RTOS_Time RTOS_GetRemainingTicks(RTOS_Task *task);

Get the number of timer ticks remaining of the current time slice of a running task. If the task parameter is NULL this function returns zero.

RTOS_SetTimeSlice

RTOS_RegInt RTOS_SetTimeSlice(RTOS_Task *task, RTOS_Time slice);

Set the length of the time slice of a time share task. This function may adjust the remaining ticks if the task is currently executing to provide a behavior consistent with the new time slice value.

Return Value:

n RTOS_ERROR_OPERATION_NOT_PERMITTED If task is NULL.

n RTOS_ERROR_FAILED If task is not a timeshare task.

n RTOS_OK Success.

 

Miscellaneous Utility Functions and Macros

RTOS_GetCurrentTask

RTOS_Task *RTOS_GetCurrentTask(void);

A function that returns the address of the task that is currently executing.

RTOS_IsInsideIsr

RTOS_IsInsideIsr()

Indicates if the OS is executing inside an ISR.

RTOS_GetTime

RTOS_Time RTOS_GetTime(void);

Return the system time (ticks since boot up).

RTOS_StartMultitasking

int RTOS_StartMultitasking(void);

Start multitasking. This function has to be defined by the target port. Normally this function will not return to its caller. If it does that means it could not start multitasking, probably because some requirement has failed.

Return Value:

n RTOS_ERROR_FAILED Could not start multitasking.

Extras

The API described above is considered to be core functionality, the defined minimum set of functions that are maintained as part of the OS. In order to perform some common operations that one might need in a multi-tasking environment, further functions can be defined. If these, however, can be implemented on top of the core API they should not be part of the core. In other words if some piece of code needs to know how the OS works internally it has to be part of the core. If, on the other hand, a piece of code can be implemented using only core API functions that are available to the application programmer that piece of code is not part of the OS core even if it is provided with the OS.

Message Queues

Messages queues are a feature commonly provided by various operating systems intended for embedded applications. An implementation of message queues is provided with JaeOS, but it is not part of the OS core, rather message queues are implemented entirely on top of the API described above.

RTOS_RegInt RTOS_CreateQueue

RTOS_RegInt RTOS_CreateQueue(RTOS_Queue *queue, void *buffer, RTOS_QueueCount size);

Initialize the message queue data structure. Make the memory pointed to by buffer its message buffer with room for size slots.

Parameters:

n Queue the message queue.

n Buffer A memory area to be used to hold the message entries.

n Size the number of slots in the queue.

Return Value:

n RTOS_OK Success.

n RTOS_ERROR_OPERATION_NOT_PERMITTED If queue or buffer is NULL is size is 0.

 

RTOS_DestroyQueue

RTOS_RegInt RTOS_DestroyQueue(RTOS_Queue *queue);

Make the message queue invalid. In particular this function will remove the queue"s buffer (so that if the buffer was dynamically allocated it can be freed after calling RTOS_DestroyQueue().

Parameters:

n Queue the message queue.

Return Value:

n RTOS_OK Success.

n RTOS_ERROR_FAILED Queue is not empty.

n RTOS_ERROR_OPERATION_NOT_PERMITTED If queue is NULL.

 

 

RTOS_RegInt RTOS_Enqueue

RTOS_RegInt RTOS_Enqueue(RTOS_Queue *queue, void *message, RTOS_Time timeout);

Append message to the end of the queue.

 

Parameters:

n Queue the message queue.

n Message The message to be appended to the queue. Only the pointer is placed in the queue buffer, data is not copied by this operation. The memory message points to cannot be freed after placing the message into the message queue.

n Timeout The number of ticks the caller is willing to wait for the operation to finish if the queue is full. If the queue is full and timeout is not zero the caller will be blocked until some function in the system consumes data from the queue thereby freeing a slot, or until the timeout expires. An ISR can only use a timeout of 0 (no wait) because an ISR cannot sleep.

Return Value:

n RTOS_OK Success.

n RTOS_ERROR_OPERATION_NOT_PERMITTED If queue is NULL or it does not have a valid buffer.

n This function can also return any status code that RTOS_GetSemaphore() can return.

RTOS_RegInt RTOS_PrependQueue

RTOS_RegInt RTOS_PrependQueue(RTOS_Queue *queue, void *message, RTOS_Time timeout);

This function is similar to RTOS_Enqueue() but instead of appending to the end of the queue it pushes the message back" to the front of the queue. This function allows using a message queue in stack-like fashion (LIFO) instead of the usual FIFO operation.

Parameters:

n Queue the message queue.

n Message The message to be appended to the queue. Only the pointer is placed in the queue buffer, data is not copied by this operation. The memory message points to cannot be freed after placing the message into the message queue.

n Timeout The number of ticks the caller is willing to wait for the operation to finish if the queue is full. If the queue is full and timeout is not zero the caller will be blocked until some function in the system consumes data from the queue thereby freeing a slot, or until the timeout expires. An ISR can only use a timeout of 0 (no wait) because an ISR cannot sleep.

Return Value:

n RTOS_OK Success.

n RTOS_ERROR_OPERATION_NOT_PERMITTED If queue is NULL or it does not have a valid buffer.

n This function can also return any status code that RTOS_GetSemaphore() can return.

RTOS_RegInt RTOS_Dequeue

 

RTOS_RegInt RTOS_Dequeue(RTOS_Queue *queue, void **message, RTOS_Time timeout);

Renove the first message at the head of the queue and return it to the called in the output parameter **message.

Parameters:

n Queue the message queue.

n Message An output parameter, the message removed from the queue will be returned at the location where *message points to.

n Timeout The number of ticks the caller is willing to wait for the operation to finish if the queue is empty. If the queue is empty and timeout is not zero the caller will be blocked until some function in the system places data to the queue, or until the timeout expires. An ISR can only use a timeout of 0 (no wait) because an ISR cannot sleep.

 

Return Value:

n RTOS_OK Success.

n RTOS_ERROR_OPERATION_NOT_PERMITTED If queue is NULL or it does not have a valid buffer. Also if message is NULL.

n This function can also return any status code that RTOS_GetSemaphore() can return.

 

RTOS_PeekQueue

RTOS_RegInt RTOS_PeekQueue(RTOS_Queue *queue, void **message);

Return the first item in the queue (if the queue is not empty) but do not remove it from the queue.

Parameters:

n Queue the message queue.

n Message An output parameter, the message removed from the queue will be returned at the location where *message points to.

 

Return Value:

n RTOS_OK Success.

n RTOS_ERROR_OPERATION_NOT_PERMITTED If queue is NULL or it does not have a valid buffer. Also if message is NULL.

n RTOS_ERROR_FAILED If queue is empty.

JaeOS Configuration

JaeOS is configured by defining a set of #define macros in the appropriate C header files as well as by defining the actual types of some data used by the operating system.

Certain configuration items do have reasonable default values if neither the target port nor the application defines them, others must be defined in the appropriate header file.

Macros, Types and Functions to be Defined by the Target Port

The header file rtos_types.h

Various things that need to be defined early (before the base JaeOS structures are defined). Mostly the definitions of various types appropriate for the target port.

typedef uint32_t RTOS_Critical_State;

The type of a variable that can hold the saved critical state (interrupts disabled/enabled) on the target CPU.

typedef uint32_t RTOS_StackItem_t;

The type of slots in the stack.

typedef struct rtos_StackFrame rtos_StackFrame;

Define a structure here that represents a saved stack frame and then create this typedef.

It will be used by some function that initializes the task"s stack frame in the target dependent code.

#define RTOS_INITIAL_STACK_DEPTH <Initial stack "depth" in BYTES">

#define RTOS_REG_INT_TYPE int

The type of a small integer variable that conveniently fits into a register. Default is the C type "int".

If that is OK this macro does not need to be defined. On some small microcontrollers a C compiler still might have 16bit "int" but the registers are 8 bit so this type might need to be defined as "char".

#define RTOS_TASKPRIORITY_TYPE <Type used for task priority.>

Defaults to the unsigned version of RTOS_REG_INT_TYPE. Probably the default is acceptable for most ports thus this macro does not need to be defined.

#define RTOS_TASKSET_TYPE uint32_t

The integer type used for representing a task set (as a bit map).

It can depend on RTOS_Priority_Highest, i.e. the actual type can change even on the same target CPU/board depending on the application"s needs.

The default is uint32_t.

#define RTOS_HIGHEST_SUPPORTED_TASK_PRIORITY <Highest Priority>

The number of bits in RTOS_TASKSET_TYPE. The default is 31.

#define RTOS_FIND_HIGHEST(X) <Code to retrieve the position of the highest bit in X or a large unsigned integer if X is 0.>

The target dependent way to find the first bit which is set in a variable of type defined by RTOS_TASKSET_TYPE.

#define RTOS_MIN_STACK_SIZE 512

The minimum number of RTOS_StackItem_t in a stack to be used by the simplest task.

Stacks must be able to accommodate whatever the running application needs, plus a saved stack frame, and on some targets whatever the OS needs if a separate stack is not provided.

The header file rtos_target.h

Macros that are defined "late" (after JaeOS) data structures.

#define RTOS_SavedCriticalState(X) RTOS_Critical_State X

A way to create a variable of type RTOS_Critical_State.

#define RTOS_EnterCriticalSection(X) <Code to disable interrupts and save the previous state in X.>

Enter critical section. Disable interrupts but save the old state in the variable provided.

#define RTOS_ExitCriticalSection(X) <Code to restore the critical state from X.>

Restore interrupts enabled/disabled state from the variable provided.

#define RTOS_DisableInterrupts() <Code to disable interrupts.>

Disable interrupts.

#define RTOS_EnableInterrupts() <Code to enable interrupts.>

Unconditionally enable interrupts (don"t restore a previous state, just enable them).

#define RTOS_INLINE static inline

The appropriate keywords to make a function and inline function. The above is appropriate for GCC.

#define RTOS_INVOKE_SCHEDULER() <Code to invoke OS"s scheduler.>

Invoke JaeOS"s scheduler. Usually it should be some sort of software interrupt or trap that ends up calling the OS.

#define RTOS_INVOKE_YIELD() <Code to invoke the OS"s scheduler for a "yield priority" operation.>

Invoke JaeOS"s scheduler during a "yield priority" operation. It is similar to RTOS_INVOKE_SCHEDULER but with a different entry point or parameter so that the OS can take appropriate action.

extern int RTOS_StartMultitasking(void);

The prototype of the target dependent RTOS_StartMultiTasking() function.

extern void RTOS_DefaultIdleFunction(void *p);

The prototype of the target dependent RTOS_DefaultIdleFunction() function.

#define RTOS_DEFAULT_IDLE_FUNCTION RTOS_DefaultIdleFunction

If this macro is defined it indicates that RTOS_DefaultIdleFunction() is provided by the port and the application can use it instead of defining a custom idle function.

Macros, Types and Functions to be Defined by the Application

The header file rtos_config.h

This header file configures JaeOS for your application.

Highest Priority in the Application

#define RTOS_Priority_Highest RTOS_Priority_Some_Important_Task

The macro RTOS_Priority_Highest MUST be defined and it has to resolve to an integer that is the priority of the highest priority task in the entire application.

Include/Exclude Features

The following macros control if various features are supported JaeOS. Simply defining the macro compiles RTOS internals with the feature enabled. It may not be enough to have the API function available the appropriate Makefile has to pull in the corresponding source file.

#define RTOS_SUPPORT_SLEEP

Compile in sleep and timeout operations such as the internal workings of RTOS_Delay() and events with timeout.

#define RTOS_SUPPORT_EVENTS

Support for events and/or semaphores.

#define RTOS_INCLUDE_DELAY

Include delay functions that sleep on time. For most application this should be defined.

#define RTOS_INCLUDE_NAKED_EVENTS

Deprecated, define RTOS_SUPPORT_SLEEP and RTOS_SUPPORT_EVENTS instead.

Include the API for accessing "naked" events.

#define RTOS_INCLUDE_SCHEDULER_LOCK

Allow locking and unlocking the scheduler.

 

 

#define RTOS_INCLUDE_SEMAPHORES

Deprecated, define RTOS_SUPPORT_SLEEP and RTOS_SUPPORT_EVENTS instead.

Include counting semaphores.

#define RTOS_INCLUDE_SUSPEND_AND_RESUME

Include the API for task suspend and resume.

#define RTOS_SUPPORT_TIMESHARE

Include the support for time sharing (non real-time) tasks.

#define RTOS_INCLUDE_WAKEUP

Allow prematurely waking up a task that is waiting or sleeping.

 

Asserts and Error Handling

#define RTOS_USE_ASSERTS

Allow JaeOS to use asserts when invalid parameters are passed to certain API functions.

This might be good for development, but for a shipping product one has to make decisions what mechanism to use to deal with errors. If the above macro is defined the applications should also provide the definition for RTOS_ASSERT(COND).

#define RTOS_ASSERT(COND) <Code to stop execution, print some message and halt the system if COND is not true.>

The default provided by JaeOS if the application does not provide a definition is to just stop execution and wait in an endless loop. This is simple, but requires a debugger to give any clue at all what has happened. It is highly recommended that the application should define its own version with more user friendly features such as printing a message. The exact details of the action taken are likely to be system dependent. E.g. How does one print or what is the proper way to provide the developer information about what went wrong when the OS is dead.

There is one more macro related to run time checks that can be defined:

#define RTOS_DISABLE_RUNTIME_CHECKS

Normally JaeOS checks if the parameters passed to its API function are correct or not. Of course it requires executing instructions at run time which might be undesirable in certain cases. Defining this macro DISABLES most of the checks thus making API calls slightly faster (at the expense of skipping safety checks). If you are running on an extremely slow microcontroller and you are certain that the application code is correct (so the checks would never fail) you can use this macro to skip checks (make the code slightly faster and smaller).

For most application disabling checks is NOT recommended.

Miscellaneous

#define RTOS_TIME_TYPE uint32_t

The type that is used to keep track of time. The JaeOS default is uint32_t and it does not need to be defined if the default is acceptable.

#define RTOS_TASK_NAME_LENGTH <A small number.>

The length of the space reserved for a task"s name in characters. If this macro is undefined then task names are not supported.

#define RTOS_TICKS_PER_SECOND 100

The number of timer ticks per second. The target port should initialize timer hardware based on this macro.

If the application"s rtos_config.h does not define it, the default value of RTOS_TICKS_PER_SECOND is 100.