備忘録も兼ねてメモしておきます
PORTAとLATAは何が違うんや。
PICマイコンでデータ出力を決める際、PORTAの更新とLATAの更新の2つの方法があります。
この2つの方法ですが、基本的には結果が同じになります。
ですが、
・読み取り時はPORTA
・書き込み時はLATA
という、暗黙のルール的なものがあります。
今回はこの辺について説明、まとめをしてみます。
データシートを読んでみる
まず最初にデータシートを確認してみます。
今回PIC16F1827について確認しますが、I/O PORTSの賞を見てみましょう。

個々の文章について、肝になるのは下記の記述です。
データラッチ(LATxレジスタ)は、I/Oピンが駆動している値に対するリード・モディファイ・ライト操作に役立ちます。
LATxレジスタへの書き込み操作は、対応するPORTxレジスタへの書き込み操作と同じ効果があります。
LATxレジスタの読み出しはI/O PORTラッチに保持されている値を読み出し、PORTxレジスタの読み出しは実際のI/Oピンの値を読み出します。
リード・モディファイ・ライトとは?
データシートに記載されていた、リード・モディファイ・ライト(Read-Modify-Write)について、簡単に説明してみます。
リード・モディファイ・ライトとは、レジスタの値を更新する際の3つの手順になります。
レジスタを書き込みにより更新する際、実は、「読み取り」「加工」「書き込み」の3つの手順が走っています。
例えばPORTAのビット0を更新したい場合に、下記のようなコードを書いたとします。
PORTA = 0x01;このコードでは、一見、PORTAのビット0だけを書き換えしているように見えるのですが、実際の動作を意訳してみると下記のようになっています。
(厳密にはもう少し異なると思いますが、動作のイメージとして処理を作っています)
unsigned char temp;
unsigned char input;
/* リード:Read */
temp = PORTA; /* 0b0110-0100 */
/* モディファイ:Modify */
input = 0x01; /* 0b0000-0001 */
temp = temp + input; /* 0b0110-0100 + 0b0110-0101 */
/* ライト:Write */
PORTA = temp;
ソースコード上では単純に1ビットを書いたとしても、実際にはレジスタ全体を書き換えており、前回まで値を確認してから更新処理が走っている・・・という動作になっています。
PORTAに書き込むのとLATAに書き込むので、何が異なるのか?
ここで、PORTAに書き込むのと、LATAに書き込むのとで、何が異なるのか考えてみます。
まずはブロック図を確認してみます。

このブロック図から、下記のことがわかります。
・LATAとPORTAの書き込み結果はともにData Registerへ入力される
・Data Registerの出力は、LATAレジスタに更新される
・Data Registerの出力は、I/O pinへ伝わる
・I/O pin の状態は、PORTAで読み取れる
ここで、レジスタへの書き込み操作を行った場合のリードの工程について考えてみると、LATAに書いた際はLATAをリード、PORTAに書いた際はPORTAをリードします。
ともに、Data Registerに書き込みを行い、ポートの出力状態が更新されるという動作は同じでも、
リードの工程に読み出す値が、どちらのレジスタに書き込むかによって異なっていることがわかります。
レジスタに連続して書き込んだ場合を想定してみる
なんとなく答えが浮かんできたかと思いますが、最後まで説明してみます。
下記のようなソースコードを作成した時に、出力がどうなるか考えてみます。
LATAへの連続書き込み
LATA |= 0x01;
LATA |= 0x02;
LATA |= 0x04;
LATA |= 0x08;
LATA |= 0x10;
//LATA |= 0x20; /* RA5は読み取り専用ポート... */
LATA |= 0x40;
LATA |= 0x80;ソースコードの動作は、LATAレジスタの0から7bit目までを、連続して1ビットずつ書き込んでいるだけです。
各行ごとに、マイコンはLATAレジスタの値を読み出し、加工し、書き込みを行っていきます。
LATAは前回までにData Registerに書き込んだ値がすぐに反映される「出力ラッチ」であるため、1行前で更新された値がしっかりと入っています。
このため、リードの工程において、前回レジスタへ設定した値をそのままリードできます。
PORTAへの連続書き込み
では、下記のソースコードを実行した場合はどうでしょうか?
PORTA |= 0x01;
PORTA |= 0x02;
PORTA |= 0x04;
PORTA |= 0x08;
PORTA |= 0x10;
//PORTA |= 0x20; /* RA5は読み取り専用ポート... */
PORTA |= 0x40;
PORTA |= 0x80;処理内容としては、先ほどと同じく0~7bit目までを、連続して1ビットずつ書き込んでいるだけです。
ただし、LATAと異なり、リード・モディファイ・ライトのリード工程において、
前回のData Registerの設定値(出力ラッチ)ではなく、現在のポート状態を読み出します。
ここで、マイコンの処理の動作速度を考えてみます。
マイコンの動作クロックは、遅くても数MHz程度あるものが一般的です。
上記の8行の処理も、時間的にはとても短い時間で完了してしまいます。
これに対して、マイコンのピンの電圧の変化はどうでしょうか?
基本的に現実の素子は、抵抗分やインダクタンス、その他の要因によって、出力を0秒で瞬時に切り替得得ることはできません。
このため、PORTAレジスタに書き込みを行っても、ポートの状態はすぐには変化できず、次のリード工程において、
まだ電圧が立ち上がっていないポートの電圧レベルを読み出してしまうのです。
このため、上記の例に挙げた2つの処理を実行すると、下記のような結果になります。
(実際にプログラムを書き込んで動作させてもこうなります。)
LATA |= 0x01;
LATA |= 0x02;
LATA |= 0x04;
LATA |= 0x08;
LATA |= 0x10;
//LATA |= 0x20; /* RA5は読み取り専用ポート... */
LATA |= 0x40;
LATA |= 0x80;
➡ポートRA0~RA4,RA6~RA7の出力電圧が、すべてHIレベルになる
/*============================================================================================*/
PORTA |= 0x01;
PORTA |= 0x02;
PORTA |= 0x04;
PORTA |= 0x08;
PORTA |= 0x10;
//PORTA |= 0x20; /* RA5は読み取り専用ポート... */
PORTA |= 0x40;
PORTA |= 0x80;
➡ポートRA7のみが、HIレベルになる※より厳密には、ポートの電圧レベルがData Registerへの書き込みと同時に見えるぐらいに、各行の間の時間が長い(クロック周波数が非常に遅い)場合は、PORTAへの書き込みでも全部のポートが更新されます。
が、そんなにクロック周波数を下げるケースはあまりないかと思います・・・・
まとめ
最後にまとめです。

今回は
・読み取り時はPORTA
・書き込み時はLATA
という、暗黙のルールがある理由を説明してみました。
まぁまぁいい感じに説明できたんじゃないかと思います。(願望)
リード・モディファイ・ライトにおける問題というと、値の更新中に割り込みが入ったときに意図しない値に書き換わる・・・・といった、
もう少し難しい条件下で議論されることが多い気がしますが、PICの場合はレジスタ構成の都合で簡単なソースコードで再現できてしまいます。。。。
いいか悪いかはさておき、説明用の題材としては申し分ないですね・・・
それでは、また。