2026年9月29日火曜日

FreeRTOS::gokan(eventloop,ledflash,message/stream)

mutex.inoではtask1が優先的に実行されるが間違いではないとのこと

nomutex.inoではtask1とtask2が交互実行されたがMutexがきかんのがわかった

nosemaphore,semaphoreでも同様だった

noqueueでは競合がおきるqueueありでは無事成功


 -----------------eventgroup.ino OK on raspico -----------------------

*****************************************************
* FreeRTOS Event Groupの例
* 3個のタスクから異なる周期でEvent Groupのビットセット
* Task4でビット区別して青,緑,赤LEDを点滅する
* Pico の場合:
* ツールの Operating System で「FreeRTOS SMP」を選択
****************************************************/
#include "event_groups.h" //Picoで必要
//--- ピン設定 ----
#define Blue 1
#define Green 2
#define Red 3
// Event Group用変数定義
EventGroupHandle_t GroupA;

//====== Task1 ========
void Task1(void*){
while(1){
vTaskDelay(pdMS_TO_TICKS(500));
xEventGroupSetBits(GroupA, 0x01);
}
}

//====== Task2 ========
void Task2(void*){
while(1){
vTaskDelay(pdMS_TO_TICKS(1000));
xEventGroupSetBits(GroupA, 0x02);
}

}
//====== Task3 ========
void Task3(void*){
while(1){
vTaskDelay(pdMS_TO_TICKS(2000));
xEventGroupSetBits(GroupA, 0x04);
}
}

//====== Task4 =======
void Task4(void*){
while(1){
// Event Group待ち
EventBits_t got = xEventGroupWaitBits(
GroupA, // グループ指定
0x07, // Bit 0,1,2指定
pdTRUE, // 完了後クリア
pdFALSE, // OR条件
portMAX_DELAY // 永久待ち
);
// LEDの制御
if(got & 0x01){digitalWrite(Blue, !digitalRead(Blue));}
if(got & 0x02){digitalWrite(Green, !digitalRead(Green));}
if(got & 0x04){digitalWrite(Red, !digitalRead(Red)); }
}
}

//======== setup/loop ====
void setup(){
Serial.begin(115200);
vTaskDelay(pdMS_TO_TICKS(200));
// LED初期化
pinMode(Blue, OUTPUT);
pinMode(Green, OUTPUT);
pinMode(Red, OUTPUT);
// EventGroup生成
GroupA = xEventGroupCreate();
// タスク生成
xTaskCreate(Task1, "task1", 2048, NULL, 1, NULL);
xTaskCreate(Task2, "task2", 2048, NULL, 1, NULL);
xTaskCreate(Task3, "task3", 2048, NULL, 1, NULL);
xTaskCreate(Task4, "task4", 2048, NULL, 1, NULL);
}
void loop(){
//なにもしない
}
-------led flash on pico OK ------------------------------------------------------

/**************************************************
* FreeRTOS Test  LED点滅をタスクで実行
* Picoの場合
*  ツールのOperating Systemで FreeRTOS SMPを選択
**************************************************/
#define Blue 1
#define Green 2
#define Red 3
portTickType LastTime;
/***********************
* Task1
************************/
void Task1(void *parameters){
while(1){
digitalWrite(Blue, HIGH);
vTaskDelay(pdMS_TO_TICKS(100));
digitalWrite(Blue, LOW);
vTaskDelay(pdMS_TO_TICKS(150));
}
}
/***********************
* Task2
************************/
void Task2(void *parameters){
while(1){
digitalWrite(Green, HIGH);
vTaskDelay(pdMS_TO_TICKS(100));
digitalWrite(Green, LOW);
vTaskDelay(pdMS_TO_TICKS(400));
}
}
/***********************
* Task3
************************/
void Task3(void *parameters){
while(1){
digitalWrite(Red, !digitalRead(3)); //トグル
LastTime = xTaskGetTickCount(); // 現在時間取得
vTaskDelayUntil(&LastTime, pdMS_TO_TICKS(2000));
}
}
void setup() {
pinMode(Blue, OUTPUT); // GPIO1
pinMode(Green, OUTPUT); // GPIO2
pinMode(Red, OUTPUT); // GPIO3
Serial.begin(9600);
delay(2000);
// タスク生成
xTaskCreate(Task1, "task1", 1024, NULL, 1, NULL);
xTaskCreate(Task2, "task2", 1024, NULL, 1, NULL);
xTaskCreate(Task3, "task3", 1024, NULL, 1, NULL);
}

void loop() {
Serial.println("From Loop");
vTaskDelay(pdMS_TO_TICKS(5000));
}

FreeRTOSの Message Buffer と Stream Buffer の最大の違いは、データの「境界(区切り)」があるかどうかです。 [1]
なお、どちらのバッファも共通して「1つの送信タスク(またはISR)対1つの受信タスク」の1対1通信に特化して最適化されており、通常のQueueよりも軽量・高速に動作します。内部的には、Message BufferはStream Bufferの仕組みをベースに構築されています。 [1]

主な違いの比較表
項目Message Buffer (メッセージバッファ)Stream Buffer (ストリームバッファ)
データの特性メッセージ単位(境界あり)バイトストリーム(境界なし)
送受信の単位送信したサイズと同じ塊(可変長)でしか受信できない任意のバイト数で書き込み、任意のバイト数で読み出せる
内部のデータ構造データ本体の直前に「メッセージ長(size_t)」が自動で付与される純粋なバイト列(リングバッファ)
タスクの起床条件1つのメッセージが完全に書き込まれたとき設定した「トリガーレベル」以上のバイト数が溜まったとき
主な用途JSON文字列、構造体、フレーム単位の通信データ(可変長データ)UART受信データの取り込みなど、途切れのない連続データ

動作の具体的な違い
1. Message Buffer(境界あり・可変長)
送信側が「10バイト」と「20バイト」のメッセージを連続で送った場合、バッファ内ではそれぞれ独立した塊として区別されます。 [1, 2]
  • 読み出しルール: 受信側が1回で読み出せるのは、必ず送信された塊(10バイト、または20バイト)ごとです。5バイトだけ部分的に読み出すといったことはできません。 [1, 2, 3]
  • メモリ消費: 送信したデータとは別に、内部で自動的にメッセージの長さを記録する領域(通常32bitマイコンであれば4バイト分)がメッセージごとに消費されます。 [1]
2. Stream Buffer(境界なし・バイト列)
送信側が「10バイト」と「20バイト」のデータを送った場合、バッファ内では区切りのない合計「30バイトの連続したデータ」として扱われます。 [1, 2]
  • 読み出しルール: 受信側は、バッファに溜まっているデータから「1バイトだけ」「15バイトだけ」など、好きな長さで自由に切り出して読み出すことができます。
  • トリガーレベル: 「バッファに10バイト以上溜まったら受信タスクを叩き起こす」といった柔軟な制御(Trigger Level)が可能です。 [1, 2, 3]

どちらを選ぶべきか?
  • Message Buffer を選ぶべきケース:
    「長さが毎回変わるパケットやコマンド文字列(JSONなど)」を、1つのまとまったメッセージとしてそのまま相手に届けたいとき。
    [1, 2]
  • Stream Buffer を選ぶべきケース:
    「UARTなどのシリアル通信」で次々と流れてくるバイトデータを、一時的にリングバッファに蓄積してタスク側でまとめて処理したいとき





0 件のコメント:

コメントを投稿