|
|
1 bulan lalu | |
|---|---|---|
| .vscode | 1 bulan lalu | |
| docs | 1 bulan lalu | |
| tinykernel | 1 bulan lalu | |
| .gitignore | 1 bulan lalu | |
| CMakeLists.txt | 1 bulan lalu | |
| LICENSE.md | 1 bulan lalu | |
| README.md | 1 bulan lalu | |
| main.cpp | 1 bulan lalu | |
| pico_sdk_import.cmake | 1 bulan lalu |
(English version follows the Japanese section)
Raspberry Pi Pico 2 (RP2350 / ARM Cortex-M33) 向けの、シンプルで実用的なリアルタイム・プリエンプティブマルチタスクカーネルです。
State::Blocked) し、CPU を他のタスクに明け渡します。tk::create() を使ってタスクを登録します(最大 30 個のユーザータスクを登録可能)。
#include "tinykernel/kernel.hpp"
static void your_task(void *arg) {
for (;;) {
// なにか仕事をする
tk::sleep_ms(200); // スリープでCPUを譲渡
}
}
int main(void) {
stdio_init_all();
// your_task を登録(タスク関数ポインタ、引数、優先度)
tk::create(your_task, nullptr, tk::PRIO_NORMAL);
// スケジューラ起動(Core 1 も自動ブート、これ以降は戻らない)
tk::start();
for (;;) {}
}
メッセージを受け取る側は tk::Actor<MsgType, QueueSize> を継承します。
#include "tinykernel/actor.hpp"
// 1. Actor が受け取るメッセージの構造体を定義
struct YourActionMessage {
uint32_t msg;
char str[16];
};
// 2. メッセージを待ち受ける Actor を作成
class YourActor : public tk::Actor<YourActionMessage, 8> {
protected:
void on_start() override {
// タスク起動時に1回だけ呼ばれる(GPIOやデバイスの初期化など)
}
void on_message(const YourActionMessage& msg) override {
// メッセージを受信すると自動起床して実行される
// msg はキューに値コピーされているため安全
}
};
YourActor g_actor;
int main(void) {
stdio_init_all();
// タスクとして登録・起動
g_actor.start(tk::PRIO_NORMAL);
tk::start();
}
メッセージを送る側は tk::publish() を呼びます。
static void your_sender(void *arg) {
for (;;) {
YourActionMessage msg{1, "Hello!"};
// YourActionMessage を受け付けるすべての Actor に配信(1対多の同報可能)
tk::publish(msg);
tk::sleep_ms(1000);
} // msg のスコープが外れてスタックが消えても、受信側は壊れない!
}
int main(void) {
stdio_init_all();
g_actor.start(tk::PRIO_NORMAL);
tk::create(your_sender, nullptr, tk::PRIO_NORMAL);
tk::start();
}
Point: 送信側は Actor のインスタンスや名前を知る必要がありません。メッセージの型情報だけで
EventHubが適切な Actor へ直接届けてくれます。
sync.hpp)スレッド間の排他制御・同期プリミティブとして以下が用意されています。
#include "tinykernel/sync.hpp"
tk::Mutex g_mutex;
tk::BinarySemaphore g_sem(0, 1);
// RAII による安全なロック(std::lock_guard 互換)
void safe_function() {
tk::LockGuard<tk::Mutex> lock(g_mutex);
// クリティカルセクション...
} // スコープを抜けると自動アンロック
待機タスクの中で一番優先度(Priority)が高いタスクから順に起床します。
tk::PreemptGuard)I2C や SPI など、途中でタスク切り替えを起こしたくないペリフェラルトランザクションを行う場合、割り込み(時計やタイマー)を止めずに自コアのタスクスイッチだけを安全に一時抑止できます。
{
tk::PreemptGuard guard;
// 割り込み(SysTick やタイマー)は通常通り動き続け、他コアも影響を受けない。
// しかし、自コアのタスクスイッチだけは確実にブロックされる!
i2c_write_blocking(i2c0, addr, data, len, false);
} // スコープを抜けると自動でタスクスイッチが再開(保留中のPendSVが即発火)
独自の Pico プロジェクトの CMakeLists.txt で tinykernel ディレクトリを追加し、リンクするだけで即座に利用できます。
add_subdirectory(tinykernel)
target_link_libraries(your_project_name
pico_stdlib
tinykernel_lib
)
A simple and practical real-time preemptive multitasking kernel for the Raspberry Pi Pico 2 (RP2350 / ARM Cortex-M33).
Register tasks using tk::create() (up to 30 user tasks can be registered).
#include "tinykernel/kernel.hpp"
static void your_task(void *arg) {
for (;;) {
// Do some work here
tk::sleep_ms(200); // Yield CPU by sleeping
}
}
int main(void) {
stdio_init_all();
// Register your_task (Task function pointer, argument, priority)
tk::create(your_task, nullptr, tk::PRIO_NORMAL);
// Start the scheduler (Core 1 is booted automatically; execution does not return from here)
tk::start();
for (;;) {}
}
To receive messages, inherit from tk::Actor<MsgType, QueueSize>.
#include "tinykernel/actor.hpp"
// 1. Define the message structure to be received by the Actor
struct YourActionMessage {
uint32_t msg;
char str[16];
};
// 2. Create an Actor that waits for messages
class YourActor : public tk::Actor<YourActionMessage, 8> {
protected:
void on_start() override {
// Called once when the task starts (e.g., GPIO or device initialization)
}
void on_message(const YourActionMessage& msg) override {
// Automatically woken up and executed upon receiving a message
// Safe because 'msg' is copied by value into the queue
}
};
YourActor g_actor;
int main(void) {
stdio_init_all();
// Register and start as a task
g_actor.start(tk::PRIO_NORMAL);
tk::start();
}
To send messages, call tk::publish().
static void your_sender(void *arg) {
for (;;) {
YourActionMessage msg{1, "Hello!"};
// Broadcast to all Actors that accept YourActionMessage (one-to-many)
tk::publish(msg);
tk::sleep_ms(1000);
} // Even after 'msg' goes out of scope and its stack is reclaimed, the receiver remains safe!
}
int main(void) {
stdio_init_all();
g_actor.start(tk::PRIO_NORMAL);
tk::create(your_sender, nullptr, tk::PRIO_NORMAL);
tk::start();
}
Point: The sender does not need to know the instance or name of the Actor. The EventHub delivers messages directly to the appropriate Actors based solely on the message type.
sync.hpp)The following primitives are provided for mutual exclusion and synchronization between threads:
#include "tinykernel/sync.hpp"
tk::Mutex g_mutex;
tk::BinarySemaphore g_sem(0, 1);
// Safe locking using RAII (compatible with std::lock_guard)
void safe_function() {
tk::LockGuard<tk::Mutex> lock(g_mutex);
// Critical section...
} // Automatically unlocked when the scope ends
Waiting tasks are woken up in order of highest priority.
tk::PreemptGuard)When performing peripheral transactions (such as I2C or SPI) where you want to prevent task switching, tk::PreemptGuard allows you to temporarily suppress task switches on the current core without disabling interrupts (like system clocks or timers).
{
tk::PreemptGuard guard;
// Interrupts (SysTick, Timers) continue to run normally, and other cores are unaffected.
// However, task switching on this specific core is guaranteed to be blocked!
i2c_write_blocking(i2c0, addr, data, len, false);
} // Task switching resumes automatically when the scope ends (pending PendSV is triggered immediately)
To integrate tinykernel into your Pico project, simply add the directory to your CMakeLists.txt and link it:
add_subdirectory(tinykernel)
target_link_libraries(your_project_name
pico_stdlib
tinykernel_lib
)