PIC16F19155でAitendoのLCDを動かす(その1)

前回は電子ペーパーを動かして消費電流を測ったところ、思いのほか大きくて、電池駆動が不可能ということが分かった。
alasixosaka.hatenablog.com
電池で駆動するためにはLCDドライバのついたPICでセグメントタイプの液晶ディスプレイを駆動してやるしかなさそうということで、今回はAitendoから購入したセグメント液晶を動かしてみました。
幸いというか、今まで使ってきたPIC16F19155は液晶ドライバ搭載タイプなので同じチップでテストしてみます。こんなこともあろうかと初めから液晶ドライバ搭載のPICを選んでおいたのかというとそんなことはなくて、PIC16F1シリーズでRTCC付きを選ぶと必然的に液晶ドライバ搭載になってしまうというのが本当の所です。最初からLCDを使った時計用途に考えられているようです。
液晶ディスプレイの方は、ドライバなしのセグメント液晶というとあまり入手性が良くないですが、家にはいくつか転がっています。これも、昔、液晶時計にトライした時の残骸で、主にAliexpressから購入していました。ところが、購入して大分日が立ってしまったので、改めてAliexpressのサイトを見に行ってももう売ってなくてデータシートも見れない。まあ、だいたいはわかるので自分で調べれば何とかなるのですが、今回は、データシートがダウンロードできるAitendoの液晶を使うことにします。
使ったのはこちら。
www.aitendo.com
その名の通り、ラジオ用の表示器です。しかし、7セグメントの表示部分が大きい数字で4桁、小さい数字で4桁(どちらも一番大きい桁は1と2のみ表示可能)、なので、秒単位での時計表示にも使える。また、曜日を示すための7セグメント表示がもう一つあり、日時と曜日を表示させることもできる(そうすると時刻表示が4桁になるので秒が表示できなくなるが)。
データシートによると、ピンは25ピンで、1/3バイアス、4COMの構成。この、バイアスとか、COMとかが液晶をドライブするときの独特の構成になりとてもややこしいのですが、簡単に書くと、少ないピン数で多くのセグメントを表示させるための工夫で、セグメントLEDのダイナミック点灯に類似のことをやっている。
COMを順番に切り替えていき、同じピンで別のセグメントを点灯できるようにしてある。ただ、液晶の場合はLEDと違って交流で駆動する必要があるので、シグナルは非常に複雑になる。それをPICの液晶ドライバが担当してくれるので、比較的楽にデバイスを作成できる。
液晶ドライブの仕組みについては、こちらのサイトが詳しいので、詳しく知りたい人は見てみてください。
edn.itmedia.co.jp
PICには液晶ドライバが搭載されているとはいえ、設定は非常にややこしいのが難点です。自分で1からブログラムするよりは楽ですし、スリープ中も液晶ドライバは動き続けるので省電力デバイスの制作にはうってつけなんですが。
今回はテストということで、1桁だけを動かして、数字の0から9までを順番に表示させるところまでやります。
液晶ディスプレイのデータシートはこちら。

液晶ディスプレイのデータシート。4COM構成で25ピン

今回は、下の大きい方の数字の一番右の一桁のみを表示させます。従って、使用するピンは、COM1からCOM4の4ピン(1番ピンから4番ピン)と上の図の3のセグメントにつながっている2つのピン(22番ピンと23番ピン)の合計6ピンだけを使います。

PICの設定

PICの設定には苦労しました。手元には、後閑哲也さんが書かれたPICF1シリーズの本があり、液晶ドライバの使い方も書かれているのですが、使っているチップがPIC16F1936でちょっと設定が違っているせいでそのままでは動きませんでした。一番引っかかったのがチャージポンプの設定で、PIC16F19155にはチャージポンプ回路が内蔵されており、電池で駆動したりするときはこれを使うことで一定電圧で液晶が駆動できることになっています。これが、PIC16F1936にはなく、チャージポンプ関連の設定がうまくいかずなかなか液晶を動かすことができませんでした。チャージポンプは便利で最終的に電池で駆動するときは使った方が良いと思いますが、今回はチャージポンプを使わない設定で最終的には動かしました。なお、チャージポンプを動かすときは最低でも外付けのフライバック用コンデンサが必要になります。
チャージポンプを使わない場合、コンフィグレーションビットでチャージポンプをオフにしておく必要があります。そうしないと、うまく動きません。
電源周りの設定はあとで詳しく書くことにして、わかりやすい設定から順番に書いていきます。
主な設定に関するレジスタは6つ。それに、ピンを設定するレジスタが6つ、データ用のレジスタが24個もあります。
レジスタの内容を順番に書いていきます。

LCDCON
LCDCON

LCDCONのビット7はLCDENで1でLCDモジュールを有効、0で無効にします。注意しないといけないのは初めにLCDモジュールを有効にしてしまうと動いている途中にいろいろ設定を書き込むことになるのでエラーになる可能性があるということです。従って、このビットは最後に立てることになります。
ビット6はSLPENで1のときはスリープ中にLCDモジュールが止まります。スリープ中も動かしたい場合は0にします。
ビット5はWERRでこのビットは読み出しのみで、1の時はエラー、0の時は正常を意味します。
ビット4はCSでクロックソースの選択です。1の時はセカンダリオシレータ、0の時は内臓のLFINTOSOが選ばれます。どちらもスリープ中も動き続けます。
ビット0から3の4ビットは液晶の駆動モードの選択です。次のようになっていて、

LMUXの設定

COM端子をいくつにするかを決定します。液晶パネルの設定に従います。今回は、COMが4つあるので、0100を選ぶことになります。

LCDPS
LCDPS

LCDPSの第7ビットは液晶を駆動するときの波形タイプを選びます。0でTypeA、1でTypeBとなります。通常はTypeAで問題ありません。
第5ビットはLCDAで、読み出し専用のビットです。1でドライバが動作中、0でドライバが停止中となります。
第4ビットはWAでデータレジスタに書き込み可能かどうかを表します。1で書き込み可能、0で書き込み不可です。プログラムでWAを見て1の時に書き込むか、割り込み処理で書き込むタイミングに割り込みをかけて書き込みます。
第0ビットから第3ビットはクロックのプリスケラーです。セカンダリオシレータに時計用の32.768kHzの振動子を選んだ場合や、内部クロック(LFINTOSO)を選んだときはプリスケラーを使わずに1:1でほぼ1kHzのシグナルが出るようになっています。従って通常は0000で問題ありません。セカンダリオシレータにもっと早いクロックを選んだ場合などはプリスケラーを設定した方が良いかもしれません。

LCDSEx

LCDSExはセグメント表示用の端子のうちどれを有効にするか設定するものです。上にも書いたように6つのレジスタがあり、次のように割り当てられています。

LCDSEx

今回は、SEG20とSEG22を使いましたので、LCDSE2を0x50とします。

LCDDATAx

LCDの各セグメントを点灯するか消灯するかを設定するレジスタです。これも非常にややこしく、何番のCOMの何番のSEGがどのレジスタに対応しているかは、データシートのTable35-3をみて見つけます。今回はSEG20とSEG22を使っていますので、COM0だと、LCDDATA2、COM1はLCDDATA8、COM2はLCDDATA14、COM3はLCDDATA20のそれぞれのレジスタのビット4がSEG20、ビット6がSEG22に対応しています。数字を表示させるときにどういうデータを書き込むかはあとで書きます。

LCDVCON1

ここからが鬼門の電源周りの設定です。

LCDVCON1

LCDVON1の第7ビットはLPENでチャージポンプの出力を設定します。0で低電流モード、1がノーマルモードです。チャージポンプを使わないときはこのレジスタの値は関係ない(と思い)ます。
第6ビットはEN5Vでチャージポンプの電圧設定。1で5V、0で3.5Vになります。なお細かい電圧の設定は下記のBIASで設定します。
第2ビットから第0ビットがBIASでチャージポンプの電圧を細かく設定します。下記のようになっています。

LCDVCON1のBIAS設定

ここで注釈があり、LCDVSRCが100、101、110のときのみ有効となってます。LCDVSRCは後で出てきますが、電源ソースの設定です。このあたりが非常にややこしいところです。

LCDVCON2
LCDVCON2

つづいてLCDVCON2ですが、下位4ビットのみが有効で、これが先ほどのLCDVSRCの設定です。これは下記のようになっています。

LCDVSRCの設定 読んでもよくわからんところがいっぱい

ここにも注釈がいっぱいあって非常にややこしいのですが、結論から言うとチャージポンプを使わないときは、この設定を0010、つまりInternal ResistorLadder + VDDにすると動きました。この設定を見つけるのに散々苦労して、MCCを使ってもうまくいかず、最後はオシロ(といっても中華製の超安物ですが)を持ち出して波形を調べてようやくこの設定で出力が出ることを見つけました。
それ以外の設定ですが、自分の理解している範囲で書きます。
0000これはすべての電源ソースを使わない設定で出力が出ません(と思います)。MCCを使うとなぜかこの設定になっていました。のでひょっとすると出力が出るのかもしれませんが。
0001 Internal Resistor Ladder + External VLCD3。VLCD3端子から外部電源を供給するときの設定(と思います)。ちなみにResistor Ladderとはラダー抵抗で抵抗を並べてどの抵抗を使うかでコントラストを調整するためのもので、内部にあるものを使うか、自分で抵抗を選んで外付けするかを選べるようになっています。
ちなみに、データシートでは

と3段階で調整できるように書いてありますが、後で出てくるように実際は8段階の調節が可能です。
0010 上に書いたようにInternal Resistor Ladder + VDDでチャージポンプを使わずに電源をVDDとする設定です。
0011 Internal Resistro Ladder + FVR FVRはADコンバーターを使うときなどに使う内部リファレンス電源です。FVRを有効にしないといけないようで、今回はこの設定では動きませんでした。
この3つ設定は注釈(4)が記載されていて、LCDPENを0にしないとだめだと書いてあります。このLCDPENははじめ何のことかわからなかったのですが、コンフィグレーションビットのチャージポンプの設定のことです。つまりチャージポンプがオフの時に有効ですという意味。
0100 Internal Resistor Ladder + External Capacitors + External VLCD3 外部電源を使って内部のラダー抵抗と外付けのコンデンサを使う設定。
0101 Internal Resistor Ladder + External Capacitors + VDD for VLCD3 VDDと内部のラダー抵抗と外付けコンデンサを使う設定(と思います)。このfor VLCD3というのが理解できないのですが...
0110 電源をチャージポンプのみから供給する。この設定ではラダー抵抗を使わず、コントラストの調整はチャージポンプの電圧設定で行うようです。
この3つの設定が、(チャージポンプの)BIASの設定の注釈で書かれていた設定ですがチャージポンプを使う設定は0110のみでどうも腑に落ちません。
また、0100と0101の注釈はその前の3つと同じく(4)で、チャージポンプをオフにするように書かれていてこちらの記述はチャージポンプを利用するかしないかの設定に合っています。もう一つの注釈(1)の説明も難解ですが後で書きます。また、0110の方は注釈が(3)となっていて、チャージポンプをオンにするように書かれていてここの記述も合っています。もう一つの注釈(2)も後で書きます。
0111 Charge Pump + Internal Resistor Ladder チャージポンプと内部ラダー抵抗の組み合わせ。
1000 External Resistor Ladder 外部にラダー抵抗を外付けするモードのようですが、電源をどうするかの記述がありません。常識的に考えて外部電源を使用することになるのでしょう。
外部に外付け部品を付ける時の例がデータシートに出ていて、一つ目が

外付け部品の例1

となっています。
まず、CFLY1とCFLY2につながっている1μFのコンデンサはチャージポンプのフライバックコンデンサでチャージポンプを使うときはこれを付けないとだめのようです。はじめはチャージポンプを使う設定で動かそうとしていましたが、動かなかったのはこのフライバックコンデンサを付けていなかったためと思われます。
それから、VLCD1、VLCD2、VLCD3にそれぞれ0.27μFのコンデンサがつながっていますが、それぞれに注釈があって、VLCD3には注釈(1)のみです。この注釈はコンデンサの容量はあくまで例で自分で選んでねと書いてあるだけなので、チャージポンプを使うときは必要なのではないかと思います。VLCD2は注釈が(2)がさらに加わり、EN5V=0、LPEN=1のとき、すなわちチャージポンプの電圧が3.5Vで、ノーマル電流低電流モードの時は不要と書いてあります。そして、VLCD1には更に注釈(3)と(4)が加わり、(3)はEN5V=1、LPEN=1のとき、すなわちチャージポンプの電圧が5Vで、ノーマル電流低電流モードの時は不要。(4)はEN5V=0、LPEN=0のとき、すなわちチャージポンプが3.5Vで低電流ノーマル電流モードの時は不要と書いてあります。つまり、VLCD1につなげるコンデンサはチャージポンプが5Vで低電流モードの時以外は不要ということになります。整理すると
                      VLCD1  VLCD2  VLCD3
チャージポンプ3.5V 低電流ノーマル電流   不要   必要   必要
チャージポンプ3.5V ノーマル電流低電流   不要   不要   必要
チャージポンプ5V  低電流ノーマル電流   必要   必要   必要
チャージポンプ5V ノーマル電流低電流     不要   必要   必要
2022/10/23訂正 ノーマル電流と低電流が間違っていました


ということのようです。
そしてもう一つの例は、外付けのラダー抵抗を使う場合で、

外付け部品の例2

また注釈が4つあります。(1)は上と同じで抵抗の値は参考値で最終的には自分で決めてねと書いてあります。(2)はフラバックコンデンサは不要ということ。(3)は1/2バイアスの時に不要と書いてあります。VLCD1とVLCD2についています。注釈(4)はスタティックモードの時不要と書いてあるのですがどこにも注釈が降られていないので、VLCD1とVLCD2のどちらかに本来ふられているべき注釈なのではと思います。いずれにしてもラダー抵抗は内臓のものを使った方が楽なのであまり気にしないでいいのではないかと思います。
そして、最後の例は、外付けのコンデンサを使う例で、

外付け部品の例3

となっています。
外付けのコンデンサを使う場合なので、LCDVSRCが0100か0101の場合に限られます。注釈の(5)に書いてあります。注釈(2)は上と同じくフライバックコンデンサ不要ということ。注釈(3)は、1/2バイアスの時は不要ということで、VLCD1が不要とのことです。注釈(4)はスタティックモードの時不要ということで、VLCD1とVLCD2の外付け部品が不要ということのようです。図の点線で区切られているところはこの意味をあらわしているようです。すなわち、スタティックモードの時は1/3バイアスや1/2バイアスの所を消して読んで、VLCD3にだけコンデンサをつけなさいというような意味のようです。データシートでは、セグメント数が多いパネルや大きなパネルを動かすときはコンデンサを付けた方が安定すると書いてあります。
レジスタの説明を続けます。

LCDRL
LCDRL

LCDRLは細かな電源管理を設定するレジスタです。
第6ビットと第7ビットがLRLAPで、第4ビットと第5ビットがLRLBPで

LRLAP

タイミングがAのとき、Bのときのパワーを4段階で選べるようになっています。
第3ビットはLCDIRIでこれもよくわからないのですが、1の時はパワーモードがBのとき内部リファレンスのバンドギャップバッファを無効にすると書いてあります。0の時はたぶんパワーモードAの時もBの時も無効になるようです。今のところ意味不明です。
第0ビットから第2ビットは、LRLATで、

LRLAT

パワーモードがAの期間とBの期間をどういう割合にするかを決める設定です。基本的には000の全部Bでよいようです。

LCDREF

最後です。ラダー抵抗を使ってコントラストを調整するレジスタで、下位3ビットのLCDCSTのみからなります。

LCDCST

上のように8段階の調整が可能で、最大コントラストは000の時です。

液晶表示用データを作る

今回はSEG20とSEG22を使いますので、上にも書いたように、LCDDATA2、LCDDATA8、LCDDATA14、LCDDATA20にデータを書き込みます。書き込むデータの作り方ですが、液晶パネルのデータシートとにらめっこし、例えば0の時にどのセグメントを点灯させるのかを見つけます。0の時は、セグメントのG以外、A、B、C、D、E、Fを点灯させます。データシートによると、AはCOM1の22番ピン、BはCOM2の22番ピン、CはCOM3の22番ピン、DはCOM4の22番ピン、EはCOM2の23番ピン、FはCOM1の23番ピンをONにすればよいことがわかります。
ところで、ちょっとややこしいのですが、液晶側のデータシートではCOM端子は1~4となっています。一方のPIC側ではCOM端子は0から始まるので、液晶側のCOM1はPICのCOM0というように読み替える必要があります。
PIC側のSEG端子は、SEG20とSEG22を使います。SEG22を液晶パネルの22番ピンに、SEG20を23番ピンに繋ぎました。これはどちらでもよいのですが、22と22を繋いだ方がわかりやすいと思ってつないだだけです。そうして次のような対応表を作成します。

これでレジスタの書き込むデータができました。
今回は行いませんが桁数を増やすときは、それぞれの桁について同じような表を作り、レジスタに書き込むデータをORして書き込みます。

配線

配線は下記のようになります。液晶パネルのパーツがないので、25ピンのピンヘッダで代用しています。左から1番ピンで右端が25番ピンです。
PICのCOM0(25番ピン)を液晶のCOM1(1番ピン)に、同様にCOM1(26番ピン)をCOM2(2番ピン)に、COM(13番ピン)をCOM3(3番ピン)に、COM3(6番ピン)をCOM4(4番ピンに接続します。また、PICのSEG22(17番ピン)を液晶の22番ピン、SEG20(15番ピン)を液晶の23番ピンに接続します。

LCDとの配線。25ピンのピンヘッダが液晶パネルのピンを表している

プログラム

今回のプログラムの全文です。

// CONFIG1
#pragma config FEXTOSC = OFF    // External Oscillator mode selection bits->Oscillator not enabled
#pragma config RSTOSC = HFINT1    // Power-up default value for COSC bits->HFINTOSC (1MHz)
#pragma config CLKOUTEN = OFF    // Clock Out Enable bit->CLKOUT function is disabled; i/o or oscillator function on OSC2
#pragma config VBATEN = OFF    // VBAT Pin Enable bit->VBAT functionality is disabled
#pragma config LCDPEN = OFF    // LCD Charge Pump Mode bit->LCD Charge Pump is disabled.
#pragma config CSWEN = ON    // Clock Switch Enable bit->Writing to NOSC and NDIV is allowed
#pragma config FCMEN = ON    // Fail-Safe Clock Monitor Enable bit->FSCM timer enabled

// CONFIG2
#pragma config MCLRE = ON    // Master Clear Enable bit->MCLR pin is Master Clear function
#pragma config PWRTE = OFF    // Power-up Timer selection bits->PWRT disable
#pragma config LPBOREN = OFF    // Low-Power BOR enable bit->ULPBOR disabled
#pragma config BOREN = ON    // Brown-out reset enable bits->Brown-out Reset Enabled, SBOREN bit is ignored
#pragma config BORV = LO    // Brown-out Reset Voltage Selection->Brown-out Reset Voltage (VBOR) set to 1.9V on LF, and 2.45V on F Devices
#pragma config ZCD = OFF    // Zero-cross detect disable->Zero-cross detect circuit is disabled at POR.
#pragma config PPS1WAY = OFF    // Peripheral Pin Select one-way control->The PPSLOCK bit can be set and cleared repeatedly by software
#pragma config STVREN = ON    // Stack Overflow/Underflow Reset Enable bit->Stack Overflow or Underflow will cause a reset

// CONFIG3
#pragma config WDTCPS = WDTCPS_31    // WDT Period Select bits->Divider ratio 1:65536; software control of WDTPS
#pragma config WDTE = OFF    // WDT operating mode->WDT Disabled, SWDTEN is ignored
#pragma config WDTCWS = WDTCWS_7    // WDT Window Select bits->window always open (100%); software control; keyed access not required
#pragma config WDTCCS = SC    // WDT input clock selector->Software Control

// CONFIG4
#pragma config BBSIZE = 512    // Boot Block Size Selection bits->Boot Block Size (Words) 512
#pragma config BBEN = OFF    // Boot Block Enable bit->Boot Block disabled
#pragma config SAFEN = OFF    // SAF Enable bit->SAF disabled
#pragma config WRTAPP = OFF    // Application Block Write Protection bit->Application Block NOT write-protected
#pragma config WRTB = OFF    // Boot Block Write Protection bit->Boot Block NOT write-protected
#pragma config WRTC = OFF    // Configuration Register Write Protection bit->Configuration Words NOT write-protected
#pragma config WRTD = OFF    // Data EEPROM Write Protection bit->Data EEPROM NOT write-protected
#pragma config WRTSAF = OFF    // Storage Area Flash Write Protection bit->SAF NOT write-protected
#pragma config LVP = OFF    // Low Voltage Programming Enable bit->High Voltage on MCLR/Vpp must be used for programming

// CONFIG5
#pragma config CP = OFF    // UserNVM Program memory code protection bit->UserNVM code protection disabled

#include <xc.h>

#define _XTAL_FREQ 1000000

const unsigned char Num[10][4] = {
    {0x50,0x40,0x50,0x40},
    {0x00,0x40,0x40,0x00},
    {0x40,0x50,0x10,0x40},
    {0x40,0x50,0x40,0x40},
    {0x10,0x50,0x40,0x00},
    {0x50,0x10,0x40,0x40},
    {0x50,0x10,0x50,0x40},
    {0x40,0x40,0x40,0x00},
    {0x50,0x50,0x50,0x40},
    {0x50,0x50,0x40,0x40}
};


void main(void) {
    ANSELA = 0b00000000;  
    ANSELB = 0b00000000;
    ANSELC = 0b00000000;
    TRISA  = 0b00000000;  
    TRISB = 0x00;
    TRISC  = 0b00000000;  
    
    // NOSC HFINTOSC; NDIV 1; 
    OSCCON1 = 0x60;
    // CSWHOLD may proceed; SOSCPWR Low power; 
    OSCCON3 = 0x00;
    // MFOEN disabled; LFOEN disabled; ADOEN disabled; SOSCEN enabled; EXTOEN disabled; HFOEN disabled; 
    OSCEN = 0x08;
    // HFFRQ 1_MHz; 
    OSCFRQ = 0x00;
    // MFOR not ready; 
    OSCSTAT = 0x00;
    // TUN 0; 
    OSCTUNE = 0x00;
    // ACTUD enabled; ACTEN disabled; 
    ACTCON = 0x00;
    
    LCDCON = 0b00010100;  //LCD disable, SLPEN, SOSC, 1/4MPLX
    LCDPS = 0b00000000;  //TypeA,  PS = 1/1
    LCDSE2 = 0b01010000;  //SE20, SEG22 on
    LCDVCON1 = 0b10000000;  //Charge pump LP, 3.5V range, 2.8V
    LCDVCON2 = 0b00000010;  // internal resistor ladder + VDD
    LCDRL = 0b11110000;  //High Power, Allways B Power
    LCDREF = 0b00000000;  //Maxmum contrast
    LCDCONbits.LCDEN = 1;   //LCD Enable
    
    while(1){
        for (int i=0;i<10;i++){
            RA0 = 1;
            while(!LCDPSbits.WA){}
            LCDDATA2 = Num[i][0];
            LCDDATA8 = Num[i][1];
            LCDDATA14 = Num[i][2];
            LCDDATA20 = Num[i][3];
            __delay_ms(500);
            RA0 = 0;
            __delay_ms(500);
        }
    }
    return;
}

ポイントだけ説明します。まず、コンフィグレーションビットは最初の方に書いたように、チャージポンプを無効にします。
#pragma config LCDPEN = OFF
次にLCD周りの設定ですが

    LCDCON = 0b00010100;  //LCD disable, SLPEN, SOSC, 1/4MPLX
    LCDPS = 0b00000000;  //TypeA,  PS = 1/1
    LCDSE2 = 0b01010000;  //SE20, SEG22 on
    LCDVCON1 = 0b10000000;  //Charge pump LP, 3.5V range, 2.8V
    LCDVCON2 = 0b00000010;  // internal resistor ladder + VDD
    LCDRL = 0b11110000;  //High Power, Allways B Power
    LCDREF = 0b00000000;  //Maxmum contrast
    LCDCONbits.LCDEN = 1;   //LCD Enable

まず、LCDCONですが、上にも書いたようにセッティング中はLCDドライバをストップさせておくので、LCDENは0です。スリープ中は一応有効にしています。今回のプログラムはスリープを使っていませんので関係ありません。クロックソースはセカンダリオシレータを使っています。COMは0-3まで使います。
LCDPSは波形パターンがTypeA、プリスケラーは1:1です。
LCDSE2でSEG20とSEG22を有効にしています。
LCDVCON1は、チャージポンプ関係の設定で今回はたぶん関係ないですが、低電流モードにして、3.5Vモードでレンジ設定は最低の2.8Vとなります。
LCDVCON2は電源を内部ラダー抵抗+VDDに設定。
LCDRLはタイミングAの時もBの時もHighパワーにしています。また、微調整は常にBパワーになるようにしています。
LCDREFはラダー抵抗の値を最低にして最大コントラストにしています。
そして、設定が終わってから、LCDCONのLCDENを立ててLCDドライバを有効にしています。

液晶に数字を表示するためのデータは二次元配列Numに入れています。
対応するレジスタが4つあり、数字が10個あるので、10×4の配列に入れてあります。

const unsigned char Num[10][4] = {
    {0x50,0x40,0x50,0x40},
    {0x00,0x40,0x40,0x00},
    {0x40,0x50,0x10,0x40},
    {0x40,0x50,0x40,0x40},
    {0x10,0x50,0x40,0x00},
    {0x50,0x10,0x40,0x40},
    {0x50,0x10,0x50,0x40},
    {0x40,0x40,0x40,0x00},
    {0x50,0x50,0x50,0x40},
    {0x50,0x50,0x40,0x40}
};

無限ループは、forループで変数iを0から9まで変化させ、Num配列から対応する値を取り出して、それぞれのLCDDATAレジスタに書き込んでいます。
これで、パネルに0から9までの数字が1秒間隔で表示されます。プログラムの動作確認用にLチカも入れてあります。

while(1){
        for (int i=0;i<10;i++){
            RA0 = 1;
            while(!LCDPSbits.WA){}
            LCDDATA2 = Num[i][0];
            LCDDATA8 = Num[i][1];
            LCDDATA14 = Num[i][2];
            LCDDATA20 = Num[i][3];
            __delay_ms(500);
            RA0 = 0;
            __delay_ms(500);
        }
    }
    return;


www.youtube.com