酔漢電子工作手帳

2008-05-02

DWM 2008/05付録基板

| 22:06 | DWM 2008/05付録基板 - 酔漢電子工作手帳 を含むブックマーク はてなブックマーク - DWM 2008/05付録基板 - 酔漢電子工作手帳

Cortex-M3は、幾分アーキテクチャがARM7と変わっています。TOPPERS/JSPを移植するとしたら何が変わるか考えてみました。

一番変わるのはIRQの処理じゃないかと思います。CORTEX-M3では割り込みに優先順位がつけられ、実行順位の管理をプロセッサが行います。

これは、Blackfinと同じ難しさか?と身構えましたが、幸いなことに割り込み前の実行順位をスタックに保存する方式でした。どうやらスタック上のその部分だけ書き換えれば、「タスクコンテキストからタスクコンテキストへと割り込み復帰」という曲芸を実装できそうです。

割り込みが発生したのがタスクか非タスクかのコンテキスト判断は、ARM7TDMI依存部と同じく、ネスティングカウンタで実装できそうです。

トラックバック - http://ecrafts.g.hatena.ne.jp/suikan/20080502

2007-03-29

1秒休止

| 23:54 | 1秒休止 - 酔漢電子工作手帳 を含むブックマーク はてなブックマーク - 1秒休止 - 酔漢電子工作手帳

昨日のプログラムは、1行印字して終了するだけでした。次に連続して何行も印字させるプログラムを作って見ましょう。

今回紹介するのは、終わりなく延々と印字するプログラムです。そのため、syslog()関数はwhile(1)ループの中にいれます。

syslog()関数では、現在何回目のループであるかも印字させることにします。C言語の標準ライブラリを使っている場合には、このような印字はprintf()で行います。TOPPERS/JSPsyslog()関数はprintf()関数と同じように使うこともできます。今回の例ではフォーマット文字列"%d"を使って10進数の印字を行います。

   syslog( "answer= %d", ans );

printfと異なるのは、勝手に改行される点です。したがって、一行にまとめたい出力は、syslog()関数一回で出力する様にしてください。

一行印字を連続して行うと、あっという間にものすごい数の行が出力されてしまいます。そこで、今回は出力後に1000mS休止させることにします。

    tslp_tsk( 1000 );

tslp_tsk()関数はuITRON4のAPIです。この関数関数を呼び出したタスクを休止状態にいれます。休止状態に入ったあと、引数で指定したミリ秒経過すると、タスクを実行可能状態に戻します。

これらを使って1秒ごとにメッセージを出力するプログラムにまとめたのが以下のコードです。ファイル名はsample1.c。

#include <t_services.h>
#include "kernel_id.h"
#include "sample1.h"

void main_task(VP_INT exinf)
{
    int i;
	
    i = 0;
    while(1){
        syslog(LOG_NOTICE, "Hello, ADuC7026! %d", i );
        i++;
        tslp_tsk( 1000 );
    }
}

sample1.hとsample1.cfgは昨日のものを使いまわします。

このタスクが印字する周期はおよそ1秒であることに気をつけてください。tslp_tsk()は引数で指示した時間休止することを保証しますが、それよりわずかに長くなるのが普通です。これは、TOPPERS/JSPの内部時間がティック・タイマーと言われるタイマーを元に計測されるためです。ティックタイマーの生成する割り込み(ティック)が時間計測の分解能になり、その分解能より高い精度は望めません。

また、仮に正確に1000mS休止して復帰しても、syslog()などの実行時間が余計にかかるため、やはり全体の周期はやや長くなります。

トラックバック - http://ecrafts.g.hatena.ne.jp/suikan/20070329

2007-03-28

最小プログラム

| 23:11 | 最小プログラム - 酔漢電子工作手帳 を含むブックマーク はてなブックマーク - 最小プログラム - 酔漢電子工作手帳

TOPPERS/JSPの最小プログラム*1です。起動するとタスクを一つだけアクティブにし、メッセージをシリアル・ポートに出力してタスクを終了します。メッセージの出力には、ロギング機能を使っています。

プログラムのビルドやダウンロードに関しては組込みプログラミングにEclipseを使う(2)を参照してください。

手順としては、

  1. sample1を構築
  2. それを改造

という流れになります。

プログラム本体

筋から言えばここはコンフィグレーション・ファイルから説明するべきかもしれません。しかしそれでは色気も何もないので、一番楽しそうなメイン・プログラムから。ファイルはsample1.cです。

#include <t_services.h>
#include "kernel_id.h"
#include "sample1.h"

void main_task(VP_INT exinf)
{
    syslog(LOG_NOTICE, "Hello, ADuC7026!" );
}

三つあるインクルード文はそれぞれ

  • TOPPERS/JSPの標準ヘッダ・ファイル
  • コンフィグレータが出力したヘッダ・ファイル
  • ユーザーが用意したヘッダファイル

を、読み込みます。一番基本的な構成です。

関数main_task()はその名の通りメイン・タスクとして実行されます。実行するとsyslog()関数を呼び出して終了します。

syslog()関数TOPPERS/JSPの持つロギング関数で、どのような媒体に出力するかは処理系依存です。しかし標準的な実装ではシリアルポートへログデータを記録します。PizzaFactory3.1 Expressに同梱されているADuC7026用のTOPPERS/JSPも、シリアルポートにしゅつりょくします。

9600bps,8bit,non-parityに設定した端末ソフトをつないでプログラムを実行すると、システム起動メッセージに続いて"Hello, ADuC7026!"と端末に表示されます。

ヘッダ・ファイル

ファイルはsample1.hです。このファイルは通常はアプリケーション自身で使うほか、コンフィグレーション・ファイルからも読み込まれます。今回のアプリケーションは規模が小さいため、コンフィグレータが使う資源のみを宣言しています。

コンフィグレータは関数プロトタイプを理解できません。コンフィグレータは_MACRO_ONLYマクロを事前に宣言しますので、プロトタイプは_MACRO_ONLYマクロが宣言されていないときにのみ有効とすることで、問題を回避しています。

タスク優先度とスタックサイズはいずれもコンフィグレーションの中で使います。この二つはプログラム中で使うことはないと思われますので、コンフィグレーション・ファイルに直接値を書きこんでもかまいません。しかし、タスクの構成に関するパラメータは、関数宣言などとともにヘッダ・ファイルにまとめたほうが、全体の見通しはすっきりするでしょう。

#include <t_services.h>

/* メインタスクのパラメタ */
#define MAIN_PRIORITY     5    /* 優先度 */
#define MAIN_STACK_SIZE 512    /* スタックサイズ */

#ifndef _MACRO_ONLY
extern void main_task(VP_INT exinf);

#endif /* _MACRO_ONLY */

コンフィグレーション・ファイル

ファイル名はsample1.cfgです。

コンフィグレーション・ファイルはコンフィグレータに対して静的にセマフォタスクといった資源を作り出すよう指示するためのファイルです。コンフィグレータが作り出した資源は、実行中にアプリケーションプログラムから利用することができます。

TOPPERS/JSPタスクセマフォアプリケーションの実行中に作り出す手段を持っていないため、コンフィグレーション・ファイルがこれらを作り出す唯一の手段になります。

コンフィグレーションファイルには二つのインクルード文が存在します。

#includeは、コンフィグレータの実行中にファイルを読み込み、コンフィグレータに利用させるためのものです。INCLUDEはコンフィグレータの出力ファイル、つまりkernel_id.hとkernel_cfg.hの中に#include文を埋め込むためのものです。両者は実際にファイルが読み込まれる時期が異なりますので注意してください。

CRE_TSKは、タスクを静的に作るためのAPIです。詳しくはuITRON4の仕様書を参照してください。今回の例ではMAIN_TASKという識別子がコンフィグレータによって生成されます。この識別子は、アプリケーション・プルグラム中で「この」タスクを示すIDとしていつでも利用できます。

コンフィグレーション・ファイルの最後にある3つのインクルード文は、ティック・タイマーとロギング機能を実装するためのタスクセマフォ割り込みハンドラを宣言するためのものです。

ティック・タイマOSの必須機能ですから、必ず宣言しなければなりません。

ロギング機能は便利ですので、特に理由がない限り使うほうがいいでしょう。

インクルード文のパスはシステムによって異なります。PizzaFactory3 Expressを使った場合にはプラグイン内部へのパスがここに現れます。したがって、以下のコンフィグレーション・ファイルをそのままPizzaFactoryで使おうとしてもうまく行きません。PizzaFactoryがsample1用に生成したパスをそのまま使ってください。

#define _MACRO_ONLY
#include "sample1.h"

INCLUDE("\"sample1.h\"");
CRE_TSK(
    MAIN_TASK, { 
        TA_HLNG|TA_ACT,     /* 高級言語記述、最初からアクティブ */
        0,                  /* extinfに渡す値 */
        main_task,          /* タスクとして使う関数 */
        MAIN_PRIORITY,      /* タスク実行順位 */
        MAIN_STACK_SIZE,    /* タスク・スタックの大きさ[Byte] */
        NULL                /* スタックのアドレス。NULLなら自動割付 */
    });

#include "/your/path/systask/timer.cfg"
#include "/your/path/systask/serial.cfg"
#include "/your/path/systask/logtask.cfg"

実行結果

ADuC7026TOPPERS/JSPアプリケーションを動かすには、プログラムを内蔵フラッシュROMに書き込みます。そしてターゲットのシリアルポートに9600bps,8bit,non-parityのターミナルを接続し、ターゲットをリセットします。

正しくビルド、書き込みができていれば以下のような出力がターミナル上に現れます。

f:id:suikan:20070328235712p:image

*1:の、ちょっと先

トラックバック - http://ecrafts.g.hatena.ne.jp/suikan/20070328

2007-03-02

デバッグできた

| 03:21 | デバッグできた - 酔漢電子工作手帳 を含むブックマーク はてなブックマーク - デバッグできた - 酔漢電子工作手帳

TOPPERS/JSPROMに焼き、TINY JTAG経由でデバッグできるようになりました。ステップ実行、ブレーク・ポイントともOKです。

  • ハードウェアブレークポイントは2つまで設定できる
  • .gdbinitは、先日指摘されたとおり間に空白を挟むパスからは読み込めないかもしれない。もうすこし検証してみる。
  • .gdbinit内部でmonitor arm7_9 force_hw_bkpts enableを宣言している。
  • mi_cmd_disassemble: Invalid filenameは、自宅のwindows 2000マシンからは出ない。やっぱり出てる。目が節穴だっただけ。
  • OpenOCDのcfgファイル中で、target variantは、arm7tdmi-s_r4と指定している。

ステップ実行するたびに

Previous frame identical to this frame (corrupt stack?)

と表示されますが、スタックが壊れている様子はありません。アプリケーションは無改造sample1

monamour555monamour5552007/03/03 09:33おそらく,フレームポインタが明後日の値になっています.> corrupt stack?
GDBの問題です.

suikansuikan2007/03/03 17:10gdbの問題ですか。昨晩googleで探し回ってソースのあたりなども眺めてみたのですが、イマイチわかりませんでした。FPがおかしい割にはデバッギは快調です。

monamour555monamour5552007/03/06 10:58「.gdbinitは、先日指摘されたとおり間に空白を挟むパスからは読み込めないかもしれない」
デグレードしていました.(以前は,空白を挟むパスからも読めていました)
http://d.hatena.ne.jp/monamour555/20070306#1173145115
昨日の修正で直っているはずです.

corrupt stackの件は,舌足らずでした.改めて日記に書こうかと思います.とりあえず,ターゲットに流し込んだコードに問題があるわけではなく,デバッガが誤動作するような類の深刻な問題ではない(当面,無視して構わない)です.

suikansuikan2007/03/07 03:07ダウンロード抑制機能ともども、確認しました。ありがとうございます!

トラックバック - http://ecrafts.g.hatena.ne.jp/suikan/20070302

2007-02-24

小さなプログラム

| 09:23 | 小さなプログラム - 酔漢電子工作手帳 を含むブックマーク はてなブックマーク - 小さなプログラム - 酔漢電子工作手帳

PizzaFactoryが生成してくれるプログラムを基に、もっと小さなTOPPERS/JSPプログラムを作ってみました。

プロジェクトのコピー

最初にプロジェクトをコピーします。プロジェクト・ビューの中で基となるプロジェクトを右クリックし、copyを実行します。次に同じビューの中でpasteを実行します。プロジェクト名を聞いてきますので適当に付け直してください。デフォルトではプロジェクト名がフォルダ名になります。私はarm_sample02にしました。

コピーが終わるとビルドが始まります。ビルド時のパラメータは、すべてもとのプロジェクトのものをそのまま引き継いでいますので、ビルドは問題なく通るはずです。ただし、生成ファイルの名前まで基のプロジェクトと同じなので、これは変更します。具体的にはプロジェクトのpropertiesを開き、C/C++ Build ⇒ Build Steps ⇒ Build output ⇒ Artifact nameを、変更します。私はプロジェクト名と同じくarm_sample02にしました。

コンフィグレーション・ファイル

コンフィグレーション・ファイルはタスクセマフォといったTOPPERS/JSPの資源を、プログラム実行前に静的に用意しておくためのものです。TOPPERS/JSPはコンフィグレーターを使ってこのファイルから、タスクセマフォなどの基本的な資源をあらかじめ生成します。

_MACRO_ONLYは、「プロトタイプ宣言を解釈できない」という意味の宣言です。

/* sample1.cfg */
#define _MACRO_ONLY
#include "sample1.h"

INCLUDE("\"sample1.h\"");
CRE_TSK(
	MAIN_TASK, { 	/* タスクを作る */
		TA_HLNG|TA_ACT,   /* 高級言語で記述。最初からアクティブ */
		0,                /* パラメータは渡さない*/
		task,             /* タスク実体の関数はtask() */
		PRIORITY,         /* 初期優先順位 */
		STACK_SIZE,       /* スタックサイズ(byte) */
		NULL              /* スタックはシステムが割り当てる */
		}
	);

    /* 必須機能 */
#include "C:/PROGRA~1/MONAMI~1/PIZZAF~1.1/plugins/JP3C0C~1.200/jsp//systask/timer.cfg"
    /* 定番補助タスク */
#include "C:/PROGRA~1/MONAMI~1/PIZZAF~1.1/plugins/JP3C0C~1.200/jsp//systask/serial.cfg"
#include "C:/PROGRA~1/MONAMI~1/PIZZAF~1.1/plugins/JP3C0C~1.200/jsp//systask/logtask.cfg"

ヘッダ・ファイル

カーニハン&リッチーの"Programming Language C"に掲載されていた"Hello, World"プログラムは、独自のヘッダ・ファイルを持ちませんでした。TOPPERS/JSPアプリケーションでは必ず持ちます。なぜならば、コンフィグレータが吐き出したヘッダ・ファイルを必ず読まなければならないからです*1。また、コンフィグレータに読ませるためのヘッダ・ファイルも必要になります。

以下のヘッダ・ファイルは、コンフィグレータに読ませるためのものです。アプリケーションでも読み込みます。スタック・サイズ、タスク優先順位、タスクの実体となる関数プロトタイプ宣言を有しています。スタック・サイズやタスクの優先順位はコンフィグレーション・ファイルに直接定数を書いてもかまいません。しかし、関数の宣言だけはヘッダ・ファイルで行います。

/* sample1.h */
#include <t_services.h>

#define PRIORITY        5       /* タスク優先順位 */
#define STACK_SIZE    256       /* タスクスタック */
#ifndef _MACRO_ONLY
	extern void	task(VP_INT tskno);
#endif

プログラム

実行プログラムの本体です。t_services.hは、TOPPERS/JSPの資源を宣言するヘッダ・ファイルです。kernel_id.hは、コンフィグレータが生成するファイルです。

printf()関数は使えませんので、代わりにsyslog()関数を使います。syslog()関数シリアル・ポートに文字列を出力します。

PizzaFactoryが自動生成するADuC7026用のコンフィグレーションでは、シリアル・ポートは9600bpsで通信を行います。

/* sample1.c */
#include <t_services.h>
#include "kernel_id.h"
#include "sample1.h"

void task(VP_INT exinf)
{
	syslog(LOG_NOTICE, "Hello, Aduc7026" );
}

インテルhexへの変換

| 09:23 | インテルhexへの変換 - 酔漢電子工作手帳 を含むブックマーク はてなブックマーク - インテルhexへの変換 - 酔漢電子工作手帳

PizzaFactoryを使うとTOPPERS/JSPのこけら落しプログラム、"sample1"を数クリックで簡単構築できます。

ただ、生成されるのはELFフォーマットの.outプログラムです。ADuC7026に焼きこむためのARMWSDプログラムは、インテルhexフォーマットしか受け付けません。そこでビルド後にインテルhexフォーマットに変換するようプロジェクトを設定します。

プロジェクトのpropertiesを開き、C/C++ Build ⇒ Build Stepsを選びます。Post-build steps:のcommand:として次の命令をタイプしてください。

arm-elf-objcopy -O ihex -S ${BuildArtifactFileBaseName}.out ${BuildArtifactFileBaseName}.hex

${BuildArtifactFileBaseName}はPizzaFactoryが作るマクロです。詳細はhelpをどうぞ。この設定を行っておけば、ビルドの後に.hexファイルがインテルhex形式で生成されます。

*1:まあ読まなくていいときもあるけど、いつも読むようにしたほうが楽