情報電子工学演習の PIC マイコンの工学実習で、 竹野がサポートとして何年か見てきた中で、 実際に受けた質問や、感じた疑問などを適宜まとめあげたものです。
ただし、実際に PIC マイコンを使ったことはなく、 佐藤先生の資料に書いてあることや 簡単に MPLAB を触わったことなどを元に、 私が考えて書いているだけのことなので、 内容が正しいという保障はありませんし、 適切な回答であるとも限りません。
佐藤先生の講義のページ http://www.esato.net/ex/micom/index.html も参照してください。
(10/26 2011: shige; 11/10 2011, 01/12 2012 修正: shige)わかりません。 佐藤先生の資料に書いてあることや 簡単に MPLAB を触わったことなどを元に、 私が考えながら書いているだけです。 また、間違ってはいないけれども、 適切ではない回答もあるかもしれません。
なお、もし間違いを見つけたら報告して頂けるとありがたいです。
(10/26 2011: shige)いいでしょう。 ただし、内容が正しいという保障はありませんので、 自己責任でお願いします。
(10/26 2011: shige)PIC マイコンのプログラムは、 数千分の 1 秒、数万分の 1 秒で次の行に進みますから、 すぐに次の LED の点灯を変える行に行ってしまい、 人間の目には分からない位の時間で LED のパターンが変わってしまうことになります。 よって、ある程度の時間 LED を光らせたままにするために、 WAIT1 や WAIT0 のルーチンへジャンプして時間かせぎをします。
WAIT1 は 100ms (= 100 ミリ秒 = 0.1 秒)、WAIT0 は 1 秒間の間だけ 無意味な作業を繰り返すことで時間をかせぐ、というためのサブルーチンです。 よって例えば LED を点灯させた後で「CALL WAIT0」とすると、 PIC マイコンは 1 秒間だけ LED には関係ない無意味な作業をして 元の場所に戻るので、その 1 秒間は LED がついたまま変化しないことになります。
(10/10 2011: shige)このサンプルプログラムでは、スイッチの変化を 以下のようにして判断しているようです:
つまり、少し時間を開けて 2 回スイッチの状態を調べることで、 その間にスイッチが切り替わったかどうかを見ているわけです。 その回数を HENKA という変数にカウントして、 それを SELECT1 以下で使用しています。
(10/26 2011: shige)データの最小単位は 0 か 1 を表す 1 ビット (bit) で、 それを 8 つひとまとまりで通常は「1 バイト」(byte) といいます。 「通常は」というのは、今は 8 ビットを 1 バイトということが標準となりましたが、 昔は必ずしもそうではなかったようです。 バイトは、コンピュータがビットのまとまりとして扱うデータの最小単位、 といったようなものだったために、 昔はコンピュータ (CPU アーキテクチャ) 毎にそのサイズは違っていて、 6 ビットや 7 ビット, 12 ビットなどを 1 バイトと数えるものもあったそうです。
「ワード」も同様で、コンピュータ (CPU アーキテクチャ) 毎に違うのですが、 コンピュータが一度に処理できるデータの大きさをワード、 と呼ぶことが多いようで、 そのため数バイトのまとまりが 1 ワード、ということも多いようです。
専門家ではないので詳しくは知りませんが、 現在は、「1 バイト = 8 ビット」があまりに定着しすぎたため、 逆にチップによっては 8 ビットでないバイトという単位を用いると 誤解を招くことが多いので、 PIC マイコンのように処理単位が 8 などではない部分があるチップでは その単位を「バイト」と呼ぶ代わりに「ワード」と呼んでいるのではないかと 想像します。
資料 7 ページに、実習で使用している PIC16F648A のプログラムメモリマップとデータメモリマップの図がありますが、 実はこの両者では最小単位が違っています (そのために長方形の横幅が少し違っていることがわかるでしょうか)。
よって、「4096 ワードのプログラムメモリ」というのは、 資料 7 ページの 0000h から 0FFFH が 163 = 4096 行ある、 ということを言っていて、それがそれぞれ 1 ワード、 すなわち全部で 4096 x 14 ビット用意されている、ということを意味します。 アセンブラ命令は、すべて 1 ワード (= 14 ビット) なので、 丁度アセンブラソースの命令 1 行がこのメモリの 1 行のところに保存されることになります。
なお、プログラム領域ではなくデータ領域の方は最小単位は 8 ビット (= 1 バイト) なので、データは通常のように 1 バイト単位で考えてください。
(12/22 2011: shige)WAIT1 は 0.1 秒の時間かせぎをするサブルーチンですから、 これを 2 回呼べばいいわけです。簡単にやるには、
CALL WAIT1
CALL WAIT1
のように 2 回 WAIT1 に飛ぶようにすればいいだけです。
また、WAIT0 のところを見ればわかりますが、 WAIT0 は WAIT1 を 10 回繰り返すことで 0.1 × 10 = 1 秒の時間かせぎをするサブルーチンなので、 それと同じことを 2 回やればいいわけです:
MOVLW D'2'
MOVWF UC
CALL WAIT0A
これと同じようにすれば、0.3 秒、0.4 秒なども簡単に作れます。
(10/10 2011: shige)
基本的には、資料 5 ページの以下の部分はおまじないだと思って、 サンプルをそのまま使ってください:
上のものは、変更すると動作しなくなる可能性が高いので、 そのまま使用してください。 それぞれの意味が知りたければ、 何らかの PIC マイコンの本を参照してください (私にもよくわかりません)。
また、資料 6 ページの「WAIT0」から始まる 「時間かせぎ」のサブルーチンも多分必要になるでしょうから、 これもそのまま使うといいでしょう。
この 4. は、WAIT0 以下のサブルーチンで変数として使われている UA, UB, UC を宣言している部分なのでこれが必要です。
(10/10 2011: shige)資料 5 ページのサンプルにある「ユーザエリア」の部分に、 以下ような形式で必要な変数を書けばいいです:
#DEFINE [変数名] [値を置く番地]
たとえば、資料 5 ページにある UA, UB, UC は WAIT0 等で使われている変数、
KAISU, HENKA は本文で使われている変数です。
もちろん、値を置く番地は別の変数と重ならないようにしないといけません。
資料 7 ページにあるように、変数は 20H から 7FH の領域に置けますので、
かなり多くの変数を使うことができます。
(10/10 2011: shige)
2 進数の並びを '' (単一引用符の組) で囲んだ前につける「B」の文字は 「ポート B」を意味しているのではなく、 資料 7 ページにあるように「2 進数」を示しています。 よって、その「B」を「A」に変えてはいけません。
ポート A へデータを出力する際は、 MOVLW でデータを W レジスタに送って、 その後に「MOVWF PORTA」とします。 ポート B へ出力したければ、 MOVLW でデータを W レジスタに送って、「MOVWF PORTB」とします。
(10/10 2011: shige)サンプルプログラムでは「INCF HENKA,1」とやっていますが、 この HENKA の後ろに書いてある 1 とは「1 増やす」という意味ではありません。
4 ページの表を見るとわかりますが、この 1 は「d == 1」を意味していて、 4 ページの表の下にあるように、 それは f として指定したレジスタに結果を保存することを意味しています。 この d には 0 か 1 のみを指定します。
値を 2 増やしたければ、INCF を 2 回行うか、 ADDWF などを使って行います。
(10/26 2011: shige)まずいくつかに分けて見ましょう。
これは、この行が「エラー」(間違い; Error) に関する行であることをまず 示しています。 エラーではなくて、間違っている可能性がある、という程度の「警告」 の場合には、「Warning[XXX]」のように表示されます。
113 は、MPLAB が各エラーに割り振っている番号 (エラーコード) のようです。 help でこのエラーコードから意味をたどることもできるようです。
これは、このエラーが起きたソースファイル名と、 そのエラーが起きた箇所 (行番号) を意味しています。 よって、そのファイルの 49 行目に問題があるとレポートしているわけです。
ただし、プログラミングの世の常で、 49 行目が直接問題なのではなくて、その前の行や、 もっと前の方で定義していてその行で使っているマクロとかに問題がある、 という場合もあります。
これは、アセンブラが出しているこのエラーの理由です。 いずれも英語ですが、それほど難しくないのでちゃんと読んでみましょう。 この場合は、「記号や文字列 (Symbol) が、 あらかじめ (previously) 定義されていない (not defined)」 と言っています。
普通は最後の「(s)」の部分に、 アセンブラがわからなかった文字列が示されます。
ちなみにこのエラーは、多くの場合は変数名やラベル名、 命令名のスペルミスだと思います。 最後に書かれている不明な文字列のスペルが正しいか確認してみましょう。 特に O (大文字のオー) と 0 (数字のゼロ)、 l (小文字のエル) と 1 (数字のイチ) などはわかりにくい間違いです。
(10/27 2011: shige)「Q 1.3: 資料 2 ページの「4096 ワード」ってどういう単位ですか」 にも書きましたが、 PIC マイコンのアセンブラ命令は 1 命令がいずれも 1 ワードに対応していて、 PIC16F648A はプログラムエリアが 4096 行用意されているので、 簡単に言えば 4000 行位書けることになります。
ただし、資料 7 ページの図にあるように、 先頭は多少予約されている部分がありますし、 実は GOTO や CALL 命令で一度にジャンプできるのが 07FFh までなので、 その先までプログラムを書くとちょっと面倒なことをしないといけないようです。
よって、「簡単にプログラムが書けるのは 2000 行位、 かなり頑張れば 4000 行位書ける」というのが正確な表現になります。
なお、プログラムの 1 行の単位とは、例えば「MOVWF PORTB」という行で言えば 「MOVWF」の部分だけを意味するわけではなくて、 「MOVWF PORTB」全体が 1 行、マシン語で 1 ワード (= 14 ビット) で表現されているようです (詳しくは、PIC マイコンに関する成書を参照のこと)。 そのためにジャンプできる距離に制限があったり、 直接データ同士の処理ができなかったり、 命令が少なかったりするのですが、 ぎりぎりそのように押し込んでいるようです。
(12/22 2011: shige).asm という拡張子のついた名前のファイルであれば、 MPLAB はそれをアセンブラソースファイルとみなして キーワードなどに色をつけてくれます。
よって、色がついていないのは、 まだそのプログラムファイルを保存していないか、 その名前に .asm という拡張子をつけて保存していないかのどちらかです。
(10/10 2011: shige)MPLAB IDE のメニューで [Edit]→[Properties...]→[Text]→[Select Font] と進めばフォントを変更できます。
(10/27 2011: shige)MOVLW などのアセンブラ命令は小文字で movlw のようにと書いても構いません。 データの型を示す 10H, D'10', B'1001' なども 10h, d'10', b'1001' と書いても構わないようです。
ただし、変数を大文字で UA のように定義した場合、 これを ua と小文字で使ってはいけません。 つまり変数名の大文字と小文字は区別されます。 同じ理由で、あらかじめ定義されている PORTA, PORTB なども小文字で使用することはできません。 逆に小文字で定義してあれば小文字で使います。
ジャンプラベルも小文字でも大文字でも定義でき、両者は区別されるようです。
なお、MPLAB 自体が特別な処理をするための キーワードをいくつか持っているようで (例えば if や upper, code など)、 その名前と一致するラベル名などを使うとエラーになりますので、 ラベル名や自分で定義する変数名は、 なるべく一般的な用語ではないものを使用しましょう。 例えば、上のような文字列でも、 番号を最後につけて if0, upper1, code3 などとすれば問題ないようです。
(10/27 2011: shige)MPLAB のバージョンによってインクルードファイルの中身が 少し変わっていることによるエラーだそうで、 こういうものがでることがあるそうです。
この場合は、先頭に書いてある「INCLUDE」の下にある行の、 最後の「& _DATA_CP_OFF」を消すとうまくいくそうです。
(01/12 2012: shige)計算機実習室のどこにあるか、という意味ならば、 現在 (01/12 2012) は、 「スタート」→「すべてのプログラム」→「CAD & グラフィックス」 の中に MPLAB IDE があります。
自分の PC 等にインストールしたい、ということであれば、 以下のマイクロチップ・テクノロジー・ジャパンのページからダウンロードできます。
「ダウンロード」→「MPLAB Integrated Development Environment」 と進むと、英語のページ (Microchip Technology) になりますが、 その一番下に最新の MPLAB IDE があります。または、 一番下にある「Arichives are available. here」のところに進めば、 各バージョンの MPLAB IDE がダウンロードできます。
(01/12 2012: shige)スイッチが入ったかどうかを知るには、2 つの方法があります。
この場合、普段は振動スイッチのことを忘れて処理をさせていて、 振動スイッチによる「割り込み」が起きたときに 現在の処理を中断して、 割り込み処理のサブルーチンへジャンプしてそちらの処理を行って、 必要なら元に戻る、という形式。
この場合は、なんらかの動作をし続ける (あるいは何もしない) 無限ループ内に振動スイッチの状態をチェックする処理を入れることで、 短い時間間隔毎に振動スイッチの状態を調べ続ける、という形式。
前者の割り込みの方がシンプルなようですが、 こちらの方がプログラムは難しくなるので、通常は後者を使います。
スイッチが ON の場合と OFF の場合で点灯パターンを変える、 というプログラムは、 例えば以下のような形で行えます。
; ON の場合の動作
SUBON
MOVLW B'1110000'
MOVWF PORTB ; 点灯パターン A で点灯
ONLOOP
BTFSC PORTA,5
GOTO SUBOFF ; ON なら SUBOFF へ
GOTO ONLOOP
; OFF の場合の動作
SUBOFF
MOVLW B'0010000'
MOVWF PORTB ; 点灯パターン B で点灯
OFFLOOP
BTFSS PORTA,5
GOTO SUBON ; OFF なら SUBON へ
GOTO OFFLOOP
ONLOOP, OFFLOOP の部分がスイッチの検出のみを行う無限ループになっています。
なお、上のそれらのループには時間を無駄につぶすものが入っていないので、
スイッチの切り替え時に起こる
チャタリングの影響を受けやすくなっています。
必要なら「CALL WAIT1」などを入れて、
その影響を押さえる方がいいかもしれません。
(10/26 2011: shige)
ある程度荒くてもよければ、以下のような方法があります (ON から OFF への時間間隔の計測):
; 1.
ONCHECK
BTFSC PORTA,5
GOTO COUNTER ; ON なら COUNTER へ
GOTO ONCHECK
; 2.
COUNTER
MOVLW D'0'
MOVWF COUNT ; COUNT = 0
; 3.
COUNTLOOP
BTFSS PORTA,5
GOTO LOOPOUT ; OFF なら LOOPOUT へ
; 4.
CALL WAIT1
; 5.
GOTO COUNTLOOP
; 6.
LOOPOUT
ただし、この方法では 100 ミリ秒単位 (WAIT1 の単位) でしか計れませんし、
スイッチのチェックも 100 ミリ秒間隔で行っているので多少誤差も出ます。
(10/26 2011: shige)
資料のサンプルプログラムがスイッチの切り替わりの回数を数えていますが、 「ある時間内」というと少し難しいですね。 割り込みを使う方法が本当は正しいのかもしれませんが、 ON/OFF の間隔が 100 ミリ秒よりも大きい (100 ミリ秒内に ON と OFF の両者が行われることはない) として、 以下のようにして数える方法はあると思います (多少誤差はあります)。
; 1.
MOVLW D'300'
MOVWF TIMES ; TIMES = 300
MOVLW D'0'
MOVWF COUNT ; COUNT = 0
; 2.
MOVWF SWSTAT ; SWSTAT = 0
BTFSS PORTA,5
GOTO CLOOP ; 現在 OFF なら CLOOP へ
COMF SWSTAT,1 ; 現在 ON なら 1 に反転
; 3.
CLOOP
CALL WAIT1
; 4.
DECFSZ TIMES,1
GOTO CHSWITCH ; TIMES > 0 なら CHSWITCH へ
GOTO LOOPOUT ; TIMES == 0 なら LOOPOUT へ
; 5.
CHSWITCH
BTFSC PORTA,5
GOTO CURON ; 現在 ON なら CURON へ
CUROFF
BTFSC SWSTAT,0
GOTO RUNDIFF
; SWSTAT == 1 (ON) なら RUNDIFF へ
GOTO CLOOP
; SWSTAT == 0 (OFF) なら CLOOP へ
CURON
BTFSC SWSTAT,0
GOTO CLOOP
; SWSTAT == 1 (ON) なら CLOOP へ
GOTO RUNDIFF
; SWSTAT == 0 (OFF) なら RUNDIFF へ
; 5-2
RUNDIFF
; SWSTAT == 1 (ON) のときだけ CONT++ を実行
; BTFSC SWSTAT,0
INCF COUNT,1 ; COUNT ++
COMF SWSTAT,1 ; SWSTAT を反転
GOTO CLOOP ; CLOOP へ戻る
; 6.
LOOPOUT
goto がかなり多いので、 まだソースの改良の余地はだいぶあると思います。 現在の状態と SWSTAT の状態の比較も XOR を取って直接比較すれば 多少すっきりすると思います。
(10/26 2011: shige)ありがちな間違いとしては、
MOVLW B'01010101'
MOVLW PORTA
MOVLW B'10101010'
MOVLW PORTB
なんてのがあります。
「指定した 2 進数データを W レジスタにロード (MOVLW)」して、
その後で「W レジスタの値を他のレジスタに移動 (MOVWF)」しなければいけないので、
PORTA, PORTB へデータを送る方は MOVLW ではなくて「MOVWF」です。
よって正しくは、
MOVLW B'01010101'
MOVWF PORTA
MOVLW B'10101010'
MOVWF PORTB
となります。確認してみてください。
(10/10 2011: shige)