【10】もっと高度なプログラムへ・・・
前回はFlash PIC ボード1の出力ポートを利用したLEDの点滅処理でした。今回はさらに入り込んで、同じボード1のパーツをフル活用してみます。
基板上のスイッチ(SW1)を押すと〝ちきどん〟が動き出し、取り付けられているマイクロスイッチ3番が押された回数を基板上の7セグメント表示器に表示して、10回数えると〝ちきどん〟を停止させるというプログラムを作ってみましよう。
プログラムする前準備として、使用するポートをまとめて一覧を作ってみました。ボード1の回路図も合わせてご覧ください。
黄色の項目が、PIC外部の状態を読み取る部分で、水色の項目がPIC外部に出力する部分になります。中でも難しいそうなところは、数字を表示させる7セグメント表示器の部分ではないでしょうか。
基板上のSW1がONの時 | RA0が〝L〟になる |
基板上のSW1がOFFの時 | RA0が〝H〟になる |
ちきどんのマイクロスイッチ3がONの時 | RA3が〝L〟になる |
ちきどんのマイクロスイッチ3がOFFの時 | RA3が〝H〟になる |
ちきどん動かす | RA6を〝L〟にする |
ちきどん止める | RA6を〝H〟にする |
7セグメント表示器 | PB0 ~ PB6(〝H〟で点灯) |
7セグメント表示器というのは、LEDが〝8〟の字になるように並べられており、好きな位置のLEDを点灯させて数字を表示させるパーツです。
ひとつひとつのLEDをセグメントといい、a~g の記号が付けられています。そして、各セグメントのアノード側は〝SEGa〟~〝SEGg〟、〝SEGdp〟の端子へ、さらに全てのセグメントのカソード側の端子はまとめられてコモン(COM)端子に出てきます。右の図ではそのままGNDへ接続しています。
たとえば数字の〝2〟を表現したい時は、〝a、b、g、e、d〟のLEDを点灯させることになりますので、
接続されている〝SEGa、SEGb・・・〟を追っていきますと、PB0・PB1・PB3・PB4・PB6を〝H〟に、PB2・PB5を〝L〟にすることになります。実際にこれをソフトウエアで行うには、ポートBに2進数〝01011011〟、16進数に直して、〝0x5B〟を出力すればいいことになります。ビット7番はPB7=SEGdpに接続されていますので、小数点LED(DP)は消しておきますので、〝0〟になっています。
数字は0~9まで必要ですので、それぞれ書き出しますと以下の表のようになります。
数字 | 2進数 | 16進数 |
〝0〟 | 00111111 | 0x3F |
〝1〟 | 00000110 | 0x06 |
〝2〟 | 01011011 | 0x5B |
〝3〟 | 01001111 | 0x4F |
〝4〟 | 01100110 | 0x66 |
〝5〟 | 01101101 | 0x6D |
〝6〟 | 01111101 | 0x7D |
〝7〟 | 00100111 | 0x27 |
〝8〟 | 01111111 | 0x7F |
〝9〟 | 01101111 | 0x6F |
この表をプログラムのどこかに作っておき、数字に応じたデータを読み出して、ポートBへ出力すればいいことになります。このように表から必要なデータを読み出すことをテーブル参照といいます。
どうやって必要なデータを取り出せばいいのでしょうか。他のCPUではプログラムに書かれている数値(データ)を直接読み取る命令がありますが、PICにはそのような命令がありません。その代わりサブルーチンコールから戻る命令〝return〟命令に、もうひとつ別の〝retlw k〟という命令があります。この〝k〟の部分は好きな数値を書くことができます。例えば〝retlw 0xFF〟という命令はサブルーチンコールした次のアドレスへ戻ると共に、Wregに16進数〝0xFF〟を入れて戻ります。C言語でいうところの戻り値の代入です。ですので、PICの場合はテーブル参照に近いですが、テーブルジャンプと呼んだ方が正しいかもしれません。
実際にプログラムで書くと次のようになります。
|
プログラム先頭でWregに0x03を入れていますが、これは Wreg が例えば0x03の時という意味です。実際は0~9が来ます。
次の、〝addwf PCL,to_F〟ですが、これは現在のPCL(プログラムアドレスの下位8ビット)にWregを加えて、〝to_F〟ですので、元のPCLレジスタに格納します。この命令を解析している段階で、PCLレジスタは〝addwf PCL,to_F〟の次、〝retlw 0x3F〟の部分になっていますので、この命令を実行した途端、プログラムアドレスは3つ先の〝retlw 0x4F〟へ飛んでいます。そして、〝retlw 0x4F〟を実行して、Wregに〝0x4F〟を入れて戻るということになります。これで、Wregに0~9の値を入れて、この処理をサブルーチンコールすると、対応する値をWregに入れて戻るという処理が完成します。
この処理の最初で、Wregが0~9以外の数値が入って来たとき、どうするかという注意が抜けています。もし、Wreg=0xFFの状態でこのサブルーチンをコールされたら、とんでもない先へジャンプして、その後は暴走することは間違いないと思います。 このようなことも考慮すると、先ほどの処理は下記のようになります。
|
▽ 論理積 △
最初の命令〝andlw 0x0F〟でWregに入っている数値を0~0x0Fに丸めてしまいます。これを〝0x0F〟でマスクするといいます。〝andlw 0x0F〟命令は、Wregに入っている値に数値〝0x0F〟と論理積演算を行って、Wregに格納する・・・ですが、論理積や論理和というのは普通の世界ではまずお目にかかれない数値演算です。しかしコンピュータの世界では頻繁に出てきます。あまり難しく考えないで、そういうもんだ、こういうときに使用するのか、程度に今は考えておいた方がいいです。
Wregに〝0x9F〟が入っているときに〝andlw 0x0F〟を行った時の動きを詳しく説明します。
解りやすいように2進数に直します。
|
〝論理積〟は〝1〟と〝1〟を論理積演算した時だけ、結果が〝1〟になりそれ以外は全て〝0〟になります。2進数ですので〝2〟や〝3〟などという数字は出てきません。すべて〝0〟か〝1〟だけの演算になります。
この、論理積の〝1〟と〝1〟は〝1〟になる。それ以外は〝0〟という性質を利用して、数値を任意の桁でマスクしているというわけです。
〝論理積は数値をマスクする時に使う命令〟と、今は考えておきましょう。。
実際は0~9でマスクしたいのですが、0~0x0Fでマスクして、16進数の0x0A~0x0Fまでのデータも拡張しておいた方が、後々便利ですのでこのようにしました。また、この処理をコールする前に変な数値にはならないように考慮したプログラムを組んでおくという手もあります。
Wregの値を7セグメントデータに変換するサブルーチンですので、この処理を〝GETDIG〟という名前にして、今回のプログラムを全て書き出しました。
|
かなり長くなりましたが、上記のプログラムを選択コピーしてFlash PIC のソースウィンドウボックス内に貼り付けて、送り込むことができます。
その前に簡単にプログラムの説明をします。
|
ここまではいつものと同じ、アセンブラに対して指示をしているところです。今回のマイクロスイッチの回数を記憶するメモリを〝COUNT〟という名前にして、番地を〝PORT_C〟の次の番地にしています。〝PORT_C〟は 0x0A ですので〝COUNT〟は 0x0B になります。
|
ここからプログラムの始まりです。初めの部分はポートの入出力ピンの設定を行ってます。〝PORT_B、PORT_C〟は全部出力ピンにして〝0〟を出力しています。〝PORT_A〟はビット6番のみ出力ピンで、その他は入力ピンです。設定値はアセンブラの指示部分で〝PA_INI EQU 0x3F〟としていますので、上記のプログラム内で〝TRIS_A〟には〝0x3F〟が書き込まれます。
最初に回数を数えるメモリをゼロクリアして、その数値=0を7セグメント用のデータに変換して、〝PORT_B〟へ出力しています。これで、表示器には〝0〟と表示されるはずです。
次に、ラベル〝WATSTART:〟で〝PORT_A〟のビット0番が〝L〟になるのを待っています。〝L〟になると〝PORT_A〟のビット6を〝L〟にして〝ちきどん〟をスタートさせます。
|
ラベル〝M_SWOFF:〟で〝ちきどん〟についているマイクロスイッチ3番がON(〝L〟)になるを待っています。ONになるとカウント値を+1して格納します。このときに数値が〝10〟(16進=0x0A)になっているか〝0x0A〟を引き算してみて=0になれば〝10〟になっていると判断します。引き算後の結果をWregに入れているのは、〝COUNT〟へ戻すと計数結果が壊れてしまうためです。そして引き算して結果が〝=0〟になれば、〝STATUS〟レジスタのビット2番(Zeroフラグ)が〝=1〟になります。ならない時は〝STATUS,2=0〟のままですので、次の命令〝goto ENT_END〟をスキップして、表示データを求めて7セグメントへ出力します。
ラベル〝M_SW_ON:〟で次に〝ちきどん〟のマイクロスイッチ3番が〝OFF(H)〟になるのを待っていますが、これは〝ちきどん〟の回転速度が遅い時、〝OFF〟を確認せずにそのままにしておきますと、PICの方が処理が速いため、1回の〝ON〟で数回
カウントしてしまうからです。
補足:Flash PIC では速度が遅いため、スイッチのチャタリングは考慮しておりませんが、実際のPICで上記のプログラムを組んだ場合、チャタリング現象のため正しくカウントされない場合があります。その時は、前回〝【9】少し高度なプログラム・・・〟で説明した〝二重カウンタ〟による時間潰し処理を〝M_SWON:〟の手前に入れることをお奨めします。
計測カウンタが〝10〟になりますと、〝0x0A〟を引き算したところで、〝STATUS,2=1〟となり、〝goto ENT_END〟に処理が移り、〝ちきどん〟の停止を行って、表示器に〝-〟を表示して、無限ループに入り実質停止します。
【11】実験しよう・・・
できたプログラムを走らせて、ただ「ふ~ん」で終わっていては、PICの達人にはなれません。(なる気は無い?)
Flash PIC の便利なシミュレータ機能を利用して、本当にその通りの動きをPICがしているのか実験してください。何をしても壊れませんので思う存分やってください。
7セグメントデータに変換する前に、数値を論理積演算でマスクしていますが、本当にそうなっているか試してみましよう。
まず、Flash PIC を起動させ、編集画面(EDIT ウィンドウ)のソースウィンドウボックスを全て選択してから削除して、中身を空っぽにしてください。
先ほどのプログラムを、もう一度下記に書き出します。それを全てマウスで選択して右クリック〝コピー〟を行って、Flash PIC の空っぽのソースウィンドウボックス内をクリックした後に右クリック〝貼り付け〟を行ってプログラムの転送をします。
|
転送ができましたら《Build》ボタン、 を押します。これでアセンブラが起動して、ソースコードがマシン語に変換されてFPICに転送されます。
《Build》ボタンを押さないとFPICにプログラムが転送されませんので押すことを忘れないでください。エラーがなければ、エラーリストウィンドウに〝現在エラーはありません〟と出ます。
エラーが出たら、そのエラー行の周辺をよく見てください。コメント行以外の全角文字やスペースもエラーの原因です。
まずは普通にPICを走らせて、動きを見てください。編集画面(EDIT ウィンドウ)が邪魔な時は〝Simple〟ボタンを押して小さくしたり、画面の下の方に移動させます。また、〝ちきどん〟の回転速度は電圧計の下の〝SpeedUp〟ボタンで速くすることができます。
表示器が9を超えて10回目で〝ちきどん〟が停止して〝-〟表示が出れば大成功です。〝HALT〟ボタンを押してプログラムを止めます。では、数値マスクの実験をしてみましよう。
|
〝movf COUNT,to_W〟と書かれていた部分を〝movlw 0x9F〟に書き換えて〝Build〟ボタンを押しアセンブルします。
〝CALL GETDIG〟を呼ぶ時にWregに〝0~0x0F〟以外が入っていると暴走するため、数値を〝0x0F〟にマスクしていますが、果たして本当にそうなるのか、ここで〝0~0x0F〟以外の数値を無理やり入れて、正しく動くかシミュレーションしてみます。
ソースウィンドウボックス左隣にある逆アセンブルボックスのアドレス〝0x0A〟番地に上記の変更部分がアセンブラされて出ていますので、そのラインをクリックして選択し、上の〝Break Point〟ボタンを押して、ブレークポイントを設定します。こうしておくとプログラムがここを通った時に、自動的に停止します。
走らせる前に、〝Reset〟ボタンを押してプログラムを先頭から走らせるようにします。押さずに走らせますと、前回止めたところから走り出しますので注意してください。
〝Reset〟を押してから〝Run〟ボタンを押します。すぐにプログラムは先ほどのブレークポイントで停止します。
下記がブレークポイントで止まったプログラムを見るために、〝Simple〟ボタンを押してソースウィンドウボックスを消し、〝Watch〟ウィンドウを出したところです。
プログラムがブレークポイントのアドレス〝0x00A〟で止まっているのが解ります。
さてここから、数値がマスクされて7セグメント表示データに変換され、戻ってくるまでをスローモーションで見てみましょう。上の写真では〝movlw 0x9F〟を実行する前で止まっていますので、〝Wreg=0x00〟となっています。
〝Step(S)Key〟ボタンを1回押します。
プログラムが次に進んで、〝Wreg=0x9F〟となりました。
〝Step(S)Key〟ボタンを1回押します。
プログラムが〝CALL GETDIT〟を処理して、サブルーチン〝GETDIT:〟へ飛びました。〝Wreg〝は、〟0x9F〟のままです。
〝Step(S)Key〟ボタンを1回押します。
〝Watch〟ウィンドウを見てください。〝andlw 0x0F〟が実行されて、〝Wreg=0x9F〟が〝0x0F〟となり、マスクされたことが解ります。
続いて、プログラムアドレスを表す〝PC〟を見てください。〝0x24〟になっています。次で変化しますので・・・。
〝Step(S)Key〟ボタンを1回押します。
〝addwf PCL,to_F〟が解析のため読み込まれた(フェッチといいます)瞬間に、プログラムアドレスは次の〝0x25〟になります、そして〝addwf PCL,to_F〟が実行されて、プログラムアドレスにWregの〝0x0F〟が加算されて〝PC〟が〝0x034〟となりました。
Windowsの電卓を[表示]→[関数電卓]にして16進加算で、
〝0x25+0x0F=0x34〟となることを確認してください。
〝Step(S)Key〟ボタンを1回押します。
〝retlw 0x71〟命令で〝Wreg=0x71〟という7セグメントの表示データを持って、サブルーチンコールした次の番地へ戻ってきています。〝0x71〟というデータは7セグメントに流すと〝F〟という文字になるはずです。
〝Step(S)Key〟ボタンを1回押しますと、プログラムが次に進んで、
7セグメントデータ〝0x71〟が流し込まれ〝F〟と表示されました。
イロイロな数値に変えて、マスクされる様子や、テーブルジャンプの様子を観察してください。また論理積を論理和〝iorwf 0x0F〟にするとどうなるのかなどの実験もできます。
論理積や論理和は2進数にして考えると、理解しやすくなります。
テーブルジャンプで、説明していなかったことがひとつあります。〝addwf PCL,to_F〟ではプログラムアドレスの下位8ビット(PCL)に対して加算されるということでしたが、下位8ビットから溢れるような加算データが来た場合どうなるのか、これもFlash PIC でシミュレーションしてみましょう。
|
〝GETDIG:〟を〝Wreg=0x0A〟で呼ぶように、ラベル〝WATSTART:〟の手前(赤色部分)に〝movlw 0x0A〟と入れます。そして、ラベル〝GETDIG:〟の手前に〝org 0x0F8〟と入れて〝Build〟ボタンを押してアセンブルします。〝org〟擬似命令はラベルと区別するために左端から、ひとつ以上のスペースあるは〝TAB〟が必要です。
〝org〟擬似命令は、これが書かれた以降のプログラムを展開するアドレスを指定できますので、〝GETDIG:〟サブルーチンをプログラムアドレス〝0x0F8〟から並べられます。どのようにアセンブルされたかは下記の写真をご覧ください。
ご覧の通り、アドレス〝0x0F8〟から〝GETDIG:〟が入っています。何をやろうとしているのかは、想像できると思いますが、こうすると、Wregに0x05以上の数値を入れて〝GETDIG:〟をコールすると、プログラムアドレスの下位8ビットから溢れる計算になるはずです。すると、どうなるかの実験です。それをするために〝GETDIG:〟を0x0F8から展開させ、さらに〝0x0A〟をWregに入れてコールするプログラムに改造してアセンブルしたというわけです。
補足:8ビット=1バイトでは0~0xFFまでの数値しか扱えません。桁あふれした数値は通常は上位桁に加算しますが、〝addwf PCL,to_F〟は桁あふれを無視します。その結果どうなるか・・・の実験です。実際のPICでもこれと同じ現象が起きます。
例の如く、〝movlw 0x0A〟のところにブレークポイントを設定してあります。〝Reset〟ボタンを押した後、〝Run〟ボタンを押すと、〝movlw 0x0A〟のところで停止します。
〝Step(S)Key〟ボタンを3回押すと、下記の状態になります。
Wregに0x0Aが入れられて、〝GETDIG:〟が呼ばれて、〝0x0F〟でWregがマスクされたところで停止しています。
次に〝Step(S)Key〟ボタンが押されると、プログラムアドレスは〝0x0FA〟になり、それにWregの〝0x0A〟が加算されますので、Windowsの電卓で先に計算すると、答えは
〝0x104〟となります。そうなるのでしょうか・・・。
〝Step(S)Key〟ボタンを押すと・・・。
結果は、とんでもないところにプログラムのポイントが飛んでいます。〝Watch〟ウィンドウのPCが〝0x004〟を差しています。逆アセンブラボックスもアドレス=〝0x004〟の〝clrw〟へ飛んでいます。
これが暴走です。プログラムがでたらめな場所に飛んでしまい、正しく動かなくなっています。
原因はPCLの加算で、桁溢れした分が上位へ加算されずに、下位8ビットだけがPCLに代入されたので、プログラムアドレスが〝0x004〟になったということです。
このような現象は8ビットPIC全てに起きる現象です。Flash PIC だから起きたというのではなく、ここまで正しくPICをシミュレートしているという証ではないでしょうか。
上記の実験プログラムのように、あなたが製作中のプログラムも正しく動くのか、部分的にFlash PIC に突っ込んでシミュレーションするという使い方をどしどし行ってみてください。Flash PICはどのような数値を突っ込んでも壊れることは絶対にありませんので、イロイロ実験をして遊んでみましよう。
Copyright(C) 2004. D-Space Keyoss. All rights reserved