FreeRTOSにiTRONタスク管理APIっぽいの入れてみた

FreeRTOSにiTRONタスク管理APIっぽいの入れてみた。

開発環境：[STM32CubeMX](https://www.stmcu.jp/design/sw_dev/pc_soft/52798/?gclid=Cj0KCQjwy8f6BRC7ARIsAPIXOjiEMJXXNcO-SRjZIrzRUmoZsjKEpgbRIVFZTzUk3IeyVl1m79gLbMIaAreDEALw_wcB "STM32CubeMX")&emsp;(Ver5.6.1)
コンパイラは、[EWARM](https://www.ss-technologies.co.jp/products/iar/ewarm/index.html "EWARM")、[MDK-ARM](https://www.arm.com/ja/products/development-tools/embedded-and-software/keil-mdk "MDK-ARM") などお好きな環境で

##FreeRTOSの初期状態からの設定変更
タスク状態やスタック領域管理のため、CubeMXにて下記FreeRTOS設定を変更した。

CubeMX画面上の、
&emsp;タブ&emsp;&emsp;&emsp;「Pinout＆Configuration」
&emsp;画面左帯&emsp;「Categories」→「Middleware」→「FREERTOS」
を選択。（Modeは「CMSIS_V1」を選択。※V2未評価のため）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569481/e9a6a94b-7a47-7e23-c34c-ffa75ffb0654.png)


「Configuration」→「Config parameters」を選択。
&emsp;&emsp;RECORD_STACK_HIGH_ADDRESS&emsp;⇒&emsp;Enabled&emsp;&emsp;&emsp;&emsp;&emsp;※スタックサイズを求めるため。
&emsp;&emsp;Memory Allocation&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;⇒&emsp;Dynamic/Static&emsp;&emsp;※静的関数(...Static)を有効に。
&emsp;&emsp;GENERATE_RUN_TIME_STATS&emsp;&emsp;&emsp;⇒&emsp;Enabled&emsp;&emsp;&emsp;&emsp;&emsp;※タスク状態確認。
&emsp;&emsp;USE_TRACE_FACILITY&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;⇒&emsp;Enabled&emsp;&emsp;&emsp;&emsp;&emsp;※タスクトレース有効化。
&emsp;&emsp;USE_STATS_FORMATTING_FUNCTIONS&emsp;⇒&emsp;Enabled&emsp;&emsp;※タスクトレース有効化。

「Configuration」→「Include parameters」を選択。
&emsp;&emsp;uxTaskGetStackHighWaterMark&emsp;⇒&emsp;Enabled&emsp;&emsp;※タスクの残りスタック量を取得するため。
<BR>

##ソースコード

【注意事項】
&emsp;・タスク管理のため、メモリ操作やソートをしまくっている無理くりソースコードです。
&emsp;・下記ソースコード中の「表示関数の別名定義」箇所（文字出力、メモリ操作関数）は実装済みの関数名に置き換えます。
&emsp;※本記事中のソフトウェアはサンプルコードであり、ソフトウェアを使用した結果いかなる損害等が発生しても当方は一切責任を負いません。
<br>

```ruby:itron.c
/****************************************************************************
 *	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 ***/
```
<br>
●ヘッダファイル

```ruby:itron.h
/*
 *  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 */
```
<br>
以上
<br>