OS  v1.7.5
Documentation
Loading...
Searching...
No Matches
Getting started

Getting the OS

Download the latest release from the official repository :

QuarkTS++ Releases

Unpack the release package and add the sources files to your project.

Cloning QuarkTS++

You only need to clone the main branch as follows:

git clone https://github.com/kmilo17pet/QuarkTS-cpp

Including QuarkTS++ as a git sub-module

Add the OS kernel as a submodule to your project:

git submodule add https://github.com/kmilo17pet/QuarkTS-cpp.git <destination path>

Then, run the initialize command to fetch the code for the first time:

git submodule update --init

To update the submodule to the latest just run:

git submodule update --remote

Get a copy of the OS configuration file

The file config.h provides specific Configuration macros to customize several aspects of the OS. In order to build your solution with QuarkTS++, you should provided your own copy of this configuration file. You can obtain a copy with the default configuration by issuing the following command:

First steps

Include the source files to your project. Also, make sure you add a copy of the file config.h and modify it according to your needs. Setup your compiler including the path of the OS directory. Include the header file QuarkTS.h and setup the instance of the kernel using the qOS::core::init() inside the main thread to initialize te kernel, specify the reference clock and the idle-task ( see Timing Approach). Additional configuration to the target compiler may be required to add the path to the directory of header files. The code below shows a common initialization procedure in the main source file.

File main.c

#include "QuarkTS.h"
using namespace qOS;
void main( void ) {
//device startup with hardware-specific code
HardwareSetup();
Configure_Periodic_Timer_Interrupt_1ms();
//end of device startup with hardware-specific code
os.init( nullptr , IdleTask_Callback );
// TODO: add Tasks to the scheduler scheme and run the OS
}
bool init(const getTickFcn_t tFcn=nullptr, taskFcn_t callbackIdle=nullptr) noexcept
Task Scheduler initialization. This core method is required and must be called once in the applicatio...
OS/Kernel interfaces.
Definition bytebuffer.hpp:7

In the above code, the following considerations should be taken:

  • The function os.init() must be called before any interaction with the OS. Here, the os its an instance of the qOS::core kernel interface.
  • The procedure HardwareSetup() should be a function with all the hardware instructions needed to initialize the target system.
  • The procedure Configure_Periodic_Timer_Interrupt_1ms() should be a function with all the hardware instructions needed to initialize and enable a timer with an overflow tick of one millisecond.

Tasks can be later added to the scheduling scheme by simply calling qOS::core::add() with any of its overloads for specific purpose tasks.

Two simple demonstrative examples

A simple scheduling

This example demonstrates a simple environment setup for multiple tasks. Initially, only task1 and task2 are enabled. task1 runs every 2 seconds 10 times and then stops. task2 runs every 3 seconds indefinitely. task1 enables task3 at its first run. task3 run every 5 seconds. task1 disables task3 on its last iteration and change task2 to run every 1/2 seconds. In the end, task2 is the only task running every 1/2 seconds.

#include "BSP.h"
#include "QuarkTS.h"
using namespace qOS;
task task1, task2, task3; /*task nodes*/
/*==================================================================*/
void interrupt Timer0_ISR( void ) {
clock::sysTick();
}
/*==================================================================*/
void Task1_Callback( event_t e ) {
BSP_UART1_WriteString( e.thisTask().getName() );
if ( e.firstIteration() ) {
task3.setState( taskState::ENABLED );
}
if ( e.lastIteration() ) {
e.thisTask().setState( taskState::SUSPENDED );
e.thisTask().setTime( 500 );
}
}
/*==================================================================*/
void Task2_Callback( event_t e ) {
BSP_UART1_WriteString( e.thisTask().getName() );
}
/*==================================================================*/
void Task3_Callback( event_t e ) {
BSP_UART1_WriteString( e.thisTask().getName() );
}
/*==================================================================*/
int main( void ) {
HardwareSetup(); /*hardware initialization function*/
/*function to fire an interrupt at 1ms - timer tick*/
Configure_Periodic_Timer0_Interrupt_1ms();
os.init();
os.add( Task1, Task1_Callback, core::MEDIUM_PRIORITY, 2_sec, 10, taskState::ENABLED );
os.add( Task2, Task2_Callback, core::MEDIUM_PRIORITY, 3_sec, task::PERIODIC, taskState::ENABLED );
os.add( Task2, Task3_Callback, core::MEDIUM_PRIORITY, 5_sec, task::PERIODIC, taskState::DISABLED );
os.run();
return 0;
}
bool run(void) noexcept
Executes the scheduling scheme. It must be called once after the task pool has been defined.
bool add(task &Task, taskFcn_t callback, const priority_t p, const duration_t t, const iteration_t n, const taskState s=taskState::ENABLED_STATE, void *arg=nullptr) noexcept
Add a task to the scheduling scheme. The task is scheduled to run every t time units,...
The task argument with all the regarding information of the task execution.
Definition task.hpp:105
bool firstIteration(void) const noexcept
Checks whether the current pass is the first iteration of the task. The value returned by this method...
Definition task.hpp:144
bool lastIteration(void) const noexcept
Checks whether the current pass is the last iteration of the task. The value returned by this method ...
Definition task.hpp:156
task & thisTask(void) noexcept
return the current task node being evaluated
Definition task.hpp:184
A task node object.
Definition task.hpp:348
bool setTime(const qOS::duration_t tValue) noexcept
Set/Change the Task execution interval.
const char * getName(void) const noexcept
Retrieves the task name.
bool setState(const taskState s) noexcept
Set the task operational state.
core & os
The predefined instance of the OS kernel interface.

Using the task argument

When adding tasks, they can accept a parameter of type pointer to void void* also called the storage pointer. This parameter could be used for multiple applications, including storage, task identification, duplication removal and others. The following example shows the usage of this argument to avoid callback duplication among tasks with the same behavior.

Consider a scenario where you have to build a digital controller for several physical variables, for example, a PID controller for temperature, humidity and light. The PID algorithm will be the same for all variables. The only difference will be the variable input, the controlled output action and the PID gains. In this case, each of the PID tasks will utilize the same callback methods. The only difference will be the I/O parameters (specific for each PID controller).

Let’s define a PID data structure with the I/O variables and gains.

typedef struct {
qOS::time_t dt; /*Controller Time Step*/
float yt; /*Measured variable (Controller Input)*/
float ut; /*Controlled variable (Controller Output)*/
float ie; /*Accumulated error*/
float pe; /*Previous error*/
float sp; /*Set-Point*/
float Kc, Ki, Kd; /*PID Gains*/
} PID_Params_t;
PID_Params_t TemperatureControl = {
1.5_sec, /*time step*/
0.0f, 0.0f, 0.0f, 0.0f, /*Initial IO state of yt and ut*/
28.5f, /*Set-Point*/
0.89f, 0.122f, 0.001f /*Kc, Ki, Kd*/
};
PID_Params_t HumidityControl= {
1_sec, /*time step*/
0.0f, 0.0f, 0.0f, 0.0f, /*Initial IO state of yt and ut*/
60.0f, /*Set-Point*/
2.5f, 0.2354f, 0.0015f /*Kc, Ki, Kd*/
};
PID_Params_t LightControl= {
0.5_sec, /*time step*/
0.0f, 0.0f, 0.0f, 0.0f, /*Initial IO state of yt and ut*/
45.0f, /*Set-Point*/
5.36f, 0.0891f, 0.0f /*Kc, Ki, Kd*/
};

A task will be added to the scheme to collect the sensor data and apply the respective control output.

os.AddTask( IO_TASK , IO_TASK_Callback , core::MEDIUM_PRIORITY , 100, task::PERIODIC, taskState::ENABLED_STATE , "iotask" );
void IO_TASK_Callback( event_t e ) {
TemperatureControl.yt = SampleTemperatureSensor();
HumidityControl.yt = SampleHumiditySensor();
LightControl.yt = SampleLightSensor();
WriteTemperatureActuatorValue( TemperatureControl.ut );
WriteHumidityActuatorValue( HumidityControl.ut );
WriteLightActuatorValue( LightControl.ut );
}

Then, three different tasks are created to apply the respective PID controller. Note that these tasks refer to the same callback method and we assign pointers to the respective variables.

os.add( TEMPERATURE_CONTROL_TASK, PIDControl_Callback,
core::HIGHEST_PRIORITY, TemperatureControl.dt ,
task::PERIODIC, taskState::ENABLED_STATE, &TemperatureControl );
os.add( HUMIDITY_CONTROL_TASK, PIDControl_Callback,
core::HIGHEST_PRIORITY, HumidityControl.dt,
task::PERIODIC, taskState::ENABLED_STATE, &HumidityControl );
os.add( LIGHT_CONTROL_TASK, PIDControl_Callback,
core::HIGHEST_PRIORITY, LightControl.dt,
task::PERIODIC, taskState::ENABLED_STATE, &LightControl );
void PIDControl_Callback( event_t e ) {
float Error, derivative;
/* Obtain the reference to the specific PID controller
* using the TaskData field from the event_t structure
*/
PID_Params_t *Controller = (PID_Params_t *)e.TaskData;
/*Compute the error*/
Error = Controller->sp - Controller->yt;
/*Compute the accumulated error using backward integral approximation*/
Controller->ie += static_cast<float>( Error*Controller->dt )/1000.0f;
/*update and compute the derivative term*/
derivative = ( Error - Controller->pe )/Controller->dt;
/*update the previous error*/
Controller->pe = Error;
/*compute the pid control law*/
Controller->ut = Controller->Kc*Error + Controller->Ki*Controller->ie + Controller->Kd*derivative;
}
void * TaskData
Task arguments defined at the time of its creation. (Storage-Pointer)
Definition task.hpp:121

Using QuarkTS++ in Arduino

QuarkTS++ is widely compatible with most Arduino cores including 8bit architectures. To install the QuarkTS++ OS into your Arduino IDE you can use the Library Manager (available from IDE version 1.6.2). Open the IDE and click to the "Sketch" menu and then Include Library > Manage Libraries.

Then the Library Manager will open and you will find a list of libraries that are already installed or ready for installation. Type in the search box "QuarkTS" and when listed, click the install button. Once it has finished, an Installed tag should appear next to the library. You can close the library manager.

You can now find the new library available in the Sketch > Include Library menu.

When invoking the os.init() for the first time, make sure to pass the millis function as argument

#include "QuarkTS.h"
using namespace qOS;
void idleTask_Callback( event_t e ) {
// TODO: add the idle activities
}
void setup() {
os.init( millis , IdleTask_Callback );
// TODO: add Tasks to the scheduler scheme and run the OS
}
void loop() {
os.run();
}