Minimalistic C++ thread scheduling kernel for Embedded systems.
STK tends to me as minimal as possible to be able to provide a multi-threading capability for your Embedded system without any attempt to abstract operations with peripherals. It does not pretent to be a fully-fledged Real-Time OS (RTOS) but instead it adds multi-threading into a bare-metal project with a very little effort.
STK is developed in C++ and follows an Object-Oriented Design principles while at the same time does not pollute namespace with exceeding declarations, nor using fancy new C++ features. It tries to be very friendly to C developers ;)
STK supports soft real-time (default) and hard-real time (HRT) modes of operation. It supports infinite looping (KERNEL_STATIC
), finite (KERNEL_DYNAMIC
) and periodic (HRT mode - KERNEL_HRT
) tasks.
STK intercepts main process program flow if it is in a KERNEL_STATIC
mode but it can also return into the main process when all tasks exited in case of KERNEL_DYNAMIC
mode.
HRT mode allows to run periodic tasks which can be finite or infinite depending on whether KERNEL_STATIC
or KERNEL_DYNAMIC
mode is used in addition to the KERNEL_HRT
. HRT tasks are checked for a deadline miss by STK automatically therefore it guarantees a fully deterministic behavior of the application.
STK’s run-time performance is comparable to other well known C-based thread schedulers but its code base is much smaller (slimmer) and therefore easier to test, maintain and advance.
KERNEL_HRT
: Tasks have hard-limited time slots, violation of the slot will fail whole application (want to launch satellite? most likely you need this mode)KERNEL_STATIC
: Tasks (threads) are allocated on startKERNEL_DYNAMIC
: Tasks (threads) can start and exit during schedulingSleep()
STK supports single-core MCUs yet, multi-core support is in to-do.
STK facilitates a productive development workflow through its special Development Mode on x86 Windows. In this mode STK’s thread scheduling is emulated on the Windows operating system. You can compile and run your embedded application code on a standard x86 platform, effectively simulating the concurrent execution of threads. This enables early-stage development, debugging, and unit testing in a convenient Windows environment (requiring the simulation or mocking of target-specific peripherals) before the final deployment and hardware-level testing on the target embedded system.
Here is an example to toggle Red, Green, Blue LEDs of the NXP FRM-K66F or STM STM32F4DISCOVERY development boards hosting ARM Cortex-M4F CPU where each thread is handling its own LED, e.g. there are 3 threads in total which are switching LEDs with 1 second periodicity.
#include <stk_config.h>
#include <stk.h>
#include "example.h"
static volatile uint8_t g_TaskSwitch = 0;
template <stk::EAccessMode _AccessMode>
class MyTask : public stk::Task<256, _AccessMode>
{
uint8_t m_taskId;
public:
MyTask(uint8_t taskId) : m_taskId(taskId) {}
stk::RunFuncType GetFunc() { return &Run; }
void *GetFuncUserData() { return this; }
private:
static void Run(void *user_data)
{
((MyTask *)user_data)->RunInner();
}
void RunInner()
{
uint8_t task_id = m_taskId;
float count = 0;
uint64_t count_skip = 0;
while (true)
{
if (g_TaskSwitch != task_id)
{
++count_skip;
continue;
}
++count;
switch (task_id)
{
case 0:
LED_SET_STATE(LED_RED, true);
LED_SET_STATE(LED_GREEN, false);
LED_SET_STATE(LED_BLUE, false);
break;
case 1:
LED_SET_STATE(LED_RED, false);
LED_SET_STATE(LED_GREEN, true);
LED_SET_STATE(LED_BLUE, false);
break;
case 2:
LED_SET_STATE(LED_RED, false);
LED_SET_STATE(LED_GREEN, false);
LED_SET_STATE(LED_BLUE, true);
break;
}
g_KernelService->Sleep(delay_ms);
g_TaskSwitch = (task_id + 1) % 3;
}
}
};
static void InitLeds()
{
LED_INIT(LED_RED, false);
LED_INIT(LED_GREEN, false);
LED_INIT(LED_BLUE, false);
}
void RunExample()
{
using namespace stk;
InitLeds();
static Kernel<KERNEL_STATIC, 3, SwitchStrategyRoundRobin, PlatformDefault> kernel;
static MyTask<ACCESS_PRIVILEGED> task1(0), task2(1), task3(2);
kernel.Initialize();
kernel.AddTask(&task1);
kernel.AddTask(&task2);
kernel.AddTask(&task3);
kernel.Start(PERIODICITY_DEFAULT);
assert(false);
while (true);
}
It is fairly easy to build and run examples without even having embedded hardware on your hands. You will need these tools to run examples on your PC:
ARM platform:
In case of NXP MCU you will only need MCUXpresso IDE which is comes with bundled GCC compler.
RISC-V platform:
If you are working with only ARM platform then you only need ARM-related tools.
Generic eclipse examples are located in the build/example/project/eclipse
folder and sorted by platform:
stm
- ARM platform, examples based on STM32 microcontrollers and can be executed on QEMU virtual machine or directly on hardware if you have corresponding board.risc-v
- RISC-V platform, examples based on QEMU virtual machine.x86
- x86 platform, examples are executed on x86 CPU.Additionally, examples for NXP MCUXpresso IDE are provided in build/example/project/nxp-mcuxpresso
folder. These examples are compatible with NXP Kinetis® K66, Kinetis® K26 and NXP i.MX RT1050 MCUs and can be executed directly on corresponding evaluation boards.
Platform independent (generic) code is 100% covered by tests. Platform dependent code is covered by tests executed on QEMU virtual machines of corresponding CPU architectures.
You are welcome to port STK to a new platform and suggest a patch. The platform dependent files are located in: stk/src/arch
and stk/include/arch
folders of STK GitHub repository.
STK is licensed under MIT license therefore you can use it freely in your personal, commercial, closed- or open- source projects.
Additional services for your project:
Dedicated license: warranty of title and perpetual right-to-use for STK’s source-code.
Technical support: integration of STK into your project, development assistance in relation to STK, and etc.
For all these questions please contact us.