※ 念願のプリント基板を製作する前に。
Flash PICの起動

【8】35個の命令・・・

 ミッドレンジPICのアセンブラ命令35個を一覧表にしました。ただし赤文字はFlash PICでは使用しません。
 表中の記号は下記の通りの意味です。

 f → レジスタのアドレス
 Wreg → Wレジスタ
 d → 指定先のことで、d=0ならWregへ d=1ならレジスタfへ
 b → ビット位置(0~7)
 k → 定数データ(0~0xFF)
 addrs → ジャンプ先のプログラムアドレス
_________________________________________________________
 演算結果の状態を表すフラグ(これらのフラグはレジスタ3番(STATUS)にあります)
 Z → ゼロフラグ
 C → キャリーフラグ
 DC → 下位4ビットのキャリーフラグ
 

ニーモニックと
オペランド
読み方説明影響を受ける
フラグ
ADDWF f,dアダーWアンド、f Wregとf レジスタの内容を加算して
指定されるd へ書き込む
Z、C、DC
ANDWF f,dアンドWウイズ、f Wregの内容とf レジスタの内容とで
論理演算のANDをして
指定されるd へ書き込む
CLRF fクリアf f レジスタの内容をゼロにクリアする
CLRWクリアW Wregの内容をゼロにクリアする
COMF f,dコンプリメントf f レジスタの内容を反転する
(bitが1なら0に)
DECF f,dデクリメントf f レジスタの内容を-1して結果を
d へ格納する
DECFSZ f,dデクリメントf
スキップイフゼロ
f レジスタの内容を-1して
結果をd へ格納。
そして結果がゼロなら
次の命令をスキップする
_
INCF f,dインクリメントf f レジスタの内容を+1
して結果をd へ格納する
INCFSZ f,dインクリメントf
スキップイフゼロ
f レジスタの内容を+1して
結果をd へ格納。
そして結果がゼロなら
次の命令をスキップする
_
IORWF f,dインクルーシブオアW
ウイズ、f
Wregの内容とf レジスタの内容を
論理演算のORをして結果をd へ書き込む
MOVF f,dムーブフロムf f レジスタの内容をd へ書き込む。
d=1なら自分自身に書き込む。その場合、
Zフラグの更新をさせることに利用
MOVWF fムーブWトゥf Wregの内容をf レジスタへ書き込む_
NOPノーオペレーティング 何もしない_
RLF f,dローテートレフトf
ウイズキャリー
f レジスタの内容を
キャリーフラグ共に
1ビット左へズラして
d へ書き込む
RRF f,dローテートライトf
ウイズキャリー
f レジスタの内容を
キャリーフラグ共に
1ビット右へズラして
d へ書き込む
SUBWF f,dサブトラクトW、
フロムf
f レジスタの内容に
Wregの内容を2の補数に
直したもので加算する。
これは減算したことになり、
結果をd へ書き込む
Z、C、DC
(結果が正か
ゼロなら
C=1)
SWAPF f,dスワップf f レジスタの内容の下位4ビットと
上位4ビットを入れ替える
_
XORWF f,dエクスクルーシブオアW、
ウイズf
Wregの内容と
f レジスタの内容を
論理演算のXORをして
結果をd へ書き込む
BCF f,bビットクリアf f レジスタのb ビットを
クリア(L)にする
_
BSF f,bビットセットf f レジスタのb ビットを
セット(H)にする
_
BTFSC f,bビットテストf
スキップイフクリア
f レジスタのb ビットがクリア(L)なら
次の命令をスキップする
_
BTFSS f,bビットテストf
スキップイフセット
f レジスタのb ビットが
セット(H)なら
次の命令をスキップする
_
ADDLW kアダーリテラル
アンドW
Wregの内容と
k の定数を加算して
Wregへ格納する
Z,C,DC
ANDLW kアンドリテラル
ウイズW
Wregの内容と
k の定数で論理演算の
ANDをしてWregへ格納する
CALL addrsコール 指定のaddrs へサブルーチンコールする。_
CLRWDTクリア
ウォッチドッグ
タイマー
ウォッチドッグタイマーを
クリアする
_
GOTO addrsゴートゥ 指定のaddrs へジャンプする。_
IORLW kインクルーシブオア
リテラルウイズW
Wregの内容と
k の定数とで
論理演算のORをしてWregへ格納する
MOVLW kムーブリテラル、
トゥW
定数 k をWregへ格納する_
RETFIEリターン
フロム
インタラプト
割り込み処理から
割り込み許可にセットして
戻る
_
RETLW kリターン
ウイズ
リテラルインW
サブルーチンコールされた次のアドレスへ戻る。
その時にWregへ定数 k を格納する
_
RETURNリターン サブルーチンコールされた次のアドレスへ戻る_
SLEEPスリープ スリープモードに入る_
SUBLW kサブトラクトW、
フロムリテラル
定数 k とWregの内容を
2の補数に直したものと加算する。
これは減算したことになり、
結果をd へ書き込む
Z,C,DC
(結果が正か
ゼロなら
C=1)
XORLW kエクスクルーシブオア
リテラルウイズ、W
Wregの内容と定数 k とで
論理演算のXORをして
結果をd へ書き込む

 いかがでしょう。他のCPUと比べたら命令数は比較的少ない方です。でもうんざりですね。私も書いていてうんざりです。(笑)
 でも安心してください。何もこれを全部使用しなければPICをプログラムできない訳ではありません。必要に応じて使用するだけです。難しそうな命令が必要になるその時は、あなたのレベルもそこまで上がってきているはずです。最初はほんとに簡単なところから始めてみましょう。それとFlash PICは何をしても壊れませんので、理解しづらい命令を走らせてみて結果がどうなるかで勉強することができますので、恐れずユックリ行きましょう。

 Flash PICの画面と、この説明画面を交互に見るとき、マウスで切り替えるのはたいへん疲れます。
そこで、〝alt〟キーを押しながら〝Tab〟キーを押して画面を切り替えることをお奨めします。



 では、命令一覧が出てきたところで、プログラムの話に移ります。
 ソースコードを消去したり、初めて起動させた時に、ソースボックスにはあらかじめ下記のプログラムが書かれています。

INDF		EQU	0x00
TMR0		EQU	0x01	;(TMR0は、まだ使用できません)
PCL		EQU	0x02
STATUS		EQU	0x03
FSR		EQU	0x04
TRIS_A		EQU	0x05
TRIS_B		EQU	0x06
TRIS_C		EQU	0x07
PORT_A		EQU	0x08
PORT_B		EQU	0x09
PORT_C		EQU	0x0A
;
		org	0x00
;ここからプログラムコードを書きます。
		MOVLW	0x3F
		MOVWF	TRIS_A
		CLRW
		MOVWF	TRIS_B
		MOVWF	TRIS_C
		MOVWF	PORT_B
		MOVWF	PORT_C

黒字はSFRのアドレスを指定している擬似命令で赤字からがプログラムです。
 これが最初の命令です。


		MOVLW	0x3F

命令一覧の〝MOVLW〟の説明を見ますと・・・。
〝定数 k をWregへ格納する〟となっています。この場合、定数 k というのは16進〝0x3F〟です。この数値をWregに入れる命令です。PICはこの命令を受けると〝0x3F〟をWregに書き込みます。

 次の命令が・・・。


		MOVWF	TRIS_A

と、なっています。同じく〝MOVWF〟の説明を見ますと
 〝Wregの内容をf レジスタへ書き込む〟となっています。Wregは先ほどの命令で〝0x3F〟ですので、その数値をf レジスタ、ここでは〝TRIS_A〟へ書き込みます。この〝TRIS_A〟という文字列は、プログラム先頭部分の擬似命令、〝TRIS_A EQU 0x05〟で0x05という数値にするように指定されていますので、この命令は・・・


		MOVWF	0x05

と、同じことになります。数値だけでプログラムするよりも〝TRIS_A〟と文字にするだけでも格段にプログラムが見やすくなるのが解りますでしょうか。〝0x05〟では数字なのかアドレスなのか一見してわかりませんが、〝TRIS_A〟となるだけで、これはSFRのポートAの端子を入力にするか出力にするかを設定するレジスタのことを表していることが一目瞭然になります。

 ではそのレジスタに〝0x3F〟を書き込むということは何を表しているかです。ここが重要な点です。

 〝0x3F〟を2進数に直してみます。

 〝0011_1111〟となります。解りやすいように4ビット目に〝_〟を入れて書いてみました。

 〝TRIS_A〟については【6】メモリーとレジスタ・・・に書いてありますが、〝1〟とした端子を入力端子に、〝0〟とした端子は出力端子になります。つまり〝0x3F〟を書き込むということは、ビット0~5を〝1〟に、ビット6、7を0にするといういことで、RA0~RA5を入力端子。RA6とRA7を出力端子に設定しているということです。ただし、Flash PICにはRA7という端子がありませんので、ビット7は〝0〟や〝1〟にしても意味はありません。ここでは解りやすいように〝0〟にしています。

 ここまで来るとあとは簡単です。続く命令はこの5行です・・・。


		CLRW
		MOVWF	TRIS_B
		MOVWF	TRIS_C
		MOVWF	PORT_B
		MOVWF	PORT_C

〝CLRW〟 は、Wregをゼロクリアする命令ですので、Wreg=0x00を実行します。
 残りはすべて〝MOVWF〟命令ですので、

 〝TRIS_B〟、〝TRIS_C〟に〝00〟を書き込みます。書き込みデータが〝00〟ですので、すべてのビットが〝0〟のデータです。つまり、ポートB、ポートCをすべて出力端子に設定していることになります。Wregの内容を他のレジスタに書き込んでもWregの内容は変化せず、ずっと〝00〟のままです。

 次の〝MOVWF PORT_B〟〝MOVWF PORT_C〟でも、同じ全ビット〝0〟のデータをそれぞれの出力端子に書き込んでいることになります。
 よってこのプログラムは、走り出してすぐに、ポートAのRA0~RA5を入力端子、RA6、ポートB、ポートCを出力端子にしてポートB、ポートCの出力端子から〝0〟(L)を出力していることになります。

 PICは電源が投入されてリセットが〝H〟になり、プログラムが走り出すと、全ポートは入力ポートに設定された状態からスタートします。プログラムが最初にする仕事がハードウエアの初期設定です。PICが載っているボードに合わせて各ポートの入出力設定を行わなければいけません。それも早急にする必要があります。出力ポートが〝H〟でリレーがONになるような場合、入力端子のままにしておくと、いきなりリレーがONになったりするものがあります。通常はプルダウンなどをして防ぎますが、早く出力ポートに設定して安定した状態にする必要があります。

 なぜ、Flash PICを初期化するとこのようなプログラムが最初から書かれているかというと、Flash PICはハードウエアが完成されたトレーニングボードで、ハードウエアの初期化は常に同じもので、変更することが無いからです。なのでこの部分はあらかじめ書かれているということです。

 ここで、言い訳が・・・。
 RA6端子の初期化がされていません。これはあきらかにバグです。作者の手抜きですね。実際のPICではこのような初期化忘れのバグには気をつけましょう。Flash PICでは特に問題がありませんので手抜きをしています。

 では、この後にLEDを点灯させるプログラムを書いていきます。

 どのLEDを点灯させましょうか・・・。基板の上部に8個のLEDが並んでいます。その部分の回路図です。

ポートCのRC0~RC7がそれぞれのLEDに接続されています。どれでもいいのですが、とりあえず基板左端のLED8を点灯させてみます。基板上では〝LED8〟とシルク印刷されていますが、回路図上ではLED7のラインがそのLEDです。ポートCのRC7端子に接続されています。この端子を〝H〟にするとLEDが点灯するはずです。
 プログラムの続きを書きます。
 ポートCの8番目のビットを〝H〟にする、すなわちプログラム的には〝1〟にする命令は何通りかあります。


方法1
		MOVLW	0x80
		MOVWF	PORT_C

と、やってもいいですし・・・。


方法2
		BSF	PORT_C,7

のひとつの命令でも可能です。

 〝方法1〟の最初の命令、〝MOVLW 0x80〟は2進数〝1000_0000〟をポートCに書き込んでいます。8番目のビット7を〝1〟にしたデータを書き込みますので、たしかにRC7が〝H〟になります。ここで問題発生。今回はRC0~RC6を使用していませんのでこれでもいいのですが、他のポートも使用していた場合、そちらのポートが強制的に〝0〟になってしまい、LED1~7は消えることになります。他のポートに影響が出ない〝方法2〟がこの場合最適です。

〝BSF PORT_C,7〟命令は、f レジスタの8番目のビット7をセット、すなわち〝1〟にせよ、です。この場合のf レジスタはPORT_C ですので、この命令でLED8が点灯するはずです。

 Flash PICのソースウィンドウをクリックして、リストの最終行に次の赤文字の命令をキーボードから書き込みます。

INDF		EQU	0x00
TMR0		EQU	0x01	;(TMR0は、まだ使用できません)
PCL		EQU	0x02
STATUS		EQU	0x03
FSR		EQU	0x04
TRIS_A		EQU	0x05
TRIS_B		EQU	0x06
TRIS_C		EQU	0x07
PORT_A		EQU	0x08
PORT_B		EQU	0x09
PORT_C		EQU	0x0A
;
		org	0x00
;ここからプログラムコードを書きます。
		MOVLW	0x3F
		MOVWF	TRIS_A
		CLRW
		MOVWF	TRIS_B
		MOVWF	TRIS_C
		MOVWF	PORT_B
		MOVWF	PORT_C
		BSF	PORT_C,7



【アセンブル機能】
 書き込んだら《Build》ボタン、 を押します。これでアセンブラが起動して、ソースコードがマシン語に変換されてFPICに転送されます。
 《Build》ボタンを押さないとFPICにプログラムが転送されませんので、ソースコードを変更したら押すことを忘れないでください。エラーがなければ、エラーリストウィンドウに〝現在エラーはありません〟と出ます。


 何らかのエラーが出たら、文字の入力が間違っていないか確認してください。コメント以外はすべて半角で書く必要があります。全角文字が混ざっていませんか? あるいは〝PORT_C〟を小文字で書いていませんか? 擬似命令で〝PORT_C  EQU 0x0A〟としていますので、大文字で〝PORT_C〟とする必要があります。命令のニーモニック部分は大文字、小文字の区別はありませんがラベル名は指定した通りでないとエラーになります。

 命令のニーモニック部分が間違っている場合は〝Syntaxエラーです〟と出ます。ラベル部分が間違っている時は〝ラベル名が見つかりません〟と出ますので区別がつくと思います。エラー部分を修正したら《Build》ボタンを再び押します。

 エラーがなければエラーリストウィンドウを消して、逆アセンブラボックスを見てください。ソースコードに書いたプログラムの部分、〝org 0x00〟から下の部分がマシン語に変換されてFPICに転送されています。その右横にニーモニックやオペランドがマシン語から再変換されて表示されています。ソースコードで書いた命令と同じモノであることを確認してください。このボックスではマシン語からアセンブラ命令に逆変換していますので、逆アセンブラと呼びます。ニーモニックが小文字表示されていて見にくい時は〝Break Point〟ボタンの左にある〝小文字表記〟ボタンを押すと大文字に変わります。

 プログラムアドレス0x000の〝MOVLW 0x3F〟はマシン語で〝0x303F〟となっています。PICは〝0x303F〟というマシン語を〝MOVLW 0x3F〟と判断して動くわけです。

 とりあえず、エラーが無かったらここで走らせてみましよう。 を押します。



補足 ソースウィンドウが邪魔な時は を押します。必要な時は を押します。(この機能はRev5.0から追加されました)


 走り出すと、一瞬LED1~8が全点灯したあとLED8だけが点灯します。そして10秒ほどするとFPICは停止して、逆アセンブラボックスの一番下の〝状態〟欄に〝プログラムエリアを越えました。CPUは停止しています〟と出ます。これは、LED8を点灯させる命令〝BSF PORT_C,7〟の後にプログラムが何も書かれておらず、残りのプログラムエリアの最後まで〝NOP〟何もしない命令を繰り返して、最終的にFPICのプログラムエリアの最後、0x1FFに達したので強制停止したことを意味します。

 本物のPICでは最終番地に達するとまた先頭番地に戻ってループを繰り返しますので、このようなプログラムはしてはいけないお手本です。ではどうするのか・・・。

 PICはスリープモードに入るなどの特別な場合を除いてプログラムを止めることはできません。このような場合は、何も影響の出ない命令を繰り返させて見た目は止まっているようにします。

 次の赤文字のプログラムを追加してください。

		BSF	PORT_C,7
TSTLOP:
		NOP
		GOTO	TSTLOP

TSTLOP:〟というラベルを使用しています。ラベルですので行の先頭から書きます。ラベル名最後の〝:〟コロンは無くても問題ありませんが、ひと目でラベルとわかるので、私は付けるようにしています。
 続いて〝NOP〟となっています。これは何もしない命令です。何もしませんので何も影響が出ません。

 その次が〝GOTO TSTLOP〟となっています。この命令は〝TSTLOP〟というラベルの示すアドレスへジャンプせよ、というものです。
 なので、〝TSTLOP〟へジャンプします。すると、また〝NOP〟となって、その次には〝TSTLOP〟へジャンプです。FPICは疲れを知りませんのでこの命令を永遠と続けます。何もしない命令を延々と続けますので見た目には停止しているのと同じです。

 新しいプログラムが追加されましたので、走らせてみましよう。 を押してエラーが無いことを確認。逆アセンブラボックスに入ったことを確認して、 を押します。


 今度は止まることなく永遠と走っています。このようなループを永久ループといいます。
 こうなるとFPICを止めるには先ほどの《Run》ボタンをもう一度押すしかありません。FPICが走っている時の《Run》ボタンは 《停止ボタン》に変わっていますのでそれを押します。押されると、FPICは強制的に停止します。逆アセンブラボックスの薄オレンジ色になっている部分が停止した位置です。


 《Run》ボタンと  《停止ボタン》を何度か押してみてください。〝NOP〟か〝GOTO TSTLOP〟のどちらかで停止するはずです。




【Break機能】
 Flash PICでは任意のアドレスにプログラムが入ったら自動的に強制停止させる機能があります。これが《Break Point》ボタン  です。
 これを使用すると好きな位置までプログラムが走ったら停止させることができますので、部分的に走らせてプログラムの挙動を把握することができるのでたいへん便利です。

 使用してみましょう。

 逆アセンブラボックスの〝GOTO TSTLOP〟と書かれた部分をマウスで左クリックして薄オレンジ色に変えます。そして《Break Point》ボタン  を押してください。

  
 赤丸に〝B〟のポイントマークが付きました。ここをブレークポイントに設定したことになります。プログラムがこのアドレスに入ると停止します。

 FPICは先ほど《Run》ボタンで強制終了させていますので、プログラムの先頭から走らせる必要があります。そのために《Reset》ボタン を押して、プログラムカウンタ(PC)を〝0x000〟にします。逆アセンブラボックスの一番下の欄の〝PC=〟というところが〝0x000〟となります。

 リセットさせたら、《Run》ボタンを押します・・・。押すのとほとんど同時にブレークポイントの赤丸〝B〟の位置で停止するはずです。



【Watch機能とStep動作】
 任意の場所でプログラムを停止させることができると、次にやりたいのがプログラムをひとつずつ進めて、PICの内部レジスタがどのように変化していくのか見たくなります。これができるとプログラム作りは格段に楽になります。おかしな動きが起きてもどこでおかしくなっているかが一目瞭然になります。

 まず、FPICの内部レジスタの状態を表示させるには《Watch》ボタン を押します。押すと下の写真のようなオレンジ色のWatchウィンドウが表示されます。
ここにはFPICが停止するたびに内部レジスタの内容が表示されます。

 一番上〝PC〟は、停止しているプログラムアドレス(プログラムカウンター値)、その右横にWregの内容。次が、演算結果の状態を表す、〝Z〟ゼロフラグと〝C〟キャリーフラグ、続いて後で述べますが、二つのスタックポインタの内容。その後にレジスタ0番から0x14番までのSFRやRAMの内容がズラリと並んでいます。


補足ソースコードで〝EQU〟定義されていないレジスタは〝undefined〟(未定義)となっています。(Rev5.0で追加された機能です)


 この《Watch》ウィンドウはプログラムを一命令ずつ走らせる《Step》動作と連携で使用しますと効果倍増です。

 《Step》操作をさせるときは を押します。


 実際にやってみましよう。
 まず、基板の〝LED8〟が見えるような位置に《Watch》ウィンドウを移動させます。そして、《Reset》ボタンを押します。《Watch》ウィンドウ内の〝PC〟が赤文字の〝0x000〟となって変更されたことを確認します。《Watch》ウィンドウでは数値に変化があったものだけが赤色になります。またFPICはリセットするとレジスタの内容はランダム値になるものがあります。


 リセットされてプログラムが先頭に戻りましたので、逆アセンブラボックスの薄オレンジ色のラインもプログラムアドレス〝0x000〟へ戻っています。


《Step》ボタン をマウスで左クリックします。


		MOVLW	0x3F

最初の命令が実行されて停止します。
最初はWregに〝0x3F〟を入れる命令ですので、左の写真のように《Watch》ウィンドウのWregの欄が赤色の〝0x3F〟と変化しています。正しくWregに〝0x3F〟が書き込まれていることが解ります。


 《Run》ボタンでプログラムを走らせると、一瞬全LEDが点灯するのが見えましたが、この理由もはっきりわかります。


 プログラムアドレス0x004番地の命令を実行したあとにLED1~8が全部点灯します。この理由は0x004番地の命令、〝MOVWF TRIS_C〟にあります。これはポートCの全端子を出力端子にするものです。

左、《Watch》ウィンドウのレジスタ0x07番〝TRIS_C〟をご覧ください。
 0x004番地でポートCの全端子が出力端子になったので、その時のポートCの内容が端子に転送されているのです。ポートCの内容はレジスタ0x0A番地の〝PORT_C〟レジスタです。《Watch》ウィンドウの〝PORT_C〟は〝0xFF〟になっています。全端子に〝1〟が転送されたためにすべてのLEDが点灯するのです。そしてプログラムが進んで行き、プログラムアドレスが0x006の〝MOVWF PORT_C〟で、やっとレジスタ〝PORT_C〟が全ビット〝0〟にクリアされて、LEDが消灯します。これが、一瞬LEDが全点灯する理由です。

補足もしLEDが瞬間でも全点灯するのが都合悪い時は、先に〝0x00〟を〝PORT_C〟に書き込んでから、〝TRIS_C〟で全出力に設定すると点灯しなくなります。

補足:ポートの挙動はどちらが正しいの?
2012.10.07

以前、上記補足で、先にポートへ0を書き込んでから出力ポートに設定をすると、0が出ると書きましたが、私の使っているICEの挙動では、そのような動きにはならず、入力ポート時に0を書き込んでおいてもそれは無効になり、入力状態が出力ポートへ反映されるような動きをしていました。

 しかし、PICのマニュアル内のに書かれているブロック図を見る限り、入力ポートに書き込まれたデータはそのままラッチされ、出力ポートに切り替わるとそのデータが出てくるような回路になっています。
 もし、ICEの動きが正しければ、FlashPICの動きが間違っていることになります。
 このあたりどちらが正しいのか、只今調査しております。解り次第ここにて発表させていただきます。


《Watch》ウィンドウと《Step》動作を利用すると、ここまではっきり解ります。

 プログラムをそのままステップさせていきますと、やがて〝BSF PORT_C,7〟でLED8が点灯します。あとは〝TSTLOP〟をぐるぐる廻るだけです。




【8】35個の命令・・・ ----------(ここまで)