要約
マキシムのマイクロコントローラの多くは、ハードウェアに実装される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の駆動に関する詳細については、以下のドキュメントを参照してください。
- アプリケーションノート3548 「Using an LCD with MAXQ Microcontrollers」
- 「MAXQ®ファミリのユーザガイド:MAXQ2000用補足資料」
- LCDパネルを動作させるにはいくつのセグメントとコモンドライバが必要ですか? スタティックLCDパネルを制御する場合は、コモン(COM)バックプレーンライン用の追加ポート端子に加えて、駆動するセグメントごとに1つの駆動ライン(ポート端子)が必要です。
図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)。
図2. スタティックLCDセグメントの交流駆動波形
図2が示すように、スタティックディスプレイのCOM端子は、VLCD (この設定の場合は5V)~GND間で50%デューティサイクル方形波によって常時駆動されます。この場合、各セグメントラインは、2つのパターンのうちいずれか1つによって駆動されます。
- セグメントをオフにするには、COM端子の駆動用の波形と同じ波形で駆動する必要があります。これによって、SEG/COMペアのDC電圧は常にゼロになります。すなわち、セグメントはオフ状態を維持します。
- セグメントをオンにするには、COM波形の反転した波形で駆動する必要があります。これは、時間の半分はセグメントが正電圧で駆動され、残りの半分の時間は負電圧で駆動されることを意味します。これらの2つの状態は外見が同じであるため、セグメントは常時オンであるように見えます。電位差の平均DC値はゼロであるため、LCDガラスに損傷を与える可能性があるスタティックDCバイアスは残りません。
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コントローラがソフトウェアで実装された後も、大多数のアプリケーションに十分な処理能力が確保されます。
この記事に関して
製品
製品カテゴリ
{{modalTitle}}
{{modalDescription}}
{{dropdownTitle}}
- {{defaultSelectedText}} {{#each projectNames}}
- {{name}} {{/each}} {{#if newProjectText}}
- {{newProjectText}} {{/if}}
{{newProjectTitle}}
{{projectNameErrorText}}