ルネサスSH7216ワークショップ [RX&SH&H8]
行きます。当選しました(笑)。400MIPSとか体験して来ます。
https://update.renesas.com/registration/CampSem.do?CampaignID=WS_SH7216&language=jp®ion=jp&TOPIC=seminar
事前に予習して行かなければいけませんね。
https://update.renesas.com/registration/CampSem.do?CampaignID=WS_SH7216&language=jp®ion=jp&TOPIC=seminar
事前に予習して行かなければいけませんね。
H8マイコンプログラミング ルネサス統合環境標準IO定義ファイルの使い方 [RX&SH&H8]
今まで解説をサボっていましたが、HEWのウイザードの途中で(勿論選択すれば)自動的に生成されるCPU内蔵周辺IOの定義ファイルに付いて解説します。
プロジェクトウイザードで生成されたCソースファイルが有る同じディレクトリに「iodefine.h」と言うヘッダーファイルが作成されています。このファイルをHEWのエディターウインドウに開いてみて下さい。
例えばIOポート5のレジスタであるPDR5レジスタは以下の様に定義されています。
H8マイコンの周辺IOはMemory mapped IOであり、プログラムメモリやワークメモリ等と同じメモリ空間に存在しています。
一番下の行の記述が、0xFFD0を構造体st_ioのアドレスとして定義しています。
構造体st_ioの定義自体がその上にある記述で、特にPDR5レジスタは符号無し8bitデータとビットフィールドで共用体を構成しています。つまりこのレジスタへのアクセス方法としてbyte単位とビット単位の2つの方法を提示しており、まあ御好きな方をお使いくださいと言ったところでしょうか。
H8/3694Fのハードウエアマニュアルを見るとPDR5レジスタは以下の様になっています。
まず肝心な事はPCR5レジスタとPDR5レジスタは対になっていると言う事です。
PDR5の0~7までのビットはそれぞれのポートに対応しており、PCR5の対応するビットが入力に設定されていた場合、PDR5レジスタを読む事で端子の状態を知る事ができます。
また、PCR5の対応するビットが出力に設定されている場合はPDR5レジスタに書き込む事で端子の状態を変化させる事ができ、読み出しを行うと、現在PDR5に書き込まれている値を読み出す事ができます。
ルネサスのH8用Cコンパイラのビットフィールドのbitオーダーは標準ではMSBから順に割り付けが行われます。他の周辺IOのレジスタもそうですが、ハードウエアマニュアルのビット名の記述となるべく一致させ、ビット毎の意味をイメージし易くしています。
※LSBから割り付けを行う事も可能です。詳しくはコンパイラーマニュアルのコンパイルオプションの項目を見てください。
ところでビットフィールドを利用するには条件があります。該当するメモリやレジスタが、必ず読み出した時に書き込んだ内容を反映する事です。
例えば「iodefine.h」の中のPCR5レジスタの記述はどうなっているか、下記に書き出してみました。
これは構造体st_ioのunsigned char型のメンバーとして登録されているだけで、PDR5の様にビットフィールドとしては記述されていません。
もう一度ハードウエアマニュアルのPCR5レジスタの表を見ると、R/Wの項目はWだけになっています。つまりこのレジスタに対しては書き込み(W)のみ保証され、読み込み(R)してもその値は書いた値を反映していないと言う事です。
組み込みマイコンで頻繁に利用されるビット操作は、Read modify Write動作、つまり読み出して、変更して、また書き込む動作なので、読み出した値が書き込んだ値を反映していない場合、書き戻しの動作時に間違った値を書き込んでしまいます。
この事からHEWが生成するiodefine.hの中では、上記の様に間違ったビット操作を行わない様に配慮されています。
さて、C言語の記述の上ではPDR5レジスタへのアクセス方法はbyte単位、ビット単位で可能です。この2つの違いは実際にはどうなのでしょうか?。
ここからは実際にコードを書いてみて確かめてみます。
ちょこっといじって確かめるにはこの辺りのプロジェクトが良さそうですね。
http://hamayan.blog.so-net.ne.jp/2009-06-07-1
記述したコードを左の図に示します。
この基板上にはP80にLEDが接続され、P80にLOWを出力するとLEDが点灯します。
更にP50にスイッチを接続し、スイッチの状態をLEDに反映する様にコードを書きました。
図の中ではビットフィールドを使用して記述しています。
もしこれをビットフィールドを使わずに記述するなら、以下の様になるでしょうか。
共にデバックモニタ上にロードして実行させ、スイッチを押したり離したりすると、それに合わせてLEDが点滅します。機能的には同じですが、コードは違うでしょう!。と言うわけでどの様なコードが書かれているか調べてみます。
この様な場合、デバックモニタ上にロードしたプログラムの逆アセンブル表示を見て比較する方法がありますが、もっと簡単にコンパイル時のオプションとしてリスト出力を設定します。
ツールチェインのコンパイラータブのカテゴリ=リストで、「コンパイルリスト出力」にチェックを入れて、「オブジェクトプログラムのリスト出力」もチェックします。
これで再ビルドを行うと、現在実行中のセッションのディレクトリの中になんとか.lstと言うファイルが作成されていますので、それを開きます。
下がビットフィールドを使った時のリスト出力です。※オリジナルはCソースと混在で比較し難いので、アセンブル出力のみ抽出。
以下はbyte単位
byte単位の場合、特定のビットのみ代入する記述ができないので、条件式により動作を変える方法を取っていますが、それがこの様な結果になっていますね。
命題として「P50の状態をP80に反映する」となっているので、ビットフィールドを使った場合の方がより直接的に理解し易く記述できている点も見逃せません。
しばしばビットフィールドは移植性が無いと敬遠され勝ちですが、組み込みマイコンのポートを操作している時点で移植性なんてまるっきり期待できませんし、コンパイラによってビットオーダーが変わるとバグになり易いとか言う話も、その辺はコンパイラメーカーも良く判っていて、先に述べた様にコンパイルオプションで対応可能なのです。基本的に新しいコンパイラを使用する時は、まずはマニュアルに目を通しましょう。
そんな訳でまあ今更ビットフィールドを否定する理由は特に無いんじゃないかと思います。
但し、HEWで生成するiodefine.hや、ルネサスの半導体セミナーのページで配付しているCPU別ヘッダーファイルは、当然その動作保証はルネサスのコンパイラに限られるので、それ以外のコンパイラで使う方は良く自分で使うコンパイラのマニュアルを調べてから使った方が良いです。
※実はこのビット同士の代入の例題はずるです。H8マイコンではこのビットフィールドの直接代入を行う記述をした方が効率が良い事は知っていましたからね。
プロジェクトウイザードで生成されたCソースファイルが有る同じディレクトリに「iodefine.h」と言うヘッダーファイルが作成されています。このファイルをHEWのエディターウインドウに開いてみて下さい。
例えばIOポート5のレジスタであるPDR5レジスタは以下の様に定義されています。
struct st_io { /* struct IO */ 途中を省略している。 union { /* PDR5 */ unsigned char BYTE; /* Byte Access */ struct { /* Bit Access */ unsigned char B7:1; /* Bit 7 */ unsigned char B6:1; /* Bit 6 */ unsigned char B5:1; /* Bit 5 */ unsigned char B4:1; /* Bit 4 */ unsigned char B3:1; /* Bit 3 */ unsigned char B2:1; /* Bit 2 */ unsigned char B1:1; /* Bit 1 */ unsigned char B0:1; /* Bit 0 */ } BIT; /* */ } PDR5; /* */ 途中を省略している。 #define IO (*(volatile struct st_io *)0xFFD0) /* IO Address*/
H8マイコンの周辺IOはMemory mapped IOであり、プログラムメモリやワークメモリ等と同じメモリ空間に存在しています。
一番下の行の記述が、0xFFD0を構造体st_ioのアドレスとして定義しています。
構造体st_ioの定義自体がその上にある記述で、特にPDR5レジスタは符号無し8bitデータとビットフィールドで共用体を構成しています。つまりこのレジスタへのアクセス方法としてbyte単位とビット単位の2つの方法を提示しており、まあ御好きな方をお使いくださいと言ったところでしょうか。
H8/3694Fのハードウエアマニュアルを見るとPDR5レジスタは以下の様になっています。
まず肝心な事はPCR5レジスタとPDR5レジスタは対になっていると言う事です。
PDR5の0~7までのビットはそれぞれのポートに対応しており、PCR5の対応するビットが入力に設定されていた場合、PDR5レジスタを読む事で端子の状態を知る事ができます。
また、PCR5の対応するビットが出力に設定されている場合はPDR5レジスタに書き込む事で端子の状態を変化させる事ができ、読み出しを行うと、現在PDR5に書き込まれている値を読み出す事ができます。
ルネサスのH8用Cコンパイラのビットフィールドのbitオーダーは標準ではMSBから順に割り付けが行われます。他の周辺IOのレジスタもそうですが、ハードウエアマニュアルのビット名の記述となるべく一致させ、ビット毎の意味をイメージし易くしています。
※LSBから割り付けを行う事も可能です。詳しくはコンパイラーマニュアルのコンパイルオプションの項目を見てください。
ところでビットフィールドを利用するには条件があります。該当するメモリやレジスタが、必ず読み出した時に書き込んだ内容を反映する事です。
例えば「iodefine.h」の中のPCR5レジスタの記述はどうなっているか、下記に書き出してみました。
unsigned char PCR5; /* PCR5 */
これは構造体st_ioのunsigned char型のメンバーとして登録されているだけで、PDR5の様にビットフィールドとしては記述されていません。
もう一度ハードウエアマニュアルのPCR5レジスタの表を見ると、R/Wの項目はWだけになっています。つまりこのレジスタに対しては書き込み(W)のみ保証され、読み込み(R)してもその値は書いた値を反映していないと言う事です。
組み込みマイコンで頻繁に利用されるビット操作は、Read modify Write動作、つまり読み出して、変更して、また書き込む動作なので、読み出した値が書き込んだ値を反映していない場合、書き戻しの動作時に間違った値を書き込んでしまいます。
この事からHEWが生成するiodefine.hの中では、上記の様に間違ったビット操作を行わない様に配慮されています。
さて、C言語の記述の上ではPDR5レジスタへのアクセス方法はbyte単位、ビット単位で可能です。この2つの違いは実際にはどうなのでしょうか?。
ここからは実際にコードを書いてみて確かめてみます。
ちょこっといじって確かめるにはこの辺りのプロジェクトが良さそうですね。
http://hamayan.blog.so-net.ne.jp/2009-06-07-1
記述したコードを左の図に示します。
この基板上にはP80にLEDが接続され、P80にLOWを出力するとLEDが点灯します。
更にP50にスイッチを接続し、スイッチの状態をLEDに反映する様にコードを書きました。
図の中ではビットフィールドを使用して記述しています。
もしこれをビットフィールドを使わずに記述するなら、以下の様になるでしょうか。
/*LEDが接続されているポートを出力に切り替える*/ IO.PCR8 = 0x01; /*0000 0001 ポート8入出力*/ /*スイッチが接続されているポートを入力に切り替える*/ IO.PCR5 = 0x00; /*0000 0000 ポート5入出力 但しリセット直後は入力となっている*/ /*スイッチが接続されているポートのプルアップを有効にする*/ IO.PUCR5.BYTE |= 0x01; /**/ while( 1 ) { if(IO.PDR5.BYTE & 0x01) IO.PDR8.BYTE |= 0x01; else IO.PDR8.BYTE &= ~0x01; }
共にデバックモニタ上にロードして実行させ、スイッチを押したり離したりすると、それに合わせてLEDが点滅します。機能的には同じですが、コードは違うでしょう!。と言うわけでどの様なコードが書かれているか調べてみます。
この様な場合、デバックモニタ上にロードしたプログラムの逆アセンブル表示を見て比較する方法がありますが、もっと簡単にコンパイル時のオプションとしてリスト出力を設定します。
ツールチェインのコンパイラータブのカテゴリ=リストで、「コンパイルリスト出力」にチェックを入れて、「オブジェクトプログラムのリスト出力」もチェックします。
これで再ビルドを行うと、現在実行中のセッションのディレクトリの中になんとか.lstと言うファイルが作成されていますので、それを開きます。
下がビットフィールドを使った時のリスト出力です。※オリジナルはCソースと混在で比較し難いので、アセンブル出力のみ抽出。
000C F801 MOV.B #1,R0L 000E 38EB MOV.B R0L,@65515:8 0010 1800 SUB.B R0H,R0H 0012 30E8 MOV.B R0H,@65512:8 0014 7FD17000 BSET.B #0,@65489:8 0018 L6: 0018 7ED87700 BLD.B #0,@65496:8 001C 7FDB6700 BST.B #0,@65499:8 0020 4000 BRA L6:8
以下はbyte単位
000C F801 MOV.B #1,R0L 000E 38EB MOV.B R0L,@65515:8 0010 1800 SUB.B R0H,R0H 0012 30E8 MOV.B R0H,@65512:8 0014 7FD17000 BSET.B #0,@65489:8 0016 L6: 0016 7ED87300 BTST.B #0,@65496:8 001A 4700 BEQ L8:8 001C 7FDB7000 BSET.B #0,@65499:8 0020 4000 BRA L6:8 0022 L8: 0022 7FDB7200 BCLR.B #0,@65499:8 0026 4000 BRA L6:8
byte単位の場合、特定のビットのみ代入する記述ができないので、条件式により動作を変える方法を取っていますが、それがこの様な結果になっていますね。
命題として「P50の状態をP80に反映する」となっているので、ビットフィールドを使った場合の方がより直接的に理解し易く記述できている点も見逃せません。
しばしばビットフィールドは移植性が無いと敬遠され勝ちですが、組み込みマイコンのポートを操作している時点で移植性なんてまるっきり期待できませんし、コンパイラによってビットオーダーが変わるとバグになり易いとか言う話も、その辺はコンパイラメーカーも良く判っていて、先に述べた様にコンパイルオプションで対応可能なのです。基本的に新しいコンパイラを使用する時は、まずはマニュアルに目を通しましょう。
そんな訳でまあ今更ビットフィールドを否定する理由は特に無いんじゃないかと思います。
但し、HEWで生成するiodefine.hや、ルネサスの半導体セミナーのページで配付しているCPU別ヘッダーファイルは、当然その動作保証はルネサスのコンパイラに限られるので、それ以外のコンパイラで使う方は良く自分で使うコンパイラのマニュアルを調べてから使った方が良いです。
※実はこのビット同士の代入の例題はずるです。H8マイコンではこのビットフィールドの直接代入を行う記述をした方が効率が良い事は知っていましたからね。
緊急デバック! H8マイコンとパスワールドさんカラーLCDをE8でデバックする。 [RX&SH&H8]
サンプルコードを貰っていたのですが、MSP430用のコードであり、H8用のコードに置き換えるのちょっと手間取ってしまったので(つまり一発では動かなかった)、急遽E8の出動です。
ステップ実行で配線も含めて確認しながらデバックを行い、サンプルの表示が出るまで進みました。
やっぱりデバッカーが有ると楽ですね、精神的にも「上手く行かなきゃ最後はこれを持ち出せばいい!」って思えるので楽です。
で!、やっぱりLCDもカラーが良いね!。
※カラーLCDに付いては、もう少しいじってからネタにするかもしれません。
H8マイコンで割り込みしよう2 デバックモニタ上で割り込みの実現をしてみる 後編 [RX&SH&H8]
ええっと、前回は何をしたんだっけ。ああそうか、デバックモニタ上でプログラムを実行する時のメモリイメージを描いたのですね。
では、実際にプロジェクトを作成してみます。
今回も新たにプロジェクトを生成します。ここでやった手順でプロジェクトを作成してください。
http://hamayan.blog.so-net.ne.jp/2009-06-07-1
最終的にはこの様にHEWが起動しています。
左のペインの中で表示されている幾つかのファイルの内、「mon_int_sample.c」はこのプロジェクトを生成した時のプロジェクト名がそのまま関数mainを含むファイルとしてウイザードが生成したものですが、更に自分で「vector.c」と「startup.c」と言うファイルを追加しています。
それではこれら2つのファイルの内容を以下に掲載します。
vector.c
startup.c
「vector.c」の中身はhttp://hamayan.blog.so-net.ne.jp/2009-06-12-1で紹介した「intprg.c」の中身に似ていますが、若干の違いが有ります。
例えば「intprg.c」の中でTimer A オーバーフロー割り込みは以下の様に記載されていました。
しかしこの「vector.c」では以下の様に記述しています。
__interruptと#pragma interruptは同じ意味で、後ろに続く関数を割り込み関数として認識させるものです。
ここでの大きな違いは(vect=19)の有り/無しです。これは前にも解説したように割り込みベクター番号19の位置に関数の先頭アドレスを埋め込む様に指示している宣言です。
なのでhttp://hamayan.blog.so-net.ne.jp/2009-06-12-1では特に割り込みベクターテーブルの記述を行っていません。
ではデバックモニタで割り込みを使用する時もHEWのウイザードが生成した「intprg.c」を使えばよいじゃないか!と思われるでしょうけれど、そうは行かないのです。
(vect=?)を使う方法はデバックモニタ上にプログラムをロードする上で問題が発生します。つまり埋め込み先はFLASH ROM領域であり、幾らデバックモニタ上にロードしてもFLASH ROMの内容までは書き換える事ができません。このプログラムを無理やり実行するとおそらくデバックモニタは不正な割り込みが発生した!とメッセージを出力し、そこで実行を停止するでしょう。
そこで今回の「vector.c」なのですが、この場合は関数が割り込み処理である事は宣言していますが、メモリ上のどの位置に割付すると言った情報は一切記載されていません。この割付に関してはビルド時にコンパイラの後の処理で実行されるリンカーに一任しています。コンパイラはいわゆるリロケータブルなオブジェクトファイルを生成します。
※これは、随分前に戻ってしまいますが、http://hamayan.blog.so-net.ne.jp/2009-06-03で紹介したワークフローの中でコンパイラからリロケータブルオブジェクトファイルを生成して、それが最適化リンケージエディタに読み込まれるまでの流れですね。
もう一つ、「startup.c」ですが、こちらは「resetprg.c」に相当するファイルです。「resetprg.c」を開いてみて下さい。あちこちコメントアウトしてあって実に読み難いファイルですが、ポイントは以下の宣言です。
__entry(vect=0) void PowerON_Reset(void);
__interruptの記述に似ていますが、__entry(または#pragma entry)はリセット時専用の宣言です。
この宣言で修飾された関数はリセット直後に実行される関数として認識され、関数の先頭に自動的にスタックポインタの初期化が挿入されます。
試しに「resetprg.c」のコンパイル時にアセンブラリスト出力を行うコンパイルオプションを設定して内容を見てみます。※実際の出力から無関係な記述は省略されています。
MOV.W #STARTOF S+SIZEOF S,R7
は、スタック領域として使用されるSセクションの先頭アドレスにスタックサイズを加算した値を、スタックポインタとして使用されるR7レジスタに代入しています。
もちろん「startup.c」でも関数「startup」で初っ端にスタックポインタの設定を行っています。この場合はBSTKと言うセクションを使用しており、BSTKの実体はこの「startup」の下に続く領域の宣言としてSTKセクションを宣言しています。
※H8のコンパイラではC言語でセクションを宣言した場合、自動的に頭に
P:プログラムコードの意味。
C:定数の意味。
D:初期化付き変数の意味。
B:0クリアのみの変数の意味。
が付加されます。
そして、
__entry(vect=0)
と記述されているところから判る様に、この宣言もまた絶対アドレスで0x0000を示しており、デバックモニタ上ではこの情報を正常にロードする事ができません。やはりスタートアッププログラムもリロケータブルである必要があります。
ちなみに、実はデバックモニタ上でユーザープログラムを実行する時は特にプログラムの初っ端でスタックポインタの設定をする必要がありません。実はです(笑)。
デバックモニタからユーザプログラムへCPUの実行が移る時、デバックモニタが適当なスタックポインタを設定してくれています。
だからスタックポインタの設定を一切行っていない”Hello World”プログラムが何の問題も無く動いたのです。
話が長くなって書いている本人が疲れたので、ここからは飛ばします。
ツールチェインのダイアログを開いて以下のポイントを変更します。
HTERMを起動し、デバックモニタが書かれているH8マイコンと接続し、今回作成したプログラムのロードを行うと画面の様になります。
黄色くハイライトされている行がPCの位置を表し、ステップ実行等を行えばこの黄色い帯も移動します。
※このプログラムは、ポートにLEDを接続しておくと、実行した時にLEDがチカチカします。
※実際のところ「vector.c」や「startup.c」は決まりきった記述、決まりきった処理なので、デバックモニタ上で動くプログラムを作成したくなったら、今回の手順でプロジェクトを生成し、上の「vector.c」と「startup.c」をコピーし、適当に編集して使うとよいと思います。
では、実際にプロジェクトを作成してみます。
今回も新たにプロジェクトを生成します。ここでやった手順でプロジェクトを作成してください。
http://hamayan.blog.so-net.ne.jp/2009-06-07-1
最終的にはこの様にHEWが起動しています。
左のペインの中で表示されている幾つかのファイルの内、「mon_int_sample.c」はこのプロジェクトを生成した時のプロジェクト名がそのまま関数mainを含むファイルとしてウイザードが生成したものですが、更に自分で「vector.c」と「startup.c」と言うファイルを追加しています。
それではこれら2つのファイルの内容を以下に掲載します。
vector.c
#include <machine.h> #include "iodefine.h" void startup( void ); /*BOOT処理*/ #pragma interrupt(abort) /*NMI*/ void abort( void ){} #pragma interrupt(TRAP0) /*トラップ命令#0*/ void TRAP0( void ){} #pragma interrupt(TRAP1) /*トラップ命令#1*/ void TRAP1( void ){} #pragma interrupt(TRAP2) /*トラップ命令#2*/ void TRAP2( void ){} #pragma interrupt(TRAP3) /*トラップ命令#3*/ void TRAP3( void ){} #pragma interrupt(ADRBRK) /*アドレスブレーク*/ void ADRBRK( void ){} #pragma interrupt(CPUSLEEP) /*スリープ命令実行による直接遷移*/ void CPUSLEEP( void ){} #pragma interrupt(IRQ0) /*外部割込み端子0*/ void IRQ0( void ){} #pragma interrupt(IRQ1) /*外部割込み端子1*/ void IRQ1( void ){} #pragma interrupt(IRQ2) /*外部割込み端子2*/ void IRQ2( void ){} #pragma interrupt(IRQ3) /*外部割込み端子3*/ void IRQ3( void ){} #pragma interrupt(WKP) /*外部割込み端子WKP*/ void WKP( void ){} #pragma interrupt(TAOVI) /*タイマーAオーバーフロー*/ void TAOVI( void ) { IRR1.BIT.IRRTA = 0; /*割込み条件のクリア*/ IO.PDR8.BIT.B0 ^= 1; /*LEDチカチカ*/ } #pragma interrupt(TIMERW) /*タイマーW*/ void TIMERW( void ){} #pragma interrupt(TIMERV) /*タイマーV*/ void TIMERV( void ){} #pragma interrupt(SCI_3) /*シリアル*/ void SCI_3( void ){} #pragma interrupt(IIC_2) /*I2C*/ void IIC_2( void ){} #pragma interrupt(ADI) /*A/D*/ void ADI( void ){} #pragma section VECT /* 仮想ベクタテーブル */ void (*const VEC_TBL[])(void) = { startup, /*Vector 0 Reset Vector*/ (void *)0, /*Vector 1*/ (void *)0, /*Vector 2*/ (void *)0, /*Vector 3*/ (void *)0, /*Vector 4*/ (void *)0, /*Vector 5*/ (void *)0, /*Vector 6*/ abort, /*Vector 7*/ TRAP0, /*Vector 8*/ TRAP1, /*Vector 9*/ TRAP2, /*Vector 10*/ TRAP3, /*Vector 11*/ ADRBRK, /*Vector 12*/ CPUSLEEP, /*Vector 13*/ IRQ0, /*Vector 14*/ IRQ1, /*Vector 15*/ IRQ2, /*Vector 16*/ IRQ3, /*Vector 17*/ WKP, /*Vector 18*/ TAOVI, /*Vector 19*/ (void *)0, /*Vector 20*/ TIMERW, /*Vector 21*/ TIMERV, /*Vector 22*/ SCI_3, /*Vector 23*/ IIC_2, /*Vector 24*/ ADI, /*Vector 25*/ };
startup.c
#include <machine.h> void main( void ); void startup( void ) { #pragma asm MOV.W #(STARTOF BSTK) + (SIZEOF BSTK),R7 #pragma endasm // set_imask_ccr(1); /*全体の割り込み禁止*/ _INITSCT(); main(); while(1) ; } /***********************************************************/ /* スタック領域として領域の確保を行う。 */ /***********************************************************/ #pragma section STK static char stack_area[256];
「vector.c」の中身はhttp://hamayan.blog.so-net.ne.jp/2009-06-12-1で紹介した「intprg.c」の中身に似ていますが、若干の違いが有ります。
例えば「intprg.c」の中でTimer A オーバーフロー割り込みは以下の様に記載されていました。
__interrupt(vect=19) void INT_TimerA(void) { IRR1.BIT.IRRTA = 0; /*割込み条件のクリア*/ IO.PDR8.BIT.B0 ^= 1; /*LEDチカチカ*/ }
しかしこの「vector.c」では以下の様に記述しています。
#pragma interrupt(TAOVI) /*タイマーAオーバーフロー*/ void TAOVI( void ) { IRR1.BIT.IRRTA = 0; /*割込み条件のクリア*/ IO.PDR8.BIT.B0 ^= 1; /*LEDチカチカ*/ }
__interruptと#pragma interruptは同じ意味で、後ろに続く関数を割り込み関数として認識させるものです。
ここでの大きな違いは(vect=19)の有り/無しです。これは前にも解説したように割り込みベクター番号19の位置に関数の先頭アドレスを埋め込む様に指示している宣言です。
なのでhttp://hamayan.blog.so-net.ne.jp/2009-06-12-1では特に割り込みベクターテーブルの記述を行っていません。
ではデバックモニタで割り込みを使用する時もHEWのウイザードが生成した「intprg.c」を使えばよいじゃないか!と思われるでしょうけれど、そうは行かないのです。
(vect=?)を使う方法はデバックモニタ上にプログラムをロードする上で問題が発生します。つまり埋め込み先はFLASH ROM領域であり、幾らデバックモニタ上にロードしてもFLASH ROMの内容までは書き換える事ができません。このプログラムを無理やり実行するとおそらくデバックモニタは不正な割り込みが発生した!とメッセージを出力し、そこで実行を停止するでしょう。
そこで今回の「vector.c」なのですが、この場合は関数が割り込み処理である事は宣言していますが、メモリ上のどの位置に割付すると言った情報は一切記載されていません。この割付に関してはビルド時にコンパイラの後の処理で実行されるリンカーに一任しています。コンパイラはいわゆるリロケータブルなオブジェクトファイルを生成します。
※これは、随分前に戻ってしまいますが、http://hamayan.blog.so-net.ne.jp/2009-06-03で紹介したワークフローの中でコンパイラからリロケータブルオブジェクトファイルを生成して、それが最適化リンケージエディタに読み込まれるまでの流れですね。
もう一つ、「startup.c」ですが、こちらは「resetprg.c」に相当するファイルです。「resetprg.c」を開いてみて下さい。あちこちコメントアウトしてあって実に読み難いファイルですが、ポイントは以下の宣言です。
__entry(vect=0) void PowerON_Reset(void);
__interruptの記述に似ていますが、__entry(または#pragma entry)はリセット時専用の宣言です。
この宣言で修飾された関数はリセット直後に実行される関数として認識され、関数の先頭に自動的にスタックポインタの初期化が挿入されます。
試しに「resetprg.c」のコンパイル時にアセンブラリスト出力を行うコンパイルオプションを設定して内容を見てみます。※実際の出力から無関係な記述は省略されています。
22: __entry(vect=0) void PowerON_Reset(void); 54: #pragma section ResetPRG 55: 56: __entry(vect=0) void PowerON_Reset(void) 0000 _PowerON_Reset: ; function: PowerON_Reset 0000 79070000 MOV.W #STARTOF S+SIZEOF S,R7 57: {
MOV.W #STARTOF S+SIZEOF S,R7
は、スタック領域として使用されるSセクションの先頭アドレスにスタックサイズを加算した値を、スタックポインタとして使用されるR7レジスタに代入しています。
もちろん「startup.c」でも関数「startup」で初っ端にスタックポインタの設定を行っています。この場合はBSTKと言うセクションを使用しており、BSTKの実体はこの「startup」の下に続く領域の宣言としてSTKセクションを宣言しています。
※H8のコンパイラではC言語でセクションを宣言した場合、自動的に頭に
P:プログラムコードの意味。
C:定数の意味。
D:初期化付き変数の意味。
B:0クリアのみの変数の意味。
が付加されます。
そして、
__entry(vect=0)
と記述されているところから判る様に、この宣言もまた絶対アドレスで0x0000を示しており、デバックモニタ上ではこの情報を正常にロードする事ができません。やはりスタートアッププログラムもリロケータブルである必要があります。
ちなみに、実はデバックモニタ上でユーザープログラムを実行する時は特にプログラムの初っ端でスタックポインタの設定をする必要がありません。実はです(笑)。
デバックモニタからユーザプログラムへCPUの実行が移る時、デバックモニタが適当なスタックポインタを設定してくれています。
だからスタックポインタの設定を一切行っていない”Hello World”プログラムが何の問題も無く動いたのです。
話が長くなって書いている本人が疲れたので、ここからは飛ばします。
ツールチェインのダイアログを開いて以下のポイントを変更します。
HTERMを起動し、デバックモニタが書かれているH8マイコンと接続し、今回作成したプログラムのロードを行うと画面の様になります。
黄色くハイライトされている行がPCの位置を表し、ステップ実行等を行えばこの黄色い帯も移動します。
※このプログラムは、ポートにLEDを接続しておくと、実行した時にLEDがチカチカします。
※実際のところ「vector.c」や「startup.c」は決まりきった記述、決まりきった処理なので、デバックモニタ上で動くプログラムを作成したくなったら、今回の手順でプロジェクトを生成し、上の「vector.c」と「startup.c」をコピーし、適当に編集して使うとよいと思います。
ITRONプログラミング入門―H8マイコンとHOSで始める組み込み開発
- 作者: 濱原 和明
- 出版社/メーカー: オーム社
- 発売日: 2005/04/25
- メディア: 単行本
H8マイコンで割り込みしよう2 デバックモニタ上で割り込みの実現をしてみる [RX&SH&H8]
前回はHEWのウイザードに従って作成したプロジェクトに、自動的に生成された割り込み関数を使って割り込みを実現しましたが、今回は自力で割り込み処理に関する記述を追加して、それをデバックモニタで利用します。
H8/3694FのRAM上にユーザープログラムをダウンロードしてデバックを行うので、デバックできるプログラムサイズは相当に小さくなってしまい、実用的なプログラムのデバックは困難です。「ならやってみる意味無いじゃん」と思われるかもしれませんが、以外と面白い話も有りますので、宜しければ読んでみてください。
まずその前にデバックモニタで割り込みを実現する概念についての解説を行います。
デバックモニターが稼動しているH8/3694F上に割り込み付きユーザープログラムをダウンロードしたイメージは図の様になります。
これはMAP出力及びデバックモニタの”?”コマンドの出力から描き起こしました。
MAP出力の一部
デバックモニタの”?”コマンド出力の一部
文字の背景色が青のところはFLASH ROM上に置かれ、ピンクのところはRAM上に置かれます。黄色はIOレジスタで、今回の話には関係しません。
さて、H8/300Hマイコンの割り込みベクターの開始アドレスは必ず0番地から開始する必要があります。しかしこのアドレスから始まる領域はFLASH ROM空間でもあり、ユーザープログラムの全てをRAM上に転送する事を前提としているデバックモニタではそのままでは割り込みが使用出来ない事となります。※FLASH ROM上にユーザープログラムやユーザーベクターを”書く”事は一応出来ない事としている。
そこでデバックモニターでは仮想ベクターと言う概念を使う事でユーザー割り込みの実現をしています。
割り込み発生時には、まずデバックモニタ上の割り込み処理が応答します。この割り込みの中では自分自身の割り込みベクター番号が判っているので、その番号に該当するユーザーベクター領域の該当アドレスから実際に行うべき関数の先頭アドレスを読み取り、そのアドレスの先にジャンプします。
例えば割り込み番号が19のタイマーAのオーバーフロー割り込みが発生したとします。
タイマーAのオーバーフロー割り込みの絶対アドレスは0x0026なので、デバックモニタの割り込み処理の中ではUser Vector領域のアドレス+0x0026番地(0xF880+0x0026=0xF8A6)を加算したところから関数の先頭アドレスを取得します。
通常の割り込み処理に比較すれば一手間多い訳ですが、それ以外のところではユーザーがあまり意識しないで割り込みを利用できます。
ユーザーが気にしなければならない点は、ユーザーベクターが必ずデバックモニタのUser Vector領域に一致すると言う点です。
その為にはユーザーがリンカーに対してユーザーベクターを所望のアドレスから配置するように設定してやる必要があります。
次回はその辺りの解説をするつもりです。
H8/3694FのRAM上にユーザープログラムをダウンロードしてデバックを行うので、デバックできるプログラムサイズは相当に小さくなってしまい、実用的なプログラムのデバックは困難です。「ならやってみる意味無いじゃん」と思われるかもしれませんが、以外と面白い話も有りますので、宜しければ読んでみてください。
まずその前にデバックモニタで割り込みを実現する概念についての解説を行います。
デバックモニターが稼動しているH8/3694F上に割り込み付きユーザープログラムをダウンロードしたイメージは図の様になります。
これはMAP出力及びデバックモニタの”?”コマンドの出力から描き起こしました。
MAP出力の一部
*** Mapping List *** SECTION START END SIZE ALIGN CVECT 0000f880 0000f8b3 34 2 P 0000f8b4 0000f949 96 2 D 0000f94a 0000f94a 0 2 C$BSEC 0000f94a 0000f94d 4 2 C$DSEC 0000f94e 0000f953 6 2 R 0000f954 0000f954 0 2 B 0000f954 0000f954 0 2 BSTK 0000f954 0000fa53 100 2
デバックモニタの”?”コマンド出力の一部
? Monitor Vector 0000 - 003F Monitor ROM 0100 - 6B09 Monitor RAM F780 - F87F User Vector F880 - F8BF
文字の背景色が青のところはFLASH ROM上に置かれ、ピンクのところはRAM上に置かれます。黄色はIOレジスタで、今回の話には関係しません。
さて、H8/300Hマイコンの割り込みベクターの開始アドレスは必ず0番地から開始する必要があります。しかしこのアドレスから始まる領域はFLASH ROM空間でもあり、ユーザープログラムの全てをRAM上に転送する事を前提としているデバックモニタではそのままでは割り込みが使用出来ない事となります。※FLASH ROM上にユーザープログラムやユーザーベクターを”書く”事は一応出来ない事としている。
そこでデバックモニターでは仮想ベクターと言う概念を使う事でユーザー割り込みの実現をしています。
割り込み発生時には、まずデバックモニタ上の割り込み処理が応答します。この割り込みの中では自分自身の割り込みベクター番号が判っているので、その番号に該当するユーザーベクター領域の該当アドレスから実際に行うべき関数の先頭アドレスを読み取り、そのアドレスの先にジャンプします。
例えば割り込み番号が19のタイマーAのオーバーフロー割り込みが発生したとします。
タイマーAのオーバーフロー割り込みの絶対アドレスは0x0026なので、デバックモニタの割り込み処理の中ではUser Vector領域のアドレス+0x0026番地(0xF880+0x0026=0xF8A6)を加算したところから関数の先頭アドレスを取得します。
FILE=C:\WorkSpace\sample\mon_int_sample\Debug\vector.obj 0000f8d0 0000f8fb 2c _abort 0000f8d0 2 func ,g * _TRAP0 0000f8d2 2 func ,g * _TRAP1 0000f8d4 2 func ,g * _TRAP2 0000f8d6 2 func ,g * _TRAP3 0000f8d8 2 func ,g * _ADRBRK 0000f8da 2 func ,g * _CPUSLEEP 0000f8dc 2 func ,g * _IRQ0 0000f8de 2 func ,g * _IRQ1 0000f8e0 2 func ,g * _IRQ2 0000f8e2 2 func ,g * _IRQ3 0000f8e4 2 func ,g * _WKP 0000f8e6 2 func ,g * _TAOVI 0000f8e8 a func ,g * _TIMERW 0000f8f2 2 func ,g * _TIMERV 0000f8f4 2 func ,g * _SCI_3 0000f8f6 2 func ,g * _IIC_2 0000f8f8 2 func ,g * _ADI 0000f8fa 2 func ,g *
通常の割り込み処理に比較すれば一手間多い訳ですが、それ以外のところではユーザーがあまり意識しないで割り込みを利用できます。
ユーザーが気にしなければならない点は、ユーザーベクターが必ずデバックモニタのUser Vector領域に一致すると言う点です。
その為にはユーザーがリンカーに対してユーザーベクターを所望のアドレスから配置するように設定してやる必要があります。
次回はその辺りの解説をするつもりです。
ITRONプログラミング入門―H8マイコンとHOSで始める組み込み開発
- 作者: 濱原 和明
- 出版社/メーカー: オーム社
- 発売日: 2005/04/25
- メディア: 単行本
H8マイコンで割り込みしよう [RX&SH&H8]
ちょっと間が空いてしまいましたが、割り込み処理をやってみます。割り込みの無いマイコンなんて、、、フッ!。
割り込みの実現の仕方について、通常の記述と(今回)、デバックモニタでの割り込みの実現方法(次回)の両方を解説します。
それでは今まで作業をしてきたsampleワークスペースを開きます。
もしHEWの画面が図の様に前回のプロジェクト名が太字になっていた場合は、始めに作成したプロジェクトを選択し、右クリックで「アクティブプロジェクトに設定」を行います。
左のペインの中の「intprg.c」をダブルクリックしてエディットWindowに「intprg.c」を開きます。
まずファイルの先頭付近、machine.hをインクルードしている直後にiodefine.hもインクルードしておきます。
#include "iodefine.h"
次に55行目付近の修正を行います。
// vector 19 Timer A Overflow
とコメントされている行の下に関数があります。今回はこのTimer Aを使って定周期で割り込みを発生させ、やっぱりLEDをチカチカさせます。
これだけです。
関数の中身の最初の行は、割り込みコントローラのフラグbitをクリアしています。これを行わないと次の割り込みが発生しなくなります割り込み処理から抜けてもまた割り込みが発生してしまい、それが永久に続くのでお忘れ無く。
次の行が実際にLEDをチカチカさせている処理です。最初にmain関数の中に記述した物をここに持って来ただけです。
次にmain関数にはどういった記述を行っているのか紹介します。
これまた簡単な内容ですが、IOポートやTimer Aの初期化を行って、割り込みをイネーブルし、最後は無限ループに入っているだけです。
このボードのCPUクロックは20MHzとなっています。TimerAをインターバルタイマーとして使用すると、TA.TMA.BIT.CKSIで選ばれた分周比でCPUクロックが分周され、次に続くカウンターで分周されたクロックを256個数えられます。
実際には0~255まで数えるのですが、TimerAは255から0に戻る時(8bitカウンターがオーバーフローする時)、割り込みがイネーブルされていた場合、割り込みを発生します。
結局20000000÷8192÷256≒9.5Hzで割り込みが発生する事となり、LEDの点滅周期は≒210msとなります。
ビルドを行い、FDTを使ってターゲットに書き込みを行います。
書き込みモードを解除して通常の動作モードに切り替えてみると、LEDがチカチカ点滅しているのを見る事が出来ると思います。
これでダサい?ソフトウエアループを使ったLEDチカチカから、割り込みを使ったスマートなLEDチカチカ(笑)にステップアップできました。
おっと、肝心な事を書き忘れていました。
__interrupt(vect=19) void INT_TimerA(void)
と記述されていますが、最初の__interruptはこの関数が割り込み処理である事を示し、続くvect=19はベクター番号を指定しています。
割り込みベクターはROMの0番地から始まる領域に書く事が決まっており、0番地がベクター番号0となっています。ベクター番号19と書く事でコンパイラは自動的にROMの書き込みアドレス(H8/Tinyの場合は19×2)を計算する事が可能となり、実際にそのアドレスの位置に割り込み関数のアドレスを埋め込みます。
後ろの型の宣言は通常のC言語の関数そのものの宣言です。
割り込みの実現の仕方について、通常の記述と(今回)、デバックモニタでの割り込みの実現方法(次回)の両方を解説します。
それでは今まで作業をしてきたsampleワークスペースを開きます。
もしHEWの画面が図の様に前回のプロジェクト名が太字になっていた場合は、始めに作成したプロジェクトを選択し、右クリックで「アクティブプロジェクトに設定」を行います。
左のペインの中の「intprg.c」をダブルクリックしてエディットWindowに「intprg.c」を開きます。
まずファイルの先頭付近、machine.hをインクルードしている直後にiodefine.hもインクルードしておきます。
#include "iodefine.h"
次に55行目付近の修正を行います。
// vector 19 Timer A Overflow
とコメントされている行の下に関数があります。今回はこのTimer Aを使って定周期で割り込みを発生させ、やっぱりLEDをチカチカさせます。
__interrupt(vect=19) void INT_TimerA(void) { IRR1.BIT.IRRTA = 0; /*割込み条件のクリア*/ IO.PDR8.BIT.B0 ^= 1; /*LEDチカチカ*/ }
これだけです。
関数の中身の最初の行は、割り込みコントローラのフラグbitをクリアしています。
次の行が実際にLEDをチカチカさせている処理です。最初にmain関数の中に記述した物をここに持って来ただけです。
次にmain関数にはどういった記述を行っているのか紹介します。
void main(void) { /*LEDが接続されているポートを出力に切り替える*/ IO.PCR8 = 0x01; /*0000 0001 ポート8入出力*/ /*TAの初期化 インターバルタイマーとして使用する*/ TA.TMA.BIT.CKSI = 0; /*CPUクロックを1/8192する*/ IENR1.BIT.IENTA = 1; /*TA割り込みを許可*/ while( 1 ) ; }
これまた簡単な内容ですが、IOポートやTimer Aの初期化を行って、割り込みをイネーブルし、最後は無限ループに入っているだけです。
このボードのCPUクロックは20MHzとなっています。TimerAをインターバルタイマーとして使用すると、TA.TMA.BIT.CKSIで選ばれた分周比でCPUクロックが分周され、次に続くカウンターで分周されたクロックを256個数えられます。
実際には0~255まで数えるのですが、TimerAは255から0に戻る時(8bitカウンターがオーバーフローする時)、割り込みがイネーブルされていた場合、割り込みを発生します。
結局20000000÷8192÷256≒9.5Hzで割り込みが発生する事となり、LEDの点滅周期は≒210msとなります。
ビルドを行い、FDTを使ってターゲットに書き込みを行います。
書き込みモードを解除して通常の動作モードに切り替えてみると、LEDがチカチカ点滅しているのを見る事が出来ると思います。
これでダサい?ソフトウエアループを使ったLEDチカチカから、割り込みを使ったスマートなLEDチカチカ(笑)にステップアップできました。
おっと、肝心な事を書き忘れていました。
__interrupt(vect=19) void INT_TimerA(void)
と記述されていますが、最初の__interruptはこの関数が割り込み処理である事を示し、続くvect=19はベクター番号を指定しています。
割り込みベクターはROMの0番地から始まる領域に書く事が決まっており、0番地がベクター番号0となっています。ベクター番号19と書く事でコンパイラは自動的にROMの書き込みアドレス(H8/Tinyの場合は19×2)を計算する事が可能となり、実際にそのアドレスの位置に割り込み関数のアドレスを埋め込みます。
後ろの型の宣言は通常のC言語の関数そのものの宣言です。
ごめんよぅルネサス、ごめんよぅ [RX&SH&H8]
設計が古いなんて書いてごめんよぅ。
最近のラインナップ知らなかったけど、今日どんな物が有るのか見てみたら、SSU(SPI)付きの物も、高速オンチップオシレーター付きの物も有るのね。
最近ではFLASH ROMの耐久性がmin1000回、typ10000回になったのね。
今まではH8/3687F最強と思っていたけれど、、、。
H8/36077が良いよね。3687Fの後継っぽい。
でも、デジキーでは0.8mmピッチのでっかい方のパッケージしか売っていなかったのよ。
と言うか、お願いだから全部入りのやつ出してー。
と言うか、早くRXマイコン触らしてくれー。
※高速オンチップオシレータとかSSUとか、なんとなく三菱の風を感じる。霧が峰か!。
最近のラインナップ知らなかったけど、今日どんな物が有るのか見てみたら、SSU(SPI)付きの物も、高速オンチップオシレーター付きの物も有るのね。
最近ではFLASH ROMの耐久性がmin1000回、typ10000回になったのね。
今まではH8/3687F最強と思っていたけれど、、、。
H8/36077が良いよね。3687Fの後継っぽい。
でも、デジキーでは0.8mmピッチのでっかい方のパッケージしか売っていなかったのよ。
と言うか、お願いだから全部入りのやつ出してー。
と言うか、早くRXマイコン触らしてくれー。
※高速オンチップオシレータとかSSUとか、なんとなく三菱の風を感じる。霧が峰か!。
あれ、今更だけれど [RX&SH&H8]
今更だけれど、H8/Tinyには多重割り込みの機能が無いじゃん。
と言う事は、HOS-V4でH8/Tinyに対応しているけれど、pacintn.src内で行っている多重割込みの処理は要らないという事になるじゃん、、、。
何故今まで気付かなかったのか。
と言う事は、HOS-V4でH8/Tinyに対応しているけれど、pacintn.src内で行っている多重割込みの処理は要らないという事になるじゃん、、、。
何故今まで気付かなかったのか。
T-SH2MBへTCP/IPプロトコルスタックを実装中 [RX&SH&H8]
次の事に進む前に、どうしてもこれが気になるので、ある程度動かせる事を確認してしまおう!と言う話。
TCPやUDPを完成させる前に、ネットワークコントローラや下位ドライバーの足腰を確認しておきます。つまり、うんと負荷を掛けた状態で、なるべくロストを小さくするとか、ハングしないとか、エラー処理がきちんと行われるとか、応答が早いか等ですね。
で、確認にはnmapが取り敢えずお手軽に負荷を掛ける事ができるので、ネットからnmapを落してきて、試してみました。
当然始めは駄目駄目で、ちまちま改良してようやくここまで来ました。
左の図はRAM上(デバックモニタ上)で動かした時の結果で、平均して1.9秒程度で完了します。
ROM上で動かすと、これが結構バスにWAITを入れねば安定しませんでしたが、平均して1.3秒程度で完了します。
まあここまで来れば十分実用になるでしょう。あまりnmap向けチューニングとかやっても実情と合わなかったりもしますので。
TCPやUDPを完成させる前に、ネットワークコントローラや下位ドライバーの足腰を確認しておきます。つまり、うんと負荷を掛けた状態で、なるべくロストを小さくするとか、ハングしないとか、エラー処理がきちんと行われるとか、応答が早いか等ですね。
で、確認にはnmapが取り敢えずお手軽に負荷を掛ける事ができるので、ネットからnmapを落してきて、試してみました。
当然始めは駄目駄目で、ちまちま改良してようやくここまで来ました。
左の図はRAM上(デバックモニタ上)で動かした時の結果で、平均して1.9秒程度で完了します。
ROM上で動かすと、これが結構バスにWAITを入れねば安定しませんでしたが、平均して1.3秒程度で完了します。
まあここまで来れば十分実用になるでしょう。あまりnmap向けチューニングとかやっても実情と合わなかったりもしますので。
SuperHで学ぶμITRON仕様OS―リアルタイムOSの動作原理と実装法がわかる!
- 作者: 鹿取 祐二
- 出版社/メーカー: 電波新聞社
- 発売日: 2005/12
- メディア: 単行本
SH2ボード(T-SH2MB)、H8ボード(T104-H8A、B)の価格 [RX&SH&H8]
T-SH2MBが基本構成で17800円、T104-H8A、Bが基本構成で19800円だそうです。
写真は基本構成と一致しない可能性がありますので、購入前にTAC様に連絡を取って確認してくださいね。
いよいよ、あの付録基板を持っているからと言って、あのベースボードを買う理由が、、、。
若松が何故あの価格で出しているのか?不明らしいです。