PIC マイコンの実習 no.2

(情報電子工学演習 IV; 2 年後期)

目次


Q and A


Q and A 目次


目次に戻る

Q and A 回答


0. メタな質問


Q 0.1: この QandA はなんですか
A 0.1

情報電子工学演習の PIC マイコンの工学実習で、 竹野がサポートとして何年か見てきた中で、 実際に受けた質問や、感じた疑問などを適宜まとめあげたものです。

ただし、実際に PIC マイコンを使ったことはなく、 佐藤先生の資料に書いてあることや 簡単に MPLAB を触わったことなどを元に、 私が考えて書いているだけのことなので、 内容が正しいという保障はありませんし、 適切な回答であるとも限りません。

佐藤先生の講義のページ http://www.esato.net/ex/micom/index.html も参照してください。

(10/26 2011: shige; 11/10 2011, 01/12 2012 修正: shige)
Q and A の目次へ戻る
Q 0.2: この QandA に書いてあることは正しいですか
A 0.2

わかりません。 佐藤先生の資料に書いてあることや 簡単に MPLAB を触わったことなどを元に、 私が考えながら書いているだけです。 また、間違ってはいないけれども、 適切ではない回答もあるかもしれません。

なお、もし間違いを見つけたら報告して頂けるとありがたいです。

(10/26 2011: shige)
Q and A の目次へ戻る
Q 0.3: この QandA に書いてあることを使っていいですか
A 0.3

いいでしょう。 ただし、内容が正しいという保障はありませんので、 自己責任でお願いします。

(10/26 2011: shige)
Q and A の目次へ戻る

1. 資料に関する質問


Q 1.1: 資料 5 ページの「100ms 時間待ち」ってなんですか
A 1.1

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)
Q and A の目次へ戻る
Q 1.2: 資料 5 ページの「スイッチの検査 2」ってなんですか
A 1.2

このサンプルプログラムでは、スイッチの変化を 以下のようにして判断しているようです:

つまり、少し時間を開けて 2 回スイッチの状態を調べることで、 その間にスイッチが切り替わったかどうかを見ているわけです。 その回数を HENKA という変数にカウントして、 それを SELECT1 以下で使用しています。

(10/26 2011: shige)
Q and A の目次へ戻る
Q 1.3: 資料 2 ページの「4096 ワード」ってどういう単位ですか
A 1.3

データの最小単位は 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)
Q and A の目次へ戻る

2. プログラミングに関する質問


Q 2.1: 0.2 秒の時間かせぎをするにはどうしたらいいですか
A 2.1

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)
Q and A の目次へ戻る
Q 2.2: サンプルはどこを変えてはいけないのですか
A 2.2

基本的には、資料 5 ページの以下の部分はおまじないだと思って、 サンプルをそのまま使ってください:

  1. 「デバイスの選択、コンフィグレーションの設定」の部分 (LIST から _DATA_CP_OFF まで)
  2. 「リセット、割り込みベクタ」の部分 (ORG 0 から GOTO MAIN まで)
  3. 「メインルーチン」の MAIN から LOOP0 の前 (NOP) までの部分

上のものは、変更すると動作しなくなる可能性が高いので、 そのまま使用してください。 それぞれの意味が知りたければ、 何らかの PIC マイコンの本を参照してください (私にもよくわかりません)。

また、資料 6 ページの「WAIT0」から始まる 「時間かせぎ」のサブルーチンも多分必要になるでしょうから、 これもそのまま使うといいでしょう。

  1. 資料 5 ページの「ユーザエリア」の UA, UB, UC を DEFINE している 3 行 (WAIT0 以下をそのまま使う場合に必要)
  2. 資料 6 ページの WAIT0 から最後のところまで

この 4. は、WAIT0 以下のサブルーチンで変数として使われている UA, UB, UC を宣言している部分なのでこれが必要です。

(10/10 2011: shige)
Q and A の目次へ戻る
Q 2.3: 新しい変数を使いたいのですが
A 2.3

資料 5 ページのサンプルにある「ユーザエリア」の部分に、 以下ような形式で必要な変数を書けばいいです:

#DEFINE [変数名] [値を置く番地]
たとえば、資料 5 ページにある UA, UB, UC は WAIT0 等で使われている変数、 KAISU, HENKA は本文で使われている変数です。 もちろん、値を置く番地は別の変数と重ならないようにしないといけません。 資料 7 ページにあるように、変数は 20H から 7FH の領域に置けますので、 かなり多くの変数を使うことができます。

(10/10 2011: shige)
Q and A の目次へ戻る
Q 2.4: 「MOVLW A'00110000'」がエラーと言われます
A 2.4

2 進数の並びを '' (単一引用符の組) で囲んだ前につける「B」の文字は 「ポート B」を意味しているのではなく、 資料 7 ページにあるように「2 進数」を示しています。 よって、その「B」を「A」に変えてはいけません。

ポート A へデータを出力する際は、 MOVLW でデータを W レジスタに送って、 その後に「MOVWF PORTA」とします。 ポート B へ出力したければ、 MOVLW でデータを W レジスタに送って、「MOVWF PORTB」とします。

(10/10 2011: shige)
Q and A の目次へ戻る
Q 2.5: 「INCF HENKA,2」がうまくいきません
A 2.5

サンプルプログラムでは「INCF HENKA,1」とやっていますが、 この HENKA の後ろに書いてある 1 とは「1 増やす」という意味ではありません。

4 ページの表を見るとわかりますが、この 1 は「d == 1」を意味していて、 4 ページの表の下にあるように、 それは f として指定したレジスタに結果を保存することを意味しています。 この d には 0 か 1 のみを指定します。

値を 2 増やしたければ、INCF を 2 回行うか、 ADDWF などを使って行います。

(10/26 2011: shige)
Q and A の目次へ戻る
Q 2.6: 「Error[113] Z:\LED01\LED01.ASM 49 : Symbol not previously defined (s)」 の見方を教えてください
A 2.6

まずいくつかに分けて見ましょう。

ちなみにこのエラーは、多くの場合は変数名やラベル名、 命令名のスペルミスだと思います。 最後に書かれている不明な文字列のスペルが正しいか確認してみましょう。 特に O (大文字のオー) と 0 (数字のゼロ)、 l (小文字のエル) と 1 (数字のイチ) などはわかりにくい間違いです。

(10/27 2011: shige)
Q and A の目次へ戻る
Q 2.7: どれくらいのプログラムを PIC マイコンに書けるのですか
A 2.7

「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)
Q and A の目次へ戻る

3. MPLAB に関する質問


Q 3.1: MPLAB で編集するときプログラムに色がつかないのですが
A 3.1

.asm という拡張子のついた名前のファイルであれば、 MPLAB はそれをアセンブラソースファイルとみなして キーワードなどに色をつけてくれます。

よって、色がついていないのは、 まだそのプログラムファイルを保存していないか、 その名前に .asm という拡張子をつけて保存していないかのどちらかです。

(10/10 2011: shige)
Q and A の目次へ戻る
Q 3.2: MPLAB のエディタの文字を大きくしたい
A 3.2

MPLAB IDE のメニューで [Edit]→[Properties...]→[Text]→[Select Font] と進めばフォントを変更できます。

(10/27 2011: shige)
Q and A の目次へ戻る
Q 3.3: アセンブラソースは大文字でなくて小文字で書いても構いませんか
A 3.3

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)
Q and A の目次へ戻る
Q 3.4: 「Symbol not previously defined (_DATA_CP_OFF)」というエラーが出ますが
A 3.4

MPLAB のバージョンによってインクルードファイルの中身が 少し変わっていることによるエラーだそうで、 こういうものがでることがあるそうです。

この場合は、先頭に書いてある「INCLUDE」の下にある行の、 最後の「& _DATA_CP_OFF」を消すとうまくいくそうです。

(01/12 2012: shige)
Q and A の目次へ戻る
Q 3.5: MPLAB はどこにありますか
A 3.5

計算機実習室のどこにあるか、という意味ならば、 現在 (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)
Q and A の目次へ戻る

4. 考え方、アルゴリズムに関する質問


Q 4.1: 振動スイッチの ON/OFF で動作を切り替えるにはどうしたらいいですか
A 4.1

スイッチが入ったかどうかを知るには、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)
Q and A の目次へ戻る
Q 4.2: 振動スイッチの ON/OFF の時間間隔を知ることはできますか
A 4.2

ある程度荒くてもよければ、以下のような方法があります (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)
Q and A の目次へ戻る
Q 4.3: ある時間内の振動スイッチの ON/OFF の回数を数えることはできますか
A 4.3

資料のサンプルプログラムがスイッチの切り替わりの回数を数えていますが、 「ある時間内」というと少し難しいですね。 割り込みを使う方法が本当は正しいのかもしれませんが、 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)
Q and A の目次へ戻る

5. その他の質問


Q 5.1: 点灯はするのですが変な点灯をしたままになります
A 5.1

ありがちな間違いとしては、

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)
Q and A の目次へ戻る

その他



作成日: 10/10 2011
竹野茂治@新潟工科大学 (shige@iee.niit.ac.jp)