LCDキャラクタ表示のプログラムにおけるPortの操作に関する初歩的な質問

皆様

先般は,CAN通信で貴重な情報をいただきありがとうございました.おかげ様でMCP2521を使い,基地局に使用されているPSUのCANデータを読み取ることができました.

今回ご教示いただきたいのは基本的なPortのコーディング方法です.下記の引用コードは,G13のStarter KitのLCD表示に関するものです.4bitインターフェースで,LCDのDB7,DB6, DB5, DB4に接続されているポートP73, P72, P71, P70にデータをセットし,Enable信号を短時間Onにするプログラムです.

ここで,ucStore = DATA_PORT; は何を意味しているのでしょうか? このような使い方は一般的でしょうか? 私は,これまでP7_bit.no0 のようにしポート制御していました.

ご教示よろしくお願いします.

=== QUOTE ===

/* RS Register Select pin */
#define RS_PIN                  P5_bit.no5
/* Display Enable pin */
#define EN_PIN                  P5_bit.no4
/* Data bus port */
#define DATA_PORT               P7
/* Bit mask from entire port */
#define DATA_PORT_MASK          0x0F
/* Number of bits data needs to shift */
#define DATA_PORT_SHIFT         0

#define DATA_WR 1
#define CTRL_WR 0

void LCD_nibble_write(uint8_t data_or_ctrl, int8_t value)
{
    int8_t ucStore;

    /* Set Register Select pin high for Data */
    if (data_or_ctrl == DATA_WR)
    {
        RS_PIN = SET_BIT_HIGH;
    }
    else
    {
        RS_PIN = SET_BIT_LOW;
    }

    /* There must be 40ns between RS write and EN write */
    DisplayDelay(1);

    /* EN enable chip (HIGH) */
    EN_PIN = SET_BIT_HIGH;

    /* Tiny delay */
    DisplayDelay(1);

    /* Clear port bits used */
    ucStore = DATA_PORT;
    ucStore &= (int8_t) ~DATA_PORT_MASK;

    /* OR in data */
    ucStore |= (int8_t) ((value << DATA_PORT_SHIFT) & DATA_PORT_MASK );

    /* Write data to port */
    DATA_PORT = ucStore;

    /* write delay while En High */
    DisplayDelay(20);

    /* Latch data by dropping EN */
    EN_PIN = SET_BIT_LOW;

    /* Data hold delay */
    DisplayDelay(20);

    if (data_or_ctrl == CTRL_WR)
    {
        /* Extra delay needed for control writes */
        DisplayDelay(0x7FF);
    }
}

=== UNQUOTE ===

Parents
  • tok2010さん、こんにちは。NoMaYです。#お久しぶりです。

    正直なところ、文面からだけでは何に困惑しているのか分からなくて確認したいのですが、こういうことですか?

    (1) 今までは、ポートを操作する時は1ビットずつ(1本ずつ)操作していた
    (2) 今回、数ビットまとめて(数本まとめて)同時に操作出来るらしいことを知って驚いた?

Reply
  • tok2010さん、こんにちは。NoMaYです。#お久しぶりです。

    正直なところ、文面からだけでは何に困惑しているのか分からなくて確認したいのですが、こういうことですか?

    (1) 今までは、ポートを操作する時は1ビットずつ(1本ずつ)操作していた
    (2) 今回、数ビットまとめて(数本まとめて)同時に操作出来るらしいことを知って驚いた?

Children
  • チョコです。

    NoMaYさんに追加しておくと、「#define DATA_PORT               P7」で、下位4ビットがLCDのDB7,DB6, DB5, DB4に接続されているポート7をDATA_PORTと名前を付けています。

    P7の上位4ビットは別の用途で使われているようなので、上位4ビットの値が変わらないように、LCDにデータをセットするためにP7に書き込みを行う前に、P7の上位データを読み出しているのが問題の処理です。

    次の"ucStore &= (int8_t) ~DATA_PORT_MASK;"でLCD用のデータ領域である下位4ビットをクリアしておきます。

    その次の"ucStore |= (int8_t) ((value << DATA_PORT_SHIFT) & DATA_PORT_MASK );"で出力したいデータを下位4ビットにセットして、"DATA_PORT = ucStore;"で、P7の上位4ビットの状態とマージしたデータをP7に書き戻すことで、LCDにデータをセットしても別の用途で使用しているP7上位4ビットが変化しないようにしているだけですね。

    これは、一般的に用いられる方法ですね。

    以上

  • チョコです。

    蛇足ながら、少し追加しておきます。

    RL78のポートに対するビット処理はリード・モディファイ・ライトと呼ばれている手順で、最初にポートの状態の読み出し(リード)、次に読み出したデータの一部の変更(モディファイ)、最後に変更した結果のポートへの書き戻し(ライト)の3段階で処理されています。

    今回の処理もリード・モディファイ・ライトです。その中のリード部分が"ucStore = DATA_PORT;"です。

    ビット処理命令がなかった時代から使われている手順です。この手順をソフトで行う場合には、割り込みで対象のポートへの書き込みがあると期待した動作と異なることになるので、排他的に処理する必要があります。ちなみにビット操作命令では、処理中に割り込みを受け付けることはないので、この問題はありませんが。

    以上

  • tok2010さん、こんにちは。NoMaYです。

    或いは、出力ポートに設定しているP7を(入力ポートでは無いのに)読み出していること、が腑に落ちないとか??

  • チョコです。

    >或いは、出力ポートに設定しているP7を(入力ポートでは無いのに)読み出していること、が腑に落ちないとか??

    それはないと思いますが、その場合には、ハードウェアマニュアルの「4. 4 ポート機能の動作」に以下のように記載されています。

    ここらで、理解できるかと思いますが。

  • チョコさん,NoMayさん

    H/Wマニュアルのリファーありがとうございます.お送りいただいた画像に,赤線を付けたものを再掲します.

    「転送命令により,出力ラッチの内容が読みだせます」が今回のP7(出力モード)にも該当すると思います.
    Cのコードでは,
    ucStore = P7;
    ですが,これが 「転送命令により」 に該当すると思います.そこで,この転送命令がどのようにH/Wレベルで行われるかということですが,P7はレジスタアドレスとして認識され,このレジスタアドレスの値が8bitのビット列になると理解しています.これは合っていますか?



    なお,私の言葉の使用法がこれまで間違っていました.ポートというのは,P7やP6のことで,P7_bit.no0は「端子」,英語ではPinですね.
    私は,これまで,これらを混同していました.なお,ucStore = P7_bit.no0; とすれば,P7ポートの端子0の値が変数に代入されます.

    ただ,例えばポートP7の端子は,個別に各々OutあるいはInに設定できるので,上の写真の文章で使用されている「入出力ポート」というのは,具体的にはポートそのものか,あるいは端子(Pin)のことを言っているのか,混乱はします.

  • チョコです。

    >そこで,この転送命令がどのようにH/Wレベルで行われるかということですが,P7はレジスタアドレスとして認識され,このレジスタアドレスの値が8bitのビット列になると理解しています.これは合っていますか?

    それは、その理解でいいかと思います。

    >ポートというのは,P7やP6のことで,P7_bit.no0は「端子」,英語ではPinですね.

    違います。P6やP7はいくつかのビットで構成されたグループで、アクセスする場合にはこのグループでまとめてアクセスされます。P7がつながった端子としては、P70~P77の8本が存在し、P6にはP60~P63の4本が存在します。各ポートのビットを区別するために下に示すように「レジスタ名.ビット番号」で扱います。

    ところが、CC-RLでは、数値の間の"."は、浮動小数点演算で使われているので、そのままは使えません。そこで、ビットフィールドとして定義して「P7_bit.no0」のような記述ができるようにしています。「P7_bit.no0」はあくまでソフト上の表現方法です。ポートが接続されている端子の名称は「P70」のようにポート名称とビット位置の数値を組み合わせたものです。

    >「入出力ポート」というのは,具体的にはポートそのものか,あるいは端子(Pin)のことを言っているのか,混乱はします.

    「入出力ポート」は機能の名前であり、その機能からデバイスの外に接続するのが物理的な端子です。物理的な端子をどのような機能で使うかを示したのが「P70」で、ポート7のビット0として使うことを明示しているだけです。これらを区別する必要はないと思います。

    少し、RL78の構成や動作について説明します。

    ①最初に、プログラムから見た内容について説明します。

    RL78では、ポートやA/Dのような内蔵周辺機能を制御するレジスタ(特殊機能レジスタ:SFRと呼ばれている)は、メモリ・マップ上に配置されています。これは、メモリ・マップト・I/Oと呼ばれる方法です。この特殊機能レジスタ(SFR)を命令で読みだしたり書き込んだりするには、対象のSFRが配置されているメモリ・アドレスを指定してアクセス(読み・書き)を行います。

    RL78のSFRは2か所に分かれて配置されています。1つはSFR領域と呼ばれる0xFFF00~0xFFFFFに配置された256アドレスの領域です。ここは、命令でもSFR領域として区別されている領域です。内蔵されている周辺機能が少ないときにはここだけでよかったのですが、内蔵される周辺機能が増えると、アドレスが足りなくなり、拡張特殊機能レジスタ領域(0xF0000~0xF07FFの2kバイト)が追加になりました。こちらは、命令で特に区別されておらず、通常のRAM領域と同じように扱われています(Cでプログラムする場合いは、意識する必要はありませんが)。

    ここらは、「第3章 CPUアーキテクチャ」に記載されています。特殊機能レジスタ領域(以下SFR領域と呼ぶ)のメモリ・マップの例を下の図に示します。

    問題のポートですが、いくつかのSFRで構成されていますが、データを読み書きするためのレジスタが"Pn"レジスタです。これは、以下のアドレスに配置されています。

    問題のP7は0xFFF07番地に配置されています。ちなみにPnレジスタはSFR領域であると同時にsadr領域でもあります。

    このため、ポートのデータに対する演算が命令として使用可能です。例えば、下に示すXOR命令を使えば、ポートの任意のビットを反転させることが可能です。

    ②もう一つのポートの構成(ハードウェア)について説明します。

    基本的に、ポートは最大8本の端子を介して、外部と信号(データ)をやり取りを行うハードウェアです。しかしながら、8本の信号線(=端子)を全て使うだけでなく1本や2本での使用もあることから、ポートの個々の端子を制御する必要があります。このために、SET1やCLR1のようなビット操作関係の命令や、BTやBFのような判定命令がデバイスとして準備されているので、これらを用いることで、1本の信号を扱うことができるようになっています。ところが、1本単位で制御するようなハードウェアは回路規模が大きくなりすぎるので実用的ではなく、読み書きはバイトでのみできるようになっています。(その代わりに、内部の演算処理で対応しています。)

    さて、問題のP7という8ビットのポートは、「2. 1 ポート機能」で下に示すように、7-1-1と7-1-2というタイプの端子で構成されていると記載されています。

    端子のタイプは「2. 4 端子ブロック図」に端子のブロック図が記載されていて、7-1-1は「図2-6」に以下のように記載されています。ここで、赤く囲んだのが入出力に関係した部分です。

    P7レジスタにデータを書くと、データは「出力ラッチ」に書き込まれます。PM7レジスタの設定が0(出力ポートとして使用)ならば、そのまま、右側の出力バッファからPmn端子(P7n端子)に出力されます。PM7レジスタの設定が1(入力ポートとして使用)ならば、出力バッファはOFFになっているので、端子には何も出力されません。

    P7を読み出す場合には、セレクタの出力が読み出されます。セレクタはPM7レジスタの値が0ならば、出力ラッチの値が選択され、1ならば、Pmn端子(P7n端子)の状態をが入力バッファで取り込んだ値が選択されます。これらのビットごとの値が組み合わされ、P7の値として読み出されます。

    ここで、注意しないといけないのはハードウェアとしてのポートはバイトでのアクセスしかできないことです。そのため、「SET1 P7.0」のようにP7のビット0(つまりはP70)をセットする命令はどうして実現できるかというのが、前回説明した「リード・モディファイ・ライト」です。

    こんなところでしょうか

  • チョコさん,

    解説ありがとうございました.チョコさんの「違います」という返信を期待していました.

    H/Wマニュアルについては,これまで参照したりあるいは眺めたりということで,深い読みはしておりませんでした.これは初心者にとっては不可避だと思いますが,今回のチョコさんの解説で,ポートに関し,平坦なマニュアルが立体的にとらえることができました.ご指導ありがとうございました.

    なお,本スレッドのチョコさんの解説は,「チョコさんのRL教室」に,「ポートについて」と題しUpされたら良いと思います.そうすれば,他の方々の目にも触れ,他の初心者にとって,大変役に立つと思います.

    ありがとうございました.