FreeRTOSにiTRONタスク管理APIっぽいの入れてみた。
開発環境:STM32CubeMX (Ver5.6.1)
コンパイラは、EWARM、MDK-ARM などお好きな環境で
##FreeRTOSの初期状態からの設定変更
タスク状態やスタック領域管理のため、CubeMXにて下記FreeRTOS設定を変更した。
CubeMX画面上の、
タブ 「Pinout&Configuration」
画面左帯 「Categories」→「Middleware」→「FREERTOS」
を選択。(Modeは「CMSIS_V1」を選択。※V2未評価のため)
「Configuration」→「Config parameters」を選択。
RECORD_STACK_HIGH_ADDRESS ⇒ Enabled ※スタックサイズを求めるため。
Memory Allocation ⇒ Dynamic/Static ※静的関数(...Static)を有効に。
GENERATE_RUN_TIME_STATS ⇒ Enabled ※タスク状態確認。
USE_TRACE_FACILITY ⇒ Enabled ※タスクトレース有効化。
USE_STATS_FORMATTING_FUNCTIONS ⇒ Enabled ※タスクトレース有効化。
「Configuration」→「Include parameters」を選択。
uxTaskGetStackHighWaterMark ⇒ Enabled ※タスクの残りスタック量を取得するため。
##ソースコード
【注意事項】
・タスク管理のため、メモリ操作やソートをしまくっている無理くりソースコードです。
・下記ソースコード中の「表示関数の別名定義」箇所(文字出力、メモリ操作関数)は実装済みの関数名に置き換えます。
※本記事中のソフトウェアはサンプルコードであり、ソフトウェアを使用した結果いかなる損害等が発生しても当方は一切責任を負いません。
/****************************************************************************
* iTRON風サポートライブラリ
****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "itron.h"
#include "clib.h"
#include "klib.h"
/*** 表示関数の別名定義 ***/
#define _PS sc_puts
#define _PF sc_printf
#define _PR sc_print
#define _NL sc_crlf
#ifdef INC_FREERTOS_H
#define _MAL pvPortMalloc // RAM割当関数
#define _FRE vPortFree // RAM開放関数
#else
#define _MAL malloc
#define _FRE free
#endif
TaskStatus_t *pxTaskStaArr = NULL;
int vTaskNum = 0;
/****************************************************************************
* テーブルソート関数
* input:
* const void *x, *y 比較データ (C標準ライブラリ参照)
* output:
* int 0 or 1 or -1
*---------------------------------------------------------------------------
* (解説)
* タスク番号順に並び替える。
*/
static int sort_info(const void *x, const void *y)
{
int r1, r2;
// タスク番号順に並び替える
r1 = ((const TaskStatus_t*)x)->xTaskNumber;
r2 = ((const TaskStatus_t*)y)->xTaskNumber;
//_PF(" r1=%d r2=%d\n", r1, r2);
#if 1
// 小さい順(昇順)に並び替える。
return (r1 == r2)? 0: (r1 > r2)? 1: -1;
#else
// 大きい順(降順)に並び替える。
return (r1 == r2)? 0: (r1 < r2)? 1: -1;
#endif
}
/************************************************/
/* Get Task Handle */
/************************************************/
TaskHandle_t GetTaskHandle(ID tid, int ref)
{
int num = uxTaskGetNumberOfTasks(); // タスク数チェック
if (num <= 0) {
// タスク数エラー
if (pxTaskStaArr) {
// 以前の情報領域を破棄
_FRE(pxTaskStaArr);
pxTaskStaArr = NULL;
}
_PF("Task Number error. %d\n", num);
vTaskNum = 0;
return NULL;
}
if (vTaskNum != num) {
// 前回チェックしたタスク数と違う
vTaskNum = num;
if (pxTaskStaArr) {
_FRE(pxTaskStaArr); // 以前の情報領域を破棄
pxTaskStaArr = NULL;
}
}
if (pxTaskStaArr == NULL) {
// 新しい情報領域を取得
pxTaskStaArr = (TaskStatus_t*)_MAL(vTaskNum * sizeof(TaskStatus_t));
if (pxTaskStaArr == NULL) {
_PS(" memory alloc error.");
vTaskNum = 0;
return NULL;
}
//_PF("Arry:%08X tasks:%d (unit-size=%d total=%d) \n\n", pxTaskStaArr, vTaskNum, sizeof(TaskStatus_t), vTaskNum * sizeof(TaskStatus_t));
ref = 1;
}
if (ref) {
// タスク情報取得
num = uxTaskGetSystemState( pxTaskStaArr, vTaskNum, NULL );
if ( vTaskNum != num ) _PF("Illegal task num. %d %d\n", vTaskNum, num);
// 管理領域をタスク番号順に並び替える。
qsort(pxTaskStaArr, vTaskNum, sizeof(TaskStatus_t), sort_info);
}
if (tid) {
for (int x = 0; x < vTaskNum; x++) {
if (tid == pxTaskStaArr[x].xTaskNumber) {
// タスクハンドルを返す。
return pxTaskStaArr[x].xHandle;
}
}
}
return NULL; // 該当なし
}
/* タスク生成 */
ER_ID acre_tsk(const T_CTSK *pk_ctsk)
{
TaskHandle_t pxTask;
TaskStatus_t xTaskStatus;
if (pk_ctsk->stk) {
// 静的スタック領域
/*
※注意
静的コール(スタックアドレス指定あり)の場合、
スタック後半ブロックをFreeRTOSのタスク管理領域
(TCB:現バージョンでは約96バイト)として使うよう
処理するので、スタック領域サイズはその分余裕をもって
確保すること。
*/
uint32_t ulStackDepth; // スタックの深さ
// スタックサイズ = (指定サイズ - TCBサイズ) / スタック型サイズ
ulStackDepth = ((pk_ctsk->stksz)-TCB_SIZE)/STK_UNIT;
if (ulStackDepth < (uint32_t)configMINIMAL_STACK_SIZE) {
// 最小スタックサイズを満たしていない。
return E_NOMEM;
}
#if 0
_PF(S_BLUE " STK=%08X\n" S_DEF, pk_ctsk->stk);
_PF(S_BLUE " SIZ=%d[w]\n" S_DEF, ulStackDepth);
_PF(S_BLUE " TCB=%08X\n" S_DEF, &((StackType_t *)pk_ctsk->stk)[ulStackDepth]);
dly_tsk(100/MSEC);
#endif
// タスク生成
pxTask = xTaskCreateStatic(
(TaskFunction_t)pk_ctsk->task, /* 実行アドレス */
pk_ctsk->name, /* タスク名 */
ulStackDepth, /* スタックサイズ (ワード単位) */
(void *)pk_ctsk->exinf, /* パラメータ */
(UBaseType_t)pk_ctsk->itskpri, /* 優先順位 */
(StackType_t *)pk_ctsk->stk, /* スタック開始ポインタ */
(StaticTask_t *)&((StackType_t *)pk_ctsk->stk)[ulStackDepth]);
/* タスク管理領域ポインタ(=スタック後半)*/
}
else {
// 動的スタック領域
BaseType_t xRsl;
unsigned short usStackDepth;
usStackDepth = (unsigned short)((pk_ctsk->stksz)/STK_UNIT);
if (usStackDepth < configMINIMAL_STACK_SIZE) {
// 最小スタックサイズを満たしていない。
return E_NOMEM;
}
// タスク生成
xRsl = xTaskCreate(
(TaskFunction_t)pk_ctsk->task, /* 実行アドレス */
pk_ctsk->name, /* タスク名 */
usStackDepth, /* スタックサイズ (ワード単位) */
(void *)pk_ctsk->exinf, /* パラメータ */
(UBaseType_t)pk_ctsk->itskpri, /* 優先順位 */
&pxTask); /* 作成したタスクのハンドルを引き渡すために使用されます。*/
if (xRsl != pdPASS) {
// タスク生成エラー
return E_SYS;
}
}
if (pxTask == NULL) {
// 生成エラー
return E_SYS;
}
/* タスク状態設定 */
if ((pk_ctsk->tskatr & TA_ACT) == 0) {
// サスペンド状態にする。
vTaskSuspend(pxTask);
}
// タスク番号を返す。
vTaskGetTaskInfo(pxTask, &xTaskStatus, pdFALSE, eInvalid);
return (ER_ID)xTaskStatus.xTaskNumber;
}
/* タスク削除 */
ER del_tsk( ID tid )
{
TaskHandle_t pxTask;
if (tid <= 0) {
return E_ID; // 不正ID
}
else if (tid == TSK_SELF) {
return E_OBJ; // 自タスク指定禁止
}
else {
pxTask = GetTaskHandle(tid, 0);
if (pxTask == NULL) {
return E_ID;
}
}
// 削除
vTaskDelete(pxTask);
return E_OK;
}
/* タスク起動 */
ER act_tsk( ID tid )
{
// レジュームと同じ扱いとする。
return rsm_tsk(tid);
}
/* タスク起動キャンセル */
ER can_act( ID tid )
{
// サスペンドと同等扱いとする。
return sus_tsk(tid);
}
/* タスク起動 */
ER sta_tsk( ID tid, int val )
{
// レジュームと同じ扱いとする。
return rsm_tsk(tid);
}
/* タスク強制終了 */
ER ter_tsk( ID tid )
{
// サスペンドと同等扱いとする。
return sus_tsk(tid);
}
/* タスク優先度変更 */
ER chg_pri( ID tid, int val )
{
TaskHandle_t pxTask;
if (tid <= 0) {
return E_ID; // 不正ID
}
else if (tid == TSK_SELF) {
pxTask = NULL; // 自タスク
}
else {
pxTask = GetTaskHandle(tid, 0);
if (pxTask == NULL) {
return E_ID;
}
}
// 変更
vTaskPrioritySet(pxTask, (UBaseType_t)val);
// 確認
if (val != (int)uxTaskPriorityGet(pxTask)) {
return E_PAR; // 設定エラー
}
return E_OK;
}
/* タスク強制待ち状態へ */
ER sus_tsk( ID tid )
{
TaskHandle_t pxTask;
if (tid <= 0) {
return E_ID; // 不正ID
}
else if (tid == TSK_SELF) {
pxTask = NULL; // 自タスク
}
else {
pxTask = GetTaskHandle(tid, 0);
if (pxTask == NULL) {
return E_ID;
}
}
// サスペンド
vTaskSuspend(pxTask);
return E_OK;
}
/* タスク再開 */
ER rsm_tsk( ID tid )
{
TaskHandle_t pxTask;
if (tid <= 0) {
return E_ID; // 不正ID
}
else if (tid == TSK_SELF) {
return E_OBJ; // 自タスク指定禁止
}
else {
pxTask = GetTaskHandle(tid, 0);
if (pxTask == NULL) {
return E_ID;
}
}
// レジューム
vTaskResume(pxTask);
return E_OK;
}
#if 0
/* タスク再開(1回版) */
ER frsm_tsk( ID tid )
{
TaskHandle_t pxTask = GetTaskHandle(tid, 0);
if (pxTask == NULL) {
return E_ID;
}
;
;
return E_OK;
}
#endif
/* タスク起床 */
ER wup_tsk( ID tid )
{
TaskHandle_t pxTask = GetTaskHandle(tid, 0);
if (pxTask == NULL) {
return E_ID;
}
;
;
return E_OK;
}
/* タスク起床キャンセル */
ER can_wup( ID tid )
{
TaskHandle_t pxTask = GetTaskHandle(tid, 0);
if (pxTask == NULL) {
return E_ID;
}
;
;
return E_OK;
}
/* タスク待ち解除 */
ER rel_wai( ID tid )
{
TaskHandle_t pxTask = GetTaskHandle(tid, 0);
if (pxTask == NULL) {
return E_ID;
}
;
;
return E_OK;
}
/************************************************/
/* Refer Task Status */
/************************************************/
ER ref_tsk(ID tid, T_RTSK *pk_rtsk)
{
StaticTask_t *pxTCB;
TaskStatus_t xTaskStatus;
TaskHandle_t pxTask = GetTaskHandle(tid, 1);
if (pxTask == NULL) {
return E_ID;
}
// タスク個別状態取得
vTaskGetTaskInfo(pxTask, &xTaskStatus, pdTRUE, eInvalid);
pk_rtsk->tskstat = (int)xTaskStatus.eCurrentState; /* task state */
pk_rtsk->tskpri = (int)xTaskStatus.uxCurrentPriority; /* current priority */
pk_rtsk->tskbpri = (int)xTaskStatus.uxBasePriority; /* base task priority */
// タスク制御ブロックポインタセット
pxTCB = (StaticTask_t *)xTaskStatus.xHandle; // ハンドルは tasks.c の tskTCB構造体ポインタと同じ。
pk_rtsk->stksz = (SIZE)((char*)pxTCB->pxDummy8 - (char*)pxTCB->pxDummy6); // pxEndOfStack - pxStack と同義
pk_rtsk->stksz = (pk_rtsk->stksz + ((SIZE)portBYTE_ALIGNMENT_MASK+1)) & ~((SIZE)portBYTE_ALIGNMENT_MASK); // マスク
#if 0
_PF(S_BLUE " pxDummy6=%08X\n" S_DEF, pxTCB->pxDummy6);
_PF(S_BLUE " pxDummy8=%08X\n" S_DEF, pxTCB->pxDummy8);
_PF(S_BLUE " SIZ=%d(%d)\n" S_DEF, (int)((char*)pxTCB->pxDummy8 - (char*)pxTCB->pxDummy6), pk_rtsk->stksz);
dly_tsk(100/MSEC);
#endif
return E_OK;
}
/*** End of file ***/
●ヘッダファイル
/*
* itron.h
*/
/*
* ITRON仕様共通規定のデータ型・定数・マクロ
*/
#ifndef _ITRON_H
#define _ITRON_H
#include "cmsis_os.h" /* FreeRTOSインクルードファイル */
#ifdef __cplusplus
extern "C" {
#endif
#define _uITRON_
/*
* ITRON仕様共通データ型
*/
#ifdef INT8_MAX
typedef int8_t B; /* 符号付き8ビット整数 */
#endif /* INT8_MAX */
#ifdef UINT8_MAX
typedef uint8_t UB; /* 符号無し8ビット整数 */
typedef uint8_t VB; /* 型が定まらない8ビットの値 */
#endif /* UINT8_MAX */
typedef int16_t H; /* 符号付き16ビット整数 */
typedef uint16_t UH; /* 符号無し16ビット整数 */
typedef uint16_t VH; /* 型が定まらない16ビットの値 */
typedef int32_t W; /* 符号付き32ビット整数 */
typedef uint32_t UW; /* 符号無し32ビット整数 */
typedef uint32_t VW; /* 型が定まらない32ビットの値 */
#ifdef INT64_MAX
typedef int64_t D; /* 符号付き64ビット整数 */
#endif /* INT64_MAX */
#ifdef UINT64_MAX
typedef uint64_t UD; /* 符号無し64ビット整数 */
typedef uint64_t VD; /* 型が定まらない64ビットの値 */
#endif /* UINT64_MAX */
typedef void *VP; /* 型が定まらないものへのポインタ */
typedef int INT; /* 自然なサイズの符号付き整数 */
typedef unsigned int UINT; /* 自然なサイズの符号無し整数 */
typedef int TMO; /* タイムアウト指定 */
typedef uint32_t SYSTIM; /* システム時刻 */
typedef INT BOOL;
typedef INT FN;
typedef INT ID;
typedef INT BOOL_ID;
typedef INT RDVNO;
typedef UINT RDVPTN;
typedef UINT ATR;
typedef UINT MODE;
typedef INT ER;
typedef INT PRI;
typedef ER ER_ID;
typedef UINT STAT;
typedef INT ER_UINT;
typedef UINT TEXPTN;
typedef UINT FLGPTN;
typedef UINT INHNO;
typedef UINT INTNO;
typedef VP VP_INT;
typedef void (*FP)();
typedef unsigned long SIZE;
typedef unsigned long ADDR;
typedef void TASK; /* */
/* tmout */
#define TMO_POL ((TickType_t)0) /* polling */
#define TMO_FEVR ((TickType_t)-1) /* wait forever */
/* tskid */
#define TSK_SELF 0 /* 自タスク指定 */
/*
* 一般定数
*/
#ifndef NULL
#define NULL 0 /* 無効ポインタ */
#endif /* NULL */
#ifndef true
#define true pdTRUE /* 真 */
#endif /* true */
#ifndef false
#define false pdFALSE /* 偽 */
#endif /* false */
#define E_OK 0 /* 正常終了 */
/*
* メインエラーコード
*/
#define E_SYS (-5) /* システムエラー */
#define E_NOSPT (-9) /* 未サポート機能 */
#define E_RSFN (-10) /* 予約機能コード */
#define E_RSATR (-11) /* 予約属性 */
#define E_PAR (-17) /* パラメータエラー */
#define E_ID (-18) /* 不正ID番号 */
#define E_CTX (-25) /* コンテキストエラー */
#define E_MACV (-26) /* メモリアクセス違反 */
#define E_OACV (-27) /* オブジェクトアクセス違反 */
#define E_ILUSE (-28) /* サービスコール不正使用 */
#define E_NOMEM (-33) /* メモリ不足 */
#define E_NOID (-34) /* ID番号不足 */
#define E_NORES (-35) /* 資源不足 */
#define E_OBJ (-41) /* オブジェクト状態エラー */
#define E_NOEXS (-42) /* オブジェクト未生成 */
#define E_QOVR (-43) /* キューイングオーバーフロー */
#define E_RLWAI (-49) /* 待ち状態の強制解除 */
#define E_TMOUT (-50) /* ポーリング失敗またはタイムアウト */
#define E_DLT (-51) /* 待ちオブジェクトの削除 */
#define E_CLS (-52) /* 待ちオブジェクトの状態変化 */
#define E_WBLK (-57) /* ノンブロッキング受付け */
#define E_BOVR (-58) /* バッファオーバーフロー */
/*
* ITRON仕様共通定数
*/
#ifndef TRUE
#define TRUE true /* 真 */
#endif
#ifndef FALSE
#define FALSE false /* 偽 */
#endif
/* tskpri */
#define TPRI_INI -1
#define TPRI_RUN -1
#define TPRI_SELF -1
#define TMIN_TPRI 0
#define TMAX_TPRI (configMAX_PRIORITIES-1)
ER dly_tsk(TMO tmout);
/*
* ITRON API変換 (FreeRTOS関数へ置き換え)
*/
#define rot_rdq(P) taskYIELD() // タスク回転
#if 0
#define dis_int() taskDISABLE_INTERRUPTS() // グローバル割込み禁止
#define ena_int() taskENABLE_INTERRUPTS() // グローバル割込み許可
#else // ↓こちらの方がネスト考慮されるらしい。
#define dis_int() taskENTER_CRITICAL() // クリティカルセクションに入る (非コンテキスト処理用)
#define ena_int() taskEXIT_CRITICAL() // クリティカルセクションから出る (非コンテキスト処理用)
#endif
typedef struct _t_ctsk {
ATR tskatr;
VP_INT exinf;
FP task;
PRI itskpri;
SIZE stksz;
VP stk;
const char *name;
} T_CTSK;
typedef struct _t_rtsk {
int tskstat;
int tskpri;
int tskbpri;
// ;
// ;
// ;
SIZE stksz;
} T_RTSK;
extern int vTaskNum;
TaskHandle_t GetTaskHandle(ID tid, int ref); /* タスクハンドル取得 (FreeRTOSとの整合性を取るための関数) */
#define TA_HLNG 0x0000
#define TA_ACT 0x0002
#define STK_UNIT sizeof(StackType_t)
#define TCB_SIZE sizeof(StaticTask_t)
// タスク状態をFreeRTOSステータスと紐づけ
#define TTS_RUN eRunning /* RUNNING */
#define TTS_RDY eReady /* READY */
#define TTS_WAI eBlocked /* WAITING */
#define TTS_SUS eSuspended /* SUSPENDED */
#define TTS_WAS eDeleted /* WAITING-SUSPENDED */
#define TTS_DMT eInvalid /* DORMANT */
ER_ID acre_tsk(const T_CTSK *pk_ctsk); /* タスク生成 */
ER del_tsk( ID tid ); /* タスク削除 */
ER act_tsk( ID tid ); /* タスク起動 */
ER can_act( ID tid ); /* タスク起動キャンセル */
ER sta_tsk( ID tid, int val ); /* タスク起動 */
ER ter_tsk( ID tid ); /* タスク強制終了 */
ER chg_pri( ID tid, int val ); /* タスク優先度変更 */
ER sus_tsk( ID tid ); /* タスク強制待ち状態へ */
ER rsm_tsk( ID tid ); /* タスク再開 */
//ER frsm_tsk( ID tid ); /* タスク再開(1回版) */
ER wup_tsk( ID tid ); /* タスク起床 */
ER can_wup( ID tid ); /* タスク起床キャンセル */
ER rel_wai( ID tid ); /* タスク待ち解除 */
ER ref_tsk(ID tid, T_RTSK *pk_rtsk); /* Refer Task Status */
#ifdef __cplusplus
}
#endif
#endif /* _ITRON_H */
以上