OS  v7.3.3
Documentation
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 kernel branch as follows:

git clone -b kernel https://github.com/kmilo17pet/QuarkTS.git

Including QuarkTS as a git sub-module

Add the OS kernel as a submodule to your project:

git submodule add -b kernel https://github.com/kmilo17pet/QuarkTS.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 qconfig.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:

curl https://raw.githubusercontent.com/kmilo17pet/QuarkTS/master/src/config/qconfig.h -o <destination path>/qconfig.h

First steps

Include the source files to your project. Also, make sure you add a copy of the file qconfig.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_Setup() 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"
#define TIMER_TICK ( 0.001f ) // 1ms
void main( void ) {
//device startup with hardware-specific code
HardwareSetup();
Configure_Periodic_Timer_Interrupt_1ms();
//end of device startup with hardware-specific code
qOS_Setup( NULL , TIMER_TICK , IdleTask_Callback );
// TODO: add Tasks to the scheduler scheme and run the OS
}
qBool_t qOS_Setup(const qGetTickFcn_t tFcn, const qTimingBase_t t, qTaskFcn_t idleCallback)
Task Scheduler Setup. This function is required and must be called once in the application main threa...
Definition: qkernel.c:126

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

  • The function qOS_Setup() must be called before any interaction with the OS.
  • 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_Add_Task() or any of the other available APIs 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 <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "BSP.h"
#include "QuarkTS.h"
#define TIMER_TICK ( 0.001f ) /* 1ms */
qTask_t task1, task2, task3; /*task nodes*/
/*==================================================================*/
void interrupt Timer0_ISR( void ) {
}
/*==================================================================*/
void Task1_Callback( qEvent_t e ) {
BSP_UART1_WriteString( "Task1" );
if ( e->FirstIteration ) {
qTask_Resume( &Task3 );
}
if ( e->LastIteration ) {
qTask_Suspend( &Task3 );
qTask_Set_Time( &Task2, 0.5 );
}
}
/*==================================================================*/
void Task2_Callback( qEvent_t e ) {
BSP_UART1_WriteString( "Task2" );
}
/*==================================================================*/
void Task3_Callback( qEvent_t e ) {
BSP_UART1_WriteString( "Task3" );
}
/*==================================================================*/
int main( void ) {
HardwareSetup(); /*hardware initialization function*/
/*function to fire an interrupt at 1ms - timer tick*/
Configure_Periodic_Timer0_Interrupt_1ms();
qOS_Setup( NULL, TIMER_TICK, NULL );
qOS_Add_Task( &Task1, Task1_Callback, 50, 2.0f, 10, qEnabled, NULL );
qOS_Add_Task( &Task2, Task2_Callback, 50, 3.0f, qPeriodic, qEnabled, NULL );
qOS_Add_Task( &Task2, Task3_Callback, 50, 5.0f, qPeriodic, qDisabled, NULL );
return 0;
}
void qClock_SysTick(void)
Feed the system tick.
Definition: qclock.c:94
qBool_t qOS_Add_Task(qTask_t *const Task, qTaskFcn_t callbackFcn, const qPriority_t p, const qTime_t t, const qIteration_t n, const qState_t init, void *arg)
Add a task to the scheduling scheme. The task is scheduled to run every t seconds,...
Definition: qkernel.c:411
qBool_t qOS_Run(void)
Executes the scheduling scheme. It must be called once after the task pool has been defined.
Definition: qkernel.c:676
#define qPeriodic
A directive indicating that the task will run every time its timeout has expired.
Definition: qkernel.h:80
#define qTask_Suspend(Task)
Put the task into a disabled state.
Definition: qtasks.h:533
qBool_t qTask_Set_Time(qTask_t *const Task, const qTime_t tValue)
Set/Change the Task execution interval.
Definition: qtasks.c:95
#define qTask_Resume(Task)
Put the task into an enabled state.
Definition: qtasks.h:549
#define qEnabled
A state value that enables a task.
Definition: qtypes.h:167
#define qDisabled
A state value that disables a task.
Definition: qtypes.h:172
The task argument with all the regarding information of the task execution.
Definition: qtasks.h:162
qBool_t FirstIteration
Indicates whether current pass is the first iteration of the task. This flag will be only set when ti...
Definition: qtasks.h:193
qBool_t LastIteration
Indicates whether current pass is the last iteration of the task. This flag will be only set when tim...
Definition: qtasks.h:201
A task node object.
Definition: qtasks.h:268

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 {
float yt; /*Measured variable (Controller Input)*/
float ut; /*Controlled variable (Controller Output)*/
float ie; /*Accumulated error*/
float pe; /*Previous error*/
float dt; /*Controller Time Step*/
float sp; /*Set-Point*/
float Kc, Ki, Kd; /*PID Gains*/
} PID_Params_t;
PID_Params_t TemperatureControl = {
0.0f, 0.0f, 0.0f, 0.0f, /*Initial IO state of yt and ut*/
1.5f, /*time step*/
28.5f, /*Set-Point*/
0.89f, 0.122f, 0.001f /*Kc, Ki, Kd*/
};
PID_Params_t HumidityControl= {
0.0f, 0.0f, 0.0f, 0.0f, /*Initial IO state of yt and ut*/
1.0f, /*time step*/
60.0f, /*Set-Point*/
2.5f, 0.2354f, 0.0015f /*Kc, Ki, Kd*/
};
PID_Params_t LightControl= {
0.0f, 0.0f, 0.0f, 0.0f, /*Initial IO state of yt and ut*/
0.5f, /*time step*/
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.

qOS_Add_Task( &IO_TASK , IO_TASK_Callback , qMedium_Priority , 0.1, qPeriodic, qEnabled , "iotask");
#define qMedium_Priority
A macro directive to indicate the medium priority level.
Definition: qkernel.h:74
void IO_TASK_Callback( qEvent_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.

qOS_Add_Task( &TEMPERATURE_CONTROL_TASK, PIDControl_Callback,
qHigh_Priority, TemperatureControl.dt ,
qPeriodic, qEnabled, &TemperatureControl );
qOS_Add_Task( &HUMIDITY_CONTROL_TASK, PIDControl_Callback,
qHigh_Priority, HumidityControl.dt,
qPeriodic, qEnabled, &HumidityControl );
qOS_Add_Task( &LIGHT_CONTROL_TASK, PIDControl_Callback,
qHigh_Priority, LightControl.dt,
qPeriodic, qEnabled, &LightControl );
#define qHigh_Priority
A macro directive to indicate the highest priority level.
Definition: qkernel.h:77
void PIDControl_Callback( qEvent_t e ) {
float Error, derivative;
/* Obtain the reference to the specific PID controller
* using the TaskData field from the qEvent 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 += Error*Controller->dt;
/*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: qtasks.h:167