RTOS: kernel hello Runtime Object View&Execution Analysis

 https://dev.ti.com/tirex/explore/node?node=AGt.afnd4skKRp7LlzeZgQ__J4.hfJy__LATEST

中ごろよりやや後ろのGetting startedからを実施する。ここに書いてある通りに実施する。


Recourse Explore からテンプレートをインポートしてくるが、今回は、TI-RTOS Kernelのhelloを選択する。
インポートされたら黙ってhello.cをビルトして送り込む。


Consoleの部分に、hello worldと出ているはず。
int main()
{
    /* Call driver init functions */
    Board_init();

    System_printf("hello world\n");

    BIOS_exit(0);  /* terminates program and dumps SysMin output */
    return(0);
}
メインの部分はSystem_printfというコマンドからコンソールに、文字列を表示しているだけ。コメントを日本語に訳してあげると、”通常のBIOSプログラムでは、BIOS_start()を呼んで割り込みを有効にし、スケジューラを起動し、BIOSを起動させます。 しかし、このプログラムは単純なサニティテストであり、代わりにBIOS_exit()を呼び出します。”ってな事が書いてある。

これを編集していく。
Project Exploerからhello.syscfgをダブルクリックするか、Open with - Sysconfig editor を選択して開く。GPIOを選択して、+ADDで二つのLEDを
CONFIG_GPIO_LED_0とCONFIG_GPIO_LED_1という名前で追加する。それぞれは、Use Hardwareのプルダウンに現れる中から二つを選ぶ。
CONFIG_GPIO_LED_0  LaunchPad LED D1
CONFIG_GPIO_LED_1 LaunchPad LED D2
をE401Yを使っているので選択した。


と編集するコード全体があるのでコピペする。
これまで見慣れたptheadとは異なる方法でスレッドを生成していて、
   /* Set up the led task */
    Task_Params workTaskParams;
    Task_Params_init(&workTaskParams);
    workTaskParams.stackSize = STACKSIZE;
    workTaskParams.priority = 2;
    workTaskParams.stack = &workTaskStack;

    Task_construct(&workTask, workTaskFunc, &workTaskParams, NULL);

    /* Start kernel. */
    BIOS_start();
となっており、プログラム上はこの時点で先々までのタスク(スレッド)を用意しているが使っているのはworkTaskFuncで、この中では、doWorkだけが呼び出されている。

これを実行すると、LEDが一つだけ点滅する。
doWorkの中で、FakeBlockingSlowWorkの前にBreakpointをセットする。
具体的には右クリックし、Breakpoint-Breakpointを選択 行数の左に青色の〇が出てくる。(消す時は、Toggle BreakpointでBreakpointが設定されていれば消えるし、設定されていなければ設定される)

デバッグ(虫のマーク)を押して、しばらくまって
デバッグモードで接続されたのを確認したら”F8”を押してBreakpointまで走らせる。



Breakpointのところに右向きの矢印が出てそこまで進み停止する。

ランタイム・オブジェクト・ビュー(ROV)は、開発者が組込みアプリケーションの状態を迅速に可視化するためのツール
https://software-dl.ti.com/ccs/esd/documents/rov_guide/html/src/rov.html
これを試す。

Tools → Runtime Object View


CONNECTを押す。


OPEN A DASHBOARDを選択するとoverviewが出てくるのでクリックする。
(出てこず、ファイルダイアログが開く場合は、overviewのファイルを選択する。拡張子をALLにすると見えるようになる)



workTaskFunc が現在実行中で、タスクのスタック使用量がこれまでのところ 1024 バイト (STACKSIZE) のうち 124 バイトでピークになっていることも確認できる
スタックオーバーフローのリスクはない。
デバイス、コンパイラ、バージョンによって、おそらく値は異なるが、一般的に、スタックサイズを大きくして開始し、すべてが適切に動作するようになったら、それを切り詰める。

この状態でもう一度、Resume(緑三角のマークを押すかF8を押す)と値が更新される。
前回から値が更新された所はバックグランドが黄色くなる。
終わる時はターミネート(赤い■)を押す。

次に実行状態グラフを見てみる。
hello.cfgというファイルがフォルダにあるので、これを編集する。


直接、テキストを編集してもよいが、GUIでの編集をできるだけここでは実施する。
hello.cfgを右クリックして、Open with でXGCONFを選択する。
BIOSの欄に、Welcome, System OverviewにならんでRuntimeがあるのでクリック。
中腹にEnable logsがあるのでこれにチェックを入れる。
hello.cfgをここでいったん閉じる。
(次の設定がGUIでできない・・どこでするか不明)

今度は、Open with でXDCScript Editorで開く。
何も考えず、hello.cfgファイルの末尾に、以下の行を追加します

var LoggingSetup = xdc.useModule('ti.uia.sysbios.LoggingSetup');
LoggingSetup.sysbiosLoggerSize = 1024;
LoggingSetup.loadLogging = false;


これにより、ログレコードが存在するターゲット上のバッファを維持するようにカーネルが設定されると書いてある。
これは、ドライバの例で使用されるデバッグTI-RTOSカーネルコンフィギュレーションプロジェクトにも含まれています。我々は、カーネルのログにしか興味がないので、CPU負荷のログを無効にするとも。
つまり、ログを残すためには、バッファを維持しておかないといけなくてその設定をしている。loadLoggingといってCPUの負荷もログが取れるが興味がないのでfalseを設定している事になる。

ここまでできるとまず先ほど、設定したブレイクポイントを削除する。
それから、ビルト(ハンマーのマーク)して、完了したらデバッグモードに入る。接続が完了したら、F8を押して10秒から15秒ぐらい走らせる。LEDがチカチカするので、10ぐらい点滅するまでそのままにして、緑の三角の横にある一時停止のマーク(Suspned)を押す。


Tool-RTOS Analyzer-Execution Analysisを選択する。

MPU名のとなりの+のマークを押して展開すると Task:workTaskFuncがずっと動作している事を示す赤いバーが横に伸びているグラフが表示される。Idleタスクは、プログラムの実行中、全く時間を得ていない事が確認できる。
これ何やっているか?というと、
タスクが行うのはdoWork関数を実行し、その後 "スリープ "に戻るだけで、スリープに入るらない。myDelay関数は、単にループでCPUサイクルを消費する関数で、これはRTOSの正しい時間をつぶす方法ではない。

スレッドで時間をつぶす最も簡単な方法の1つは、Task_sleep(numTicks)を使う事で、これはすでに解説済み。

そこで、ソースコードを改変する。
#include <ti/sysbios/knl/Clock.h>
を/* TI-RTOS Header files */に配置する。
workTaskFunc
の中のmyDelayをコメントアウトして、
Task_sleep(500 * (1000 / Clock_tickPeriod));
に置き換える。
ビルトしてからデバッグモードで走らせて、F8を押してから10秒ぐらい待ち、一時停止をしてからTool-RTOS Analyzer-Execution Analysisを選択してStartを押す。
最初は赤いラインしか見えないかもしれながいが、虫眼鏡のマイナスのボタンを適当に押して範囲を変えると以下のようになる。


今度は、Task:workTaskFunc以外に、idleに移行している事が確認できる。この方が低消費電力にできる。

次にさらに、hello.cを改変する。
Void workTaskFunc(UArg arg0, UArg arg1)
の内容をコピーして、もう一つ、urgentWorkTaskFuncというタスクを作る。

Void urgentWorkTaskFunc(UArg arg0, UArg arg1)
{
    while (1) {

        /* Do work */
        doUrgentWork();

        /* Wait a while, because doWork should be a periodic thing, not continuous.*/
        //myDelay(24000000);
        Task_sleep(50 * (1000 / Clock_tickPeriod));
    }
}

また、スタックサイズの変数ももう一つ
static uint8_t workTaskStack[STACKSIZE];
static uint8_t urgentWorkTaskStack[STACKSIZE];
と後の行を追加する。
メインの中でタスクをもう一つ生成する記述を追加する。

   /* Set up the Urgent task */
       workTaskParams.priority = 1;
       workTaskParams.stack = &urgentWorkTaskStack;

       Task_construct(&urgentWorkTask, urgentWorkTaskFunc, &workTaskParams, NULL);

この時Urgent taskのプライオリティを1に設定して、workTaskFuncは2に設定しておく。
これをビルトしてまずは何も考えずに、送り込んで動作を見る。
urgentWorkTaskの速いタイミングで点滅をするが、遅い方のworkTaskFuncタスクが割り込むと早い方は一旦処理をとめているのが確認できる。

Execution Analysisで確認してみる。

赤い方が、urgentWorkTaskで、早い周期で処理をしているが、青のworkTaskFuncが入ると処理を止めてしまって終わるまでタスクが入らない事がわかる。これは優先順位をworkTaskFuncの方が高くしているから。(priorityを大きな数字にしているから)

urgentWorkTaskのpriorityをworkTaskFuncより高い3に変更して実行すると以下になる。

urgentWorkTaskは優先されて実行されるので、青を途中で止めて赤に終わったらもとに戻ってまだ青をして・・・になる。


















コメント

このブログの人気の投稿

Attiny85とAQM0802A(LCD)のI2C接続

CH9329で日本語キーボード109で正しく表示する方法