記事インデックス

メインマイコンボード搭載ボードにあるキャラクタLCD(SC1602BS-B)を制御するプログラムを作成します.このLCDには2行16文字の英数記号文字を表示する能力を有しており,加えて安価であるため,組込みシステムではよく利用されます.ただこのモジュールはVccが5[V]であるため,端子電圧が3.3[V]のマイコンで制御しようとするとレベルシフタが必要です.TKM-HでもTI社製レベルシフタICを使って双方向レベル変換をしています .

 

キャラクラLCD SC1602BS-B

  1. 端子

    SC1602BS-Bに備わる端子の役割を説明します.
    1. D0~7

      マイコンとLCDとの間でデータをやり取りするための端子です.8本ありますが,端子の少ないマイコンでも制御できるように,D4~D7だけでも制御できるようになっています.この場合,データを送受信するとき上位4ビットと下位4ビットに分けて送信します.ただし,TKM-Hでは,8本すべて繋げてありますのでこの手間はかかりません.
    2. RS

      このキャラクラLCDはいくつかのインストラクションコード,言い換えれば命令コマンドが存在し,それらをマイコンから受信し,フォントサイズを変えたり,文字を表示する位置を変えたりします.文字をLCDに表示するにはキャラクタコードをマイコンから送信し,表示している文字をマイコンで得るにはキャラクタコードをLCDから送信します.インストラクションとキャラクタコードを切り替える端子がRSです.ちなみに,RSはRegister Selectの略で,LCDの内部ではマイコンから送られてきたデータをインストラクションレジスタとデータレジスタのどちらに入れるか切り替えています.
    3. W/R

      多くのインストラクションコードはマイコンからLCDへ送りますが,一部は反対の場合があります.例えば,LCDに内蔵したパターンをマイコンへ送る場合です.このように,データバスの方向を変えるときW/R端子を切り替えます.
    4. E

      この端子はEnable Signalの略でマイコンから見たとき,出力端子となります.インストラクションコードをマイコンから送信する場合,この端子からの信号を立ち下げたときのデータバスからの信号をLCDは取り込みます.つまり,データバスの信号をLCDに取り込ませるタイミングといえます.
    5. VO

      LCDの文字のコントラストを調整するとき用います.具体的には,VOに印加される電圧とVDDとの比率で決まります.
    6. VDD

      プラス電源として5[V]を印加します.
    7. VSS

      グランドです.
  2. ブロック図

    SC1602BS-Bのブロック図のうち,ごく簡単たところだけ抜粋したものを下に示します.左側にあるのがRX62Nマイコン,右側にあるのがキャラクタLCD(SC1602BS-B)です.LCDには先ほど説明したインタフェースRS,R/W,E,D0~D7があり,マイコンの端子と接続されています.
    さて,下図を使って文字を出すまでの流れを簡単に説明します.インストラクションレジスタ(IR)とデータレジスタ(DR)がインタフェースと接続されており,ともに8ビット幅となっています.これらのレジスタに対して読み書きするかはRSにより決定します.具体的には,RS=0のときにはインストラクションレジスタと,RS=1のときにはデータレジスタと読み書きを行います.インストラクションレジスタに格納されるのは,次節で説明するインストラクション(平たく言えばLCDに対する命令)であり,これをインストラクションデコーダで意味を理解し命令を実行するのです.データレジスタに格納されるのはキャラクタコードや表示するパターンです.キャラクタコードとは文字に割り振られたID(=コード)であり,ASCIIコードを想像してもらえばわかりやすいと思います.SC1602BS-Bにはあらかじめ文字パターンを記憶しており,それぞれにキャラクタコードが割り振られています.こちらにキャラクタコードとそれに対応したパターンを置いておきます.さて,文字パターンがLCDに記憶されているといいましたが,これを記憶しているのがキャラクタジェネレータROM(CGROM)です.そして,ディスプレイデータRAM(DDRAM)にはキャラクタコードを格納し,LCDに表示するときにはCGROMから表示するパターンを参照するようになっています.
    先ほど示しましたキャラクタコード表の0x00~0x0FにはCGRAM(1)~(8)と書かれていると思います.これは,自作のパターンをキャラクタジェネレータRAM(CGRAM)に格納しておき,そのパターンを表示できることを示しています.CGRAMに自作パターンを格納するときもDRを利用します.DRにあるデータをCGRAMもしくはDDRAMのどちらに書き込むか決めるインストラクションがあります.これは次章で説明します.
    IRとDR以外に,アドレスカウンタ(AC)とビジーフラグをマイコンは取得することが可能です.これらを取得するためのインストラクションがあります.
    BlockDiagram
  3. インストラクションコードとキャラクタコード

    このLCDには下に示す表のインストラクションコードとキャラクタコードがあります.インストラクションコードを送るにはRSを0,キャラクタコードを送受するにはRSを1にします.

    コマンドの種類

    RS W/R D7 D6 D5 D4 D3 D2 D1 D0 詳細
    表示クリア 0 0 0 0 0 0 0 0 0 1 全表示クリア後,カーソルをホーム位置(左上)へ戻す.

    カーソルホーム

    0 0 0 0 0 0 0 0 1 X カーソルをホーム位置に戻す.シフトしていた表示も元に戻る.DDRAMの内容は変化しない.

    エントリモードセット

    0 0 0 0 0 0 0 1 I/D S データの書き込み及び読み出し時に,カーソルの進む方向,表示をシフトさせるかどうかの設定.
    表示オン,オフコントロール 0 0 0 0 0 0 1 D C B 全表示のオンオフ(D),カーソルのオンオフ(C),カーソル位置にある桁のブリンク(B)をセット.

    カーソル表示シフト

    0 0 0 0  1 S/C  R/L  X  X DDRAMの内容を変えずにカーソルの移動と表示シフトを行う.
    ファンクションセット 0 0  0 0 1 D/L N F  X  X インターフェースデータ長(D/L)行数(N),および文字フォント(F)を設定する.
    CGRAMアドレスセット 0 0  0  1 ACG CGRAMのアドレスをセット.以降送受するデータはCGRAMのデータ.
    DDRAMアドレスセット 0 0  1 ADD DDRAMのアドレスをセット.以降送受するデータはDDRAMのデータ.
    ビジーフラグ,アドレス読み出し 0 1 B/F AC モジュールが内部動作であることを示すB/FおよびACの内容を読み出す.
    CGRAM,DDRAMデータ書き込み 1 0 書き込みデータ(キャラクタコード) CGRAMまたはDDRAMにデータを書き込む.
    CGRAM,DDRAMデータ読み出し 1 読み込みデータ(キャラクタコード) CGRAMまたはDDRAMからデータを読み出す.
    ※XはDon't Careを表します.

    省略名 0 1
     I/D 左側へ移動  右側へ移動 
    S 表示のシフトをしない 表示のシフトをする
    S/C  カーソルの移動 表示のシフト
    R/L 左シフト 右シフト
    D/L 4ビット 8ビット
    N 1桁 2桁
    F 5×7ドット 5×10ドット
    D ディスプレイをオフ ディスプレイをオン
    C カーソルをオフ カーソルをオン
    B カーソルにある文字を点滅させない カーソルに文字を点滅させる
    B/F インストラクション受付中 内部動作中
    ACG CGRAMのアドレス
    ADD DDRAMのアドレス
    AC アドレスカウンタ

    コマンドの種類には,インストラクションコード(RS=0)とキャラクタコード(RS=1)があります.さらに,インストラクションコードには,マイコンから送信するもの(W/R=0)と,LCDから送信するもの(W/R=1)の2種類があります.その多くは前者であり,表示する文字やその位置,カーソルの有無などを設定するときに用います.後者はLCDの内部状態をマイコンが取得するときに使用します.
    キャラクタコードもW/Rが0のときと1のときでデータの送信方向が変わります.例えば,「A(キャラクターコード0x41)」という文字をLCDの左上に表示したい場合,W/Rを0にしてマイコンからLCDへ送信する状態にした上で,DDRAMアドレスセットによりADD=0x00を送信した後,CGRAM,DDRAMデータ書き込みでキャラクタコード0x41を送信します.
    DDRAMのアドレスとパターンの表示位置との関係について説明します.まずは下の図をご覧ください.緑色の枠で囲った箇所がパターンの表示位置であり,各セルに書かれている数字がDDRAMのアドレスです.例えば,2行目左から3列にパターンを表示したい場合にはDDRAMアドレスを0x02にすればよいです.注意しなければならないことは,2行目の左端アドレスが0x40となっていることです.実はSC1602BS-Bには1行に40パターンを記憶するだけのDDRAMが用意されているため,2行目左端のアドレスが0x10ではなく0x40となっているのです.なお,0x10以上0x27以下および0x50以上0x67以下に記憶されたパターンは,カーソル表示シフト命令にあるS/Cを0(表示シフト)にすることで表示させることができます.
    DDRAMAddress

クラスの構成

次に,関係するクラスを図によって説明します.まずはインストラクションです.今回使用するLCDには,前節で説明したインストラクションがあり,それぞれをクラスとしモデリングします.下にクラス図を示します.このクラス図は,やや細かくなってしまっていますので,こちらにPDF化したものも置いておきます
LCDCommand上記図について説明します.まず,基底のスーパクラスとなるのがLcdCmdクラスです.このクラスにはインストラクションコードや文字コードを格納するための_cmd,レジスタセレクトがインストラクションもしくはキャラクタのどちらになっているかを示す_registerSelect,インストラクションの実行時間を記憶する_executeTimeなどがフィールドとして存在します.
LcdCmdにはサブクラスとしてLcdTransmitCmdクラスとLcdReceiveCmdクラスがあります.LcdTransmitCmdは,マイコンからLCDへ送信するコマンドのスーパクラスとなっており,LcdReceiveCmdはその反対にマイコンがLCDから受信するクラスのスーパクラスになっています.以下では,それらサブクラスを説明します.

  1. LcdInstruction

    LcdInstructionには8個のサブクラスがあります.これらのサブクラスはRS=0,R/W=0のインストラクションをモデリングしたものです.これらのクラスは,D7からD0に向かって走査していき,はじめて1となったビットの位置で識別できます.このように,はじめて1となるビットのみを取り出した数値を_CODE_IDとして各クラスで定義しています.例えば,表示クリアコマンド(ClearCmd)ではビット0が1でありますので_CODE_IDは0x01,ファンクションセットコマンド(FunctionSetCmd)ではビット5が1となっていますので_CODE_IDは0x20となります.
    各コマンドには最大実行時間が決まっています.例えばエントリモードセットコマンド(EntryModeSetCmd)は,LCDが実行する最大時間は40[us]です.このような時間を_EXECUTE_TIMEとして各クラスでは定義しています.
    以上で説明した,_CODE_IDと_EXECUTE_TIMEを各クラスのコンストラクタで_codeIdと_executeTimeに代入します.
  2. WriteCharacterCmdとReadCharacterCmd

    LcdTransmitCmdのサブクラスには,CGROMにあるパターンを表示するとき,もしくはCGRAMにユーザのオリジナルパターンを設定するときに使うWriteCharacterCmdクラスがあり,LcdReceiveCmdのサブクラスにはCGROMもしくはCGRAMにあるパターンをマイコンが取得するReadCharacterCmdクラスがあります.これらのコマンドはともにRS=1ですので,LCDのインターフェイスを介してデータレジスタとやり取りされます.
  3. ReadBusyFlagCmd

    コマンドには前述のとおり実行時間があります.コマンドを実行したのち,所定の時間以上待てば終了するはずですが,より早く確実に処理の終了をするためにビジーフラグがLCDにあります.このフラグをマイコン側で得るために使うのがReadBusyFlagCmdです.このコマンドでは,アドレスカウンタも同時に得ることができるようになっています.

さて,上記で説明したコマンドクラスを送受信するには,LcdCmdTranceiverクラスにあるtransmitメソッドとreceiveメソッドを用います.transmitメソッドはLcdTransmitCmdのオブジェクトを引数としているため,LcdTransmitCmdをスーパクラスとするクラスのコマンドはすべて引数として渡すことができ,それらのコマンドをLCDに送ることができます.また,receiveメソッドはLcdReceiveCmdのオブジェクトを引数としているため,LcdReceiveCmdをスーパクラスとするクラスのコマンドはすべて引数として渡すことができ,それらのコマンドをLCDから受信することができます.


 プログラム

クラス図をもとにプログラムを組んでいきましょう.いくつかの手順を踏む必要がありますので順を追って説明していきます.

  1. スケルトンコード生成

    こちらにastahで書いたクラス図を置いておきますのでスケルトンコードを生成してください.deviceフォルダ内にあるクラスのみで結構です.
  2. プロジェクト作成

    今回,LCDを動作させるにはWaitTimerが必要となりますので,以前作成したプロジェクトをもとに作成していってください.これまで作成したプロジェクトを流用して新規プロジェクトを作成する方法についてはこちらに載っています
  3. スケルトンコードを追加

    1で生成したスケルトンコードをプロジェクトに追加します.今回追加するファイルのうち,ヘッダはC++ Headers/device/display/lcd,ソースはC++ Sources/device/display/lcdに置くことにします.2でインポートしたプロジェクトにはdevice/displayがありますので,displayの下にlcdというカテゴリを追加してください.
  4. エラーの修正

    スケルトンコードを追加した直後にビルドするといくつかのエラーが出ます.その中で多いのがシングルトンによるエラーです.具体的には,シングルトンとした場合,LcdCmdクラスではgetRegisterSelectの戻り値が0となっており,エラーとなります.ここでは_registerSelectを戻り値にしてください.ついでに,getExecuteTimeの戻り値を_executeTimeにしましょう
    W5H7PatternクラスおよびH5W10PatternクラスにあるgetPatternTypeの戻り値はそれぞれW5H7,W5H10にしましょう

以上で,コンパイルエラーは修正されたはずです.一通りのクラスができましたので,ソースおよびヘッダを整形しておきましょう.こちらのページを参考に整形してください.以降では,各クラスごとに作成をしていきましょう.


LcdPatternに関係するクラス(W5H7Pattern,W5H10Pattern)
LcdPatternに関係するクラスをまずは作成しましょう.このクラスは,幅5高さ7および幅5高さ10のサイズの文字パターンを記憶するためのクラスです.SC1602BS-Bはこの2種類のサイズのオリジナルパターンを記憶することができます.それぞれのパターンを記憶するため,W5H7PatternとW5H10Patternクラスがあります.これらのクラスのコンストラクタには文字のパターンを表す配列の先頭アドレスを渡せるようになっており,この配列に入っているデータにもとづきオリジナルパターンを記憶することができます.
以上のように,オリジナルパターンを作成するとき,幅5高さ7もしくは幅5高さ10のサイズか選ぶことができます.ただし条件があり,幅5高さ7のフォントは8種類登録できますが,幅5高さ10のフォントでは4種類しか登録できない仕様となっています.RAMの容量のためです.

  1. LcdPatternクラス

    1. コンストラクタ

      _width,_heightおよび_dataが初期化されており,それ以外に行うことはありません
    2. デストラクタ

      _data配列として確保したメモリを解放するのでdelete _data;となります.
    3. getRowData

      _data配列のrow番目の要素を返せばよいのでreturn _data[row];となります.
    4. setRowData

      _data配列のrow番目に,仮引数であるdataを代入すればよいので_data[row] = data;となります.
  2. W5H7Patternクラス

    1. コンストラクタ

      まず幅を5,高さを7に設定するため,_width=5;_height=7;と書きます.次に_data配列のために_height分だけメモリを確保するため,_data=new unsigned char[_height];となります.最後に,仮引数patternを_dataにコピーするため,for文でheight回繰り返し,_patternから_dataにコピーします.例えば下のようになります.
      for(int y=0; y<_height; y++) {
        _data[y] = pattern[y];
      }
    2. getPatternType

      この関数では,Font型の定数であるW5H7を返せばよいですので,return  W5H7;と書いてください.
  3. W5H10Patternクラス

    1. コンストラクタ

      W5H7Patternクラスを参考にして書いてください.違うところは_height=7が_height=10;となるところだけです.
    2. getPatternType

      return W5H10;と書いてください.


LcdCmdをスーパクラスとするコマンド用クラス

LcdCmdをスーパクラスとするクラス群を実装しましょう.

  1. LcdCmdクラス

    このクラスにはprotectedなメンバである_cmd,_registerSelect,_executeTimeがあり,サブクラスから読み書きできるようになっています.そのうち,_registerSelectと_executeTimeはLcdCmdクラスにgetterがありますので実装してください
  2. LcdReceiveCmdクラス

    このクラスには,LCDのデータバスから取得したデータを設定するsetSerialDataメンバ関数があります.仮引数serial_dataを_cmdに代入してください
  3. ReadBusyFlagCmdクラス

    このクラスのコンストラクタでは,_registerSelectをインストラクションコードにすることと,実行時間を設定してください.isBusy関数では,_cmdの最上位ビットが1であったらtrue,そうでなかったらfalseを返してください.getAddressCounter関数では,_cmdの最上位ビットを除く7ビットを返してください
  4. ReadCharacterCmdクラス

    このクラスのコンストラクタでは,_registerSelectをデータにすることと,実行時間を設定してください.getCharacter関数では,_cmdを返してください
  5. LcdTransmitCmdクラス

    このクラスにはLCDのデータバスを介して送信したいデータを取得するgetSerialDataメンバ関数があります.この関数では_cmdを返すようにしてください
  6. LcdInstructionクラス

    このクラスのコンストラクタでは,_registerSelectをインストラクションコードにしてください
  7. WriteCharacterCmdクラス

    このクラスのコンストラクタでは,_registerSelectをデータにすることと,実行時間を設定してください.setCharacter関数では,仮引数characterを_cmdに代入してください
  8. LcdInstructionクラスのサブクラス

    8種類のインストラクションをモデル化したクラスはすべてLcdInstructionクラスのサブクラスとなっています.それらのクラスではおおよそ同じようなことを行っております.このページではFunctionSetCmdを例にとって説明しますので,他のクラスはすべて皆さんが考えて実装してください
    まずはコンストラクタです.定数_EXECUTE_TIMEと_CODE_IDがありますので,コンストラクタで_executeTimeと_cmdに設定してください.具体的には次のようになります._cmdの代入方法に注意をしてください.なぜこのようになるかというと,すべてのインストラクションは最上位から走査していき最初に1となるビットの位置でインストラクションを識別することができます.この「1」を_CODE_IDとして書くクラスでは定義してありますので,_CODE_IDを_cmdに論理和してあげる必要があるのです.
    FunctionSetCmd::FunctionSetCmd(){
      /* 実行時間を設定する */
      _executeTime = _EXECUTE_TIME;
      /* このコマンドであることを表すビットを設定する */
      _cmd |= _CODE_ID;
    }
    

    次に,メンバ関数について説明します.FunctionSetCmdにはsetInterfaceDataWidth関数があります.この関数はインストラクションコード表にあるD/Lを表しています.この関数の仮引数widthがWIDTH8であったらD4(ビット4)を1にすればよいですので,_cmdには0x10(=2進数では00010000)と論理和したものを代入してください.反対に,widthがWIDTH4であったらD4を0にすればよいですので,_cmdには0x10を反転した(~0x10)と論理積したものを代入してください.具体的には下のようになります.

    void FunctionSetCmd::setInterfaceDataWidth(Interface
      /* データバスの幅を表すwidthにもとづき処理を分ける */
      switch ( width ) {
      /* 8ビット幅であったら */
      case WIDTH8:
        _cmd |= 0x10;
        break;
      /* 4ビット幅であったら */
      case WIDTH4:
        _cmd &= ~( 0x10 );
        break;
      }
    }
    
     このように,インストラクションコード表を読んで,対応するビットを操作するようにしてください.

    演習問題

    FunctionSetCmdにある残りのメンバ関数に加え,以下のクラスにあるメンバ関数を実装してください.
    CursorHomeCmd,ClearCmd,DisplayCmd,EntryModeSetCmd,CursorShiftCmd
  9. DdramAddressSetCmdクラス

    まずコンストラクタでは,先ほど説明したLcdInstructionクラスのサブクラスと同様に,_executeTimeと_cmdを設定してください.さて,DdramAddressSetCmdクラスでは,以前説明したように,LCD内にあるDDRAMのアドレスを設定することで,パターンを表示する位置を指定することができます.そこで,setLocateメンバ関数を作り,表示位置をxとyで指定できるようにしましょう.なお,0<=x<=15,0<=y<=1と指定することとしますので,例えば左から5列目,2行目にパターンを表示したい場合にはx=4,y=1とするようにしましょう.この関数で行う処理の流れをアクティビティ図にしたものを示します.オブジェクトノードの色の意味等についてはこちらで確認しておいてください.
    setLocation
    この処理で難しいのはビットの操作です.ビット6はDDRAMアドレスの最上位ビットですのでy=0のときには0,y=1のときには1となるようにする必要があります.ただし,ビット7にはDDRAMアドレスセットインストラクションを識別するためのビットとなっているので,これを消さずにビット6~0を操作しなければなりません.従いまして,1行目の場合次のようになります.
    _cmd = _CODE_ID | x;

    同じ要領で2行目のときのプログラムを考えてください.

  10. CgramAddressSetCmdクラス

    まずコンストラクタでは,先ほど説明したLcdInstructionクラスのサブクラスと同様に,_executeTimeと_cmdを設定してください.さて,CgramAddressSetCmdではオリジナルパターンを設定するときに使うsetCharacterPatternAddress関数があります.この関数では,オリジナルパターンを格納するCGRAMのアドレスを設定します.オリジナルパターンには5×7と5×10があり,前者の場合には8個,後者の場合には4個のオリジナルパターンを設定できます.5×7の場合,仮引数character_idを左へ3ビットシフトしたものと_CODE_IDとの論理和を_cmdに設定します.5×10の場合,仮引数character_idを左へ4ビットシフトしたものと_CODE_IDとの論理和を_cmdに設定します.下にこの関数の流れを表した図を示します.
    setCharacterPatternAddress

LcdCmdTranceiverクラス

このクラスにはコマンドを送信するtransmit,コマンドを受信するreceive関数があり,これらを使ってLCDを制御します.ここでは,LcdCmdTranceiverクラスにあるメンバ関数を実装していきましょう.

  1. シングルトン関連

    LcdCmdTranceiverクラスはシングルトンになっています.従いまして,コンストラクタ,コピーコンストラクタおよび代入演算子はすべてprivateです.また,staticなgetInstance関数によりシングルトンを取得することができるようにしてあります.以前紹介したこちらを参考に,下記の関数を実装してください
    コンストラクタ,コピーコンストラクタ,代入演算子,デストラクタ,getInstance(後ほど実装する_initialize関数を呼び出すようにしてください)
  2. setInterfaceDataWidth関数

    この関数は送受信を行うデータバスの幅を設定するときに用いられます.仮引数widthを_dataWidthに代入してください.加えて,データバスの幅が変わるので,_initialize関数を呼び出してください.
  3. _changeDataBus関数

    LCDのD0~D7端子と接続されているマイコンの端子は,コマンド送信時には出力端子,受信時には入力端子に設定しなければなりません.この入出力端子に設定する関数が_changeDataBusです.この関数には入出力方向を表すrwと,バス幅を表すwidthの2個の引数があり,rwがWRITEのときには端子を出力,READのときには端子を入力に設定します.また,widthがWIDTH8のときにはD0~D7,WIDTH4のときにはD4~D7と繋がっているマイコンの端子に対して入出力の設定をします.
    下の図に,LCDのD0~D7がマイコンのどの端子に接続されているか示します.この表にもとづき入出力の設定をしてください.
    LCDの端子 マイコンの端子
    D0 ポート4ビット
    D1 ポート4ビット
    D2 ポート4ビット
    D3 ポート4ビット2
    D4 ポート7ビット3
    D5 ポート0ビット7
    D6 ポート0ビット5
    D7 ポート6ビット0

    設定の流れを示したアクティビティ図を下に示しますので参考にしてください.処理の大部分は入出力端子を設定することなので,DDR(データディレクションレジスタ)の修正となります.
     changeDataBus
  4. _initialize関数

    この関数では,LCDが繋がっているマイコンの端子を初期設定します.具体的には,R/W,E,RSを汎用出力端子として,D0~D7端子を_changeDataBus関数により汎用入出力端子として設定します.ただし,出力端子に設定した時点から不定な出力が開始されてしまうため,事前に出力する値を0にした上で出力端子として設定します.
    下にR/W,E,RS端子とそれらがつながっているマイコンの端子の対応表を示します.
     LCDの端子 マイコンの端子 
    R/W  ポート9ビット2 
    ポート9ビット1
    RS  ポート9ビット0

    下の図は_initialize関数をアクティビティ図で表したものです.DR(データレジスタ)により出力を0にしています.
     initialize
  5. _transmitCmd8関数

    LCDへマイコンがデータを送るとき,データバス幅が8である場合に用いるのがこの関数です.下に処理の流れを示します.まず,データバスD0~7を出力端子に設定します.これには,_changeDataBus関数が役立つでしょう.次に,wait関数により1[us]待った後,RSおよびW/R端子を設定します.RS端子からは仮引数rs,W/R端子からは定数WRITEを代入します.再び1[us]待ち,その後E端子からHighを出力します.さらに1[us]待った後,データバスからデータを出力し,その後EからLowを出力します.なぜこのようにEをHighにしてからLowにするかというと,E端子が立ち下がったときにデータバスの状態をキャラクタLCDは取り込む仕様になっているためです.最後に送ったインストラクションが終わるまで待ちます.待ち時間は_transmitCmd8関数の仮引数execute_timeですのでこれをwait関数に渡します.
    以上の処理をプログラミングしてください.
     transmitCmd8

  6. _transmitCmd4関数

    この関数は,_transmitCmd8関数と同様にLCDへマイコンがデータを送るときに用います.ただし,データバス幅が4であるため,1つのインストラクションを上位4ビットと下位4ビットに分けて送信します.また,データバスはD4~7までしか使用しないため,D0~3に繋がっているマイコンの端子には何もしません.下に流れ図を示します.これに従いプログラミングしてください
     transmitCmd4

  7. _receiveCmd8関数

    この関数はデータバス幅8でデータを受信するときに用います.送信するとき用いられる関数_transmitCmd8との違いは,データバスの端子を入力にしW/R端子からREADを出力します.加えて,受信したデータをローカル変数cmdに入れることも異なることとして挙げられます.下の図は関数_receiveCmd8の流れを示したものです.この図にもとづきプログラミングしてください
     receiveCmd8
  8. _receiveCmd4関数

    _receiveCmd4関数はデータバス幅4のときに用いられる関数です.受信にはD4~D7の端子を用い,最初に上位4ビット,その後に下位4ビットを受信します.下図は_receiveCmd4関数のアクティビティ図です.これを参考にプログラムを組んでください
     receiveCmd4

  9. transmit関数

    この関数はpublicな関数になっており,外部から呼び出せるようになっています._transmitCmd8と_transmitCmd4は共にprivateな関数ですので,データ幅により切り分けてそれらの関数をtransmit関数で呼び出します.引数はLcdTransmitCmdクラスとなっておりますので,そのクラスに含まれるシリアルデータ,レジストセレクトおよび実行時間を取得し,_transmitCmd8もしくは_transmitCmd4関数を呼び出します.下図はtransmit関数の流れを表しています.参考にしてプログラミングしてください
    transmit
  10. receive関数

この関数もtransmit関数と同様にpublicな関数であり,外部クラスから呼び出せるようになっています.下にアクティビティ図を示しますので,これもプログラミングしてください
receive


Lcdクラス

LCDを操作するプログラムを書こうとする人がLCDを操作するときLcdクラスのインスタンスを用います.このにはLCDへ送受信するクラスのインスタンスがあり,それらを使ってLCDへコマンドを送受信することができます.LCDを操作するときに用いるため,多くのメンバ関数があるため実装することは一苦労ですが頑張りましょう.なお,Lcdクラスにプログラミングするとき,LcdCmdTranceiver.hppをインクルードする必要があります.Lcd.cppの冒頭で「#include "LcdCmdTranceiver.hpp"」と書いておきましょう

  1. シングルトン関連

    Lcdクラスはシングルトンになっています.従いまして,コンストラクタ,コピーコンストラクタおよび代入演算子はすべてprivateです.また,staticなgetInstance関数によりシングルトンを取得することができるようにしてあります.以前紹介したこちらを参考に,下記の関数を実装してください
    コンストラクタ,コピーコンストラクタ,代入演算子,デストラクタ,getInstance(後ほど実装する_initialize関数を呼び出すようにしてください)
  2. _initialize関数

    この関数では,LCDをセットアップするときに行ういくつかのインストラクションを行います.下の図をご覧ください.多くのコマンドを設定し,一定時間待ったのち送信することの繰り返しです.下記のとおり,順番に処理を行うプログラムを作成してください.
    Lcd initialize
  3. writeChar関数

    この関数は引数として渡すx列y行の場所に1文字表示するためのものです.文字を表示するには,DDRAMアドレスセット(_ddramAddresSetCmd)を使い,文字を表示する位置を指定し,そののち文字コード書き込み(_writeCharacterCmd)を行います.なお,それぞれのインストラクションは既にインスタンス化してあります.Lcd.hを確認してください.下の図は,処理の流れを表したものです.参考にしてプログラミングしてください
    writeChar
  4. writeString関数

    この関数は文字列をLCDに表示することができます.内部的には,引数として渡されるstringから1文字ずつ順番に取り出し,writeChar関数を使って出力していきます.下の図はwriteString関数の流れを示したものです.制御変数iをfor文で繰り返し,1文字ずつwriteChar関数へ表示したい文字パターンのコードを送ります.また,表示する位置を右側に送るため,x+iをwriteChar関数に送る必要があります.この図を参考にプログラムを作成してください
    writeString
  5.  LCDを操作するための関数群

    Lcdクラスにメンバとして存在するコマンドクラスのインスタンスを使うことで,LCDを操作することができます.下の表にあるメンバ関数はいずれもLCDを操作するためのものですので,コマンドクラスのインスタンスをうまく使いこなして実装してください.ただし,setInterfaceDataWidth関数では,LcdCmdTranceiverにもデータ幅を設定するようにしてください.また,いずれのコマンドクラスのインスタンスもLcdCmdTranceiverのtransmit関数を使って送信してください.インスタンスにただ設定しただけではLCDに送信されません
    関数名 用途
    clear 画面をクリアする
    moveCursorToHome カーソルをホームへ移動する
    setEntryDirection 挿入される文字列の進行方向を設定する
    enableShift 表示のシフトをさせる
    disableShift 表示のシフトをしない
    displayAll すべての文字を表示する
    hideAll すべての文字を非表示にする
    displayCursor カーソルを表示する
    hideCursor カーソルを隠す
    blink 点滅させる
    light 点灯させる
    setCursorDirection カーソルのシフト方向を設定する
    setMovementObject 移動対象を設定する
    setLineNum 行数を設定する
    setFont フォントを設定する
    setInterfaceDataWidth インターフェースデータ幅を設定する
  6. writeHex関数

    writeHex関数は,与えられた数値を16進数で表示するときに用いられます.この内部では1文字分の16進数を表示するプライベートな_writeHex関数を使っており,この関数を16進数の桁ごとに呼び出しています._writeHex関数については後ほど説明するとして,ここでは16進数の桁ごとに分けて_writeHex関数を呼び出す処理について説明します.
    writeHex関数には,表示するさまざまな型に対応するため,オーバロードしてあります.方によって表示する桁数が異なり,例えばunsigned short型であれば16進数で4桁(=16ビット)となります.この桁数回だけ_writeHex関数を呼び出します.最上位4ビット,つまりビット12~15を取り出すには,そのビットのみ1となっている0xF000と論理積をとり,さらに《表示する数値のビット数(16)-4=12》右シフトします.この数値を_writeHex関数に渡せばよいのです.同様に,ビット8~11を取り出すには,0x0F00と論理和をとり,《表示する数値のビット数(16)-8=8》右シフトします.
    以上のように,各桁を取り出し,_writeHex関数を呼び出すようにしてください.下図はunsigned short の数値であるvalueを列x,行yに表示するwriteHex関数の流れ図を表しています._writeHex関数には3個の引数があり,第1引数に表示する数値,第2引数に列,第3引数に行があります.この関数を使って下記のようなプログラムを作成してください.さらに,writeHex関数の第1引数の型がunsigned charのもの,unsigned intのもの,unsigned long longのものも実装してください.
    writeHex
  7. _writeHex関数

    この関数では1文字分の16進数の数値,すなわち0~Fまでを表示します.表示する”数値”と言っていますが,その実は0~9までの"数値"と,A~Fまでの"文字"が混在しています.そこで,数値と文字を分けて取り扱うことにします.下図は_writeHex関数で行う処理の流れを表したものです.「1文字表示する」にはwriteChar関数を使用してください.これを参考にしてプログラミングしてください.
     writeHex
  8. writeBin関数

    writeBin関数でも内部では_writeBin関数を使っています.ただし,_writeHex関数のように1文字ずつ表示するのではなく,_writeBin関数では4文字ずつ,つまり4ビットずつ表示する仕様となっています.従いまして,writeHex関数と同じように,4ビットずつ区切りながら_writeBin関数を呼びます.下図はunsigned shortの値を2進数で表示するwriteBin関数の流れ図です.4ビットの2進数を表示するには_writeBin関数をお使いください._writeBin関数には3個の引数があり,第1引数に表示する数値,第2引数に列,第3引数に行があります.この関数を使って下記のようなプログラムを作成してください.さらに,writeBin関数の第1引数の型がunsigned charのもの,unsigned intのもの,unsigned long longのものも実装してください.
    writeBin
  9. _writeBin関数

    _writeBin関数では,staticでconstなテーブル_DEC_TO_BINを使って,引数valueを4桁の2進数に変換します.例えば,valueが3の場合,_DEC_TO_BIN[value]="0011"となりますので,この0011文字列をwriteStringで表示してください.以上のようなプログラムを記述してください.
  10. writeDec関数

    この関数では10進数の数字をLCDに表示することができます.下図ではwriteDecの処理内容を示しています.
    writeDecまず,表示する値valueが正数なのか負数なのか調べ,負数であったら最初に"-"を表示します.次に,下の桁から上の桁の順番でwriteCharを使い表示していきます.この処理を行う途中で桁数を得る処理が必要ですので,新たに_getDigitNum関数を作成します.一例として_getDigitNum関数を下に示します._getDigitNum関数などを使ってwriteDec関数を完成させてください.
    unsigned char Lcd::_getDigitNum(long long value){
      /* 比較対象 */
      long long tmp;
      /* 制御変数 */
      unsigned char i;
      /* 負数のとき0,0を含む正数のとき0となる変数 */
      unsigned char sign = 0;
      /* 絶対値を求める */
      if(value < 0) {
        value *= -1;
        sign = 1;
      }
      for(i=0, tmp=10; i<10; i++, tmp*=10) {
        if(tmp > value) {
          return i+sign;
        }
      }
      return 0;
    }

     

  11. setCharacterPattern関数

    この関数は,オリジナルのパターンをLCDに送信するときに用いられます.ここではプログラムを示しますのでコピーしてください.処理内容は,CGRAMのアドレスをセットしたうえでパターンを送信する,というところでしょうか.
    void Lcd::setCharacterPattern(unsigned char character_id, const LCDPattern* pattern){
      /* アドレスをインクリメントするようにする */
      _entryModeSetCmd.setDirection ( FORWARD );
      /* コマンドを送信する */
      LcdCmdTranceiver::getInstance()->transmit(&_entryModeSetCmd);
      /* アドレスを設定する */
      if(pattern->getPatternType() == W5H7) {
        _cgramAddressSetCmd.setCharacterPatternAddress(W5H7, character_id);
      }
      else if(pattern->getPatternType() == W5H10) {
        _cgramAddressSetCmd.setCharacterPatternAddress(W5H10, character_id);
      }
      /* コマンドを送信する */
      LcdCmdTranceiver::getInstance()->transmit(&_cgramAddressSetCmd);
    
      /* 幅5高さ7のパターンの場合 */
      if(pattern->getPatternType() == W5H7) {
        /* 0から7 */
        for(int y=0; y<7; y++) {
          /* パターンを設定する */
          _writeCharacterCmd.setCharacter(pattern->getRowData(y));
          /* コマンドを送信する */
          LcdCmdTranceiver::getInstance()->transmit(&_writeCharacterCmd);
        }
        /* カーソル部分は白画素にしておく */
        _writeCharacterCmd.setCharacter(0x00);
        /* コマンドを送信する */
        LcdCmdTranceiver::getInstance()->transmit(&_writeCharacterCmd);
      }
      /* 幅5高さ10のパターンの場合 */
      if(pattern->getPatternType() == W5H10) {
        for(int y=0; y<10; y++) {
          /* パターンを設定する */
          _writeCharacterCmd.setCharacter(pattern->getRowData(y));
          /* コマンドを送信する */
          LcdCmdTranceiver::getInstance()->transmit(&_writeCharacterCmd);
        }
        /* カーソル部分は白画素にしておく */
        _writeCharacterCmd.setCharacter(0x00);
        /* コマンドを送信する */
        LcdCmdTranceiver::getInstance()->transmit(&_writeCharacterCmd);
      }
    }
    

     

  12. getCharacterPattern関数

    この関数は,LCDに設定されているパターンをマイコンで取得するためのものです.ここではプログラムを示しますのでコピーしてください.
    void Lcd::getCharacterPattern(unsigned char character_id, LCDPattern* pattern){
      /* アドレスをインクリメントするようにする */
      _entryModeSetCmd.setDirection ( FORWARD );
      /* コマンドを送信する */
      LcdCmdTranceiver::getInstance()->transmit(&_entryModeSetCmd);
      /* アドレスを設定する */
      if(pattern->getPatternType() == W5H7) {
        _cgramAddressSetCmd.setCharacterPatternAddress(W5H7, character_id);
      }
      else if(pattern->getPatternType() == W5H10) {
        _cgramAddressSetCmd.setCharacterPatternAddress(W5H10, character_id);
      }
      /* コマンドを送信する */
      LcdCmdTranceiver::getInstance()->transmit(&_cgramAddressSetCmd);
    				
      /* 幅5高さ7のパターンの場合 */
      if(pattern->getPatternType() == W5H7) {
        /* 0から7 */
        for(int y=0; y<7; y++) {
          /* コマンドを受信する */
    						LcdCmdTranceiver::getInstance()->receive(&_readCharacterCmd);
          /* パターンを設定する */
          pattern->setRowData(y, _readCharacterCmd.getCharacter());
        }
        /* カーソル部分は白画素にしておく */
        pattern->setRowData(7, 0);
      }
      /* 幅5高さ10のパターンの場合 */
      if(pattern->getPatternType() == W5H10) {
        for(int y=0; y<10; y++) {
          /* コマンドを受信する */
    						LcdCmdTranceiver::getInstance()->receive(&_readCharacterCmd);
          /* パターンを設定する */
          pattern->setRowData(y,  _readCharacterCmd.getCharacter());
        }
        /* カーソル部分は白画素にしておく */
        pattern->setRowData(7, 0);
      }
    }

 以上でLcd.cppは完成したはずです.


Main関数

最後にmain関数を使ってLCDを操作してみましょう.ここではごく簡単に,「ABC」という文字列を表示させます.下に示しますように,まずはLcd.hppをインクルードするとともに,device::display::lcd名前空間を使うように設定します.そして,main関数内ではlcdのインスタンスを取得し,writeString関数で「ABC」を表示します.

#include "Lcd.hpp"

using  namespace device::display::lcd;

void main(void){
  /* LCDの初期化 */
  Lcd *lcd = Lcd::getInstance();
  /* 文字を表示する */
  lcd->writeString("ABC", 0,0);
  /* 無限ループ */
  while(1) {
  }
}

 

JSN Teki is designed by JoomlaShine.com