初版作成 2009.11
最終更新 2024.04.01
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 のコンデンサを装着すると、誤動作はなくなった。