目次>9章 PIC18F1320編 > (06)PORTレジスタとLATレジスタ

PORTレジスタとLATレジスタ

更新日2008.11.22

PICには、入出力ポートを制御するためにPORTレジスタ(PORTA/PORTBなど)があります。これはPIC16もPIC18も共通です。
PIC18では、これに加えてLATレジスタ(LATA/LATBなど)が追加されました。

PIC18を始めて使う時に疑問になる事の一つが、このLATレジスタだと思います。(必要性とか使い方とか)
今回は、このLATレジスタについて、実験をしながら有効性を確認します。

実験回路

過去3回の実験で使った回路と同様に、2つのLEDをRB0/RB1に接続します。
今回の違いは、RB0/RB1に0.01uFのコンデンサを追加している点です。

通常はこのような回路は組みませんが、ある事象を発生させるために意図的にこのようにしています。これが今回の実験の肝になります。

680Ωの抵抗2本は、300Ω〜1KΩ程度の範囲であれば何でもOKです。

回路図にはパスコンがありませんが、PICのVddとGND間に0.01uF程度のコンデンサを入れて下さい。

こちらは、ブレッドボードに仮組みした状態です。
右上の6ピンヘッダ周りはICSP用で、回路図にはありません。

過去3回のプログラムで動作させるとどうなるか?

過去3回で、以下のクロック動作を確認しました。
いずれも、RB0/RB1のLEDを交互に点滅させるプログラムです。
・40MHz動作(HSPLL)
・10MHz動作(HS)
・1MHz動作(内部クロック)
この3つのプログラムを今回の回路で動かして見て下さい。
そのままのプログラムで動作可能です。

どんな動きになりましたか?


私の環境では以下のようになりました。
クロックRB0のLEDRB1のLED
40MHz点滅しない前回同様に点滅
10MHz点滅しない前回同様に点滅
1MHz前回同様に点滅前回同様に点滅
クロックが速いとRB0のLEDが点滅しなくなる、と言う結果になりました。

なぜそうなるのか?

なぜこのような動作になるのでしょう。

過去3回のプログラムのメインルーチンを以下に引用しました。

main
BSFPORTB,RB0;(1)
BCFPORTB,RB1;(2)
CALLwait1s;(3)
BCFPORTB,RB0;(4)
BSFPORTB,RB1;(5)
CALLwait1s;(6)
GOTOmain;(7)
(1)(2)および(4)(5)に何かがありそうです。

【PICの動作1】 BSF/BCF命令の動作

まず始めに、BSF/BCF命令の動きを確認しましょう。

BSF/BCF命令などのレジスタ(SFR,GPR)を変更する命令は全て、1命令で以下の動作が行われます。
(下図ではPORTBレジスタを例にしています。)

例えば、
 BSF PORTB,0
を実行すると以下の内部動作が行われます。

これだけであれば問題なく動作しそうです。

【PICの動作2】 PORTレジスタと入出力ピン

続いて、PORTレジスタと入出力ピンの関係です。

下のイメージ図のようにPORTレジスタと入出力ポートは直結しています。
もっと感覚的に表現すると、入出力ポートそのものがPORTレジスタであると言えます。

今回の実験回路では出力ポートにコンデンサを接続しました。
この状態でRB0を0から1に変更すると以下のように変化します。

直前までの状態BSF PORTB,RB0
を実行
RB0の電圧変化

RB0に1をセットしてもコンデンサに
充電している間はHレベルにならない

逆にRB0を1から0に変更すると以下のように変化します。

直前までの状態BCF PORTB,RB0
を実行
RB0の電圧変化

RB0に0をセットしてもコンデンサが
放電している間はLレベルにならない

このように、出力ポートに容量性の負荷(コンデンサ)が接続されていると、BSF/BCF命令の結果が反映されるまでに時間がかかります。

【まとめ】 動作1、動作2から

動作1、動作2から以下の命令の結果を見てみます。

BSFPORTB,RB0;(1)
BCFPORTB,RB1;(2)
2つの命令の内部動作を表したものが下図です。
命令内部動作
BSF PORTB,RB0
PORTB00000010
内部メモリ00000010
内部メモリ00000011
PORTB0000001 (1)RB0はすぐには1にならない
BCF PORTB,RB1
PORTB0000001 (2)RB0が1になる前に次の命令で読み込み
内部メモリ00000010
内部メモリ00000000
PORTB00000000 (3)
上の(3)もRB1は遅れて0になりますが、この命令の直後でPORTBを使っていませんので結果的に正しく機能します。
また、クロックが遅い場合は、(2)の命令実行時にはRB0が1になっているため、意図した動作になります。


このような動作が行われるため、RB0のLEDだけ点滅しなくなりました。
この動きはPIC16ファミリーでも同じです。


解決策としてのLATレジスタ

PIC16であれば、NOP命令を入れて処理を遅延させるような事を行います。

一方PIC18では、PORTレジスタに加えLATレジスタが追加されました。
LATレジスタは出力ポートの制御に使いますが、下のイメージ図のように出力ポートの影響を受けないような構成になっています。


それではメインルーチンのPORTBをLATBに変更して動かして見ましょう。

main
BSFLATB,RB0;(1)
BCFLATB,RB1;(2)
CALLwait1s;(3)
BCFLATB,RB0;(4)
BSFLATB,RB1;(5)
CALLwait1s;(6)
GOTOmain;(7)

どうなりましたか?

クロックが速くても、以前の回路で動かした時と同じようにLEDが点滅すると思います。


PIC18の勧め(Vol.5)

その6. LATレジスタにより出力ポートの状態を意識せず意図通りに制御できる
PIC18ではPORTレジスタに加えLATレジスタが追加されました。
LATレジスタは出力ポートを制御するレジスタですが、出力ポートの影響を受けないレジスタであり、出力ポートの状態に依存せず意図した通りに制御できます。