後輩たちのためのEV3rt講座⑧(タスク・周期ハンドラ)

#はじめに…#
皆さん、パソコン使ったことありますよね？
あれっていくつもの作業を同時並行でやりますよね。
でも、コンピュータは基本的に一つの計算しか出来ない訳で、同時並行で行うなんて本来不可能です。
では、どうしてそんなことが出来るのか？
それは、「高速でやる作業を切り替えて、あたかも同時並行でやっているかのように見せかけているだけ」ということなんです。図解するとこんな感じ。
![IMG_0340.JPG](https://qiita-image-store.s3.amazonaws.com/0/183170/970e7b38-bf4f-6ea0-fb24-0924e551d425.jpeg)
このように分けられた仕事は「タスク」と呼ばれ、同時並行処理の事を「タスク分岐」と呼ぶこともあります。
さて、それでは本題ですが、今までの例ではEV3は一つのタスクしか行っていませんでしたが、EV3もタスク分岐を行うことができます。今回はその方法を教えていきたいと思います。

#タスクの宣言の仕方#
タスクを作りかたは関数を作るときと少し似ています。しかし、今回はapp.cだけでなく、app.hとapp.cfgも書き足しが必要です。順に見ていきましょう。

##①app.cfg##
まずはこのファイルに書き出すのですが、既に何か書かれていると思います。特にmain_taskと書かれているところですが、その名の通りメインタスクに関して宣言されています。これと同じように宣言するのですが、以下のような書式があります。

`CRE_TSK(タスクID,{タスク属性,タスクに渡す引数,タスクの関数名,優先度,スタックサイズ,スタックの先頭番地})`

###タスクID###
タスクの名前と考えてもらって構いません。なんでもいいですが、同じ名前でタスクIDは大文字、タスクの関数名は小文字にして使い分けるのが定番です。

###タスク属性###
タスクの初期状態を設定します。起きた状態にしておきたければTA_ACT、寝かした状態にして起きたければTA_NULLに設定します。起きるとか寝ているとか、よくわからない場合はとりあえずTA_ACTにしておいてください。

###タスクに渡す引数###
関数同様、タスクに一つだけ引数を渡すことができます。必要なければ0としておけばよいでしょう。（私は使ったことがありません。）

###タスクの関数名###
こっちが本当の名前です。app.cに追加する関数名となります。先述の通り同じ名前でタスクIDは大文字、タスクの関数名は小文字にすればよいでしょう。

###優先度###
タスクはいくつも作成することができるのですが（作れる数に制限あり）、複数タスクがあったときに、どのタスクを優先して実行するかを指定する必要があります。
メインタスクは8なので、8以上の数字で順番に入力してください。

###スタックサイズ###
タスク用に確保するメモリの領域を指定します。main_taskは4096確保されていますが、正直そんなに必要ないでしょう。1024でいいと思います。

###スタックの先頭番地###
あらゆるサンプルでもNULLと書かれていますので、特に指定する必要はないのでしょう。よって、NULLとだけ書いておいてください。

(例)

```app.cfg
INCLUDE("app_common.cfg");

#include "app.h"

DOMAIN(TDOM_APP) {
CRE_TSK(MAIN_TASK, { TA_ACT, 0, main_task, TMIN_APP_TPRI + 1, STACK_SIZE, NULL });
CRE_TSK(SUB_TASK, { TA_NULL, 0, sub_task, TMIN_APP_TPRI + 2, STACK_SIZE, NULL });//追加文
}

ATT_MOD("app.o");
```

##②app.h##
続いてapp.hファイルです。タスクを作るときは関数の時と同様に、本文の前に一文宣言が必要です。app.hはapp.cのインクルードファイルなので、app.cに書いても良いのですが、メインタスクのがapp.hに書かれているので、その流れで他のタスクもapp.hに書きましょう。（ヘッダーファイルの意味がわからない人は今の一文流してもらって構いません。）

(例)

```app.h
/*
 *  TOPPERS/ASP Kernel
 *      Toyohashi Open Platform for Embedded Real-Time Systems/
 *      Advanced Standard Profile Kernel
 *
 *  Copyright (C) 2000-2003 by Embedded and Real-Time Systems Laboratory
 *                              Toyohashi Univ. of Technology, JAPAN
 *  Copyright (C) 2004-2010 by Embedded and Real-Time Systems Laboratory
 *              Graduate School of Information Science, Nagoya Univ., JAPAN
 *
 *  上記著作権者は，以下の(1)〜(4)の条件を満たす場合に限り，本ソフトウェ
 *  ア（本ソフトウェアを改変したものを含む．以下同じ）を使用・複製・改
 *  変・再配布（以下，利用と呼ぶ）することを無償で許諾する．
 *  (1) 本ソフトウェアをソースコードの形で利用する場合には，上記の著作
 *      権表示，この利用条件および下記の無保証規定が，そのままの形でソー
 *      スコード中に含まれていること．
 *  (2) 本ソフトウェアを，ライブラリ形式など，他のソフトウェア開発に使
 *      用できる形で再配布する場合には，再配布に伴うドキュメント（利用
 *      者マニュアルなど）に，上記の著作権表示，この利用条件および下記
 *      の無保証規定を掲載すること．
 *  (3) 本ソフトウェアを，機器に組み込むなど，他のソフトウェア開発に使
 *      用できない形で再配布する場合には，次のいずれかの条件を満たすこ
 *      と．
 *    (a) 再配布に伴うドキュメント（利用者マニュアルなど）に，上記の著
 *        作権表示，この利用条件および下記の無保証規定を掲載すること．
 *    (b) 再配布の形態を，別に定める方法によって，TOPPERSプロジェクトに
 *        報告すること．
 *  (4) 本ソフトウェアの利用により直接的または間接的に生じるいかなる損
 *      害からも，上記著作権者およびTOPPERSプロジェクトを免責すること．
 *      また，本ソフトウェアのユーザまたはエンドユーザからのいかなる理
 *      由に基づく請求からも，上記著作権者およびTOPPERSプロジェクトを
 *      免責すること．
 * 
 *  本ソフトウェアは，無保証で提供されているものである．上記著作権者お
 *  よびTOPPERSプロジェクトは，本ソフトウェアに関して，特定の使用目的
 *  に対する適合性も含めて，いかなる保証も行わない．また，本ソフトウェ
 *  アの利用により直接的または間接的に生じたいかなる損害に関しても，そ
 *  の責任を負わない．
 *
 *  $Id: sample1.h 2416 2012-09-07 08:06:20Z ertl-hiro $
 */

/*
 *  ターゲット依存の定義
 */
#include "target_test.h"

/*
 *  各タスクの優先度の定義
 */

#define MAIN_PRIORITY	5		/* メインタスクの優先度 */
								/* HIGH_PRIORITYより高くすること */

#define HIGH_PRIORITY	9		/* 並行実行されるタスクの優先度 */
#define MID_PRIORITY	10
#define LOW_PRIORITY	11

/*
 *  ターゲットに依存する可能性のある定数の定義
 */

#ifndef STACK_SIZE
#define	STACK_SIZE		4096		/* タスクのスタックサイズ */
#endif /* STACK_SIZE */

#ifndef LOOP_REF
#define LOOP_REF		ULONG_C(1000000)	/* 速度計測用のループ回数 */
#endif /* LOOP_REF */

/*
 *  関数のプロトタイプ宣言
 */
#ifndef TOPPERS_MACRO_ONLY

extern void main_task(intptr_t exinf);
extern void sub_task(intptr_t exinf);//追加文
//extern void	tex_routine(TEXPTN texptn, intptr_t exinf);
//#ifdef CPUEXC1
//extern void	cpuexc_handler(void *p_excinf);
//#endif /* CPUEXC1 */
//extern void	cyclic_handler(intptr_t exinf);
//extern void	alarm_handler(intptr_t exinf);
//
//extern void	gpio_handler_initialize(intptr_t exinf);
//extern void	gpio_handler(void);//
//extern void	uart_sensor_monitor(intptr_t exinf);
//
//extern void	ev3_uart_cyclic_handler(intptr_t exinf);
//extern void	ev3_uart_daemon(intptr_t exinf);
//extern void	ev3_uart_port2_irq(void);
//
//extern void initialize_ev3(intptr_t exinf);
#endif /* TOPPERS_MACRO_ONLY */

```

##③app.c##
さて、ここから本文ですが、既にmain_taskが{}でくくられていると思います。新しいタスク用の関数もこの形式を模して書いてください。その際、main_taskの外（基本的にはすぐ下）に書かないといけません。関数なので当たり前ですが…

(例)

```app.c
#include "ev3api.h" //絶対必要
#include "app.h"//絶対必要
#include <stdio.h>
#include <stdbool.h>

#if defined(BUILD_MODULE)
#include "module_cfg.h"
#else
#include "kernel_cfg.h"
#endif

#define DEBUG

#ifdef DEBUG
#define _debug(x) (x)
#else
#define _debug(x)
#endif

void main_task(intptr_t unused)
{
    //処理 
　　act_tsk(SUB_TASK);

    tslp_tsk(1000);
}

//追加文
void sub_task(intptr_t unused)
{
    //処理
}
```

tslp_tsk(1000)の間、main_taskは眠った状態になります。
そしてこのとき、優先度の低いsub_taskが初めて実行されるのです。

#タスクの弱点#
このタスク処理ですが、一つ弱点があります。先述の通りタスクには優先度があり、優先度の高い方のタスクが眠っていないと低い方は実行されません。
しかし、本来の目的である同時並行を行うにはマイクロ秒単位での切り替えが必要で、これは人力では不可能です。(main_taskにおいて、一定周期で、しかも人間が数えれるような単位ではないごくごく短い時間で、sleepを入れてやる必要があるということです。)
![IMG_0340-2.png](https://qiita-image-store.s3.amazonaws.com/0/183170/7aeac363-24dd-5e2d-6268-bbabdf34ced6.png)
while文などの繰り返し処理を使えば実行できたりもしますが、ロボットを動かすのに常に繰り返!
し処理をするわけにはいきません。

なんとかタスクの切り替えを自動でできないか？ということです。
(この説明がわからない人は、近くの詳しい人に聞いてみましょう。このタスクの内容をネット記事だけで説明するのは少し無理があります…)

答えは、**「周期ハンドラ」**です。
指定した秒数単位で**自動**でタスクを実行します。
![IMG_0339.JPG](https://qiita-image-store.s3.amazonaws.com/0/183170/3937f8b5-3e8a-58a1-71ea-1d8ac3edeb6a.jpeg)
ただ、プログラムの動かし方が他と違うため、少し慣れが必要です。まずは宣言の仕方から説明していきます。

#周期ハンドラの宣言の仕方#

##①app.cfg##

タスクの時と同様にapp.cfgに書き加えることがあります。まずはその書式から。

`EV3_CRE_CYC(周期ハンドラの名前,{周期ハンドラの状態,周期ハンドラに渡す引数,周期ハンドラの名前(関数),起動周期,起動位相});`

###周期ハンドラの名前###
その名の通り、周期ハンドラの名前です。ただ今回も、1つ目のは大文字、2つめのは小文字にするのがおすすめです。つまり、こっちは大文字。

###周期ハンドラの状態###
アプリケーションが起動したときに周期ハンドラが寝てるか、起きてるかを指定します。
寝た状態ならTA_NULL、起きた状態ならTA_STAと書きます。
寝た状態にした場合、main_taskでev3_sta_cycを呼び出さない限り、動きません。

###周期ハンドラに渡す引数###
タスクの時と同様に、一つだけ引数を設定できます。使ったこと無いですが…

###周期ハンドラの名前(関数)###
こっちは後々app.cに追加する関数の名前となります。先述の通り、こっちは小文字。

###起動周期###
このタスクが実行される周期をマイクロ秒(1/1000s)単位で設定します。
例えば、5と設定すると、5mms秒、すなわち0.005秒ごとに関数の内容が実行されます。

ちなみに、EV3のクロック周波数は4mmsのため、4mms以下に設定しても意味がないので4mms以上にしておきましょう。(クロック周波数とは、ざっくり言うとコンピューターが処理を何秒ごとに行うかという数値のこと。)

###起動位相###
アプリケーション起動後、周期ハンドラを実行するまでのウェイトを設定できるようです。
おそらく起動すぐの初期設定部分などでの干渉をさけるためでしょう。
僕の場合はそもそもTA_NULLの状態で実行していたので、これはあまり関係ありませんでした。
なにもこだわりがなければ0としておきましょう。

(例)

```app.cfg
INCLUDE("app_common.cfg");

#include "app.h"

DOMAIN(TDOM_APP) {
CRE_TSK(MAIN_TASK, { TA_ACT, 0, main_task, TMIN_APP_TPRI + 1, STACK_SIZE, NULL });
EV3_CRE_CYC(SUB_CYCHDR,{TA_NULL,0,sub_cychdr,5,0});//追加文
}

ATT_MOD("app.o");
```

##②app.h##
これに関してはタスクの時と非常に似ているので説明は省略。
要は先程app.cfgで決めた関数名をここに書き込むわけです。

```app.h
/*
 *  TOPPERS/ASP Kernel
 *      Toyohashi Open Platform for Embedded Real-Time Systems/
 *      Advanced Standard Profile Kernel
 *
 *  Copyright (C) 2000-2003 by Embedded and Real-Time Systems Laboratory
 *                              Toyohashi Univ. of Technology, JAPAN
 *  Copyright (C) 2004-2010 by Embedded and Real-Time Systems Laboratory
 *              Graduate School of Information Science, Nagoya Univ., JAPAN
 *
 *  上記著作権者は，以下の(1)〜(4)の条件を満たす場合に限り，本ソフトウェ
 *  ア（本ソフトウェアを改変したものを含む．以下同じ）を使用・複製・改
 *  変・再配布（以下，利用と呼ぶ）することを無償で許諾する．
 *  (1) 本ソフトウェアをソースコードの形で利用する場合には，上記の著作
 *      権表示，この利用条件および下記の無保証規定が，そのままの形でソー
 *      スコード中に含まれていること．
 *  (2) 本ソフトウェアを，ライブラリ形式など，他のソフトウェア開発に使
 *      用できる形で再配布する場合には，再配布に伴うドキュメント（利用
 *      者マニュアルなど）に，上記の著作権表示，この利用条件および下記
 *      の無保証規定を掲載すること．
 *  (3) 本ソフトウェアを，機器に組み込むなど，他のソフトウェア開発に使
 *      用できない形で再配布する場合には，次のいずれかの条件を満たすこ
 *      と．
 *    (a) 再配布に伴うドキュメント（利用者マニュアルなど）に，上記の著
 *        作権表示，この利用条件および下記の無保証規定を掲載すること．
 *    (b) 再配布の形態を，別に定める方法によって，TOPPERSプロジェクトに
 *        報告すること．
 *  (4) 本ソフトウェアの利用により直接的または間接的に生じるいかなる損
 *      害からも，上記著作権者およびTOPPERSプロジェクトを免責すること．
 *      また，本ソフトウェアのユーザまたはエンドユーザからのいかなる理
 *      由に基づく請求からも，上記著作権者およびTOPPERSプロジェクトを
 *      免責すること．
 * 
 *  本ソフトウェアは，無保証で提供されているものである．上記著作権者お
 *  よびTOPPERSプロジェクトは，本ソフトウェアに関して，特定の使用目的
 *  に対する適合性も含めて，いかなる保証も行わない．また，本ソフトウェ
 *  アの利用により直接的または間接的に生じたいかなる損害に関しても，そ
 *  の責任を負わない．
 *
 *  $Id: sample1.h 2416 2012-09-07 08:06:20Z ertl-hiro $
 */

/*
 *  ターゲット依存の定義
 */
#include "target_test.h"

/*
 *  各タスクの優先度の定義
 */

#define MAIN_PRIORITY	5		/* メインタスクの優先度 */
								/* HIGH_PRIORITYより高くすること */

#define HIGH_PRIORITY	9		/* 並行実行されるタスクの優先度 */
#define MID_PRIORITY	10
#define LOW_PRIORITY	11

/*
 *  ターゲットに依存する可能性のある定数の定義
 */

#ifndef STACK_SIZE
#define	STACK_SIZE		4096		/* タスクのスタックサイズ */
#endif /* STACK_SIZE */

#ifndef LOOP_REF
#define LOOP_REF		ULONG_C(1000000)	/* 速度計測用のループ回数 */
#endif /* LOOP_REF */

/*
 *  関数のプロトタイプ宣言
 */
#ifndef TOPPERS_MACRO_ONLY

extern void	main_task(intptr_t exinf);
extern void	sub_cychdr(intptr_t exinf);//追加文

//extern void	tex_routine(TEXPTN texptn, intptr_t exinf);
//#ifdef CPUEXC1
//extern void	cpuexc_handler(void *p_excinf);
//#endif /* CPUEXC1 */
//extern void	cyclic_handler(intptr_t exinf);
//extern void	alarm_handler(intptr_t exinf);
//
//extern void	gpio_handler_initialize(intptr_t exinf);
//extern void	gpio_handler(void);//
//extern void	uart_sensor_monitor(intptr_t exinf);
//
//extern void	ev3_uart_cyclic_handler(intptr_t exinf);
//extern void	ev3_uart_daemon(intptr_t exinf);
//extern void	ev3_uart_port2_irq(void);
//
//extern void initialize_ev3(intptr_t exinf);
#endif /* TOPPERS_MACRO_ONLY */
```

##③app.c##
あとは、実際に関数の内容を書くだけです。

```app.c
#include "ev3api.h" //絶対必要
#include "app.h"//絶対必要
#include <stdio.h>
#include <stdbool.h>

#if defined(BUILD_MODULE)
#include "module_cfg.h"
#else
#include "kernel_cfg.h"
#endif

#define DEBUG

#ifdef DEBUG
#define _debug(x) (x)
#else
#define _debug(x)
#endif

void main_task(intptr_t unused)
{
    //処理
    ev3_sta_cyc(SUB_CYCHDR);//追加文
    //このあとから周期ハンドラが実行される。
    ev3_stp_cyc(SUB_CYCHDR);//追加文
}

//追加文
void test_cychdr(intptr_t unused)
{
    //処理
}
```

また、周期ハンドラをONにするときは ev3_sta_cyc(SUB_CYCHDR); を、
OFFにするときは ev3_stp_cyc(SUB_CYCHDR); を実行します。

#これで同時処理ができる!!#
と、思ったでしょう。
まあ出来るんですが、周期ハンドラの使い方には少し注意が必要です。
タスクの場合は優先度の高い方のタスクがsleepしていない限り、割り込んででも実行されるので、仮にsub_task内で繰り返し処理(for文やwhile文)が実行されていても、その処理は一度中断された上で、元のmain_taskに戻ります。
一方、周期ハンドラには優先度が無く、一度実行に移された場合、周期ハンドラの関数の処理が全て終わらない限りmain_taskには戻りません。つまり、周期ハンドラ内にfor文やwhile文などを書いてしまうと、main_taskに戻ってこず、影響を与える可能性があるのです。
周期ハンドラを作る上で注意するポイントは2つ。

- 短い処理内容にする
- 繰り返し実行されることを頭に入れておく

「センサー値を読み込む」などがいい例です。ロボットを動かしながら値を取得したいときなどは、周期ハンドラで一定間隔で測ったら、うまくとれそうですよね。
また、「モーターを角度〇〇°まで回す」といった命令でも、「〇〇°まで周りきるまで待つ」のではなく、「〇〇°以下ならON/〇〇°以上ならOFF」という処理にするのです。

```app.c
void test_cychdr(intptr_t unused)
{
    //いい例
    if(ev3_motor_get_counts(EV3_PORT_A)<100){
        ev3_motor_set_power(EV3_PORT_A,50);
    }
    else{
        ev3_motor_stop(EV3_PORT_A,true);
    }

    //悪い例
    while(ev3_motor_get_counts(EV3_PORT_A)<100){
        ev3_motor_set_power(EV3_PORT_A,50);
    }
    ev3_motor_stop(EV3_PORT_A,true);
}
```

これ、一見失敗しそうに見えますよね。なぜなら、この関数単体なら一回しかif文が実行されないからです。しかし、これは周期ハンドラ、一定周期で処理が行われる。すなわち、周期ハンドラをOFF(ev3_stp_cyc)にしない限り、無限にif文は実行されるのです。

#最後に#
今回はLCDに数値を書き込むサンプルを記しておきます。
LCDに表示される数値のうち、上段はmain_task、中段はnew_task、下段はtest_cychdrによる処理です。

- main_taskは1sごと(sleep1000mmsを入れているから)
- new_taskは限りなく早く
- test_cychdrは5mmsごと(周期ハンドラの起動周期)

実行タイミングにより数字の上がり方に差がでるのでわかりやすくなっています。
プログラムをいじってみて、自分で確かめてみましょう。

```app.c
#include "ev3api.h" //絶対必要
#include "app.h"//絶対必要
#include <stdio.h>
#include <stdbool.h>

#if defined(BUILD_MODULE)
#include "module_cfg.h"
#else
#include "kernel_cfg.h"
#endif

#define DEBUG

#ifdef DEBUG
#define _debug(x) (x)
#else
#define _debug(x)
#endif

int a=0;
int b=0;

void main_task(intptr_t unused)
{
    ev3_lcd_set_font ( EV3_FONT_MEDIUM );
    int i=0;
    char str[20];
    act_tsk(NEW_TASK);
    ev3_sta_cyc(TEST_CYCHDR);
    while(1){
        sprintf(str,"%d",i);
        ev3_lcd_draw_string(str,0,0);
        i++;
        tslp_tsk(1000);
    }
    ev3_stp_cyc(TEST_CYCHDR);
}

void new_task(intptr_t unused)
{
    while(1){//while文を消すとどうなる？
        char str2[20];
        sprintf(str2,"%d",a);
        ev3_lcd_draw_string(str2,0,50);
        a++;
    }
}

void test_cychdr(intptr_t unused)
{
    /*ここのコメント解除するとどうなる？
    while(1){
        
    }
    */
   
    char str3[20];
    sprintf(str3,"%d",b);
    ev3_lcd_draw_string(str3,0,100);
    b++;
}

```

```app.h
/*
 *  TOPPERS/ASP Kernel
 *      Toyohashi Open Platform for Embedded Real-Time Systems/
 *      Advanced Standard Profile Kernel
 *
 *  Copyright (C) 2000-2003 by Embedded and Real-Time Systems Laboratory
 *                              Toyohashi Univ. of Technology, JAPAN
 *  Copyright (C) 2004-2010 by Embedded and Real-Time Systems Laboratory
 *              Graduate School of Information Science, Nagoya Univ., JAPAN
 *
 *  上記著作権者は，以下の(1)〜(4)の条件を満たす場合に限り，本ソフトウェ
 *  ア（本ソフトウェアを改変したものを含む．以下同じ）を使用・複製・改
 *  変・再配布（以下，利用と呼ぶ）することを無償で許諾する．
 *  (1) 本ソフトウェアをソースコードの形で利用する場合には，上記の著作
 *      権表示，この利用条件および下記の無保証規定が，そのままの形でソー
 *      スコード中に含まれていること．
 *  (2) 本ソフトウェアを，ライブラリ形式など，他のソフトウェア開発に使
 *      用できる形で再配布する場合には，再配布に伴うドキュメント（利用
 *      者マニュアルなど）に，上記の著作権表示，この利用条件および下記
 *      の無保証規定を掲載すること．
 *  (3) 本ソフトウェアを，機器に組み込むなど，他のソフトウェア開発に使
 *      用できない形で再配布する場合には，次のいずれかの条件を満たすこ
 *      と．
 *    (a) 再配布に伴うドキュメント（利用者マニュアルなど）に，上記の著
 *        作権表示，この利用条件および下記の無保証規定を掲載すること．
 *    (b) 再配布の形態を，別に定める方法によって，TOPPERSプロジェクトに
 *        報告すること．
 *  (4) 本ソフトウェアの利用により直接的または間接的に生じるいかなる損
 *      害からも，上記著作権者およびTOPPERSプロジェクトを免責すること．
 *      また，本ソフトウェアのユーザまたはエンドユーザからのいかなる理
 *      由に基づく請求からも，上記著作権者およびTOPPERSプロジェクトを
 *      免責すること．
 * 
 *  本ソフトウェアは，無保証で提供されているものである．上記著作権者お
 *  よびTOPPERSプロジェクトは，本ソフトウェアに関して，特定の使用目的
 *  に対する適合性も含めて，いかなる保証も行わない．また，本ソフトウェ
 *  アの利用により直接的または間接的に生じたいかなる損害に関しても，そ
 *  の責任を負わない．
 *
 *  $Id: sample1.h 2416 2012-09-07 08:06:20Z ertl-hiro $

/*
 *  ターゲット依存の定義
 */
#include "target_test.h"

/*
 *  各タスクの優先度の定義
 */

#define MAIN_PRIORITY	5		/* メインタスクの優先度 */
								/* HIGH_PRIORITYより高くすること */

#define HIGH_PRIORITY	9		/* 並行実行されるタスクの優先度 */
#define MID_PRIORITY	10
#define LOW_PRIORITY	11

/*
 *  ターゲットに依存する可能性のある定数の定義
 */

#ifndef STACK_SIZE
#define	STACK_SIZE		4096		/* タスクのスタックサイズ */
#endif /* STACK_SIZE */

#ifndef LOOP_REF
#define LOOP_REF		ULONG_C(1000000)	/* 速度計測用のループ回数 */
#endif /* LOOP_REF */

/*
 *  関数のプロトタイプ宣言
 */
#ifndef TOPPERS_MACRO_ONLY

extern void	main_task(intptr_t exinf);
extern void	new_task(intptr_t exinf);
extern void	test_cychdr(intptr_t exinf);

//extern void	tex_routine(TEXPTN texptn, intptr_t exinf);
//#ifdef CPUEXC1
//extern void	cpuexc_handler(void *p_excinf);
//#endif /* CPUEXC1 */
//extern void	cyclic_handler(intptr_t exinf);
//extern void	alarm_handler(intptr_t exinf);
//
//extern void	gpio_handler_initialize(intptr_t exinf);
//extern void	gpio_handler(void);//
//extern void	uart_sensor_monitor(intptr_t exinf);
//
//extern void	ev3_uart_cyclic_handler(intptr_t exinf);
//extern void	ev3_uart_daemon(intptr_t exinf);
//extern void	ev3_uart_port2_irq(void);
//
//extern void initialize_ev3(intptr_t exinf);
#endif /* TOPPERS_MACRO_ONLY */
```

```app.cfg
INCLUDE("app_common.cfg");

#include "app.h"

DOMAIN(TDOM_APP) {
CRE_TSK(MAIN_TASK, { TA_ACT, 0, main_task, TMIN_APP_TPRI + 1, STACK_SIZE, NULL });
CRE_TSK(NEW_TASK, { TA_ACT, 0, new_task, TMIN_APP_TPRI + 2, STACK_SIZE, NULL });
EV3_CRE_CYC(TEST_CYCHDR,{TA_NULL,0,test_cychdr,5,0});
}

ATT_MOD("app.o");
```
このタスク・周期ハンドラについて、より詳しい、初心者でもわかるような解説ページを作ろうかと考えております。
そして、次回はいよいよBluetoothについてです。
まずは環境ｋからですね。

次回:[第9回](https://qiita.com/koushiro/items/578a1d65721b982c55c6)
前回:[第7回](https://qiita.com/koushiro/items/3d7911b4d6a9477904e1)