某ゲーム機用ルーターについていたグラフィック液晶をHACKしてみました。
液晶モジュールは台湾のPowertipという会社のFP-PE12832-002という製品で、128x32ドットでコントローラはST7565Rのようです。ST7565RはSPIのインターフェースも使えるようですが、このモジュールでは8Bitパラレルで使われています。
2010年前後の新しいものなので、古いものとは違ってVRAMが入っています。
製品ではHT82K68Eというマイコンで制御されてました。このマイコンはOneTimePrograming ROMな製品で、専用の処理が実装されているようなのでそのままでは使えなさそうでした。
いろいろ考えてArduino pro miniで制御することにしました。
手持ちのArduino pro miniは5V/16MHzの製品で、基板は3.3Vだったのでセラロックを外して基板についていた8MHzの水晶を使うようにしてみました。
Arudinoで外部クロックを変えた場合にはブートの焼き直しが必要で、ATTinyISPで焼き直しました。
Arduino pro mini | ISP |
---|---|
11 | MOSI |
12 | MISO |
13 | SCK |
VCC | Power |
RST | Reset |
GND | Grund |
接続は以下のようにしました。
HT82K68E | LCD-Pin | LCD-Sym | Arduino |
---|---|---|---|
15 | 28 | /CS1 | PD2 |
16 | 25 | /WR(R/W) | PD3 |
17 | 24 | /RD(E) | PB4 |
18 | 26 | A0 | PB5 |
23 | 27 | /RES | PC0 |
27 | 16 | D7 | PB3 |
28 | 17 | D6 | PB2 |
1 | 18 | D5 | PB1 |
2 | 19 | D4 | PB0 |
7 | 20 | D3 | PD7 |
8 | 21 | D2 | PD6 |
9 | 22 | D1 | PD5 |
10 | 23 | D0 | PD4 |
処理しやすいように接続するのがミソです。開発はPlatfomioで行います。データの書き込みコードは以下のようにしました。
#define CS1_P PORTD
#define CS1 (1 << 2)
#define RW_P PORTD
#define RW (1 << 3)
#define E_P PORTB
#define E (1 << 4)
#define A0_P PORTB
#define A0 (1 << 5)
#define RST_P PORTC
#define RST (1 << 0)
void writelcd(int dat, int a0)
{
CS1_P &= ~CS1;
RW_P &= ~RW;
if (a0 == 0)
A0_P &= ~A0;
else
A0_P |= A0;
PORTD = (PORTD & 0b00001111) | ((dat & 0b00001111) << 4);
PORTB = (PORTB & 0b11110000) | (dat >> 4);
E_P |= E;
E_P &= ~E;
CS1_P |= CS1;
}
a0が0の場合は制御用のデータで1の場合はビットデータになります。
Display ONにすると真っ黒くなってしまいまが、ちゃんとコマンドは効いているようです。
しなぷすさんのページを参考にして試行錯誤して、初期化は以下のようにしました。
DDRD = 0b11111100;
DDRB = 0b00111111;
DDRC = 0b00000001;
PORTD = 0;
PORTB = 0;
PORTC = 0;
RST_P |= RST;
delay(150);
RST_P &= ~RST;
delay(150);
RST_P |= RST;
delay(150);
// LCD bias set - 1/9 bias
writelcd(0xa2, 0);
// ADC select - normal
writelcd(0xa0, 0);
// Common output mode select - reverse
writelcd(0xc8, 0);
writelcd(0x2f, 0);
writelcd(0x21, 0);
writelcd(0x81, 0);
writelcd(0xc, 0);
// Display ON
writelcd(0xaf, 0);
VRAMは128x32で8ビット毎に4ページの構成になっています。
左上の指定はこのようにします。
writelcd(0x10, 0);
writelcd(0x0, 0);
writelcd(0xb0, 0);
最初の二つがコラムの設定で、最後の一つがページの設定になります。
これでデータを書くと左上の8ドットが埋まります。次に書くと右の8ドットが埋まります。
writelcd(0xff, 1);
writelcd(0xff, 1);
ページを変えて4つ書くとこうなります。
openGLCDのソースツリーにあったfontを使って表示してみました。8ポイントであれば1ページのデータで書けるのですが、見にくいので15ポイントのデータを使っています。10x15のフォントで10バイトずつ上下2ページに書いています。
上下逆になっているので、このままケースに入れると逆さまの表示になります。
fontデータを普通に配列にするとメモリを圧迫するので、flashに置くのが良いようです。検索すると古い書き方が引っかかって、コンパイルできなくてちょっとはまりました。
const unsigned char font[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // *space*
拾い出す時はこんな感じです。
writelcd(pgm_read_byte_near(font + 9 - i + off), 1);
反転は以下の方法でやりました。
- Common output modeをnormal(0xc0)
- fontデータのメモリへのコピーを逆にした
- 一行の書く順番を逆にした
この基板で残念なのはバックライトの制御ができないことです。液晶自体はOFFにするとパワーセーブできるようなのですが、一番大きいバックライトがつきっぱなしでは意味がありません。
書き込み時はこうやってます。