diff --git a/README.md b/README.md index 4e4ae36..6828159 100644 --- a/README.md +++ b/README.md @@ -2,15 +2,17 @@ [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://github.com/MannyPeterson/HeliOS/blob/master/LICENSE.md) ![GitHub last commit](https://img.shields.io/github/last-commit/MannyPeterson/HeliOS) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/MannyPeterson/HeliOS) # Overview -HeliOS is an embedded operating system that is free for anyone to use. While called an operating system for simplicity, HeliOS is better described as a multitasking kernel for embedded systems. HeliOS is very small. In fact, it is small enough to run on most 8-bit microcontrollers including the popular AVR based Arduino Uno. Written entirely in C, HeliOS runs on a variety of microcontrollers and integrates easily into any project. HeliOS is also easy to learn with an Application Programming Interface (API) consisting of only 20 function calls. HeliOS contains the following key features: -* Cooperative Multitasking (Runtime Balanced) +HeliOS is an embedded operating system that is free for anyone to use. While called an operating system for simplicity, HeliOS is better described as a multitasking kernel for embedded systems. HeliOS is very small. In fact, it is small enough to run on most 8-bit microcontrollers including the popular AVR based Arduino Uno. Written entirely in C, HeliOS runs on a variety of microcontrollers and integrates easily into any project. HeliOS is also easy to learn with an Application Programming Interface (API) consisting of only 21 function calls. HeliOS contains the following key features: +* Cooperative Multitasking (Run-time Balanced) * Event Driven Multitasking * Task Notification/Messaging * Timers * Managed Memory * Non-Preemptive (no mutexs! no deadlocks! no race conditions! no headaches!) -The HeliOS scheduler implements two types of multitasking: cooperative and event driven. Unlike many cooperative multitasking embeded operating systems, which use a round-robin scheduling strategy. HeliOS's scheduler implements a runtime balanced strategy which ensures tasks with shorter runtimes are scheduled for execution more frequently than tasks with longer runtimes. This way all running tasks receive approximately equal total runtime without using context switching. The other option in HeliOS is event driven multitasking, which uses the wait/notify and timer interfaces. Mixing cooperative and event driven tasks is not a problem. +As noted in the key features, HeliOS supports two types of multitasking: cooperative and event driven. Cooperative multitasking embedded operating systems and task schedulers often use a round-robin scheduling strategy. However, HeliOS uses a run-time balanced strategy which ensures tasks with shorter run-times are prioritized over tasks with longer run-times. This ensures all running tasks receive approximately equal total run-time without using context switching. The other multitasking option available in HeliOS is event driven multitasking, which uses the wait/notify and timer interfaces. Mixing cooperative and event driven tasks in HeliOS is not a problem. +# Why HeliOS? +There are already so many great and proven RTOS and scheduler options available, then why HeliOS? That is certainly a valid question. HeliOS was never intended to replace or compete with the other options already available today (if you have not checked out FreeRTOS - you should, it is nothing short of an amazing RTOS for embedded and is more accessible today than ever before through the Arduino Library Manager). HeliOS, however, due to its size and simplicity, is intended to play in the space between RTOS's and task schedulers. HeliOS is tiny (smaller than FreeRTOS), easy to use and a great place to start for enthusiasts, hobbyists and researchers. # Getting Started ## Arduino Because HeliOS is compliant with the Arduino 1.5 (rev. 2.2) Library Specification, getting up and running is quick and easy. HeliOS can be installed directly from the Arduino Library Manager or downloaded and installed manually. Both options are described [here](https://www.arduino.cc/en/Guide/Libraries#toc3). Once up and running, check out one of the example sketches or refer to the HeliOS Programmer's Guide in the Documentation section. @@ -53,9 +55,9 @@ void taskBlink(int id_) { void setup() { xHeliOSSetup(); - + pinMode(LED_BUILTIN, OUTPUT); - + int id = xTaskAdd("TASKBLINK", &taskBlink); xTaskWait(id); xTaskSetTimer(id, 1000000); @@ -67,7 +69,8 @@ void loop() { ``` # Releases All releases, including the current release, can be found [here](https://github.com/MannyPeterson/HeliOS/releases). -* 0.2.1 - The first official release. +* 0.2.2 - Additional function calls, minor fixes and documentation enhancements +* 0.2.1 - The first official release # Contributing While all contributions are welcome, contributions are needed most in the following areas: * HeliOS Programmer's Guide @@ -75,8 +78,8 @@ While all contributions are welcome, contributions are needed most in the follow * Stability Enhancements * Testing -Please contact the author through GitHub if you are interested in contributing. +To contribute, please create an issue and indicate how you would like to contribute. Please fork from the **develop** branch as **master** is kept even with the current release. # License HeliOS is copyrighted open source software licensed under the Free Software Foundation's GNU General Public License Version 3. The license can be found [here](/LICENSE.md). # Important -HeliOS is **not** certified for use in safety-critical applications. The HeliOS source code, whether in full or in part, must **never** be used in applications where a risk to life exists. \ No newline at end of file +HeliOS is **not** certified for use in safety-critical applications. The HeliOS source code, whether in full or in part, must **never** be used in applications where a risk to life exists. diff --git a/extras/HeliOS_Programmers_Guide.md b/extras/HeliOS_Programmers_Guide.md index 539ebb2..0f4f0cf 100644 --- a/extras/HeliOS_Programmers_Guide.md +++ b/extras/HeliOS_Programmers_Guide.md @@ -1,11 +1,243 @@ # HeliOS Programmer's Guide -This guide contains documentation of HeliOS and its Application Programming Interface (API). This is a work in progress. +This guide contains documentation of HeliOS and its Application Programming Interface (API). This guide a work in progress. # Multitasking +HeliOS provides two types of multitasking: cooperative and event driven multitasking. Both types can be used together. This section provides an explanation of and how to use each. ## Cooperative Multitasking +The most straightforward type of multitasking in HeliOS is cooperative multitasking. Cooperative multitasking is best used in cases where continuous polling of a sensor or other device is required. + +Any task with a state of **running** is scheduled cooperatively by the HeliOS scheduler. The **xTaskStart()** function call sets the state of any task, provided it is not in an **errored** state, to **running**. However, just because a task is in a **running** state, does not guarantee the scheduler will schedule the task for execution. A task in a **running** state will only be scheduled for execution if (1) it is the only task in a **running** state, or (2) it is the task with the least total run-time. + +This cooperative multitasking strategy is called **balanced run-time**. This strategy allows the HeliOS scheduler to ensure that all tasks in a **running** state will receive approximately the same amount of run-time within any given period. The total run-time of a task can be obtained using the **xTaskGetInfo()** function call and inspecting the **totalRuntime** member of the **xTaskGetInfoResult** structure. + +As a practical example of how the balanced run-time strategy works, imagine a microcontroller with two tasks. Both tasks are in a **running** state and each task's run-time differs from the other. Task "A" has a 5 microsecond run-time and task "B" has a 50 microsecond run-time. Assuming each task's run-time remains constant, Task "A" will be executed 10 times for every one time task "B" executed. This ensures that task "A" receives approximately the same total run-time as task "B" without using priorities or context switching. + +If the run-time of either task "A" or "B" were to change during run-time, the HeliOS scheduler would dynamically adjust the schedule to ensure the total run-times of both tasks would remain approximately equal. + +It is important to note that the use of function calls like **delay()** must be avoided. If specific timing requirements exists, then event driven multitasking must be used. See the section on **Event Driven Multitasking** for further details. + +Below is an Arduino example of how two tasks are added to HeliOS and scheduled cooperatively. + +```C +#include + +/* + * Create the first task "A" + */ +void taskA(int id_) { + // DO SOMETHING +} + +/* + * Create the second task "B" + */ +void taskB(int id_) { + // DO SOMETHING +} + +void setup() { + /* + * xHeliOSSetup() must be the first function call + * made to initialize HeliOS and its data structures + */ + xHeliOSSetup(); + + /* + * Declare and initialize an int to temporarily hold the + * task id. + */ + int id = 0; + + /* + * Pass the task friendly name and function to xTaskAdd() + * to add the task to HeliOS. xTaskAdd() will return a + * task id greater than zero if the task is added unsuccessfully. + */ + id = xTaskAdd("TASKA", &taskA); + + /* + * Pass the task id of the task to set its state from stopped + * to running. + */ + xTaskStart(id); + + /* + * Pass the task friendly name and function to xTaskAdd() + * to add the task to HeliOS. xTaskAdd() will return a + * task id greater than zero if the task is added unsuccessfully. + */ + id = xTaskAdd("TASKB", &taskB); + + /* + * Pass the task id of the task to set its state from stopped + * to running. + */ + xTaskStart(id); +} + +void loop() { + /* + * Pass control to the the HeliOS scheduler. xHeliOSLoop() should + * be the only code inside the microcontroller project's + * main loop. + */ + xHeliOSLoop(); +} +``` + ## Event Driven Multitasking -# Notification & Timers -## Notification -## Timers +The other type of multitasking supported by HeliOS is event driven multitasking. Event driven works as its name suggests. The HeliOS scheduler will only schedule an event driven task for execution when an event is raised. Any tasking in a **waiting** state is considered to be using event driven multitasking. To place a task in the **waiting** state, the **xTaskWait()** function call must be called. The HeliOS scheduler supports two types of events. The first is a notification event and the second is a timer event. Both types are covered in more detail below. +### Notification +A notification occurs when the **xTaskNotify()** function call occurs. All tasks in the **waiting** state will respond to notification events, even tasks configured with a timer. Calling the **xTaskNotify()** function call on tasks in a **stopped** or **running** state has no affect. When calling **xTaskNotify()** the notification bytes and value must be specified along with the id of the receiving task. A notification value is a small character buffer (default size is 16 bytes). The notification bytes is the size of the notification value contained in the character buffer (or notification value). For example, **xTaskNotify(3, 5, "ABCDE")** would send task 3 a 5 byte notification value of "ABCDE". When task 3 receives the notification, it will use the notification bytes to determine the number of bytes to read from the notification value. It is the task notification bytes and value feature of HeliOS that allows inter-task messaging as part of the wait/notify functionality. + +Below is an Arduino example of two tasks. Task "A" continuously sends notifications to task "B". + +```C +#include + +/* + * Create the first task "A" + */ +void taskA(int id_) { + /* + * Obtain the task id of task "B" using its friendly name + * since it should not be assumed that task "B" will always + * have a task id of 2. Then pass then send a 5 byte notification + * value with "HELLO" in the character buffer. + */ + xTaskNotify(xTaskGetId("TASKB"), 5, "HELLO"); +} + +/* + * Create the second task "B" + */ +void taskB(int id_) { + /* + * Obtain the notification bytes and value from the xTaskGetInfoResult + * structure by first making a call to the xTaskGetInfo() function call + * using the task's id stored in id_. + */ + struct xTaskGetInfoResult* res = xTaskGetInfo(id_); + /* Because xTaskInfo() can return a null pointer, always check + * that the structure's pointer is not null before accessing + * its members. + */ + if(res) { + /* + * res->notificationBytes contains the notification bytes + * res->notificationValue contains the notification value + */ + } + /* Always call xMemFree() to free the managed memory allocated by + * xTaskGetInfo(); + */ + xMemFree(res); +} + +void setup() { + /* + * xHeliOSSetup() must be the first function call + * made to initialize HeliOS and its data structures + */ + xHeliOSSetup(); + + /* + * Declare and initialize an int to temporarily hold the + * task id. + */ + int id = 0; + + /* + * Pass the task friendly name and function to xTaskAdd() + * to add the task to HeliOS. xTaskAdd() will return a + * task id greater than zero if the task is added unsuccessfully. + */ + id = xTaskAdd("TASKA", &taskA); + + /* + * Pass the task id of the task to set its state from stopped + * to running. + */ + xTaskSart(id); + + /* + * Pass the task friendly name and function to xTaskAdd() + * to add the task to HeliOS. xTaskAdd() will return a + * task id greater than zero if the task is added unsuccessfully. + */ + id = xTaskAdd("TASKB", &taskB); + + /* + * Pass the task id of the task to set its state from stopped + * to waiting. + */ + xTaskWait(id); +} + +void loop() { + /* + * Pass control to the the HeliOS scheduler. xHeliOSLoop() should + * be the only code inside the microcontroller project's + * main loop. + */ + xHeliOSLoop(); +} +``` + +### Timer +The other event type is a timer event. It functions like an egg timer for tasks. After a certain duration has passed, the HeliOS scheduler schedules the task for execution. Like all tasks using event driven multitasking, the task state must be **waiting**. To configure a task's timer, the **xTaskSetTimer()** function call must be called which specifies the duration, in microseconds, that must elapse before HeliOS executes the task. HeliOS will automatically reset the timer every time it elapses. The timer can also be reset by calling the **xTaskresetTimer()** function call. Task timers are recommended over free running tasks that are cooperatively scheduled because they offer greater control over the timing of microcontroller operations. Below is an example of an event driven task on Arduino. In this example, task "A" will be executed every 1,000,000 microseconds (1 second). + +```C +#include + +/* + * Create the first task "A" + */ +void taskA(int id_) { + // DO SOMETHING +} + +void setup() { + /* + * xHeliOSSetup() must be the first function call + * made to initialize HeliOS and its data structures + */ + xHeliOSSetup(); + + /* + * Declare and initialize an int to temporarily hold the + * task id. + */ + int id = 0; + + /* + * Pass the task friendly name and function to xTaskAdd() + * to add the task to HeliOS. xTaskAdd() will return a + * task id greater than zero if the task is added unsuccessfully. + */ + id = xTaskAdd("TASKA", &taskA); + + /* + * Pass the task id of the task to set its state from stopped + * to waiting. + */ + xTaskWait(id); + + /* + * Set the timer on the task to 1,000,000 microseconds. + */ + xTaskSetTimer(id, 1000000); +} + +void loop() { + /* + * Pass control to the the HeliOS scheduler. xHeliOSLoop() should + * be the only code inside the microcontroller project's + * main loop. + */ + xHeliOSLoop(); +} +``` + # Critical Blocking HeliOS implements cricitcal blocking which prevents some functions from being called that may alter the state of HeliOS during critical operations. Critical blocking is in effect while the scheduler is running and prevents functions like xTaskAdd() and xTaskRemove() from altering the state of the HeliOS. Thus, you cannot, for example, remove a task using xTaskRemove() while inside a running task. Functions that do not alter the state of HeliOS may be called during critical blocking. For example, function calls like xTaskGetInfo() may be called during critical blocking as they do not update the state of HeliOS. # Data Types @@ -148,4 +380,4 @@ None ### int TaskListSeek(int) ### struct xTaskGetListResult* xTaskGetList(int*) ### void xTaskSetTimer(int, int) -### void xTaskResetTimer(int) \ No newline at end of file +### void xTaskResetTimer(int) diff --git a/keywords.txt b/keywords.txt index 54dba88..01a6077 100644 --- a/keywords.txt +++ b/keywords.txt @@ -6,41 +6,43 @@ # Datatypes (KEYWORD1) ####################################### -xTaskState KEYWORD1 +xTaskState KEYWORD1 ####################################### # Functions (KEYWORD2) ####################################### -xHeliOSGetInfo KEYWORD2 -xHeliOSLoop KEYWORD2 -xHeliOSSetup KEYWORD2 -xMemAlloc KEYWORD2 -xMemFree KEYWORD2 -xMemGetSize KEYWORD2 -xMemGetUsed KEYWORD2 -xTaskAdd KEYWORD2 -xTaskClear KEYWORD2 -xTaskGetId KEYWORD2 -xTaskGetInfo KEYWORD2 -xTaskGetList KEYWORD2 -xTaskNotify KEYWORD2 -xTaskNotifyClear KEYWORD2 -xTaskRemove KEYWORD2 -xTaskResetTimer KEYWORD2 -xTaskSetTimer KEYWORD2 -xTaskStart KEYWORD2 -xTaskStop KEYWORD2 -xTaskWait KEYWORD2 +xHeliOSGetInfo KEYWORD2 +xHeliOSLoop KEYWORD2 +xHeliOSSetup KEYWORD2 +xMemAlloc KEYWORD2 +xMemFree KEYWORD2 +xMemGetSize KEYWORD2 +xMemGetUsed KEYWORD2 +xTaskAdd KEYWORD2 +xTaskClear KEYWORD2 +xTaskGetId KEYWORD2 +xTaskGetInfo KEYWORD2 +xTaskGetList KEYWORD2 +xTaskGetNotif KEYWORD2 +xTaskNotify KEYWORD2 +xTaskNotifyClear KEYWORD2 +xTaskRemove KEYWORD2 +xTaskResetTimer KEYWORD2 +xTaskSetTimer KEYWORD2 +xTaskStart KEYWORD2 +xTaskStop KEYWORD2 +xTaskWait KEYWORD2 ####################################### # Structures (KEYWORD3) ####################################### xHeliOSGetInfoResult KEYWORD3 -xTaskGetInfoResult KEYWORD3 -xTaskGetListResult KEYWORD3 +xTaskGetInfoResult KEYWORD3 +xTaskGetListResult KEYWORD3 +xTaskGetNotifResult KEYWORD3 ####################################### # Constants (LITERAL1) -####################################### \ No newline at end of file +####################################### diff --git a/library.properties b/library.properties index cabb60f..a3bd369 100644 --- a/library.properties +++ b/library.properties @@ -1,10 +1,10 @@ name=HeliOS -version=0.2.1 +version=0.2.2 author=Manny Peterson maintainer=Manny Peterson sentence=The free embedded operating system. -paragraph=HeliOS is an embedded operating system that is free for anyone to use. While called an operating system for simplicity, HeliOS is better described as a multitasking kernel for embedded systems. HeliOS is very small. In fact, it is small enough to run on most 8-bit microcontrollers including the popular AVR based Arduino Uno. Written entirely in C, HeliOS runs on a variety of microcontrollers and integrates easily into any project. HeliOS is also easy to learn with an Application Programming Interface (API) consisting of only 20 function calls. -category=Uncategorized +paragraph=HeliOS is an embedded operating system that is free for anyone to use. While called an operating system for simplicity, HeliOS is better described as a multitasking kernel for embedded systems. HeliOS is very small. In fact, it is small enough to run on most 8-bit microcontrollers including the popular AVR based Arduino Uno. Written entirely in C, HeliOS runs on a variety of microcontrollers and integrates easily into any project. HeliOS is also easy to learn with an Application Programming Interface (API) consisting of only 21 function calls. +category=Timing url=https://github.com/MannyPeterson/HeliOS architectures=* -includes=HeliOS_Arduino.h \ No newline at end of file +includes=HeliOS_Arduino.h diff --git a/src/HeliOS.h b/src/HeliOS.h index 120e4fc..38ab901 100644 --- a/src/HeliOS.h +++ b/src/HeliOS.h @@ -49,7 +49,7 @@ #define PRODUCTNAMESIZE 16 #define MAJORVERSION 0 #define MINORVERSION 2 -#define PATCHVERSION 1 +#define PATCHVERSION 2 #define PRODUCTNAME "HeliOS" #define MEMALLOCTABLESIZE 50 #define WAITINGTASKSIZE 8 @@ -73,6 +73,11 @@ struct xTaskGetInfoResult { unsigned long timerStartTime; }; +struct xTaskGetNotifResult { + int notifyBytes; + char notifyValue[NOTIFYVALUESIZE]; +}; + struct xHeliOSGetInfoResult { int tasks; char productName[PRODUCTNAMESIZE]; @@ -129,4 +134,4 @@ int strncmp_(const char*, const char*, size_t n); #ifdef __cplusplus } // extern "C" { -#endif \ No newline at end of file +#endif diff --git a/src/task.c b/src/task.c index 13fcf86..93b3247 100644 --- a/src/task.c +++ b/src/task.c @@ -115,12 +115,14 @@ int xTaskGetId(const char* name_) { void xTaskNotify(int id_, int notifyBytes_, char* notifyValue_) { struct Task* task = NULL; - if (TaskListSeek(id_)) { - task = TaskListGet(); - if (task) { - if (task->state != TaskStateErrored) { - task->notifyBytes = notifyBytes_; - memcpy_(task->notifyValue, notifyValue_, NOTIFYVALUESIZE); + if(notifyBytes_ > 0 && notifyBytes_ <= NOTIFYVALUESIZE && notifyValue_) { + if (TaskListSeek(id_)) { + task = TaskListGet(); + if (task) { + if (task->state != TaskStateErrored) { + task->notifyBytes = notifyBytes_; + memcpy_(task->notifyValue, notifyValue_, NOTIFYVALUESIZE); + } } } } @@ -139,6 +141,22 @@ void xTaskNotifyClear(int id_) { } } +struct xTaskGetNotifResult* xTaskGetNotif(int id_) { + struct Task* task = NULL; + struct xTaskGetNotifResult* taskGetNotifResult = NULL; + if (TaskListSeek(id_)) { + task = TaskListGet(); + if (task) { + taskGetNotifResult = (struct xTaskGetNotifResult*)xMemAlloc(sizeof(struct xTaskGetNotifResult)); + if (taskGetNotifResult) { + taskGetNotifResult->notifyBytes = task->notifyBytes; + memcpy_(taskGetNotifResult->notifyValue, task->notifyValue, NOTIFYVALUESIZE); + } + } + } + return taskGetNotifResult; +} + struct xTaskGetInfoResult* xTaskGetInfo(int id_) { struct Task* task = NULL; struct xTaskGetInfoResult* taskGetInfoResult = NULL; @@ -233,4 +251,4 @@ void xTaskResetTimer(int id_) { } } } -} \ No newline at end of file +} diff --git a/src/task.h b/src/task.h index 66e41fe..d1ee917 100644 --- a/src/task.h +++ b/src/task.h @@ -30,6 +30,7 @@ void xTaskWait(int); int xTaskGetId(const char*); void xTaskNotify(int, int, char*); void xTaskNotifyClear(int); +struct xTaskGetNotifResult* xTaskGetNotif(int); struct xTaskGetInfoResult* xTaskGetInfo(int); int TaskListSeek(int); struct xTaskGetListResult* xTaskGetList(int*); @@ -38,4 +39,4 @@ void xTaskResetTimer(int); #ifdef __cplusplus } // extern "C" { -#endif \ No newline at end of file +#endif