So-net無料ブログ作成

GR-SAKURAではじめる超お手軽マルチタスク2 setjmpとlongjmpを捨てる刻 [RX&SH&H8]

前回のマルチタスクではC言語で標準サポートされているsetjmp、longjmpを使って
実現していました。

この方法の良いところは、setjmp、longjmpがC言語で標準でサポートされている事か
ら、どの様なマイコンでも容易に利用できる点です。
しかし逆アセンブルの結果や、そもそも目的が異なる点から不満が無い訳でもありま
せんでした。

最大の問題はやはり無駄が多い点で、この無駄がコードサイズやRAMサイズ、実効速度
の足を引っ張る事となります。

まあそうなれば自分でコードを書くしかない訳で、コンテキストを入れ替える処理と
タスクをスタートさせる処理のみアセンブラで記述してみました。
アセンブラと言う事で難しい印象はありますが、極めて限定的な処理のみ記述してい
ます。

以下はタスクをスタートさせる処理です。
/************************************************
  コンテキストをロードし、タスクを開始
  void sta_ctx(
    void *exe )  //実行コンテキストの保存先
************************************************/
  .text
  .align 2
_sta_ctx:
  mov.l 0[r1],r0  /*スタックポインタ設定*/
  popm r6 - r14   /*r6-r14を復帰*/
  rts

リターン命令を入れても、3行でできます。

以下はコンテキストを切り替える処理です。
/************************************************
  実行コンテキストの切替
  void swi_ctx(
    void *pre,   //現在のコンテキストの保存先
    void *post)  //切り替えるコンテキスト
************************************************/
  .text
  .align 2
_swi_ctx:
  pushm r6 - r14  /*r6-r14を退避。r6が最も若いアドレスになる*/
  mov.l r0,0[r1]  /*スタックポインタ保存*/
  mov.l 0[r2],r0  /*スタックポインタ復帰*/
  popm r6 - r14   /*r6-r14を復帰*/
  rts

リターン命令を入れても、5行でできます。

この2つをC言語から呼び出しています。つまりスタックやレジスタの操作のみ
アセンブラで記述し、それ以外は全てC言語で記述できます。

setjmp、longjmpを使った方法と異なり、今回の方法は汎用レジスタは
タスクスタックに保存しています。スタックポインタのみ大域変数領域に保存しま
す。
ちょうど、以下の図と同じ展開となります。
multitask_004.png


setjmpでは決められたアドレスに決められた手順で汎用レジスタが退避されますが、
今回の方法はスタックに退避される為、汎用レジスタの具体的な退避アドレスを予
め知る事は困難です。
その為スタックポインタのみ、アドレスが判明している領域に保存するのです。

言い換えれば宝物を何処かに埋めて、その地図を作成する様な物です。
宝物を掘り返す時は地図でその位置を見付けます。カリブの海賊みたいな話ですネ。

スタックに退避する長所は、複数レジスタを一発で退避、呼出しできる命令を使え
る点です。
「popm r6 - r14」とか「pushm r6 - r14」がそれで、R6~R14までの汎用レジスタを、
スタックポインタが示す位置から1命令で退避や呼出しします。
その他、PCも自動的にスタックに退避され、また、スタックから読み出しを受けるの
で、特殊な領域を用意してそこに保存するより、スタックに保存する方が全体的に
自然な感じです。

またR6~R14のレジスタのみ退避する理由は、おそらくこのコンパイラでは関数の
呼出しで値の保障が必要とされるレジスタがR0、R6~R14だと思われます。
他のレジスタの値は関数の前後で保障する必要は無い筈です。

必要なレジスタのみ退避する事で、処理時間の短縮や必要なメモリサイズの削減が
できます。

今回の改造ではコンテキストの切り替えの最適化以外に、起動時引数を最大4個まで
渡せる様にしました。決して綺麗な方法ではありませんが、この機能が有ると様々
な目的で使用する事ができます。
以下の様な関数でタスクを登録する時、この起動時引数を指定します。
ER reg_tsk( ID tid, void *tsk, void *stk, int stk_sz,
            VP_INT exinf1, VP_INT exinf2, VP_INT exinf3, VP_INT exinf4 );
※記述例
  reg_tsk( ID_LED1Task, (void *)LEDTask, tsk4_stk, sizeof(tsk4_stk),
            100,PIN_LED0,0,0 );


タスクID、タスクの起動アドレス、スタック領域アドレス、スタックサイズに
続いて引数を渡します。

タスクのを以下の様に記述できればスマートだったのですが、

void LEDTask( VP_INT exinf1, VP_INT exinf2, VP_INT exinf3, VP_INT exinf4 );

ちょっと上手い方法が思いつきませんでした。
以下の様な方法で起動時引数をタスクの中で得る事ができます。
  VP_INT exinf[2];

  get_par( exinf, 2 );  //起動時引数を取り込んでいる

  pinMode( exinf[1], OUTPUT );  //LED?。exinf[1]にはLED pin番号が入っている

VP_INT型の配列を宣言してget_par関数の引数とします。数字の2は引き取りたい引数の
数です。
pinModeで配列の一部のデータを引数に渡しています。このデータはPIN番号です。
※上記の記述例を参照

起動時引数利用の一例を挙げて置きます。
以下はLEDを点滅させるだけのタスクの記述例です。GR-SAKURAには4つの青LEDが有りま
すので、それぞれのLEDに付き1つのタスクで制御しています。実に贅沢ですね。
/****************************************************************************/
/* LEDタスク                                                                */
/****************************************************************************/
void LEDTask( void )
{
  bool OnOff = false;
  VP_INT exinf[2];

  get_par( exinf, 2 );  //起動時引数を取り込んでいる

  pinMode( exinf[1], OUTPUT );  //LED?。exinf[1]にはLED pin番号が入っている

  for( ;; )  //関数(タスク)を抜けても行く先が無いので、必ず無限ループにしておく
  {
    dly_tsk( exinf[0] );  //時間が経過するまで留まる。 exinf[0]には相対待ち時間が入っている
    if( OnOff == false )
    {
      OnOff = true;
      digitalWrite( exinf[1], HIGH );
    }
    else
    {
      OnOff = false;
      digitalWrite( exinf[1], LOW );
    }
  }
}

このコードだけで4つのタスクを実行しています。
LEDの点滅を行うだけですが、どのLEDを点滅させるのか?、どれ位の周期で点滅させるのか?
そう言ったパラメーターが存在します。

コードは1つしか無いので、4つのパターンを指定するのには引数等でそのパラメータを
渡す必要があります。起動時引数はこの様な目的で使用できます。


タスクスイッチのパフォーマンスを計ってみます。
setjmp、longjmp方式と比べてどれ程高速化しているでしょうか?。

シミュレーターで計測してみるとsetjmp、longjmp方式は37サイクル、新しい方式だと
24サイクルでした。頻繁に呼ばれる処理なので、なるべく少ないサイクル数で処理できれ
ばそれに越した事は無い訳です。市販のRTOSの場合、このタスクの切り替えに掛かる時間
が、商品の指標の一つになっています。

以下のリンク先からプロジェクトを入手できます。

https://dl.dropbox.com/u/60463387/grsakura/multitask2.zip

ITRONプログラミング入門―H8マイコンとHOSで始める組み込み開発

ITRONプログラミング入門―H8マイコンとHOSで始める組み込み開発

  • 作者: 濱原 和明
  • 出版社/メーカー: オーム社
  • 発売日: 2005/04/25
  • メディア: 単行本






クラウドを利用したVoice Controlled Carの製作 GR-SAKURAとスマートアナログでやるよ [RX&SH&H8]

voice_ctrl_car_03.png


voice_ctrl_car_04.png
CQ出版のInterface誌2012年11月号に掲載されているAndroidアプリケーションの
Wi-Fi Text Playを利用して音声でコントロールする車の製作です。
この工作、Wi-Fi Text PlayとXBee Wi-Fiを用意すればできたも同然で、相変わらず
他力本願ですいません。



この作品は、複数の要素技術を組み合わせて作成しています。使った要素技術は以下
の通りです。

1.クラウドを使って複雑な処理を簡単に

2.アナログセンシング

3.マルチタスクシステムを使って柔軟なシステムの構成

※1でWi-Fi Text Playを利用します。Wi-Fi Text PlayはGoogle Playから入手で
きます。Wi-Fi Text Playの詳細はInterface誌2012年11月号を読んで下さい。

1.クラウドを使って複雑な処理を簡単に
Wi-Fi Text Playに喋った言葉がクラウドサーバーで処理されて文字列(ローマ字及び数字)
としてAndroid端末に届き、Android端末からこのリモコンカーに無線LANを通して届く
物です。

リモコンカーの頭脳であるGR-SAKURA上で届いた文字列の解釈を行い、その内容によっ
て車体を転回させたり、前進、後退、停止をすると言ったごく単純な作品です。

まあ難しい事はWi-Fi Text Playとクラウドサーバーがみんな処理してくれますから、
GR-SAKURA側は比較的簡単な処理で済みます。
Wi-Fi Text PlayとXBee Wi-Fiさえあれば、誰でも音声リモコンが即!作れます。

システムの構成は以下の様になります。
voice_ctrl_car_01.png


Android端末に喋った音声はGoogleのサーバーに送られ、日本語に変換されて返ってきます。

変換された日本語は今度はYahooのサーバーに送られ、ローマ字と数字に変換されて来ます。

本来はXBee Wi-Fiに直接接続された音声合成ICに音声データパターンを送る為のアプリケーション
なので、ローマ字や数字以外に若干の制御コードが入って来ますが、単に文字列の比較を
行うだけなのでここからの文字列処理はとても簡単です。

Android端末とVoice Controled Car間は無線LANで通信します。図では直接赤線を引いて
いますが、実際は直接この間で通信はできないので、必ずアクセスポイントか、
無線LANルーターを経由する事となります。

2.アナログセンシング
クラウドサーバーへ2往復もしている関係上、音声を入力しても実際にGR-SAKURAに届くまで
2~3秒程度掛かっています。人が車両の状況を見ながら進行停止や回転停止の指示を行
っても、オーバーランしてしまったり、クルクルその場で回ってしまいます。
その為、移動距離や回転角度等はVoice Controled Car自身が判断する必要があります。

ただし移動距離に関してはスマートアナログシールドにはセンサーが搭載されていないの
で今のところ計り様がありません。適当な時間でやっています。
回転角度は前回の書込みで電子分度器を作りましたので、これを実装してみます。

http://hamayan.blog.so-net.ne.jp/2012-12-24-1

3.マルチタスクシステムを使って柔軟なシステムの構成

Voice Controled Carの構成を以下に示します。
voice_ctrl_car_02.png


(1)SAシールド(スマートアナログシールド)はGYROを搭載しているので、角度の検出に
利用します。

(2)XBee Wi-FiはAndroid端末から無線LANで音声制御を受ける為に用意します。

(3)BluetoothモジュールはPCと接続し、現在の状況をモニターしたり、PC側からの直接
制御を受けます。

(4)Voice Controled Carは履帯で進行したり方向転換を行いますので、2つのモーター
を制御する必要があります。

(5)電源にはモバイルバッテリーパックを利用しました。

主なソフトウエアの構成は以下の様になっています。
3つのタスクを立て、それぞれが独立に動いています。それそれのタスク間で依存性が低い
為、例えばどれかのタスクが何かの待ち状態になったとしても、それ以外のタスクの動作は
影響を受けず、今まで通り動きます。
voice_ctrl_car_05.png


それぞれのタスクの役割は、

(1)SAシールドからGYROの出力を受け取っているタスクは、一定周期毎(10ms)のA/D変換
結果を受け取り、アンプのゲインやGYROの係数を元に各サンプリング毎の出力値を時
間積分しています。
この値は大域変数に保存され、他のタスクから利用されます。

(2)Android端末から無線LAN経由で送られて来る制御文字列を受けるタスクは、アルファ
ベットと数字で表記された文字列を解釈し、数字であれば車体を転回させ、前進、後退、
停止の文字列であればそれに応じた動作を行います。

但し、必ずしも期待された文字列ばかりが送られて来る訳では無く、文字にブレが生
じていますので、簡単なフィルター処理を行っています。

車体の転回は、(1)のタスクが更新を行っている角度の積分値と、上位から受け取った
角度の値を比較し、その値に一致するまで車体を回転させ続けます。

前進や後退の場合は、10秒間モーターをその方向に動かし続けます。
停止の場合は、現在前進または後退中であればその動作を止めさせます。

(3)BluetoothでPCと通信しているタスクは、車体の動作モニター(設定角度と積分値の表示)
や、PC側からのコマンドに拠る車体の制御を行っています。
この処理自体はデバック以外の状況で必ずしも必要とされない処理であり、Bluetoothで
回線が開かれるまでは有意な動作を何もしません。

実際に組み立ててみて判った事

(1)回転の中心とGYROの実装位置の問題なのか、左回転と右回転で補正値に差が有り、時間と
ゲイン、GYROの出力の計算でスパッと角度を算出できなかった。
バッテリーの重量が大きいので設置位置で車体の回転する中心がずれてしまう。

その為、実際に動かしてBluetoothでモニターしながら補正値のトリミングを行っている。

しかし必ずしもうまい方法とは言えず、今は90度の回転で精度が最大となるような値とな
っている。

(2)モーターを動かすと車体が回転していないにも係わらずGYROの出力が乗ってしまう。
原因は振動の可能性も有るし、電気的なノイズの可能性も有る、、、気がする。
モーター側とはシールド処理が必要になるのかもしれない。

以下のリンク先にサンプルのプロジェクトを置いて置きます。

https://dl.dropbox.com/u/60463387/grsakura/voice_controled_car.zip

ITRONプログラミング入門―H8マイコンとHOSで始める組み込み開発

ITRONプログラミング入門―H8マイコンとHOSで始める組み込み開発

  • 作者: 濱原 和明
  • 出版社/メーカー: オーム社
  • 発売日: 2005/04/25
  • メディア: 単行本



GYROとGR-SAKURAとスマートアナログで電子分度器の製作 [RX&SH&H8]

gyro_05.png
β版のスマートアナログシールドには2軸のGYROセンサーが搭載されており、これを使って
分度器を作ってみよう!と言うのが今回の企画です。


1.スマートアナログICの利用

まずジャイロ(ENC03)の出力は結構小さいです。最大検出範囲が±300deg/secであり、
感度が0.67mV/deg/secである事から、出力の最大振幅は201mVpとなります。
gyro_01.png


一方RXマイコンのADCの入力範囲はVrefで決まってしまいますが、電源電圧をVrefとした
場合は3.3Vのダイナミックレンジ。つまり最大3.3V入力できるところに402mVしか
入れないのは実に勿体無い。
※402mV/3.3Vで約1/8では、12bitの分解能が有っても実質9bit相当で使う事になる。

そこでスマートアナログICのコンフィギュレーションアンプの設定でこれを最適化します。

まずはSA Designerで回路構成を見てみます。ブロック図から直接設定を行う事で、反転、
非反転、差動、計装アンプの組み合わせを行う事ができ、またゲインやオフセットを
調整できます。
gyro_02.png
例えばゲイン設定はマウスオーバーすれば番号とゲイン(dBまたは帰還抵抗の値)の対応
が表示されますので、その中から適切な値を選択します。
図では4を選択していますが、非反転アンプの場合は16dBとなります。

オフセットはDACの出力を設定します。表示の1.9Vは実は5V基準で計算した値で、この辺
は直して欲しいのですが、3.3Vなら1.25V辺りとなります。
なおDACの上限、下限の基準電圧は4つのパターンから選ぶ事ができ、DAC自体は8bitの分
解能ですが、上限、下限の設定で調整範囲をもっと狭くすればより精度を上げる事ができ
ます。


スマートアナログIC(SA500)にはこれ以外にフィルター回路やレギュレーター、汎用アンプ
等がICの中に組み込まれていますが、今回は初段のアンプのみ使用しています。

2つのジャイロセンサーの出力をそれぞれ初段のAMP1、AMP2に入力し、ゲインとオフセッ
トを設定して可能な限りADCのダイナミックレンジを有効に使うようにスマートアナログ
ICを設定します。

スマートアナログICを使った設計の支援としてWEB上で実行できるシミュレーターをルネサス
は用意していますが、この位の回路なら手元でやってしまうのも手ですね。
NI Multisim アナデバ版で動作確認してみました。
gyro_03.png


まあ大体良いんじゃないでしょうか。もう少しオフセットを大きくすれば動作点が下がり
ます。振幅が最大、最小を超えなければ、後はソフトウエアで対応可能ですので。

2.角度を計算する為には
ジャイロセンサーは軸を中心とした回転運動の角速度を出力する物らしい。なので角速度
を時間で積分すれば角度になる筈。ADCで定周期にサンプリングを行い、それを足し算して
行けば良いのか!。
※実際には先の0.67mV/deg/secの係数とか、ゲインとかから実際の加速度を求める

ENC03についてぐぐる先生に聞いてみました。他力本願ですいません。温度ドリフトが結
構有るみたいで、その分の補正が必要みたいです。そうしないとドリフト分を全部積分し
て謎の角度を出してしまいます。

このドリフトをキャンセルする方法として良さそうなのが最小二乗法でした。最小二乗法
を使って温度ドリフトの予測値を算出し、それで補正を掛けます。

もう少し詳しく書くと、静止状態の時に予測する為のデータを収集し、動作中にこのデータ
を基にした予測値で補正を入れます。

こうして定周期でサンプリングしたADCの値からオフセット分をキャンセルした値を加算
して行けば、変化した角度を算出する事ができます。

3.GR-SAKURAのADCの使い方
タイマーと12bit ADCを連動させて定周期でアナログデータの変換を掛け、変換完了後の
ADCの割込みでバッファに取り込んでいます。

定周期を発生するタイマーは8bitタイマーをカスケードに使用して16bitのタイマーとして
構成しています。このサンプルでは10ms周期でやっています。

12bit ADC自体も、機能として最大4回のサンプリングを加算してレジスタに保存する事が
できる加算機能を使ってサンプリングの精度を良くしています。まあ使ってみたかった
だけですが。

変換結果はADCの割込みでハンドラーを起動し、その中でリングバッファに保存しています。
その為、WEBコンパイラのgr_common下に有るintvect.cの内容を修正する必要があります。
※通常のanalogReadは割込みを使用していない。

※ちょっと訳有で本来はAN001とAN002に入る入力ですが、AMP1の出力をAN004、AMP2の出力
をAN005に繋ぎ直しています。

4.実際に角度を計ってみよう
gyro_05.png
写真の様に基板を置きます。スマートアナログシールド上のGYROセンサーには、基板を平
たく置いた時に検出する軸が無いので、この様な置き方になります。ちょっとやり難いです。

紙に30度刻みで直線を引き、その上に基板を置いて回転させてみて、モニター出力の角度を
見てみました。


gyro_04.png
この様にUSB(シリアル)に接続したPC上のTeratermで現在の角度とADC出力の積分値を見る事
ができます。
※この写真を撮影した後に表示はsen1だけにしてしまいました。

本当はGR-SAKURA上の青ボタンを押すと表示上の角度を0クリアするのですが、とてもじゃな
いけれどまともに触れないので、一定時間出力が安定すると自動的に0クリアしています。

※たまに発散する様な現象が見れるのだが、なんだろう?あれは。


以下のリンク先にサンプルのプロジェクトを置いて置きます。

https://dl.dropbox.com/u/60463387/grsakura/gyroProtractor.zip

XBeeで作るワイヤレスセンサーネットワーク (Make: PROJECTS)

XBeeで作るワイヤレスセンサーネットワーク (Make: PROJECTS)

  • 作者: Robert Faludi
  • 出版社/メーカー: オライリージャパン
  • 発売日: 2011/12/09
  • メディア: 大型本






楽器は弾けないし、音符も読めないけれど、GR-SAKURAでMIDIで演奏してみた [RX&SH&H8]

あっちが飛んでしまったので、こっちに書き直し。
midi_play_02.jpg


midi_play_01.jpg


MFT2012のソーシャルツリーに飾るネタとして、やっぱりツリーに飾るのだから音と
光を出したいなぁ!と思って急遽作成したものです。
BEEP音で音階を作ってやっています。つまりオルゴールです。

今までのやり方(あくまでも個人的なやり方)だとプログラムは、音を鳴らす部分と
演奏する部分、それにデータとして楽譜データを手でチマチマと入れていたのです
が、特に楽譜データの入力が手間が掛かってしまいました。
そこで従来からMIDIデータを利用できないものかと考えてはいたのですが、これを
機会にプログラムを作成してみたものです。

そもそもMIDIとは何?からスタートするのですが、インターネットっていいですね、
情報が沢山落ちていて。ネットに落ちている情報を拾いながらMIDIデータの解析プ
ログラムをなんとか構築できました。

もっとも基本的に音楽的な素養は一切無いのでMIDIには色々制御コードが出てくる
のですが、それが意味するところがさっぱり判りません。とりあえず音符データと
時間データを使って演奏をしていますが、本当のところはまともに動いているのか
どうかは不明です。

RXマイコンで使用したリソースは8bitタイマーを2個使っています。
8bitタイマーの出力端子はTMO0(IO2)とTMO3(IO6)となります。

2個の8bitタイマー出力を2個の圧電ブザーに接続し、同時発音可能な音の数を2と
しています。
まあ基本BEEP音なので音の強弱も付けられませんし、実に貧相な音ですが、しかし
複数の音が同時発生すると結構楽しいものです。
RXマイコンにはタイマーが沢山あるので、出力可能なタイマーの数だけ圧電ブザー
を用意し演奏するってのも楽しそうです。
接続なんて実に簡単です。タイマー出力端子と圧電ブザーを線で繋ぐだけですから。

MIDIデータはSDカードに保存しています。ROOTディレクトリ下に全部のMIDIファイル
(拡張子が.midのファイル)を入れておきます。
電源の起動後、見つけたMIDIファイルを片っ端から演奏を始めます。

同時にLEDも適当にチカチカさせています。どの端子をチカチカさせているのかは
ソースコードをごらん下さい。

MIDIデータの良いところは、
1.音のデータではなく演奏データなので、まっとうな音源を用意すればよい音
で演奏できる点です。将来的には音源データを搭載?、、、いやいや大変なので止
めておこう。

2.楽譜データもネットからいくらでも落とす事ができます。著作権を主張して
いるデータも有りますのでその扱いは慎重になる必要がありますが、個人的に利用
する場合は自由に使えるものは沢山あります。

3.データサイズの小ささもありますね。元々MFT2012で動かした時はSDカード
にMIDIデータを入れていたのではなく、MIDIファイルをそのままバイナリー化して
ROMに埋め込んで使っていました。しかしひとつのファイルサイズはせいぜい数kbyte
程度なので、10曲位入れてもROM領域を圧迫する程にはなりません。

RXマイコンではなく8bit位のリソースの小さなマイコンで扱ったとしても、ROM
に何曲かは入れ込む事ができるのではないでしょうか。


MIDIデータの困ったところは、
1.フォーマットが複数存在し、あらゆるデータに対応するとなると大変な事にな
りそう。その為今回のプログラムはSMF、フォーマット0のみの対応となります。

2.曲によっては演奏しても何の曲だかさっぱり判らなくなる。複数パートに別けて
演奏するようなデータの場合、音が細分化され過ぎて2つしか音源の無い今回の作品
ではまともに演奏にならない可能性があります。

3.演奏出来ない音階がある(笑)。まあこれはハードウエアの問題で、圧電ブザーな
ので、数百Hzから4KHz位までしかまともに音が出ません。


そんな訳でクリスマスのツリーの飾り付け用の電子工作ネタにはぴったりです。
クリスマスに間に合わなくてもお正月休みのネタとしてどうぞ!。

サンプルプロジェクトは以下のリンクから。
sdmmcライブラリを使用します。

https://dl.dropbox.com/u/60463387/grsakura/midi_play.zip




GR-SAKURAではじめる超お手軽マルチタスク setjmpとlongjmpの利用 [RX&SH&H8]

multitask_01.png
これはマルチタスクではあってもリアルタイムではありません!。
今から紹介するのは、全てのタスクが同一、、、と言うより優先度の制御が入っていない
マルチタスクシステムであり、μITRONの様にユーザーが意識しなくても優先度に応じて
カーネルが自動的にタスク切り替えを行いません。と言うかカーネルそのものが無いです。

上記欠点はありますが、setjmp、longjmpと言ったC言語でサポートされている関数ライブ
ラリを利用できる点や、タスクを起動する為の初期化手順が単純なため、GR-SAKURA上の
動作環境(WEBコンパイラで実現できる範囲内)でマルチタスクを実現できるメリットが有り
ます。
また、タスクを起動する為の手順が判ってしまえば別のマイコンへの応用も容易です。


1.今一度、割り込みの手順を考察(※RX63Nの汎用割り込みの場合)

(1)命令実行中に割り込み要求を受付

(2)一部の命令を除いて実行中の命令を完了後、割込み処理を開始

(3)PSW(ステータスレジスタ)をスタックに保存

(4)PC(プログラムカウンタ)をスタックに保存

(5)割り込みフラグ等のCPUステータスを変化させる

(6)割り込み処理ルーチンにジャンプ

(7)割り込み処理ルーチン内で必要なレジスタをスタックに退避する

※1~6まではハードウエアが実行し、7からは割り込み処理ルーチンの責任でソフトウ
エアで実行します。

割込時のスタックの状況を図にしてみると図の様になります。
multitask_002.png
事実上、スタック領域Aと、スタック領域Bは別の目的の為に使用されていて、相互に関係を
持ちません。これらのスタック領域は、物理的には別空間ではなく連続空間にあるけれど、
概念的には別空間にあると考える事ができます。



2.割込とマルチタスクの共通点

それぞれ使用目的別にスタック領域を使い分ける概念は、割り込みとマルチタスクで共通し
ています。言い換えれば割り込みの手続きと同じ事ができればスタック領域を使い分ける事が
でき、ひいてはマルチタスクシステムを実現できます。
※仮想メモリ空間を持たないマイコンの場合の話

実際のマルチタスクシステムのスタックの例です。
multitask_003.png
※マルチタスクシステムによっては割り込みスタック領域も別に用意する



マルチタスク?だがRXマイコンはCPUが一つしか無い

マルチコアでないマイコンでマルチタスクシステムを実現?現実的には一つのCPUコアは一つ
の命令しか実行できません。マルチタスクでそれぞれの処理が平行に動いている様に思えるけ
れど、実際はCPUで実行する処理を適宜切り替えてるだけで、同時に処理は行っていません。

一般的なマルチタスクシステムの処理の切り替え手順

タスク1からタスク2へ切り替え手順を解説。※ここでは関数コールで切り替えを実現している
為、PSWは保存しない。
multitask_004.png


(1)まずタスク1の中でタスク切り替えを行う専用の関数をコールする。

(2)その関数の中でPCや汎用レジスタの一切をタスク1専用のスタック領域に退避する。

(3)SP(スタックポインタ)をタスク2のSPに書き換えてしまう。この時、SPはタスク2のス
タック領域のアドレスを示している。

(4)タスク2のスタック領域に退避されている汎用レジスタを書き戻す。

(5)PCにタスク2で実行していたアドレスを代入する事でタスク2の実行が再開される。

※関数からのRETで実現※PCや汎用レジスタの退避やロードと言った処理は、割り込み
の出入り口処理と同じである事が判る。

3.setjmp、longjmpの利用

μITRON等のRTOSは、初期化手順がちょっと難易度が高い。判ってしまえば何等問題無いので
すが。また、タスクの切り替え処理は基本的にCPUの特権モードで行われるので、割り込みが自
由に使えないといけない。しかしC言語にはPCや汎用レジスタを指定した領域に退避したり、指
定した領域に退避されているレジスタを書き戻す関数が大昔から用意されています。それが
setjmp、longjmpで、これを利用すれば簡易的なマルチタスクシステムを実現できます。

※但し割り込みで切り替える訳ではないのでリアルタイム性の実現は困難

setjmpが退避する関数であり、longjmpが退避された値をレジスタに書き戻します。
以下はsetjmpの逆アセンブル結果で、片っ端から汎用レジスタをR1で示されるメモリに代入し
ている事が判ります。
multitask_005.png


うーん、イマイチ効率の良いコードではない!気がしますね。
以下は同じgccですが某マイコンアーキテクチャのsetjmp、longjmpです。命令でバレバレですが。
multitask_007.png
実際のところRXマイコンにも汎用レジスタをまとめて退避、ロードする命令は有りますし、
関数コールで実現しているので、必ずしも全てのレジスタを退避する必要は無い筈です。

まあ今回はおいて置きます。



汎用レジスタR1は退避先のアドレスであり、汎用レジスタR0はRXマイコンの場合はスタックポイ
ンタを示します。※赤枠
また、関数コールを行っている事からPCは既にスタックに退避されています。つまりタスク1の
スタックと、レジスタの退避領域は以下の様になっています。
multitask_006.png


以下はlongjmpの逆アセンブル結果で、片っ端から退避した値を汎用レジスタに書き戻している
事が判ります。
multitask_008.png
汎用レジスタR1は退避先のアドレスであり、赤枠に注目すると最初にスタックポインタを元に戻
しています。次に退避したPCの値をスタックポインタが示すアドレスに代入しています。これで
関数から返る時に実行先のPCの値が代入され、中断していた処理が再開される事となります。


multitask_009.png


ちなみに汎用レジスタを保存する領域、jmp_bufって何?と調べてみるとgccでは以下の様になっ
ています。
#ifdef __RX__
#define _JBLEN 0x44
#endif

#ifdef _JBLEN
#ifdef _JBTYPE
typedef	_JBTYPE jmp_buf[_JBLEN];
#else
typedef	int jmp_buf[_JBLEN];
#endif
#endif


なんて事無いint型の配列ですが、その配列サイズが問題ですね。0x44!本来なら汎用レジス
タとPCの分だけ領域が有ればいい筈ですが、、、0x44は10進で68ですから、17×4byte=68byte
と勘違?

setjmp、longjmpのコーディング例
#include 

  jmp_buf tsk_env;

  setjmp(tsk_env);
  longjmp( tsk_env, 1 );

と、こう書いてしまうとsetjmpとlongjmpの間で無限ループになってしまいます。そこで以下の
様に書く必要があります。
#include 

  jmp_buf tsk_env;

  setjmp(tsk_env);
  何らかの待ち状態を解除する条件式;
  longjmp( tsk_env, 1 );

例えばタイマー割り込みでカウントアップされる変数を条件にすると以下の様になります。
#include 

jmp_buf tsk_env;

void wai_systim_eq_100()
{
  setjmp(tsk_env);
  If( systim >= 100 ) return;
  longjmp( tsk_env, 1 );
}


実際のところマイコンの処理のほとんどは「何かを待つ」です。時間であったり、AD変換結
果であったり、シリアルからの受信であったり、送信バッファの空き待ちであったりと、とに
かく待ってばかりです。この何かを待つ間、CPUが無駄にループするのではなく、別の処理を
行ってしまえば、たとえ高速でないマイコンであっても効率良く仕事をさせる事ができますし、
複数の処理を平行して行いたい時、大概の場合シングルタスクでは上手く構成できない事が多
いです。
例えば先のsetjmp、longjmpのコーディング例では条件が一致するまでループを継続していま
したが、条件が一致しないなら別のタスクに処理を切り替えてしまえば良いわけです。
#include 

jmp_buf tsk1_env,tsk2_env;

void task1()
{
  for(;;)
  {
    //何かの処理
    dly_tsk_100();
  }
}

void dly_tsk_100()
{
  long long tim = systim + 100;
  setjmp(tsk1_env);  //今居る位置の状態を保存
  If( systim >= tim ) return;  //待ち状態を解除する式
  longjmp( tsk2_env, 1 );  //task2にジャンプする
}

void task2()
{
  for(;;)
  {
    //何かの処理
    dly_tsk_200();
  }
}

void dly_tsk_200()
{
  long long tim = systim + 200;
  setjmp(tsk2_env);  //今居る位置の状態を保存
  If( systim >= tim ) return;  //待ち状態を解除する式
  longjmp( tsk1_env, 1 );  //task1にジャンプする
}


4.如何にしてタスクを起動するか
setjmpは実行中、setjmpが呼ばれた時のコンテキストを保存する関数ですし、longjmp
は保存されたコンテキストを展開する関数です。
マルチタスクに適用する時問題になるのが、如何にしてタスクを起動するか?となります。
タスクを起動する前に初期化処理の中でsetjmpを呼んだとしても、その初期化処理の位置
を覚えてしまうに過ぎません。さて、、、

そこでjmp_bufの中身をもう一度思い出してみます。
setjmpの逆アセンブル結果から保存領域の先頭にスタックポインタ、最後にプログラムカ
ウンタ(PC)を保存していました。
つまり予め用意された保存領域の先頭と最後にタスク用として確保したスタック領域の
再下端とタスクの開始アドレスを保存して、longjmpを行えばそこに飛ぶ事になります。

以下の様な処理を書いてみました。
/****************************************************************************/
/* タスク初期化                                                             */
/****************************************************************************/
ER reg_tsk( ID tid, void (*task)(void), void *stk, unsigned int sz )
{
  if( tid < 0 || tid >= MAX_TASK_NUMBER ) return E_ID;

  tsk_env[tid][0] = (int)stk + sz - 8;  //タスクのスタックを登録
  tsk_env[tid][16] = (int)task;         //タスクのエントリーポイントの登録
  rdy_flg[tid] = TTS_DMT;

  return E_OK;
}


(1)それぞれのタスクには予めID番号を振り分けます。

(2)tsk_envはsetjmpの保存領域で、タスクの数だけ確保します。

(3)スタックポインタの代入される領域にタスク専用のスタックの開始アドレスを
代入します。

(4)PCの代入される位置にタスクの開始アドレスを代入します。

(5)それぞれのタスクの起動状態を代入します。まだ起動していないのでDORMANTと
しています。

※スタックポインタと開始アドレス以外の汎用レジスタの値はどうでもいいです。
この時点では実行に関係しません。

全てのタスクがCPUの起動直後に起動するとは限らないので、起動が必要なタスクの
状態をREADYに変化させる関数を書いてみました。

/****************************************************************************/
/* タスク開始                                                               */
/* 実際はスケジューラーに登録                                               */
/****************************************************************************/
ER sta_tsk( ID tid )
{
  if( tid < 0 || tid >= MAX_TASK_NUMBER ) return E_ID;

  rdy_flg[ tid ] = TTS_RDY;  //タスク登録

  return E_OK;
}


これを初期化処理の中に記載し、初期化処理が完了する時に以下の関数を呼びます。
/****************************************************************************/
/* ラウンドロビン開始                                                       */
/****************************************************************************/
void sta_rdq( ID tid )
{
  cur_tid = tid;
  longjmp( tsk_env[ tid ], 0 );  //
}


マルチタスクのサンプルを以下のリンク先において置きます。
https://dl.dropbox.com/u/60463387/grsakura/start_multitask.zip

5.問題は
タスク自身が他のタスクへの切り替えを意識して行う必要があります。
何かを待つ時、常にタスクの切り替え処理を入れておきましょう。

6.もう少しまともなマルチタスクが良いなら

HOS(Hyper Operating System)はμITRON4.0仕様のRTOSであり、無償で利用可能です。
すでにRXマイコンの実装依存部が提供されています。
http://sourceforge.jp/projects/hos/


ITRONプログラミング入門―H8マイコンとHOSで始める組み込み開発

ITRONプログラミング入門―H8マイコンとHOSで始める組み込み開発

  • 作者: 濱原 和明
  • 出版社/メーカー: オーム社
  • 発売日: 2005/04/25
  • メディア: 単行本



GR-SAKURAとPCをBluetooth(FB155BC)で接続 [RX&SH&H8]

fb155bc_01.png
FB155BCは国内で正式に利用できるBluetoothモジュールの中ではもっとも安い部類
の製品だと思います。
モジュール基板の外部と接続する端子は2.54mmピッチに配置されている為に、ブレッド
ボードやユニバーサル基板への実装が容易です。

接続性は、Windowsとの接続は問題無くできます。またAndroidとの接続もOKです。

出力はClass2で小さいのですが、安価で、とても使い易いモジュールだと思います。

メーカーサイト
http://firmtech.co.kr/01pro/main_eng.php?index=100&proinfo=13

取扱い 株式会社ジャングル
http://www.junglejapan.com/products/surfgrid/
購入ページ
http://store.junglejapan.com/a/other/bluetooth-module

技術的なサイト。「ArduinoからTwitterに投稿するサンプル」とかも紹介されています。
http://dev.surfgrid.org/

ルネサスナイト2で発表した作品でこのモジュールを使ってみました。
GR-SAKURAにはこのモジュールを搭載できるパターンは用意されていませんので、XBee
のフットプリントに変換する下駄を作成しています。わざわざ2mmピッチに変換するって
なんか変ですけれど(笑)。

取り敢えずPCに接続するまでのプロジェクトを作ってみました。
接続完了後、PCから送られてきたデータを折り返します。

以下のリンクにGR-SAKURAのスケッチを置いて置きます。

https://dl.dropbox.com/u/60463387/grsakura/connect_fb155bc.zip

超お手軽無線モジュールXBee: すぐにつながる!どこまでもひろがる! (トライアルシリーズ)

超お手軽無線モジュールXBee: すぐにつながる!どこまでもひろがる! (トライアルシリーズ)

  • 作者: 濱原 和明/佐藤 尚一他
  • 出版社/メーカー: CQ出版
  • 発売日: 2012/09/21
  • メディア: 単行本



RXマイコン。良いのではないでしょうか。 [RX&SH&H8]

title_rx_workshop_b.jpg
それでRXマイコンのワークショップに行って来たんですよ。もう手元のルネサスマイコンはR8C、H8 Tiny、H8/300H、H8-SX、無印SH-2、SH-2 Tiny、SH-2Aも有るんですけれどね。

でもルネサスの最も新しいマイコンなんで、結構よさげです。
オンチップデバッカーではE8aの後継の安価なE1か、E-10A USBの後継で価格が約半額のE20か、またはフルICEと、従来よりも高機能になったツールが揃っていますし、なかなかパフォーマンスも良さそうですね。

RXの特徴は、簡単に言えばハーバードアーキテクチャのCISCマイコンであり、基本的なところはR16C、R32C等の三菱系CPUコアに、H8-SXに搭載されていた、つまり日立系周辺、統合環境の使い方はやはりSH、H8系を踏襲しています。
勿論最近のマイコンなので、コード効率だの、1命令1サイクル実行だの、低消費電力だの、割込み応答性だの、OS Readyだのは当然です。

ワークショップは既に締め切りとなっていますが、
http://update.renesas.com/registration/campaign/CampSem.do?CampaignID=RX_Stick&language=jp®ion=jp&TOPIC=rxstick
抽選でRX-Stickが当たるキャンペーンはまだ継続中なので、申し込んでみては如何でしょう。

H8-SXやSH-2Aの様にHEW上からデバックも可能です。と言うより、HEW上からでしか動作できませんが。



※HEWから接続する時に失敗する時は、一旦USBケーブルを抜き差ししてからもう一度試すと上手く行くようです。
※ドキュメント類は、RENESASのプログラムグループのマニュアルナビゲーターから開くと、RX関連のドキュメントを見る事ができます。
※USB/シリアル変換とRXマイコンの間にR8Cが仲立ちしてしまって、直接ブートモードでの書き込みができません。さて、どうしたものかね。


今すぐ使える!H8マイコン基板 2010年 04月号 [雑誌]

今すぐ使える!H8マイコン基板 2010年 04月号 [雑誌]

  • 作者:
  • 出版社/メーカー: CQ出版
  • 発売日: 2010/02/25
  • メディア: 雑誌



C言語でH8マイコンを使いこなす

C言語でH8マイコンを使いこなす

  • 作者: 鹿取 祐二
  • 出版社/メーカー: オーム社
  • 発売日: 2003/10
  • メディア: 単行本



エレキジャックフォーラムで行われたH8/SX基板の講習では、こんな事も行いました。一例です。 [RX&SH&H8]


液晶の仕組みを理解して、描画速度の向上を行う講義でした。あの基板本のプログラムよりも高速化しているそうです。



ちなみにこっちはARIES(STM32F103 Cortex M3 72MHz+グラフィックコントローラIC)でH8/SXのプログラムを移植して動かした物です。
ありゃ、なんじゃこりゃ。駒落ちしまくっている。
こっちで見た方が良いかも。
http://www.youtube.com/watch?v=LVpo02cNcHs
http://www.youtube.com/watch?v=hV7SEd-zzWY


今すぐ使える!H8マイコン基板 2010年 04月号 [雑誌]

今すぐ使える!H8マイコン基板 2010年 04月号 [雑誌]

  • 作者:
  • 出版社/メーカー: CQ出版
  • 発売日: 2010/02/25
  • メディア: 雑誌



H8マイコン完全マニュアル

H8マイコン完全マニュアル

  • 作者: 藤沢 幸穂
  • 出版社/メーカー: オーム社
  • 発売日: 2000/12
  • メディア: 単行本



エレキジャックフォーラムのH8/SXの電子工作教室に参加してきた [RX&SH&H8]

藤沢さん(藤沢主管技師殿)とは初めてお会いしました。
Img_1885.jpg
Img_1886.jpg
内容はH8/SXの概要とかHEWの使い方はさらっと解説し、それよりも今回のメインである液晶の仕組みとかをしっかりと講義した上で、実際に自前のパソコンにHEWのワークスペースを入れて、実機で動作をさせます。

この液晶の仕組みをちゃんと理解した上で、更に描画の高速化を行うと言うかなり実戦的な講義が行われ、その点ですごく良かったですね。

それと、今回の参加費は教材費、マルツ電波さんのあれで言えばTBと言う基板セット(定価9800円)が8820円であったばかりではなく、アクリル板セット(定価2980円)とか、更に何故かCPU基板まで付属?(帰りに特に外して机の上に置いておいてとは言われなかったが、良いんだよね、もって帰って来て。)と、なんだか物凄くおまけが沢山ありました。

ええ勿論、家に帰ってから速攻で半田付けのやり直しはしましたが(笑)。

追伸
勿論SH-2A基板が付属するインタフェース誌も買ってきましたよ。重いので一冊だけですが。
あと何冊か買うのは地元の本屋かアマゾンにしようっと。

H8マイコン完全マニュアル

H8マイコン完全マニュアル

  • 作者: 藤沢 幸穂
  • 出版社/メーカー: オーム社
  • 発売日: 2000/12
  • メディア: 単行本



これならわかるSuperHマイコン

これならわかるSuperHマイコン

  • 作者: 藤沢 幸穂
  • 出版社/メーカー: オーム社
  • 発売日: 2003/02
  • メディア: 単行本



H8-SX基板 デバックモニタにコンテンツを追加してRAMデバックを快適にしよう [RX&SH&H8]

h8sx_005.png
h8sx_006.png
今回のデバックモニタのH8SX側にインストールされるプログラムはモトローラSフォーマットのファイル(1655.mot)として提供されています。ソースコードで提供されている訳ではないのでこのデバックモニタ自体に機能を追加する事はできませんが、とは言え500KbyteもあるFLASH ROM領域の殆どを空けたままというのも勿体無いので、FLASH領域に何か置いてみましょう!と言うのがこの企画です。

図の様に1655.motをテキストエディタで開くと、テキストファイルとして実際に読む事ができます。
※全て16進表記となっています。
先頭の2文字がレコードタイプ、次の2文字はこれ以降行末までのデータの数(見た目にはその半分)、次の4文字(先頭のS1タイプの場合)がデータの開始アドレス、それ以降行末の2文字手前までが実際のデータであり、最後の2文字はチェックサムと言う事になります。

1655.motファイルの先頭のレコードは
S00E000031363535202020206D6F7450
1665.motのアスキーコードです。

一番最後のレコードは
S9031000EC
エントリーポイントを示しています。

さてモトローラSフォーマットはこの様にテキストとして扱え、非常に簡単な構造で、しかもレコードは各行で完結しているので、ここに自分で作成したレコードを追加するのは容易です。
今回の場合は
S9031000EC
の直前に自分で作成したレコードを追加してみました。

但し注意すべき点としてアドレスは必ず重ならないようにしておく必要があります。
1655.motの最後から2番目のレコードに着目してみると、
S10B2EF00000077800000A301D
となっています。
つまりS1レコード、11byte、0x2EF0からスタートなので終了アドレスは0x2EF8と言う事になります。
これ以降のアドレスからユーザーが作成したレコードを置く事ができます。

ここには何を置けばよいのでしょうか?。
ROM化できるものは何を置いても大丈夫です。プログラムコードであっても、定数データであっても、H8は全アドレス空間がリニアにアクセスできますので問題ありません。

例えばグラフィックLCDに文字を出力したい時、FONTデータ等はしばしばROM化しますが、16×16のビットマップ日本語FONTならまず入り切ります。外部のシリアルROM辺りに置くより高速にアクセスできますので、その分表示が高速になりますね。

プログラムを置いた場合はリンカーではアドレスの解決ができないので、アドレス情報を書いたマクロをプロトタイプ宣言として用意して置けば、やはりFLASH ROM上に置いたプログラムも実行可能となります。

下の図は1655.motにhtmlファイルをmotファイル化したものを追加してH8-SX基板に焼き込み、ブラウザからアクセスしてみたものです。データ自体はRAM上にはありません。ROMから引っ張ってきた物です。

ITRONプログラミング入門―H8マイコンとHOSで始める組み込み開発

ITRONプログラミング入門―H8マイコンとHOSで始める組み込み開発

  • 作者: 濱原 和明
  • 出版社/メーカー: オーム社
  • 発売日: 2005/04/25
  • メディア: 単行本