アプリケーションノート 4039

スタティックLCDディスプレイコントローラとしてのDS89C450の使用方法


要約: マキシムのマイクロコントローラの多くは、ハードウェアに実装されるLCDディスプレイ用のコントローラを内蔵しています。DS89C450などの一部のマイクロコントローラはこの機能を搭載していませんが、ソフトウェアで簡易なディスプレイコントローラを実装することができます。このアプリケーションノートでは、超高速フラッシュマイクロコントローラのDS89C450を使って7セグメント桁を備えるスタティックLCDパネルを駆動する方法について説明します。

概要

液晶ディスプレイ(LCD)パネルは、計算器、ハンドヘルド血糖測定器、ガソリンスタンド給油装置、およびテレビなどの様々な現代の電子機器に使用されています。LCDは消費電力量が低く、直接光でも視認性が良いので、多くのアプリケーションにおいて従来のLEDディスプレイに取って代わっています。(MAXQ2000などの)一連のマイクロコントローラは、最大¼マルチプレックスデューティサイクルでLCDパネルを駆動可能なLCDコントローラを内蔵しています。ただし、場合によっては特定アプリケーションに最適なマイクロコントローラがLCDコントローラを内蔵していないことがあります。こうした状況の場合は、ディスプレイを駆動するためにマイクロコントローラのポート端子を使用すれば、ソフトウェアでディスプレイコントローラを実装することができます。

このアプリケーションノートでは、超高速フラッシュマイクロコントローラのDS89C450を使って7セグメント桁を備える簡易なスタティックLCDパネル用のディスプレイコントローラを実装する方法について説明します。DS89C450独自の機能が使用されないため、アプリケーションで使用されるLCDパネルを駆動するのに十分な数のポート端子がある限り、いずれの8051互換マイクロコントローラにもこのコード例を容易に移植することができます。

このアプリケーションノートのコード例は、ダウンロードして入手することができます。

LCDパネルの選択

アプリケーション用にLCDパネルを選択する際には、LCDを互換マイクロコントローラまたはLCDディスプレイコントローラと整合させるように注意してください。この決定を行う際には、以下の設問を考慮する必要があります。
  • LCDの動作電圧範囲はどのような範囲ですか? DS89C450は5Vマイクロコントローラであり、そのポート端子は5Vレベルで動作するため、5V LCDパネルを選択する必要があります。なお、LCDコントローラを内蔵する多くのマイクロコントローラは専用の電源入力(VLCD)を使って、そのLCDコントローラで使用される電圧範囲を設定します。
  • LCDのデューティサイクルはどのくらいですか? スタティックLCDパネルは、ディスプレイの各セグメントを専用駆動ラインに接続します。したがって、セグメントドライバの数は駆動するLCDセグメントの数と一致する必要があります。ただし、マルチプレックスLCDパネルは、各セグメント駆動ライン(SEG)当たり1つ以上のLCDセグメントを駆動します。これらのパネルは複数のコモンバックプレーン(COM)出力を使って、使用されるデューティサイクルに応じてSEGおよびCOMライン上にVLCD~GND間の複数のレベルを駆動します。8051マイクロコントローラであるDS89C450は、ポート端子ラインを5VおよびGNDに駆動することしかできないため、この例はスタティックLCDに限定されます。マルチプレックスLCDの駆動に関する詳細については、以下のドキュメントを参照してください。
  • LCDパネルを動作させるにはいくつのセグメントとコモンドライバが必要ですか? スタティックLCDパネルを制御する場合は、コモン(COM)バックプレーンライン用の追加ポート端子に加えて、駆動するセグメントごとに1つの駆動ライン(ポート端子)が必要です。
このアプリケーションノートでは、Lumex® LCD-S401C52TRディスプレイが選択されています。このLCDは、7セグメント4桁および3つのアナンシエータセグメント(コロンおよび3つの小数点)を備える5Vのスタティックディスプレイパネルです。LCD上の数字はそれぞれ、図1に図示されるように7つのセグメントから構成されます。この図では、A、B、G、E、およびDのセグメントがオンにされ、「2」の数字を表示しています。

Figure 1. Seven-segment LCD display digit.
図1. 7セグメントのLCDディスプレイ桁

LCD-S401C52TRディスプレイは、単一のCOMバックプレーン(2つの端子に接続)および32のディスプレイセグメントを備え、これらのセグメントはそれぞれセグメント駆動端子に接続されます。この例の場合は、3桁の7セグメントのみを使用します。すなわち、DS89C450は21のSEGライン(各セグメント桁当たり7セグメント)と1つのCOMラインを駆動する必要があります。このため、合計22個のポート端子が必要です。DS89C450は拡張メモリバス構成で動作しない場合は、24個のプッシュプルポート端子を備えています。このため、マイクロコントローラはこのタスクに十分なI/O能力を備えています。(ポート0で8個の追加端子が利用可能です。ただし、これらの端子はオープンドレイン型であり、汎用I/Oとして使用するにはプルアップ抵抗の追加が必要です)。

ハードウェアの設定

この例のハードウェアの設定は、メモリインタフェースCPLD (U5)を備え、両方の外部メモリチップ(U6およびU7)を取り外したDS89C450の評価(EV)キット(Rev B)に基づきました。この変更によって、アプリケーションで使用するための追加ポート端子数 が自由になります。本来は、これらの端子は拡張メモリバス、具体的にはポート0 (合計8ライン)、ポート2 (合計8ライン)、ポート3.6および3.7を実装するために使用されます。表1を参照してください(:ポート0はこのアプリケーション例では使用されません)。DS89C450は64kBの内部コード空間と1kBの内部データSRAMを内蔵しているので、この例には十分です。

LCD-S401C52TRディスプレイのセグメントおよびコモンラインは、プロトタイピング領域に隣接するJ4ヘッダを使って、DS89C450のポート端子に接続されます。これらのセグメントラインは、ポート端子に直接接続されずに、1kΩの抵抗を通じてポート端子に接続されます。この後のステップが実行されたのは、DS89C450のポート端子の駆動能力が、LCDパネル駆動ラインで通常使用される場合に比べて高いためです(0の状態およびワンショットの場合は強プルダウン、1の状態の場合は強プルアップに続く弱プルアップ)。COMラインはセグメントラインに比べ大きな容量を備え、強力なドライバを必要とするため、ポート端子に直接接続されています。ただし、このアプリケーションでは、セグメントラインがポート端子によって直接駆動されることを推奨しません。そうした構成では問題が発生します。すなわち、LCDディスプレイのセグメントおよびコモンプレーン間を介した容量性結合は、COMラインが意図した状態から逸脱する傾向があるので、セグメントがますますターンオンします。(アクティブセグメントはコモンプレーンと常に逆の電圧であるため、この問題が発生します)。その結果、オフであるべきセグメントが一部オンになります。このため、抵抗を介してポート端子を接続して駆動能力を削減することにより、この問題が解消されます。

表1. LCDパネルおよびポート端子の接続
DS89C450 Port Pin J4 Header Pin LCD Pin(s) LCD Signal Notes
P1.0 1 21 4A Through 1kΩ
P1.1 2 20 4B Through 1kΩ
P1.2 3 19 4C Through 1kΩ
P1.3 4 18 4D Through 1kΩ
P1.4 5 17 4E Through 1kΩ
P1.5 6 22 4F Through 1kΩ
P1.6 7 23 4G Through 1kΩ
P1.7 8 1, 40 COM Connect directly
P2.0 21 25 3A Through 1kΩ
P2.1 22 24 3B Through 1kΩ
P2.2 23 15 3C Through 1kΩ
P2.3 24 14 3D Through 1kΩ
P2.4 25 13 3E Through 1kΩ
P2.5 26 26 3F Through 1kΩ
P2.6 27 27 3G Through 1kΩ
P3.0 10 30 2A Through 1kΩ
P3.1 11 29 2B Through 1kΩ
P3.2 12 11 2C Through 1kΩ
P3.3 13 10 2D Through 1kΩ
P3.4 14 9 2E Through 1kΩ
P3.5 15 31 2F Through 1kΩ
P3.6 16 32 2G Through 1kΩ

以下のように、ハードウェア設定に関して注意すべきその他の項目がいくつかあります。
  • 16.384MHzの標準水晶(Y1に挿入)を使って、DS89C450にクロックを供給します。
  • アプリケーションを実行する際には、DIPスイッチSW1.1およびSW4.2はオン、残りはすべてオフである必要があります。
  • (MAXQマイクロコントローラツールキット(MTK)または別の開発ツールを使って)アプリケーションをロードする際には、DIPスイッチSW1.1、SW1.2、SW1.3、SW4.1、およびSW4.2はオン、残りはすべてオフである必要があります。
  • LCDディスプレイが実行中の際は、ポート1のアクティビティはLEDバーグラフディスプレイU10にも表示されます。この表示は正常であり、LCDディスプレイはバッファされるため、アプリケーションに影響を与えません。
  • P3.0およびP3.1は、シリアルポート0のTx/Rxラインにも使用されます。このため、アプリケーションのロード時(シリアルポートブートローダーによって)、LCDの1つまたは2つのセグメントがこれらのラインの動作のため、ちらつく場合があります。このちらつきは正常です。アプリケーションを実行中のときには、DIPスイッチSW1.2およびSW1.3をオフにして、シリアルポート機能をディセーブルする必要があります。
  • LCDディスプレイの未使用セグメントを明示的にオフ状態にする必要があり、フロートさせることはできません。この作業は、1つまたは複数の未使用セグメントをオフ状態(COMと同じ電圧波形)にされたポート端子に接続するか、または未使用セグメントをCOMに直接接続することによって、実行することができます。

LCDセグメントの駆動

LCDセグメントのデフォルト状態はオフ(すなわち透明)です。電圧が印加されない場合は、セグメントは透明になり、LCDパネルを背景にすると見えません。また、同じ電圧がセグメントライン(SEG)とコモンバックプレーン(COM)にともに印加されると、セグメントはオフ状態を維持します。セグメントのSEG端子とCOMプレーン間の電位差が印加されると、そのセグメントのみオン(すなわち、不透明)状態に切り替わります。この電圧はスレッショルド電圧とされる特定のレベルを上回るため、セグメントは不明瞭になり、最終的に完全に不透明になります。LCDパネルの指定の動作電圧のパーセント値であるこのスレッショルド電圧は、LCDごとに異なります。

電圧差動の極性は、LCDセグメントの駆動には重要ではありません。たとえば、3Vスレッショルド電圧でLCDを駆動するコントローラは、COMをグランドに設定しSEGnを3Vに設定するか、またはCOMを3Vに設定しSEGnをグランドに設定することによって、セグメントnをスイッチオンにすることができます。このことは重要です。なぜなら、スタティックDC電圧がLCDセグメントに長時間残る場合は、セグメントは損傷を受ける場合があり、もはや適切に切り替わらなくなるためです。この問題を回避するために、LCDセグメントは交流波形によって常に駆動され、セグメントがオン状態またはオフ状態であるかにかかわらず、各セグメントのDC電圧全体が常にゼロになるようにします(図2)。

Figure 2. Alternating drive waveforms for static LCD segments.
図2. スタティックLCDセグメントの交流駆動波形

図2が示すように、スタティックディスプレイのCOM端子は、VLCD (この設定の場合は5V)~GND間で50%デューティサイクル方形波によって常時駆動されます。この場合、各セグメントラインは、2つのパターンのうちいずれか1つによって駆動されます。
  • セグメントをオフにするには、COM端子の駆動用の波形と同じ波形で駆動する必要があります。これによって、SEG/COMペアのDC電圧は常にゼロになります。すなわち、セグメントはオフ状態を維持します。
  • セグメントをオンにするには、COM波形の反転した波形で駆動する必要があります。これは、時間の半分はセグメントが正電圧で駆動され、残りの半分の時間は負電圧で駆動されることを意味します。これらの2つの状態は外見が同じであるため、セグメントは常時オンであるように見えます。電位差の平均DC値はゼロであるため、LCDガラスに損傷を与える可能性があるスタティックDCバイアスは残りません。
LCDが駆動される周波数(フレーム周波数と呼ばれる)は、LCDパネルごとに異なります。任意のアプリケーションの適正値は通常、特定のハードウェア設定に関する実験によって得られます。LCDセグメントが状態を遷移させることが可能なレートはセグメントの総容量によって制限されるため、LCDは特定のフレーム周波数範囲でのみ適切に動作します。通常は、この範囲は20Hz~200Hzに及びます。このアプリケーションノートのコード例は、約30HzでLCDを駆動します。特定ディスプレイに対して過度に高いまたは低いフレームレートによって、LCDセグメントがちらついたり、見た目が薄暗くなります。

LCDセグメントを駆動するアプリケーション例のメインループを以下に示しています。
Main:
   mov    IE, #080h          ; Disable timer 0 interrupt temporarily
   mov    R2, DigitP1        ; Grab local copies of digit variables
   mov    R3, DigitP2
   mov    R4, DigitP3 
   mov    IE, #082h          ; Re-enable timer 0 interrupt

   mov    A, R2               
   call   getDigit           ; Calculate segment pattern for ones digit
   anl    A, #01111111b      ; Ensure that COM (P1.7) is driven low
   mov    P1, A

   mov    A, R3       
   call   getDigit           ; Calculate segment pattern for tens digit
   mov    P2, A

   mov    A, R4
   call   getDigit           ; Calculate segment pattern for hundreds digit
   mov    P3, A

;;;;  Delay loop  ;;;;

   mov    R0, #0FFh
L1A:
   mov    R1, #0FFh
L1B:
   djnz   R1, L1B
   djnz   R0, L1A

;;;;;;;;;;;;;;;;;;;;;;

   mov    A, R2               
   call   getDigit           ; Calculate segment pattern for ones digit
   cpl    A                  ; Inverse of the pattern driven on the first frame half
   orl    A, #10000000b      ; Ensure that COM (P1.7) is driven high
   mov    P1, A
 

   mov    A, R3
   call   getDigit           ; Calculate segment pattern for tens digit
   cpl    A                  ; Inverse of the pattern driven on the first frame half
   mov    P2, A

   mov    A, R4
   call   getDigit           ; Calculate segment pattern for hundreds digit
   cpl    A                  ; Inverse of the pattern driven on the first frame half
   mov    P3, A

;;;;  Delay loop  ;;;;

   mov    R0, #0FFh
L2A:
   mov    R1, #0FFh
L2B:
   djnz   R1, L2B
   djnz   R0, L2A

;;;;;;;;;;;;;;;;;;;;;;

   ljmp   Main               ; Go back for another frame cycle (endless loop)
なお、(P1.7に接続される) COMラインは、フレームの前半はロー、後半はハイという同じ波形によって常に駆動されます。セグメントラインの場合は、フレームの前半で駆動されるパターンは後半で反転されます。3桁はそれぞれ3個の各ポートに同じように接続されているため、セグメントAは常にPx.0に、セグメントBはPx.1に、以下同様に接続されています。この構成によって、このコード例がgetDigitルーチンを用いて、LCDパネルの3桁ごとにセグメントパターンを計算することができます。
;***************************************************************************
;*
;*  getDigit
;*  
;*  Returns an LCD segment pattern (in Acc) for the decimal digit (0 to 9)
;*  input (also in Acc)
;*

getDigit:
   cjne   A, #0, getDigit_not0
;             xgfedcba
   mov    A, #00111111b         ; Zero
   ret
getDigit_not0:
   cjne   A, #1, getDigit_not1
;             xgfedcba
   mov    A, #00000110b         ; One
   ret
getDigit_not1:
   cjne   A, #2, getDigit_not2
;             xgfedcba
   mov    A, #01011011b         ; Two
   ret
getDigit_not2:
   cjne   A, #3, getDigit_not3
;             xgfedcba
   mov    A, #01001111b         ; Three
   ret

getDigit_not3:
   cjne   A, #4, getDigit_not4
;             xgfedcba
   mov    A, #01100110b         ; Four
   ret
getDigit_not4:
   cjne   A, #5, getDigit_not5
;             xgfedcba
   mov    A, #01101101b         ; Five
   ret
getDigit_not5:
   cjne   A, #6, getDigit_not6
;             xgfedcba
   mov    A, #01111101b         ; Six
   ret
getDigit_not6:
   cjne   A, #7, getDigit_not7
;             xgfedcba
   mov    A, #00000111b         ; Seven
   ret
getDigit_not7:
   cjne   A, #8, getDigit_not8
;             xgfedcba
   mov    A, #01111111b         ; Eight
   ret
getDigit_not8:
   cjne   A, #9, getDigit_not9
;             xgfedcba
   mov    A, #01101111b         ; Nine
   ret
getDigit_not9:
   mov    A, #0
   ret   

カウンタの実行

コード例によってLCDに表示されるパターンは3桁の10進カウンタです。このカウンタは電源投入時に000から始まり、001、002のように999に達するまでインクリメントしていき、ロールオーバします。プログラムのメインループはLCDセグメントとコモンパターンを駆動するため、カウンタ値を定期的にインクリメントする別の方法を見つける必要があります。1つのソリューションは、タイマ0を使って割込みを定期的にトリガすることです。
   mov    TMOD, #021h        ; Timer 1: 8-bit autoreload from TH1
                             ; Timer 0: 16-bit
   mov    TCON, #050h        ; Enable timers 0 and 1
   mov    CKMOD, #038h       ; Use system clock for all timer inputs

   mov    IE, #082h          ; Enable timer 0 interrupt
タイマ割込みが発生するごとに、レジスタメモリ内の遅延カウンタはデクリメントされます。この遅延カウンタがゼロに達すると、LCDの3桁カウンタ値は1ずつインクリメントされ(必要に応じて、各桁をロールオーバして)、遅延カウンタはその最大値に再初期化されます。タイマ0は16ビット幅で、コード例は遅延カウンタを20に設定するため、3桁カウンタは、約(1/16.384MHz) × (216) × 20 = 0.08秒ごとに、または毎秒約12回インクリメントします。
   org     000Bh             ; Timer 0 interrupt
   ljmp    IntTimer0


;***************************************************************************
;*
;*  IntTimer0 (INTT0)
;*  
;*  Timer interrupt service routine
;*

IntTimer0:
   push    ACC               ; Save off accumulator and R0
   push    R00

   mov     R0, Count         ; Only increment LCD digits every [CountMax]
                             ; interrupt cycles
   djnz    R0, INTT0_Done 
   
   inc     DigitP1           ; Increment ones digit on display
   mov     A, DigitP1
   cjne    A, #10, INTT0_Continue      ; Check for rollover

   mov     DigitP1, #0
   inc     DigitP2           ; Increment tens digit on display
   mov     A, DigitP2
   cjne    A, #10, INTT0_Continue      ; Check for rollover

   mov     DigitP2, #0
   inc     DigitP3           ; Increment hundreds digit on display
   mov     A, DigitP3
   cjne    A, #10, INTT0_Continue      ; Check for rollover

   mov     DigitP3, #0

INTT0_Continue:
   mov     R0, CountMax      ; Reset to starting cycle count
INTT0_Done:
   mov     Count, R0         ; Update cycle counter
   pop     R00
   pop     ACC               ; Restore accumulator and R0
   reti

結論

多くのマイクロコントローラの専用ディジタルペリフェラルと同様に、スタティックまたはマルチプレックスLCDディスプレイコントローラを必要に応じてソフトウェアで実装することができます。スタティックディスプレイは簡素であるため、この実装は極めて簡単です。DS89C450などの8051マイクロコントローラの標準的な汎用I/O 機能を使って、LCDでSEGおよびCOM波形を駆動することができます。高性能のDS89C450を使用すると、LCDコントローラがソフトウェアで実装された後も、大多数のアプリケーションに十分な処理能力が確保されます。