以前、買うだけ買ってほったらかしにしていたNokia6100液晶を使ってみました。きっかけはトラ技11月号にこの液晶を使った製作記事が載っていたからです(この記事について以前書いたもの)。トラ技11月号のソースプログラムはダウンロードできるのですが、PIC用なのでAVRではそのまま使えません。PICのソースを見ながら初期化の手順や描画手順のエッセンスを抜き出してAVRに移植しはじめました。
ということで一応動くものができたのですが、ちょっと疑問がわいてきたので別のソースも参考にして比較検討をはじめました(参考にしたNokia LCD Driver for 4096 Colors by Loren Blaney)。
SparkFunのColor LCDモジュールです。
キャリーボードは液晶が必要とする電圧を全て作ってくれます。必要なのは3.3Vだけです。
SparkFunのColor LCDモジュールのコントローラはPhilipsのPCF8833とEPSONのS1D15G10があります。うちのはEPSONのでした。EPSONにはS1D15G00という似た名前(最後が00か10かの違い)のコントローラがあって、たとえばストロベリーリナックスでダウンロードできるデータシートは10ではなくて00の方のデータシートです。00と10は微妙にコマンドが違っています。一番大きな違いは10の方では4096色モードが2種類あることです。
4096色モードの一つ目(00互換)は、2ドットを3バイトで表します。4096色は12ビットで表現できます。2つで24ビットなので3バイトです。
1バイト目 RRRRGGGG (左のドットのRとG)
2バイト目 BBBBRRRR (左のドットのBと右のドットのR)
3バイト目 GGGGBBBB (右のドットのGとB)
のように2つのドットの情報をまぜて3バイトを作ります。必ず2つのドットを変更しないといけないようです(要確認)。
4096色モードの二つ目(10専用?)は、1ドットを2バイトで表します。
1バイト目 0000RRRR (R)
2バイト目 GGGGBBBB (GとB)
のように表します。
どちらのモードがいいかというと互いに得意と不得意があります。その1の方はデータがコンパクトに表現できるので、ROMサイズが少なかったり、通信量が少なかったりします。そのかわり細かいドットの操作が面倒です。その2の方は逆でサイズは大きくなりますが、2つのドットを組み合わせたりとか面倒なことがいりません。
モードの切り替えはDATCTLコマンドの3つ目のパラメータで選択します。
- 1のとき、256色モード
- 2のとき、4096色モードその1
- 4のとき、4096色モードその2
です。
トラ技のソースでは4096色モードその1を使っています。Blaneyさんのソースはその2を使っています。また、トラ技のソースはy座標が連続しない設定になっているのと、windowを使った高速転送を使っていません。
このコントローラには矩形転送のためのおもしろいコマンドがあります。通常四角い絵を描くときは、左上のドットをうって、その右隣のドットをうって、順にうっていって右上までいったら、y座標を一つ増やしてx座標を左に戻してといった折り返しの処理が必要になります。このコントローラはあらかじめ周りを囲む四角の座標を設定しておけば、折り返しの処理を自動で行ってくれるので、CPU側はデータを必要な分だけ送れば四角い絵を描けることになります。8x8ドットのフォントを描きたいときは、文字を囲む四角を設定してから、フォントのデータを左上から順に送るだけで画面上は勝手に折り返してくれます。
動かした結果です。
黒、赤、緑、青の4色のHello, worldと、中央の4096色を64x64の四角にしたものを描いています。横方向に4つの繰り返しが見えます。これはBが0~F、0~F、0~F、0~Fと4回動いているからです(その間にGは4つ増える)。
8x8のフォントを16x16(= 256)で表示しています。
ソースです。
このコントローラは9ビットのSPIを使います。AVRのSPIハードは8ビットSPIしかできないので、ソフトで実現しています。1ビット目がコマンド(0)またはデータ(1)の区別を表していて、残りの8ビットがコマンドまたはデータになります。データシートを見ると1サイクルが50nsなので20MHzまで平気です。ソフトだともっと遅くなります。
初期化は結構めんどうです。たくさんコマンドを送らないといけません。結局、ほとんどBlaneyさんのコードと同じようなものになりました。BlaneyさんのコードはSRAMの初期化の部分に間違いがあるので修正してあります。
もうちょっと書きたいことがあるのですが、長くなってきたので続きます。
ということで一応動くものができたのですが、ちょっと疑問がわいてきたので別のソースも参考にして比較検討をはじめました(参考にしたNokia LCD Driver for 4096 Colors by Loren Blaney)。
SparkFunのColor LCDモジュールです。
キャリーボードは液晶が必要とする電圧を全て作ってくれます。必要なのは3.3Vだけです。
SparkFunのColor LCDモジュールのコントローラはPhilipsのPCF8833とEPSONのS1D15G10があります。うちのはEPSONのでした。EPSONにはS1D15G00という似た名前(最後が00か10かの違い)のコントローラがあって、たとえばストロベリーリナックスでダウンロードできるデータシートは10ではなくて00の方のデータシートです。00と10は微妙にコマンドが違っています。一番大きな違いは10の方では4096色モードが2種類あることです。
4096色モードの一つ目(00互換)は、2ドットを3バイトで表します。4096色は12ビットで表現できます。2つで24ビットなので3バイトです。
1バイト目 RRRRGGGG (左のドットのRとG)
2バイト目 BBBBRRRR (左のドットのBと右のドットのR)
3バイト目 GGGGBBBB (右のドットのGとB)
のように2つのドットの情報をまぜて3バイトを作ります。必ず2つのドットを変更しないといけないようです(要確認)。
4096色モードの二つ目(10専用?)は、1ドットを2バイトで表します。
1バイト目 0000RRRR (R)
2バイト目 GGGGBBBB (GとB)
のように表します。
どちらのモードがいいかというと互いに得意と不得意があります。その1の方はデータがコンパクトに表現できるので、ROMサイズが少なかったり、通信量が少なかったりします。そのかわり細かいドットの操作が面倒です。その2の方は逆でサイズは大きくなりますが、2つのドットを組み合わせたりとか面倒なことがいりません。
モードの切り替えはDATCTLコマンドの3つ目のパラメータで選択します。
- 1のとき、256色モード
- 2のとき、4096色モードその1
- 4のとき、4096色モードその2
です。
トラ技のソースでは4096色モードその1を使っています。Blaneyさんのソースはその2を使っています。また、トラ技のソースはy座標が連続しない設定になっているのと、windowを使った高速転送を使っていません。
このコントローラには矩形転送のためのおもしろいコマンドがあります。通常四角い絵を描くときは、左上のドットをうって、その右隣のドットをうって、順にうっていって右上までいったら、y座標を一つ増やしてx座標を左に戻してといった折り返しの処理が必要になります。このコントローラはあらかじめ周りを囲む四角の座標を設定しておけば、折り返しの処理を自動で行ってくれるので、CPU側はデータを必要な分だけ送れば四角い絵を描けることになります。8x8ドットのフォントを描きたいときは、文字を囲む四角を設定してから、フォントのデータを左上から順に送るだけで画面上は勝手に折り返してくれます。
動かした結果です。
黒、赤、緑、青の4色のHello, worldと、中央の4096色を64x64の四角にしたものを描いています。横方向に4つの繰り返しが見えます。これはBが0~F、0~F、0~F、0~Fと4回動いているからです(その間にGは4つ増える)。
8x8のフォントを16x16(= 256)で表示しています。
ソースです。
#include <avr/io.h> #include <avr/pgmspace.h> #include <util/delay.h> // ピン接続 #define MOSI PD7 // 動かせない #define SCK PB2 #define RST PB1 // pull-down #define CS PB0 // pull-up // S1D15G10コマンド #define DISON 0xaf // 1. Display on P0 #define DISOFF 0xae // 2. Display off P0 #define DISINV 0xa7 // 4. Inverse display P0 #define COMSCN 0xbb // 5. Common scan direction P1 #define DISCTL 0xca // 6. Display control P4 (データシートp.30のP3は間違い) #define SLPOUT 0x94 // 8. Sleep out P0 #define PASET 0x75 // 9. Page Address set P2 #define CASET 0x15 // 10.Column Address set P2 #define DATCTL 0xbc // 11.Data scan direction, etc. P3 #define RAMWR 0x5c // 13.Writing to memory P:data #define OSCON 0xd1 // 21.Internal oscillation on P0 #define PWRCTR 0x20 // 23.Power control P1 #define VOLCTR 0x81 // 24.Electronic colume control P2 // マクロ #define sck() do { PORTB |= _BV(SCK); PORTB &= ~_BV(SCK); } while(0) #define cs1() PORTB |= _BV(CS) #define cs0() PORTB &= ~_BV(CS) // 外部変数 extern const uint8_t font[256][8] PROGMEM; // データ形式は0000rrrrggggbbbbの16bit uint16_t fgcolor = 0x000; uint16_t bgcolor = 0xfff; // S1D15G10にコマンドを送る void lcd_cmd(uint8_t c) { register uint8_t t = c; cs0(); PORTD = 0; sck(); // 0 PORTD = t; sck(); t <<= 1; // bit7 PORTD = t; sck(); t <<= 1; // bit6 PORTD = t; sck(); t <<= 1; // bit5 PORTD = t; sck(); t <<= 1; // bit4 PORTD = t; sck(); t <<= 1; // bit3 PORTD = t; sck(); t <<= 1; // bit2 PORTD = t; sck(); t <<= 1; // bit1 PORTD = t; sck(); // bit0 cs1(); } // S1D15G10にデータを送る void lcd_data(uint8_t c) { register uint8_t t = c; cs0(); PORTD = _BV(MOSI); sck(); // 1 PORTD = t; sck(); t <<= 1; // bit7 PORTD = t; sck(); t <<= 1; // bit6 PORTD = t; sck(); t <<= 1; // bit5 PORTD = t; sck(); t <<= 1; // bit4 PORTD = t; sck(); t <<= 1; // bit3 PORTD = t; sck(); t <<= 1; // bit2 PORTD = t; sck(); t <<= 1; // bit1 PORTD = t; sck(); // bit0 cs1(); } // S1D15G10のwindow設定 void lcd_window(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1) { lcd_cmd(CASET); lcd_data(x0); lcd_data(x1); lcd_cmd(PASET); lcd_data(y0 + 2); lcd_data(y1 + 2); } // S1D15G10初期化 void lcd_init(void) { uint16_t i; // ポート設定とリセット PORTB = _BV(CS); // CS=1, RST=0 DDRB = _BV(SCK) | _BV(RST) | _BV(CS); DDRD = _BV(MOSI); _delay_ms(10); // トラ技は200ms PORTB |= _BV(RST); _delay_ms(10); // トラ技は100ms // S1D15G10の初期化 lcd_cmd(DISCTL); // display control lcd_data(0); // - (トラ技は3) lcd_data(32); // - 132line / 4 - 1 = 32 (31にすると126ライン) lcd_data(11); // - (トラ技は12) lcd_data(0); // - P4はS1D15G00にはない。0:dispersion 1:non-dispersion lcd_cmd(COMSCN); // コモンのスキャン方向 lcd_data(1); // - 1:1→68, 132←69 (トラ技は0) lcd_cmd(OSCON); // 内部発振器ON lcd_cmd(SLPOUT); // sleep out lcd_cmd(PWRCTR); // power control lcd_data(15); // - 15固定 lcd_cmd(DISINV); // inverse display (0:暗 - f:明) lcd_cmd(DATCTL); // data control lcd_data(0); // - 描画方向 lcd_data(0); // - lcd_data(4); // - トラ技は2 (S1D16G00のデータシートにはないモード) lcd_cmd(VOLCTR); // 電子ボリューム制御 lcd_data(33); // - トラ技は31 ← コントラスト lcd_data(3); // - 3固定 lcd_cmd(CASET); // SRAMクリア lcd_data(0); lcd_data(131); lcd_cmd(PASET); lcd_data(0); lcd_data(131); lcd_cmd(RAMWR); // メモリ書き込み (RAM初期化) for(i = 0; i < 132 * 132; i++){ lcd_data(bgcolor >> 8); lcd_data(bgcolor); } _delay_ms(100); lcd_cmd(DISON); // ディスプレイON } // 8x8文字出力 void lcd_putc(uint8_t x, uint8_t y, uint8_t c) { uint8_t i, j, w; uint16_t dot; // 最初にwindowをセットしてやれば後はデータを送るだけ lcd_window(x * 8, y * 8, x * 8 + 7, y * 8 + 7); lcd_cmd(RAMWR); for(i = 0; i < 8; i++){ w = pgm_read_byte(&font[c][i]); for(j = 0; j < 8; j++){ dot = (w & 0x80) ? fgcolor : bgcolor; lcd_data(dot >> 8); lcd_data(dot); w <<= 1; } } } // 文字列出力(ROM文字列) void lcd_puts_p(uint8_t x, uint8_t y, char *s) { uint8_t c; while((c = pgm_read_byte(s++)) != 0) lcd_putc(x++, y, c); } void lcd_pset(uint8_t x, uint8_t y, uint16_t c) { lcd_window(x, y, x, y); lcd_cmd(RAMWR); lcd_data(c >> 8); lcd_data(c); } int main() { uint16_t i; lcd_init(); // 文字列出力 lcd_puts_p(0, 0, PSTR("Hello, world")); fgcolor = 0xf00; lcd_puts_p(0, 1, PSTR("Hello, world")); fgcolor = 0x0f0; lcd_puts_p(0, 2, PSTR("Hello, world")); fgcolor = 0x00f; lcd_puts_p(0, 3, PSTR("Hello, world")); // 64x64の色変化四角を描く lcd_window(32, 32, 95, 95); lcd_cmd(RAMWR); for(i = 0; i < 4096; i++){ lcd_data(i >> 8); lcd_data(i); } while(1) ; // 無限ループ }
このコントローラは9ビットのSPIを使います。AVRのSPIハードは8ビットSPIしかできないので、ソフトで実現しています。1ビット目がコマンド(0)またはデータ(1)の区別を表していて、残りの8ビットがコマンドまたはデータになります。データシートを見ると1サイクルが50nsなので20MHzまで平気です。ソフトだともっと遅くなります。
初期化は結構めんどうです。たくさんコマンドを送らないといけません。結局、ほとんどBlaneyさんのコードと同じようなものになりました。BlaneyさんのコードはSRAMの初期化の部分に間違いがあるので修正してあります。
もうちょっと書きたいことがあるのですが、長くなってきたので続きます。
ありがとうございます。おかげさまでなんとか動かせました。AVR用ですが作ったプログラムをメールで送らせていただきました。
送っていただいたソースをPIC24Fに移植して動きました。赤枠の外にも白い部分が数ドット右と下にあるので、128より多いみたいです。
もうできちゃったんですね。移植おつかれさまでした。掲示板の方も拝見させていただきました。
やはりこの液晶って130x130表示なんですね。SRAMが132x132あって、そのうちの一部が表示されているみたいです。そのあたりの話を次に書こうと思っていたので、ちょうど検証していただいて都合がよかったです。
Nokia液晶のwindowはすばらしいですね。
試行錯誤したのはBitmapのアプリ側です。VCのBitmap関係をすっかり忘れてしまって、なかなかうまくいきませんでした。もしVCをお持ちでしたらアプリ側をメールで送ります。画素2バイト中、上位4ビットを0にしたのですが、考えてみれば0をなくせばもっとデータを圧縮できます。ファームが面倒ですが。
掲示板の写真拝見しました。かわいい猫ちゃんですね。
かなり昔のVisual Studio 6というのを持っています。最近のはないです。よろしければ送ってください。
http://www.aitendo.co.jp/product/1239
コントローラは同じなのですが見た目が違います。
一応、こちらの掲載のタイプのもののコードで動かしているのですが、画面の上部に3,4本の線が入るし、四角を表示しても薄い表示でほとんど見えません。
もちろん、バックライトは点灯しています。
LCDの故障も考えられるのでもう一つ注文しました。
故障でなかったら何なのだろう・・・
あと、以下の中国人?はハードSPIを使って上手く9bit転送しています。
こんな方法もあるんですね。
http://www.ouravr.com/bbs/bbs_content.jsp?bbs_sn=838628&bbs_page_no=1&search_mode=1&search_text=3300&bbs_id=1000
600円とは安くていいですね。VOLCTLのパラメータを調節しても薄いですか?あとはDISCTLの第2パラメータを32でなく31にする必要があるかもしれません。32だと132ライン表示で31が128ライン表示です。私が使った方は実は130ラインなので32の設定にしています。何か分かったら教えてください。
ハードSPIはおもしろいですね。中国語は読めませんがソースが載っていたので眺めてみました。最初の1ビットだけソフトSPIで通信して、残りの8ビットをハードSPIで通信しているみたいでした。私も試してみたくなりました。うまくいったら報告させていただきます。
VOLCTLやDISCTLのパラメータをいじっても駄目でした。
もう一つ注文したLCDの到着を待ちます。
最初に配線するときにこちらで紹介しているタイプと同じだろうと思い、そのピンアサインで配線してしまったのでもしかしたら壊れているかもです。
電源回路の配線ミスで電源電圧が5Vになってました。(T_T)
画面の上部の3,4本の線はきっと過電圧による損傷でしょう。(T_T)
新しいLCDがくれば解決です。
ひとまずこちらのサンプルコードで表示させたら左右が反転しました。
たぷんこれは設定で直るでしょう。
まぁ、新しいLCDがくる前にミスが分かって良かったです。
お騒がせしてすみませんでした。m(__)m