今までに作ったライブラリの中でも個人的に便利で気に入ってるものを集めてAVR(Arduino)用に再構成してみた。
【概要】
・MCore (ボード依存機能, digispark/leonardo/unoのLED設定など)
各ライブラリの初期化とボードのビルトインLEDのセットアップ、バックグランド処理やタスクスイッチング等を行う。
・Led (LED管理)
今までLED制御(Lチカ)をライブラリ化する発想など全くなかったが時間指定でLEDを制御できるのは意外に便利だ。LEDってどのピンに接続されていて光らせるためにはどうればいいんだったっけ?などと毎回回路図を探しまくってしまうLEDの管理ができるだけでも有り難い。
・Alarm (タイマー割り込み/ポーリング)
一個のハードウェア・タイマーのみでマイクロ秒精度のタイマー割込を制限なく提供する。それなりのオーバーヘッドはあるもののメリットがデメリットを上回ると言える便利さがある。また、割込を使わないポーリング機能はシンプルにコーディングできてクセになりそうだ。
※アラーム機能はTIMER0を利用している。TIMER0のPWM機能を使うと誤動作することに注意。
・Task (タスク管理)
ATtiny85でも動作するシンプルで省メモリな協調型マルチタスク機能は学習用にもいいかもしれない。但し、ATtiny85ではメモリの余裕がないので規定のスタックサイズでは1タスクのみしか動作しない。2タスク動作させるにはスタックサイズの調整が必要である。必要なスタックサイズは単純なプログラムで確認したところ64バイト未満だったので余裕をみてその2倍の128バイトをスタックサイズの規定値としているが単純なプログラムであれば80~90バイト程度あれば問題はないと思う。より大きいスタックサイズを必要とするプログラムでは適切な調整が必要となる。
・Fifo (バッファ管理)
世の中のRTOSでは標準機能となっているメールボックスやメッセージバッファを実装したライブラリ。テンプレートクラスにより様々なデータ型が扱えるようになっている。データ取得できるまでタスクはスリープして待つので効率の良いシステムの構築には必須かも。使い方次第でメールボックス或いはメッセージバッファのどちらとしても利用できるはずだ。
・Hardware (ハードウェア依存処理)
ハードウェア依存のコードを集めてあるのでクラス実装を追加するだけで他のCPUやOSにも対応可能だ。
【basic.ino】
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include "mcore.h" void setup() { /* Setup MCore */ MCore::begin(); } void loop() { /* Handle MCore */ MCore::handle(); } |
【blink.ino: auto blink】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#include "mcore.h" void setup() { /* Setup MCore */ MCore::begin(); /* Blink LED */ Led::toggle(LED_R, 1000); // blink 1000 ms } void loop() { /* Handle MCore */ MCore::handle(); } |
【alarm1.ino: alarm interrupt】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#include "mcore.h" Alarm alarm; bool AlarmCallback([[maybe_unused]] Alarm& alarm) { Led::toggle(LED_R); return true; // true=repeat, false=stop } void setup() { /* Setup MCore */ MCore::begin(); /* Setup Alarm Interrupt */ AlarmTimer.start(alarm.interval(1000000).handler(AlarmCallback)); } void loop() { /* Handle MCore */ MCore::handle(); } |
【alarm2.ino: alarm polling】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#include "mcore.h" Alarm alarm; void setup() { /* Setup MCore */ MCore::begin(); /* Setup Alarm Interval */ alarm.interval(1000000); } void loop() { /* Blink LED */ if (alarm.expire()) Led::toggle(LED_R); /* Handle MCore */ MCore::handle(); } |
【task.ino: multi task】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
#include "mcore.h" void task0([[maybe_unused]] void *arg) { while (1) { Task::sleep(1000); Led::toggle(LED_R); } /* The task ends when return from the function */ } void task1([[maybe_unused]] void *arg) { Task::sleep(500); while (1) { Task::sleep(1000); Led::toggle(LED_R); } /* The task ends when return from the function */ } void setup() { /* Setup MCore */ MCore::begin(); /* Setup Task */ Task::start(task0); Task::start(task1); } void loop() { /* Handle MCore */ MCore::handle(); } |
【fifo.ino】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
#include "mcore.h" Fifo<uint8_t, 16> fifo; void task0([[maybe_unused]] void *arg) { uint8_t data; while (1) { Task::sleep(1000); Led::toggle(LED_R); fifo.put(&data); } /* The task ends when return from the function */ } void task1([[maybe_unused]] void *arg) { uint8_t data; while (1) { fifo.get(&data); Task::sleep(500); Led::toggle(LED_R); } /* The task ends when return from the function */ } void setup() { /* Setup MCore */ MCore::begin(); /* Setup Task */ Task::start(task0); Task::start(task1); } void loop() { /* Handle MCore */ MCore::handle(); } |
【download】
mcore-avr.zip (Arduino Library)
【update】
2022-06-29
昨日のSemクラスの修正の修正。
2022-06-28
Semクラスのタイミングバグ修正とTask::start()に負数のスタックサイズ指定をしたとき登録タスクではなく直前の登録タスクのスタックサイズ指定になっていて混乱しやすいので修正。
2022-06-24
Fifoクラスにオーバーライト機能を追加。
2022-06-23
Semクラスのバグ修正とTaskクラスのチューニング。それと2レベルではあるがタスク優先度を追加し起床タスクの優先度を一時的にあげる仕組みを追加してみた。リアルタイムにはスイッチングしないが休止状態から起床されたタスクが最優先で実行されるようになる。可能ならリアルタイム性を追加したいところではあるがリアルタイムやタイムスライスに対応すると標準ライブラリを含む全てのライブラリもマルチスレッド対応にする必要があったりしてとても大変なことになるで対応はしない。
2022-06-21
Task::start()の最適化を行った。
2022-06-20
Taskクラスの微妙な感じがしていたタスク切り替え処理を改良してみた。今のところ対応は考えてないけどタイムスライスも行けそうな感じになってきた気がする。
2022-06-18
Fifoクラスのバグ修正。
2022-06-17
Fifoクラスの仕様変更を行った。
2022-06-16
Fifoクラスを追加。
2022-06-15
Sync/Mutex/Sem/Eventクラスのコード最適化の続きを行った。
2022-06-14
Sync/Mutex/Sem/Eventクラスのコード最適化を行った。
2022-06-13
Eventクラスがバグッてたので修正。
2022-06-12
Task::start()で指定するタスクのスタックサイズが割り当てモード毎に違っていて混乱するのでスタックサイズにはTask::TCB_SIZEを加算したサイズが指定されているものと仮定した仕様変更を行った。
例えば、スタックサイズとして128バイトを指定する場合は
128 + Task::TCB_SIZE
を指定しなければならない。(AVRのTask::TCB_SIZEは31バイト)
2022-06-11
いつの間にかタスクが動作しなくなっていたのとSem/Mutex/Eventのウェイト処理のタイミングバグを修正。あとメッセージバッファのような機能を作りやすくするためSemクラスのwait()/post()にカウント値引数を追加。
2022-06-10
Semクラスコンストラクターにカウンター初期値を設定するための引数を追加
2022-06-09
タスク終了時のタイミングバグを修正。
2022-06-08
Sync::sleep()のバグ修正と割り込み禁止期間のチューニングを行った。
【MCore】
1 |
static void begin(void) |
ライブラリの初期化とボードのビルトインLEDのセットアップを行う。
1 |
static void handle(void) |
バックグラウンド処理とタスクスイッチングを行う。
【Led】
1 |
static void setup(uint8_t led, uint8_t pin, uint8_t off); |
登録番号、LED出力ピン、LEDオフ出力値を指定してLED登録する。
1 |
static void off(uint8_t led = 0, uint16_t ms = 0); |
指定時間後にLEDを消灯する。
1 |
static void on(uint8_t led_t = 0, uint16_t ms = 0); |
指定時間後にLEDを点灯する。
1 |
static void toggle(uint8_t led = 0, uint16_t ms = 0); |
指定時間後にLEDの点灯状態を反転する。
1 |
static bool status(uint8_t led = 0); |
LEDの点灯状態を返す。
1 |
static bool expire(uint8_t led = 0); |
LEDタイマー指定後に指定時間が経過したかどうかを返す。
【Alarm】
1 |
typedef bool (*handler_t)(Alarm& alarm); |
アラーム割り込みハンドラー型の定義。
1 |
Alarm& interval(int32_t interval, bool fromnow = true); |
アラーム・インターバル(マイクロ秒)を設定する。アラームタイマー割り込み処理でインターバルを変更するときはfromnowをfalse指定すること。
1 |
int32_t interval(void); |
アラーム・インターバル(マイクロ秒)を取得する。
1 |
Alarm& handler(handler_t handler); |
アラーム割り込みハンドラーを設定する。
1 |
handler_t handler(void); |
アラーム割り込みハンドラーを取得する。
1 |
Alarm& param(uint8_t num, void *val); |
アラーム引数を設定する。
1 |
void *param(uint8_t num); |
アラーム引数を取得する。
1 |
bool expire(void); |
アラーム時刻が過ぎたかどうかを返す。ポーリング専用。
【AlarmClass】
1 |
bool start(Alarm& alarm); |
アラーム・タイマーを開始する。
1 |
void cancel(Alarm& alarm); |
アラーム・タイマーを取消する。
1 |
void cancel(uint8_t num, void *arg); |
アラーム・タイマーを取消する。
【Task】
1 |
typedef void (*func_t)(void *arg); |
タスク関数型。
1 |
static bool start(func_t func, void *arg, int size, void *stack = 0); |
タスクを登録する。sizeに負数指定時は現在のスタックを分割使用し正数指定時はスタック領域のサイズと見なす。stackに静的配列変数アドレスを指定時はその変数領域をスタック領域とし、stackにゼロ指定時はmalloc()により動的にスタック領域を割り当てる。malloc割り当て時はタスク終了時にfree()されるため動的なタスク生成/終了が可能。
1 |
static void stop(void); |
タスクを終了する。メイン・タスクは終了できない。
1 |
static bool yield(void); |
ラウンドロビン・アルゴリズムにより次の実行待ちタスクへ切替える。MCore::handle()からも呼び出されるが協調型マルチタスクはこの関数の実行でのみタスク切替が起こるためタスク処理内では積極的にこの関数を呼び出す必要がある。逆に言えばこの関数を呼び出さない限りCPUを占有することができるということになりそれがメリットにもなりえたりする。
1 |
static size_t current(void); |
カレントタスクの識別子を返す。
1 |
static void sleep(int32_t ms, uint8_t busyLoop = 0); |
指定時間(ms)休止する。タスクはアラームタイマー割り込みにより起床される。正確な時間を要求するためにbusyLoopを指定した場合は指定時間のbusyLoop前に起床した後ビジーループにより正確な時間判定を行う。最大約35分まで指定可能。
1 |
static void sleepMicroseconds(int32_t us, uint8_t busyLoop = 0); |
指定時間(us)休止する。タスクはアラームタイマー割り込みにより起床される。正確な時間を要求するためにbusyLoopを指定した場合は指定時間のbusyLoop前に起床した後ビジーループにより正確な時間判定を行う。最大約35分まで指定可能。
1 |
static void delay(uint32_t ms, uint8_t busyLoop = 0); |
指定時間(ms)待つ。指定時間経過するまでタスクはyield()を繰り返す。正確な時間を要求するためにbusyLoopを指定した場合は指定時間のbusyLoop前からビジーループにより正確な時間判定を行う。最大約71分まで指定可能。
1 |
static void delayMicroseconds(uint32_t us, uint8_t busyLoop = 0); |
指定時間(us)待つ。指定時間経過するまでタスクはyield()を繰り返す。正確な時間を要求するためにbusyLoopを指定した場合は指定時間のbusyLoop前からビジーループにより正確な時間判定を行う。最大約71分まで指定可能。
【Sync】
1 |
Sync(void); |
コンストラクター。
1 |
bool sleep(int32_t timeout = -1); |
指定時間経過するまで休止する。timeoutに負数指定時はwakeupされるまで待ち続け、正数指定時はwakeupにより起床された場合はtrue、タイムアウト時はfalseを返す。最大約35分まで指定可能。
1 |
size_t wakeup(size_t task = 0); |
休止タスクを起床する。taskにゼロ指定すると休止タスク順に起床されゼロ以外を指定するとそのタスク識別子をもつ休止タスクを検索して見つかれば起床する。起床したタスク識別子を返しタスクが起床できなければゼロを返す。割り込み処理からも呼び出すことが可能。
【Mutex】
1 |
Mutex(void); |
コンストラクター。
1 |
bool lock(int32_t timeout = -1); |
ロックする。多重ロックが可能。timeoutに負数指定時はロックを獲得するまで待ち続け、正数指定時はロックを獲得できた場合はtrue、タイムアウト時はfalseを返す。最大約35分まで指定可能。
1 |
void unlock(void); |
アンロックする。多重ロックが全て解除されたときにアンロックされる。割り込みは不特定のタスク実行中に割り込んでくるため割り込み処理から呼び出してはいけない。
【Sem】
1 |
Sem(size_t limit = (size_t)-1, size_t count = 0); |
コンストラクター。limitにはカウント最大値を、countにはカウント初期値を指定する。
1 |
bool wait(int32_t timeout = -1); |
カウントを取得する。timeoutに負数指定時は取得できるまで待ち続け、 正数指定時は取得できた場合はtrue、取得できなかった場合はfalseを返す。最大約35分まで指定可能。
1 |
void post(void); |
カウントを返却する。割り込み処理からも呼び出すことが可能。
【Event】
1 |
Event(void); |
コンストラクター。 実装はバイナリセマフォであり単一イベントにのみ対応している。
1 |
bool wait(int32_t timeout = -1); |
イベントを取得する。timeoutに負数指定時はイベント取得できるまで待ち続け、 正数指定時はイベント取得できた場合はtrue、イベント取得できなかった場合はfalseを返す。
1 |
void signal(void); |
イベントを通知する。割り込み処理から呼び出すことが可能。
【Fifo】
1 |
template<typename T, size_t SIZE> |
テンプレート引数。
1 |
Fifo(void); |
コンストラクター。
1 |
size_t get(T *item, size_t count = 1, int32_t timeout = -1); |
データを取得する。timeoutに負数指定時は取得できるまで待ち続け、 正数指定時は取得できたカウントを返す。
1 |
size_t put(T *item, size_t count = 1, int32_t timeout = -1); |
データを追加する。timeoutに負数指定時は追加できるまで待ち続け、 正数指定時は追加できたカウントを返す。割り込み処理から呼び出すことも可能であるがtimeout引数にゼロ指定する必要がある。割り込み処理からtimeout引数にゼロ以外を指定し呼び出した場合はバッファフル時にタスクスイッチ(yield)してしまい誤動作することに注意。
【library】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
/* mcore.h - Micro Core Libraries for Embedded System Copyright (c) 2022 Sasapea's Lab. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #pragma once #include "task.h" #include "fifo.h" #include "led.h" #include "alarm.h" #include "hardware.h" /* Board Library Include */ #ifndef BOARD_NAME #if defined(ARDUINO_AVR_UNO) || \ defined(ARDUINO_AVR_DUEMILANOVE) || \ defined(ARDUINO_AVR_LILYPAD) || \ defined(ARDUINO_AVR_NG) || \ defined(ARDUINO_AVR_UNO_WIFI_DEV_ED) #define BOARD_NAME uno #elif defined(ARDUINO_AVR_LEONARDO) || \ defined(ARDUINO_AVR_LEONARDO_ETH) || \ defined(ARDUINO_AVR_ESPLORA) || \ defined(ARDUINO_AVR_LILYPAD_USB) || \ defined(ARDUINO_AVR_A_STAR_32U4) #define BOARD_NAME leonardo #elif defined(ARDUINO_AVR_DIGISPARK) #define BOARD_NAME digispark #else #define BOARD_NAME none #endif #endif #define STRINGIFY(str) #str #define EXPANDSTR(x) STRINGIFY(x) #define EXPAND1(x) x #define EXPAND2(x, y) EXPAND1(x)y #define EXPAND3(x, y, z) EXPAND2(x, y)z #undef INCLUDE_NAME #define INCLUDE_NAME EXPANDSTR(EXPAND3(mcore_,BOARD_NAME,.h)) #include INCLUDE_NAME |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
/* mcore_none.h - Board Library Copyright (c) 2022 Sasapea's Lab. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #pragma once #include "task.h" #include "led.h" #include "alarm.h" #define LED_R 0 #define LED_G 1 class MCore { public: static void begin(void) { AlarmTimer.begin(); Task::begin(); /* Setup LEDs */ Led::begin(); } static void handle(void) { Led::handle(); Task::yield(); } }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
/* mcore_digispark.h - Board Library for Digispark ATtiny85 Copyright (c) 2022 Sasapea's Lab. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #pragma once #include "task.h" #include "led.h" #include "alarm.h" #define LED_R 0 #define LED_G 1 class MCore { public: static void begin(void) { AlarmTimer.begin(); Task::begin(); /* Setup LEDs */ Led::begin(); Led::setup(LED_R, 1, 0); } static void handle(void) { Led::handle(); Task::yield(); } }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
/* mcore_leonardo.h - Board Library for Arduino Leonardo Copyright (c) 2022 Sasapea's Lab. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #pragma once #include "task.h" #include "led.h" #include "alarm.h" #define LED_R 0 #define LED_G 1 class MCore { public: static void begin(void) { AlarmTimer.begin(); Task::begin(); /* Setup LEDs */ Led::begin(); Led::setup(LED_R, 17, HIGH); Led::setup(LED_G, 30, HIGH); } static void handle(void) { Led::handle(); Task::yield(); } }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
/* mcore_uno.h - Board Library for Arduino UNO Copyright (c) 2022 Sasapea's Lab. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #pragma once #include "task.h" #include "led.h" #include "alarm.h" #define LED_R 0 #define LED_G 1 class MCore { public: static void begin(void) { AlarmTimer.begin(); Task::begin(); /* Setup LEDs */ Led::begin(); Led::setup(LED_R, 4, HIGH); Led::setup(LED_G, 5, HIGH); } static void handle(void) { Led::handle(); Task::yield(); } }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
/* led.h - LED Library for Arduino Copyright (c) 2022 Sasapea's Lab. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #pragma once #include <stdint.h> #include <stdbool.h> #include "hardware.h" #include "alarm.h" #ifndef LED_BITS #define LED_BITS 1 #endif #define LED_COUNT (1 << LED_BITS) #define LED_MASK (LED_COUNT - 1) class Led : protected Hardware { public: static void begin(void); static void setup(uint8_t led, uint8_t pin, uint8_t off); static void off(uint8_t led = 0, uint16_t ms = 0); static void on(uint8_t led_t = 0, uint16_t ms = 0); static void toggle(uint8_t led = 0, uint16_t ms = 0); static bool status(uint8_t led = 0); static bool expire(uint8_t led = 0); static void handle(void); private: typedef enum : uint8_t { MODE_DISABLE, MODE_OFF, MODE_ON, MODE_TOGGLE, } MODE; typedef struct { uint8_t pin; uint8_t off; MODE mode; bool expire; Alarm alarm; } CTRL; static CTRL _leds[LED_COUNT]; static void control(CTRL& ctrl, MODE mode, uint16_t ms); }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
/* led.cpp - LED Library for Arduino Copyright (c) 2022 Sasapea's Lab. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "led.h" Led::CTRL Led::_leds[]; #define LED_NONE (uint8_t)-1 void Led::begin(void) { CTRL *ctrl = _leds; for (uint8_t i = 0; i < LED_COUNT; ++i, ++ctrl) { ctrl->pin = LED_NONE; ctrl->off = LOW; ctrl->mode = MODE_DISABLE; ctrl->expire = false; } } void Led::setup(uint8_t led, uint8_t pin, uint8_t off) { CTRL& ctrl = _leds[led & LED_MASK]; ctrl.pin = pin; ctrl.off = off; ctrl.mode = MODE_DISABLE; ctrl.expire = false; digitalWrite(pin, off); pinMode(pin, OUTPUT); } void Led::off(uint8_t led, uint16_t ms) { control(_leds[led & LED_MASK], MODE_OFF, ms); } void Led::on(uint8_t led, uint16_t ms) { control(_leds[led & LED_MASK], MODE_ON, ms); } void Led::toggle(uint8_t led, uint16_t ms) { control(_leds[led & LED_MASK], MODE_TOGGLE, ms); } void Led::control(CTRL& ctrl, MODE mode, uint16_t ms) { ctrl.expire = false; if (ms) { ctrl.mode = mode; ctrl.alarm.interval(ms * 1000UL); } else { ctrl.mode = MODE_DISABLE; if (ctrl.pin != LED_NONE) { switch (mode) { case MODE_OFF: digitalWrite(ctrl.pin, ctrl.off); break; case MODE_ON: digitalWrite(ctrl.pin, !ctrl.off); break; case MODE_TOGGLE: digitalWrite(ctrl.pin, !digitalRead(ctrl.pin)); break; default: break; } } } } bool Led::status(uint8_t led) { CTRL& ctrl = _leds[led & LED_MASK]; return ctrl.pin != LED_NONE ? digitalRead(ctrl.pin) != ctrl.off : false; } bool Led::expire(uint8_t led) { CTRL& ctrl = _leds[led & LED_MASK]; bool rv = ctrl.expire; ctrl.expire = false; return rv; } void Led::handle(void) { CTRL *ctrl = _leds; for (uint8_t i = 0; i < LED_COUNT; ++i, ++ctrl) { MODE mode = ctrl->mode; if ((mode != MODE_DISABLE) && ctrl->alarm.expire()) { control(*ctrl, mode, 0); ctrl->expire = true; if (mode == MODE_TOGGLE) ctrl->mode = mode; } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
/* alarm.h - Alarm Library for Arduino Copyright (c) 2022 Sasapea's Lab. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #pragma once #include <stddef.h> #include <stdint.h> #include <stdbool.h> #include "hardware.h" class Alarm : protected Hardware { public: typedef bool (*handler_t)(Alarm& alarm); Alarm& interval(int32_t interval, bool fromnow = true); int32_t interval(void); Alarm& handler(handler_t handler); handler_t handler(void); Alarm& param(uint8_t num, void *val); void *param(uint8_t num); bool expire(void); protected: friend class AlarmClass; Alarm *_next; uint32_t _alarm; int32_t _interval; handler_t _handler; void *_params[4]; }; class AlarmClass : protected Hardware { public: void begin(void); bool start(Alarm& alarm); void cancel(Alarm& alarm); void cancel(uint8_t num, void *arg); protected: bool add(Alarm& alarm); private: bool timeup(void); friend void AlarmClass_timeup(void); Alarm *_alarm_list; }; extern AlarmClass AlarmTimer; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
/* alarm.cpp - Alarm Library for Arduino Copyright (c) 2022 Sasapea's Lab. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <stddef.h> #include "alarm.h" #define lengthof(a) (sizeof(a)/sizeof(a[0])) AlarmClass AlarmTimer; Alarm& Alarm::interval(int32_t interval, bool fromnow) { if (fromnow) _alarm = micros() + interval; _interval = interval; return *this; } int32_t Alarm::interval(void) { return _interval; } Alarm& Alarm::handler(Alarm::handler_t handler) { _handler = handler; return *this; } Alarm::handler_t Alarm::handler(void) { return _handler; } Alarm& Alarm::param(uint8_t num, void *val) { if (num < lengthof(_params)) _params[num] = val; return *this; } void *Alarm::param(uint8_t num) { return num < lengthof(_params) ? _params[num] : NULL; } bool Alarm::expire(void) { if ((int32_t)(micros() - _alarm) < 0) return false; _alarm += _interval; return true; } void AlarmClass::begin(void) { setupAlarmTimer(); } bool AlarmClass::start(Alarm& alarm) { if ((alarm._interval < 1) || !alarm._handler) return false; size_t save = disableAndSaveInterrupts(); if (add(alarm)) startAlarmTimer(_alarm_list->_alarm - micros()); restoreInterrupts(save); return true; } void AlarmClass::cancel(Alarm& alarm) { size_t save = disableAndSaveInterrupts(); for (Alarm **p = &_alarm_list; *p; p = &(*p)->_next) { if (*p == &alarm) { *p = (*p)->_next; break; } } restoreInterrupts(save); } void AlarmClass::cancel(uint8_t num, void *arg) { if ((num < lengthof(Alarm::_params)) && arg) { size_t save = disableAndSaveInterrupts(); for (Alarm **p = &_alarm_list; *p; p = &(*p)->_next) { if ((*p)->_params[num] == arg) { *p = (*p)->_next; break; } } restoreInterrupts(save); } } bool AlarmClass::add(Alarm& alarm) { Alarm **p; for (p = &_alarm_list; *p && ((int32_t)((*p)->_alarm - alarm._alarm) <= 0); p = &(*p)->_next) continue; alarm._next = *p; *p = &alarm; return p == &_alarm_list; } bool AlarmClass::timeup(void) { Alarm *p = NULL; while (_alarm_list) { int32_t t = _alarm_list->_alarm - micros(); if (p || (t > 0)) { startAlarmTimer(t); return true; } _alarm_list = (p = _alarm_list)->_next; if (p->_handler(*p)) { p->_alarm += p->_interval; if (!add(*p)) p = NULL; } else p = NULL; } return false; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
/* task.h - Smallest Non Preemptive Multi Task Library for Embedded System Copyright (c) 2022 Sasapea's Lab. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #pragma once #include <stddef.h> #include <stdint.h> #include <stdbool.h> #include <setjmp.h> #include "hardware.h" #include "alarm.h" #define TASK_DEFAULT_STACK_SIZE (DEFAULT_STACK_SIZE + Task::TCB_SIZE) class Task : protected Hardware { public: typedef void (*func_t)(void *arg); protected: typedef struct tcb { struct tcb *next; jmp_buf context; void *alloc; void *arg; func_t start; } tcb_t; typedef struct { Alarm alarm; size_t save; bool retval; } ctrl_t; static void enqueue0(tcb_t *tcb); static void enqueue1(tcb_t *tcb); static tcb_t *dequeue(void); static void switching(void); static void dispatch(void); public: static const size_t TCB_SIZE = sizeof(tcb_t); static void begin(size_t size = TASK_DEFAULT_STACK_SIZE); static bool start(func_t func, void *arg = 0, int size = TASK_DEFAULT_STACK_SIZE, void *stack = 0); static void stop(void); static bool yield(void); static size_t current(void); static void sleep(int32_t ms, uint8_t busyLoop = 0); static void sleepMicroseconds(int32_t us, uint8_t busyLoop = 0); static void delay(uint32_t ms, uint8_t busyLoop = 0); static void delayMicroseconds(uint32_t us, uint8_t busyLoop = 0); private: static tcb_t *_disp; static tcb_t *_curr; static tcb_t *_head; static tcb_t *_tail[2]; static size_t _used; }; class Sync : protected Task { public: Sync(void); bool sleep(int32_t timeout = -1); size_t wakeup(size_t task = 0); protected: bool sleep(ctrl_t& ctrl); static bool timeup(Alarm& alarm); tcb_t *_head; tcb_t *_tail; }; class Mutex : protected Sync { public: Mutex(void); bool lock(int32_t timeout = -1); void unlock(void); protected: size_t _owner; size_t _count; }; class Sem : protected Sync { public: Sem(size_t limit = (size_t)-1, size_t count = 0); bool wait(size_t count = 1, int32_t timeout = -1); void post(size_t count = 1); protected: size_t _limit; size_t _count; size_t _wait; }; class Event : protected Sem { protected: Event(void); bool wait(int32_t timeout = -1); void signal(void); }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 |
/* task.cpp - Smallest Non Preemptive Multi Task Library for Embedded System Copyright (c) 2022 Sasapea's Lab. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <stdlib.h> #include <alloca.h> #include "task.h" Task::tcb_t *Task::_disp; Task::tcb_t *Task::_curr; Task::tcb_t *Task::_head; Task::tcb_t *Task::_tail[]; size_t Task::_used; void Task::begin(size_t size) { /* Registration of main task */ static tcb_t tcb; _curr = &tcb; _used = size; } bool Task::start(func_t func, void *arg, int size, void *stack) { tcb_t *tcb; if (size < 0) { /* Use the current stack area */ size_t n = - size; size = _used; stack = 0; _used += n; } else if (stack) { /* Set the stack area to the variable area */ size = (char *)&tcb - (char *)stack - size + sizeof(*tcb); stack = 0; } else { /* Dynamically allocate stack area */ stack = malloc(size); if (!stack) return false; size = (char *)&tcb - (char *)stack - size + sizeof(*tcb); } tcb = (tcb_t *)alloca(size); tcb->alloc = stack; tcb->arg = arg; tcb->start = func; enqueue0(tcb); if (setjmp(tcb->context)) { _curr = _disp; _curr->start(_curr->arg); stop(); } return true; } void Task::stop(void) { if (_curr->start) { if (_curr->alloc) free(_curr->alloc); dispatch(); } } void Task::enqueue0(tcb_t *tcb) { size_t save = disableAndSaveInterrupts(); if (_tail[0]) { tcb->next = _tail[0]->next; _tail[0] = (_tail[0]->next = tcb); } else { tcb->next = _head; _tail[0] = (_head = tcb); } restoreInterrupts(save); } void Task::enqueue1(tcb_t *tcb) { size_t save = disableAndSaveInterrupts(); (_tail[1] = *(_tail[1] ? &_tail[1]->next : (_tail[0] ? &_tail[0]->next : &_head)) = tcb)->next = 0; restoreInterrupts(save); } Task::tcb_t *Task::dequeue(void) { tcb_t *tcb; size_t save = disableAndSaveInterrupts(); if ((tcb = _head)) { if (_head == _tail[0]) _tail[0] = 0; else if (_head == _tail[1]) _tail[1] = 0; _head = _head->next; } restoreInterrupts(save); return tcb; } size_t Task::current(void) { return (size_t)_curr; } bool Task::yield(void) { enqueue1(_curr); switching(); return !_curr->start; } void Task::switching(void) { if (!setjmp(_curr->context)) dispatch(); _curr = _disp; } void Task::dispatch(void) { while (1) { if ((_disp = dequeue())) longjmp(_disp->context, 1); idle(); } } void Task::sleep(int32_t ms, uint8_t busyLoop) { sleepMicroseconds(ms * 1000L, busyLoop); } void Task::sleepMicroseconds(int32_t us, uint8_t busyLoop) { uint32_t t = micros(); if (us > busyLoop) { Sync sync; sync.sleep(us -= busyLoop); t += us; } if (busyLoop) { while (micros() - t < busyLoop) continue; } } void Task::delay(uint32_t ms, uint8_t busyLoop) { delayMicroseconds(ms * 1000UL, busyLoop); } void Task::delayMicroseconds(uint32_t us, uint8_t busyLoop) { uint32_t t = micros(); if (us > busyLoop) { us -= busyLoop; while (micros() - t < us) yield(); t += us; } if (busyLoop) { while (micros() - t < busyLoop) continue; } } Sync::Sync(void) : _head(0) , _tail(0) { } bool Sync::timeup(Alarm& alarm) { if (((Sync *)alarm.param(0))->wakeup((size_t)alarm.param(1))) *(bool *)alarm.param(2) = false; return false; } bool Sync::sleep(ctrl_t& ctrl) { tcb_t *tcb = (tcb_t *)current(); (_tail = *(_tail ? &_tail->next : &_head) = tcb)->next = 0; restoreInterrupts(ctrl.save); ctrl.alarm.param(0, this); ctrl.alarm.param(1, tcb); ctrl.alarm.param(2, &ctrl.retval); ctrl.alarm.handler(timeup); AlarmTimer.start(ctrl.alarm); switching(); return ctrl.retval; } bool Sync::sleep(int32_t timeout) { if (timeout == 0) return false; ctrl_t ctrl; ctrl.alarm.interval(timeout); ctrl.retval = true; ctrl.save = disableAndSaveInterrupts(); return sleep(ctrl); } size_t Sync::wakeup(size_t task) { tcb_t *tcb = 0; size_t save = disableAndSaveInterrupts(); if (task) { for (tcb_t **p = &_head; *p; p = &(*p)->next) { if (*p == (tcb_t *)task) { if (!(*p = (*p)->next)) _tail = (p == &_head ? 0 : (tcb_t *)p); tcb = (tcb_t *)task; break; } } } else _head = ((tcb = _head) != _tail ? _head->next : _tail = 0); restoreInterrupts(save); if (tcb) { AlarmTimer.cancel(1, tcb); enqueue0(tcb); } return (size_t)tcb; } Mutex::Mutex(void) : _owner(0) { } bool Mutex::lock(int32_t timeout) { ctrl_t ctrl; ctrl.alarm.interval(timeout); ctrl.retval = true; ctrl.save = disableAndSaveInterrupts(); size_t task = current(); if (!_owner) { _owner = task; _count = 1; } else if (_owner == task) ++_count; else if (timeout == 0) ctrl.retval = false; else return sleep(ctrl); restoreInterrupts(ctrl.save); return ctrl.retval; } void Mutex::unlock(void) { size_t save = disableAndSaveInterrupts(); if (_owner == current()) { if (--_count == 0) { _owner = wakeup(); _count = 1; } } restoreInterrupts(save); } Sem::Sem(size_t limit, size_t count) : _limit(limit) , _count(count) , _wait(0) { } bool Sem::wait(size_t count, int32_t timeout) { ctrl_t ctrl; ctrl.alarm.interval(timeout); ctrl.retval = true; ctrl.save = disableAndSaveInterrupts(); if ((count == 0) || (count > _limit)) ctrl.retval = false; else if (!_wait && (_count >= count)) _count -= count; else if (timeout == 0) ctrl.retval = false; else { ++_wait; ((tcb_t *)current())->arg = (void *)count; sleep(ctrl); disableAndSaveInterrupts(); --_wait; while (_head) { count = (size_t)_head->arg; if (_count < count) break; _count -= count; wakeup(); } } restoreInterrupts(ctrl.save); return ctrl.retval; } void Sem::post(size_t count) { size_t save = disableAndSaveInterrupts(); if (count && (count + _count <= _limit)) { _count += count; if (_head) { count = (size_t)_head->arg; if (_count >= count) { _count -= count; wakeup(); } } } restoreInterrupts(save); } Event::Event(void) : Sem(1) { } bool Event::wait(int32_t timeout) { return Sem::wait(1, timeout); } void Event::signal(void) { Sem::post(); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
/* fifo.h - First-In First-Out Buffer Template Library Copyright (c) 2022 Sasapea's Lab. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #pragma once #include <string.h> #include "task.h" #define FIFO_NO_WAIT 0 #define FIFO_INFINITE_WAIT -1 #define FIFO_OVERWRITE -2 template<typename T, size_t SIZE> class Fifo : protected Sync { private: size_t _wridx; size_t _rdidx; size_t _count; size_t _losts; T _buffer[SIZE]; void read(T *buf, size_t count) { _count -= count; if (_rdidx + count > SIZE) { size_t n = SIZE - _rdidx; memcpy(buf, _buffer + _rdidx, n * sizeof(T)); buf += n; count -= n; _rdidx = 0; } memcpy(buf, _buffer + _rdidx, count * sizeof(T)); _rdidx += count; } void write(const T *buf, size_t count) { _count += count; if (_wridx + count > SIZE) { size_t n = SIZE - _wridx; memcpy(_buffer + _wridx, buf, n * sizeof(T)); buf += n; count -= n; _wridx = 0; } memcpy(_buffer + _wridx, buf, count * sizeof(T)); _wridx += count; } public: Fifo(void) : _wridx(0) , _rdidx(0) , _count(0) , _losts(0) { } size_t available(void) { size_t save = disableAndSaveInterrupts(); size_t rv = _count; restoreInterrupts(save); return rv; } size_t availableForWrite(void) { return SIZE - available(); } size_t losts(void) { size_t save = disableAndSaveInterrupts(); size_t rv = _losts; _losts = 0; restoreInterrupts(save); return rv; } bool get(T *item, size_t count = 1, int32_t timeout = FIFO_INFINITE_WAIT) { size_t n, rv = 0; ctrl_t ctrl; while (count) { ctrl.alarm.interval(timeout); ctrl.retval = true; ctrl.save = disableAndSaveInterrupts(); if (_count) { n = count < _count ? count : _count; read(item, n); restoreInterrupts(ctrl.save); item += n; count -= n; rv += n; } else { if (timeout == FIFO_NO_WAIT) restoreInterrupts(ctrl.save); else if (sleep(ctrl)) continue; break; } } return rv; } size_t put(const T *item, size_t count = 1, int32_t timeout = FIFO_INFINITE_WAIT) { size_t n, rv = 0; Alarm alarm; alarm.interval(timeout); while (count) { size_t save = disableAndSaveInterrupts(); n = SIZE - _count; if (n) { if (count < n) n = count; write(item, n); restoreInterrupts(save); wakeup(); item += n; count -= n; rv += n; alarm.interval(timeout); } else if (timeout == FIFO_OVERWRITE) { n = _count < count ? _count : count; _count -= n; _losts += n; if ((_rdidx += n) >= SIZE) _rdidx -= SIZE; restoreInterrupts(save); } else { restoreInterrupts(save); if (timeout == FIFO_NO_WAIT) break; else if ((timeout > FIFO_NO_WAIT) && alarm.expire()) break; yield(); } } return rv; } }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
/* hardware.h - Hardware Library Copyright (c) 2022 Sasapea's Lab. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #pragma once #include <stddef.h> #include <stdint.h> #include <stdbool.h> #ifdef ARDUINO #include <Arduino.h> #else #define INPUT 0 #define OUTPUT 1 #define INPUT_PULLUP 2 #define HIGH 1 #define LOW 0 #endif #define DEFAULT_STACK_SIZE 128 class Hardware { public: #ifdef ARDUINO static uint32_t micros(void) { return ::micros(); } static void pinMode(uint8_t pin, uint8_t mode) { ::pinMode(pin, mode); } static void digitalWrite(uint8_t pin, uint8_t val) { ::digitalWrite(pin, val); } static uint8_t digitalRead(uint8_t pin) { return ::digitalRead(pin); } #else static uint32_t micros(void); static void pinMode(uint8_t pin, uint8_t mode); static void digitalWrite(uint8_t pin, uint8_t val); static uint8_t digitalRead(uint8_t pin); #endif static size_t disableAndSaveInterrupts(void); static void restoreInterrupts(size_t save); static void idle(void); protected: /* For Alarm Class */ static void setupAlarmTimer(void); static void startAlarmTimer(int32_t interval); }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
/* hardware_avr.cpp - Hardware Library for AVR Copyright (c) 2022 Sasapea's Lab. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef __AVR__ #include <avr/io.h> #include <avr/sleep.h> #include <avr/interrupt.h> #include "hardware.h" #include "alarm.h" #ifdef TIFR #define TIFR0 TIFR #endif #ifdef TIMSK #define TIMSK0 TIMSK #endif #if (F_CPU == 16000000) || (F_CPU == 16500000) #define PERIOD_US 4 #elif (F_CPU == 8000000) #define PERIOD_US 8 #else #error "System Clock Not Supported. (8MHz or 16MHz)" #endif inline void AlarmClass_timeup(void) { if (!AlarmTimer.timeup()) TIMSK0 &= ~_BV(OCIE0B); } ISR(TIMER0_COMPA_vect) { AlarmClass_timeup(); } void Hardware::setupAlarmTimer(void) { TCCR0A = 0; // standard mode } void Hardware::startAlarmTimer(int32_t interval) { uint8_t save = disableAndSaveInterrupts(); if (interval <= 2 * PERIOD_US) OCR0A = TCNT0 + 2; else if (interval <= 256 * PERIOD_US) OCR0A = TCNT0 + (uint8_t)((uint16_t)interval / PERIOD_US) - 1; else if (!(TIMSK0 & _BV(OCIE0A))) OCR0A = TCNT0 - 1; TIFR0 = _BV(OCF0A); TIMSK0 |= _BV(OCIE0A); restoreInterrupts(save); } size_t Hardware::disableAndSaveInterrupts(void) { uint8_t save = SREG; cli(); return save; } void Hardware::restoreInterrupts(size_t save) { SREG = save; } void Hardware::idle(void) { sei(); set_sleep_mode(SLEEP_MODE_IDLE); sleep_mode(); } #endif |