初版作成 2009.11
最終更新 2025.05.14
http://ww2.tiki.ne.jp/~maro/old/PIC/tips.html
様々なデータの移動は汎用レジスタである Wreg を介して 行うことが多い。
ニーモニックは大文字と小文字を区別しない。 変数名、ラベル名は大文字と小文字を区別する。 ラベル名以外は小文字を使用するのが見やすいと思う。
2進数 B'11000100' 10進数 D'12' 16進数 H'6A' or 6AH or 0x6A アスキー A'a' 'a' のアスキーコードが入る
L : リテラル W : Wreg(ワーキングレジスタ) F : ファイルレジスタ(PIC ではメモリをファイルレジスタと呼ぶ)
banksel addr そのアドレスが存在するバンクに切り替える
アドレスはバンク 0 が 00h〜7Fh
バンク 1 が 80h〜FFh
インクルードファイルを参考にすること
C:\Program Files\Microship\MPASM Suite 以下に P16F84A.INC のようなファイルがあり、その中にレジスタのアドレス、 定数、コンフィギュレーションの設定項目などが列挙されている。 STATUS レジスタ用 Z 2 ゼロフラグの位置 C 0 キャリーフラグの位置 MOVF などのコマンド用 W 0 ワーキングレジスタ (Wreg) F 1 ファイルレジスタ(メモリをこう呼ぶ)
T1 EQU 0x20 アドレスを指定して変数名として使うことが多い T2 EQU 0x21 T3 EQU 0x22
一番原始的な方法は、EQU を使って定数定義する方法である。
CBLOCK を使うのが良い
cblock 0x20 確保したい領域の先頭アドレス
VAR1, VAR2
VAR3
endc
上記の方法では変数 1 個につき 1 バイトづつ確保する。
VAR1 = 0x20, VAR2 = 0x21, VAR3 = 0x22 となる。
複数バイト確保したい場合は次のように書く
org 0x20 先頭アドレス
VAR1 RES 2 2 バイト確保する
VAR2 RES 1 1 バイト確保する
VAR3 RES 10 0x10 ( 16 ) バイト確保する
以上のように書くと、VAR1 = 0x20, VAR2 = 0x22, VAR3 = 0x23 と
なる。バイト数は 16 進表記なのに注意。
org を忘れると、プログラム先頭に書いた場合、org = 0 なので、
特殊レジスタ領域と重なる場所に変数領域をとることになり、ダメである。
PIC はデータとコードを異なるメモリに格納するが、
org 命令は両方に作用する。
プログラム先頭で上のように変数領域を確保した後、必ず、絶対に
org 0
を入れてから、コードを書く。
これを怠ると、上の例の場合コードが 0x33 から配置される
(HEX ファイルを閲覧するとわかる)。
リセット直後は 0 番地から実行するので、PIC がまともに動かない。
PIC はリセット直後は 0 番地から実行し、割り込みがかかると 4 番地
から実行するので、
org 0h
goto MAIN
org 4h
goto INTERRPT
MAIN
という書き方が定番的である。
;#define PIC84A
#define PIC819
ifdef PIC84A
LIST P=PIC16F84A
INCLUDE P16F84A.INC
__CONFIG _HS_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF
endif
ifdef PIC819
LIST P=PIC16F819
INCLUDE P16F819.INC
__CONFIG _HS_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF & _BODEN_ON & _MCLR_ON & _LVP_OFF
endif
-------------------------- ここから命令 ------------------------
bcf addr,b ビット b をクリア(0 にする) b は 0〜7 の値をとる bsf addr,b ビット b をセット(1 にする) <注意!> ビット操作命令をポートに対して行う場合、ポートから全ての bit を読み出し、 指定のビットをセットしてから、ポートに書き戻す。 その直後にビット操作命令がきた場合、ポートに書き戻したデータが確定しない まま読み出しが起こり、ビットが反転する可能性がある。 20 MHz で駆動する場合、ポートに対するビット操作命令が連続する場合、 nop を入れる必要がある。 例 bsf addr,0 アドレス addr の bit 0 を 1 にする bcf addr,7 アドレス addr の bit 7 を 0 にする
clrf addr addr ← 0 clrw Wreg ← 0 その結果 Z ← 1 例 clrf INTCON 割り込み禁止
movlw B'00001111' Wreg ← B'00001111'
movf addr,d d = W(0) : Wreg ← addr
d = F(1) : addr ← addr
Z フラグに影響する
movwf addr Wreg → addr
例 : アドレス DATA に 3 を入れる
movlw D'3' Wreg ← 3
movwf DATA Wreg → DATA
addwf addr,d d = W(0) : Wreg ← Wreg + addr
d = F(1) : addr ← Wreg + addr
addlw num Wreg ← Wreg + num
subwf addr,d d = W(0) : Wreg ← addr - Wreg(順番に注意!)
d = F(1) : addr ← addr - Wreg
結果が正または 0 のとき C フラグが 1
addr の内容が 5 ならジャンプ、というようなとき
d = W(0) として addr の内容は不変にして
フラグを見てジャンプをすればよい。
sublw num Wreg ← num - Wreg(順番に注意!)
例1 : 加算
movf DATA_A,W Wreg ← DATA_A
addwf DATA_B,W Wreg ← Wreg + DATA_B
例2 : 減算
movf DATA_A,W Wreg ← DATA_A
subwf DATA_B,W Wreg ← DATA_B - Wreg
DATA_B >= DATA_A のとき C = 1
decf addr,d addr の内容を -1 する
d = W(0) : 結果を Wreg に格納
d = F(1) : 結果を addr に格納
incf addr,d addr の内容を +1 する
decfsz addr,d addr の内容を -1 した後、結果が 0 のとき
次の命令をスキップする。
例 ループ
movlw D'250'
movwf address,F
LOOP_TOP
nop
decfsz address,F
goto LOOP_TOP
return
1 桁目から文字列を書き始める。ラベルの後に : は不要
goto LABEL 無条件ジャンプ call LABEL サブルーチンコール return サブルーチンからリターン
btfsc addr,b addr のビット b が 0 なら次の命令をスキップ btfss addr,b addr のビット b が 1 なら次の命令をスキップ 例1 ループ decf COUNT,F COUNT の値を -1 して結果を COUNT に格納 btfss STATUS,Z Z フラグが 1 なら(結果が 0) goto LOOP_TOP 次の GOTO は無視してループを抜ける 例2 比較 movf DATA_B,W Wreg ← DATA_B subwf DATA_A,W Wreg ← DATA_A - Wreg btfss STATUS,C C フラグが 1 なら ( DATA_A >= DATA_B ) goto C_EQ_0 次の命令は無視 例3 Wreg が num と等しいとき sublw num ; Wreg ← num - Wreg btfsc STATUS,Z goto 等しいときの処理 sublw num btfss STATUS,Z goto 等しくないときの処理 例4 DATA が num と等しいとき movlw num subwf DATA,W ; Wreg ← DATA - Wreg(num) btfsc STATUS,Z goto 等しいときの処理 movlw num subwf DATA,W ; Wreg ← DATA - Wreg(num) btfss STATUS,Z goto 等しくないときの処理 別のやり方 movf DATA,W ; Wreg ← DATA sublw num ; Wreg ← num - Wreg(DATA) btfsc STATUS,Z goto 等しいときの処理 movf DATA,W ; Wreg ← DATA sublw num ; Wreg ← num - Wreg(DATA) btfss STATUS,Z goto 等しくないときの処理 例5 DATA が 0 のとき movf DATA,F btfsc STATUS,Z goto 0 のときの処理 movf DATA,F btfss STATUS,Z goto 0 以外のときの処理 例6 DATA が num 以上のとき movlw num subwf DATA,W ; Wreg ← DATA - num btfsc STATUS,C goto 条件成立のときの処理 movlw num subwf DATA,W ; Wreg ← DATA - num btfss STATUS,C goto 条件不成立のときの処理 例7 DATA が num 未満のとき movlw num subwf DATA,W ; Wreg ← DATA - num btfss STATUS,C goto 条件成立のときの処理 movlw num subwf DATA,W ; Wreg ← DATA - num btfsc STATUS,C goto 条件不成立のときの処理 例8 Wreg が num+1 以上のとき sublw num ; Wreg ← num - Wreg btfss STATUS,C ; 0 または 正のとき C フラグが 1 になる goto 条件成立のときの処理 sublw num ; Wreg ← num - Wreg btfsc STATUS,C ; 0 または 正のとき C フラグが 1 になる goto 条件不成立のときの処理 例9 Wreg が num 以下のとき sublw num ; Wreg ← num - Wreg btfsc STATUS,C ; 0 または 正のとき C フラグが 1 になる goto 条件成立のときの処理 sublw num ; Wreg ← num - Wreg btfss STATUS,C ; 0 または 正のとき C フラグが 1 になる goto 条件不成立のときの処理
rlf addr,d addr の内容を 1 ビット左へずらし、結果を
d に書き込む。0 bit 目は C が入る。
d = W(0) : Wreg
d = F(1) : addr
rrf addr,d addr の内容を 1 ビット右へずらす
andlw B'00001111' Wreg ← Wreg && '00001111'
andwf addr,d d = W(0) : Wreg ← Wreg && addr
d = F(1) : addr ← Wreg && addr
iorlw B'00001111' Wreg ← Wreg || '00001111'
iorwf addr,d d = W(0) : Wreg ← Wreg || addr
d = F(1) : addr ← Wreg || addr
xorlw B'11111111' Wreg ← Wreg XOR '00001111'
xorwf addr,d d = W(0) : Wreg ← Wreg XOR addr
d = F(1) : addr ← Wreg XOR addr
swapf addr,d 上位 4 bit と下位 4 bit を入れ替える
d = W(0) : Wreg ← addr を入れ替えたもの
d = F(1) : addr ← addr を入れ替えたもの
FSR に読み出したいアドレスを入れる。 INDF にアクセスすると、FSR に入れたアドレスのデータにアクセスできる。
STATUS 6-5 バンク切り替え
2 Z フラグ
0 C フラグ
PCLATH プログラムカウンタ
INTCON 割り込みの状態
PORTA 入出力用ポート
PORTB 同上
TRISA PORTA の in/out の方向を決める
0 = output 1 = input
TRISB PORTB の in/out の方向を決める
C:\Program Files\Microchip\MPASM Suite 以下に
P***.INC というファイルがあるので、自分がインクルードする
PIC の INC ファイルを開く。一番最後に指定項目が載っている。
各項目の意味は、各型番ごとに用意されたデータシートの
Special features of CPU という項目に書いてある。
--------------------------------------------
movf ADDR
上のは
movf ADDR,W
のつもりだった。,W を省略してもコンパイルエラーにならず、
,F と解釈される。従って、上の命令は Wreg <--- ADDR のつもりが、
ADDR <--- ADDR となり、Z フラグの変化だけでプログラムが進行する。
---
movf D'10'
上のは
movlw D'10' のつもりだった( Wreg ← 10 )。
上述の通り、movf の引数を省略したときは ,F とみなされる
ので、10 番地の内容により Z フラグが変化するだけである。
同様に下記のような間違いもある。
andwf B'01111111'
上のは andlw のつもりだったが、うっかり andwf と書いてしまった。
andwf は引数を 2 つ取るが、省略してもコンパイルが通り、,F と
みなされる。その結果
Wreg ← Wreg && '01111111'
のつもりが
アドレス 7F ← Wreg && アドレス 7F の内容
という動作になってしまう。
A/D コンバータは入力チャンネル切り替え後、電圧を Hold する
キャパシタの充電が完了するまで 20μs が必要なので、
「チャンネルセレクト設定」→「20μs のディレイ」→「AD 変換開始」
という手順が必要である。
コントロールレジスタ
ADCON0 7-6 W : AD コンバータのクロック ADCON1 の bit6 も関係する
5-3 W : 入力チャンネル番号
2 R/W : 1 で変換開始
0 になったら変換終了
0 W : 1 で AD 変換を使用する
ADCON1 7 W : 変換結果の格納フォーマット
0 : 左寄せ 1 : 右寄せ
6 W : AD コンバータのクロック
3-0 W : 5 つの A/D 入力をどのように使うか
アナログ/デジタル/リファレンス電圧
結果格納用レジスタ
ADRESL
ADRESH
AD コンバータのクロック : 最小値は 1.6μs
クロック 20MHz のとき Fosc/32
クロック 10MHz のとき Fosc/16
クロック 8MHz のとき Fosc/16
が使用可能
16F886, 16F88 は CONFIG レジスタが 2 バイトある。
下記の例は 16F886 用である。
conf1 = _LVP_OFF & _FCMEN_OFF & _IESO_OFF
conf1 &= _BOR_OFF & _CPD_OFF & _CP_OFF & _MCLRE_ON
conf1 &= _PWRTE_ON & _WDT_OFF & _HS_OSC
__CONFIG _CONFIG1, conf1
PORTB に対して bsf PORTB,2 とやると、bit 2 は 1 になるが、bit 0〜5 が全て 0 になるという 現象が発生する。 16F873A ではこの現象は発生しない。 この理由は bsf 命令は一旦 PORTB の内容を読み出した後、 bit 2 を 1 にセットしてから PORTB に書き出すからである。 デフォルトで PORTB の bit 0〜5 はアナログモードになっており、 読み出すと 0 になるようだ。 これを防止するには PORTB をデジタル入出力モードに設定する。 PORTB のモードは ANSELH レジスタで設定する。 ANSELH の bit 0〜5 が PORTB の bit 2,3,1,4,0,5 に対応している。 0 がデジタルモード、1 がアナログモードなので、 以下のようにすればよい。 なお、ANSELH レジスタはバンク 2 という珍しい位置にあるので、 バンクの切り替えにも要注意である。 banksel ANSELH clrf ANSELH
普通の PIC では AD 変換スタートは ADCON0 の bit 2 だが、 16F886 は ADCON0 の bit 1 である。ソースファイルの流用時に注意。
16F84A 用のプログラムを 16F819 に流用するときの注意点 ・_CONFIG で _LVP_OFF (Low Voltage Programming) に設定する デフォルトは _LVP_ON。このとき、PORTB の bit 0 (3 だったかも) から出力出来ない。 ・ユーザー用メモリの開始アドレス: 0x1c → 0x20 ・PORTA の入力モードはデフォルトでアナログ デジタルにするには ADCON1 を設定する
IOポートの使用法(アナログ、デジタル)
ANSEL p.113 : 0=digital 1=analog
IOポートの入出力方向
TRISA, TRISB : 0=output 1=input
AD変換
ADCON0 7-6 : クロック
5-3 : 入力チャンネル
2 : 1 で変換スタート 0 になったら変換終了
0 : 1 で AD 変換モジュール on
ADCON1 7 : 結果を格納するフォーマット
0 = 左寄せ 1 = 右寄せ
6 : AD クロックを 1/2 にするためのビット
0 = 何もしない
1 = システムクロック使用時 AD 変換クロック 1/2 にする
5-4 : リファレンスの取り方
00= Vdd と Vss
RS-232C 通信の設定
TXSTA 6 : 8 or 9 ビット転送
0 = 8bit 1 = 9bit
5 : 1 のとき RS-232C 送信を行う
4 : 同期モード
0 = 非同期 1 = 同期
2 : 非同期モード使用時のボーレート
0 = 低速用の表使用 1 = 高速用の表使用
1 : 送信用レジスタのステータス
0 = full 1 = empty
0 : 9 ビット送信時のパリティビット
SPBRG p.101 の表に設定すべき値が 10 進数で書いてある
Fosc は 8, 4, 2, 1 MHz のときの表しかない。
20 MHz のときはどうなるのか?
87x の表と比べると 4 MHz のときの値は同一なので 87x の
表に従って設定すればよいと思われる。
RCSTA 7 : 1 のとき RS-232C 受信を行う
6 : 8 or 9 bit
0 = 8bit 1 = 9bit
5 : 非同期モードのとき関係ない
4 : 連続受信可能か否か
0 = 不可能 1 = 可能
3 : 非同期 9 bit のときのみ関係ある
2 : フレームエラー発生か否か
0 = なし 1 = 発生
1 : オーバーランエラー発生か否か
0 = なし 1 = 発生
0 : 受信データの 9 ビット目
PIC の RS-232C バッファは 2 byte しかない。
RS-232C からデータを読み出さない状態で、PC から 3 バイト送ると
オーバーランエラーのフラグが 1 になる。
オーバーランエラーのフラグが 1 になった場合、
クリアせねばならない。そのためには、
RCSTA のビット 4 (CREN : Continuous Receive Enable) を
まず 0 にセットし、次に 1 にセットする。
これを怠ると
1 byte 目 ---> 2 byte 目 ---> 2 byte 目 ---> 2 byte 目
となり、永遠に 2 byte 目が読み出され、新しいデータを読み込めない。
フラグをクリアしてから順番に読み出すと
1 byte 目 ---> 2 byte 目 ---> 次のデータ
となり、3 バイト目は失われる。
--------------------------------------------
PIC でディレイを実現するには、無駄なループを回す。
4 クロックで 1 サイクル
大抵の命令は 1 サイクル。goto, call, return のみ 2 サイクル
条件分岐において次の命令をスキップするとき、次の命令を nop に置き換えて
実行する。
20MHz のとき 1 サイクルは 0.2 μs
ディレイは次のようなパターンとなる
DELAY
movlw D'x' 1
movwf count 1
nop を α 回
LABEL
decfsz count,F 1
goto LABEL 2
return 2
ループを回っている間は、ループ 1 回あたり decfsz と goto で
合計 3 サイクル。ループを抜けるときは goto が nop に置き換わる
ので、decfsz と nop で 2 サイクル。ゆえに上記の関数の所用サイクルは
2 + α + 3 * x - 1 + 2
である。ディレイ時間をサイクル数で割ると、何サイクルのディレイが必要かが
わかる。これを D 回とすると
2 + α + 3 x - 1 + 2 = D
3 * x = D - 3 - α
例えば、D = 40 のとき 3 * 12 = 37 - 1 だから、
x = 12, α = 1 となる。
長いディレイを組む場合、上で示したサブルーチン DELAY を
上位のルーチンが呼ぶという構成になる。
LOOP2
call DELAY 2
decfsz count2,F 1
goto LOOP2 2
上記のループ全体のサイクル数は「サブルーチン DELAY で
消費するサイクル数 + 5 サイクル」である。
-------------------------------------------
Vcc と GND の間に 0.1μF 程度のコンデンサを入れるとノイズによる 誤動作が軽減され、動作が安定すると言われている。 しかし、ブレッドボードで組んだときに、コンデンサの位置が悪い場合に 「コンデンサの挿入により誤動作する」という現象に遭遇した。 PIC のすぐ傍に設置れば問題なかった。 赤外線リモコンのフォーマットで、8 bit の信号を送信、受信する回路を それぞれ組んだところ、以下の現象に遭遇した。 < 送信回路 > ブレッドボード上に送信回路を組んだところ、16F84A の場合は 0.1μF の コンデンサをブレッドボード上のどこに差しても正常に動作した。 16F819の場合は、コンデンサを PIC の真上に取り付けた場合は正常に動作するが、 PIC からかなり離れた場所(5cm 以上?)に取り付けると正常に動作しない。 動作が異常な状態で、オシロのプローブを PORTB の bit 0 や 1 に 取り付けると動作が正常になり、オシロのアースの位置を変えると、 再び正常に動作しなくなった。原因は不明である。 < 受信回路 > ブレッド上に受信回路を組み、送受信の信号は 8 bit とし、 受信した 8 bit のパターンを白色 LED で表現する回路を組んだところ、 (1) リセットスイッチを押した直後 (2) 9V の電池を 3 端子レギュレータで 5V に落として使う (3) 普通の直流電源 以上の場合において、PIC が誤作動する場合があった。 LED の点滅パターンがデタラメになった。 ただし、必ず起こるとは限らない。 0.1μF のコンデンサを装着すると、誤動作はなくなった。