激安液晶駆動ログ

aitendoで激安液晶モジュールなんてものがあったから買ってみた
仕様的に動かしやすそうだからとりあえず遊んでみたログ

しよう?

この激安液晶モジュールはバックライトの有無とかで幾つかの種類がある
今回買ったのはTS119-5という型番だった

コントローラとバックライト付きの数字7+記号2桁の8セグ液晶モジュールらしい
コントローラはHT1621B,通信方式はSPI?,バックライトは3V駆動?のLED
ロジックも3Vで駆動するらしい

なんとか3VのTrinketでも動かせそう

まずはググったりデータシート(PDF注意)見たりしてみよう

はいせん

ピンアサインはLEDを表にして左から順に

CS RD WD DATA GND VCC ? LED- LED+
- - - - - ~5V ? - ~5V

となっているらしい

CS

Clock-Signalかと思ってたらChip-Selectの略だったっていう
複数のデバイスを繋いだ時のチップ指定端子らしい
データシートを見たところHIGHでOFF,LOWでONになるとか
普通と逆なんねと思ったらIC内部でプルダウンしてあるから
これだけ繋ぐなら結線しなくていいようになっているとか?
とりあえず抜いて動かす
どうやらコマンドラインリセットにも使っているらしいから結線は必要
そして通信しない時はHIGHに固定しなきゃ駄目っぽい
Trinketの4に挿した

RD

Read-Data
通信バスかと思ったらこっちがクロック制御だった
データを読む時のクロックを入れるらしい
データシートによると立ち下がりでCLKっぽい(DS:P13参照)
これも最初はとりあえず抜いておく

WD

Write-Data
通信バスかと思ったら(ry
書き込みに使う以外はRDと同じなので説明省略
Trinketの6に挿した

DATA

これが本当のバスライン
このピンにI/Oしてデータをやりとりするらしい
Trinketの8に挿した

GND

グランドライン
そのままTrinketのGNDラインに繋いだ

VCC

ロジック電源
LEDと並列に繋いだから不安だけど消費電力少ないから多分平気

?

不明 調べてもどういうピンなのかわからなかった. そもそも結線されてない?
データシートに書いてある中でそれっぽいのはオシレータインプット(OSCI)だった
ググってみたらIRQっぽい?
これもとりあえずどこにも繋がないでおく

LED-

バックライトLEDのカソード
GNDラインに繋いだ

LED+

バックライトLEDのアノード
LEDも詳細がわからない とりあえず3V3OUTを繋げたら眩しいくらいに光った
一応怖いから47Ωの抵抗付けたけど暗いし抵抗要らないかもしれない(基板に実装されてるし)

つーしん

通信方式は販売ページにSPIって書いてあった
けど実際には独自形式のシリアル通信っぽい aitendoェ・・・
CSをLowにしてRD/WDでクロック送ってDATAで実データ(1bit)やりとりすればいいらしい
ただしクロックはWrite3.34μs/Read6.67μs(5V駆動時は半分?)以上のタイミングじゃないと駄目らしい(DS:P8参照)
とりあえず10μくらいで様子見る

たいみんぐ

f:id:lagryz:20170717040348p:plain 具体的な通信方法とタイミングはデータシートに書いてある(DS:P13~16参照)
通信にはシーケンシャルアクセスとランダムアクセスがあるっぽい(DS:P13~P15参照)
どっちもCSをLOWにした半クロック後にWD/RDのクロックを開始してからID(3bit)を流して
その後にIDに対応した信号を流していく
通信が終わったらCSをHIGHにしておく
書き込むときはまた最初から繰り返し
ビット長はコマンドも読み書きも12bit
ビット長はコマンドが12bit(3+8+1)で,W/Rが13bit(3+6+4)だった
基本こんな感じらしい

こまんど

このチップは電源を入れただけじゃ起動しないらしい
ちゃんとコマンド入れて初期化しないと駄目だとか
コマンド一覧もデータシートにちゃんとある(DS:P18~P19参照)
初期化はLCD ONしてからBIAS 1/2してやればいい?BIAS 1/3でした
とりあえずコード書いて動かしてみる:

/* HT1621B Controlling Test */
//ピン定義
#define _PIN_CS 4
#define _PIN_RD 0
#define _PIN_WD 6
#define _PIN_DA 8
//クロックディレイ(MicroSec,1/2クロック)
#define _CLK_DELAY 6
//シグナルID
#define _CMD_SIGID (unsigned char)0b00000100//100 COMMAND
#define _DRD_SIGID (unsigned char)0b00000110//110 READ
#define _DWD_SIGID (unsigned char)0b00000101//101 WRITE
//コマンドコード
//最後に1bitのダミーデータを噛ませること
#define _CMD_SYSEN (unsigned char)0b00000001//内蔵オシレータ起動
#define _CMD_LCDON (unsigned char)0b00000011//LCDバイアスジェネレータ起動
#define _CMD_TOPTM (unsigned char)0b11100000//Test Mode?
//1/2 BIASモード
#define _CMD_BIAS2_2C (unsigned char)0b00100000//2 commons
#define _CMD_BIAS2_3C (unsigned char)0b00100100//3 commons
#define _CMD_BIAS2_4C (unsigned char)0b00101000//4 commons

void pinSetup(){
    //CS初期化
    pinMode(_PIN_CS, OUTPUT);
    digitalWrite(_PIN_CS, HIGH);
    //RD初期化
    //pinMode(_PIN_RD, OUTPUT);
    //digitalWrite(_PIN_RD, HIGH)
    //WD初期化
    pinMode(_PIN_WD, OUTPUT);
    digitalWrite(_PIN_WD, HIGH);
    //DATA初期化
    pinMode(_PIN_DA, OUTPUT);
    digitalWrite(_PIN_DA, HIGH);
}
void writeSerialLine(unsigned char len, unsigned char data){
    unsigned char _D;
    for(unsigned char i=len; i!=0; i--){//ID送信
        _D = (unsigned char)( (data&(1<<(i-1))) >> (i-1) );
        digitalWrite(_PIN_DA, (_D ? HIGH : LOW));//データセット
        digitalWrite(_PIN_WD, LOW);//WD立ち下げ
        delayMicroseconds(_CLK_DELAY);//半クロック待つ
        digitalWrite(_PIN_WD,HIGH);//WD立ち上げ
        delayMicroseconds(_CLK_DELAY);//半クロック待つ
    }
}
void transmit(unsigned char id,unsigned char cmd,unsigned char data){
    //データ送信開始
    delayMicroseconds(_CLK_DELAY);//連続して読んだ時のために最初にウェイトを入れておく
    digitalWrite(_PIN_CS, LOW);//CSを下げてデータ転送を開始
    delayMicroseconds(_CLK_DELAY);//半クロック待つ
    writeSerialLine(3, id);//ID送信
    switch(id){//オペランド処理
        case _CMD_SIGID://コマンドの場合
            writeSerialLine(8, cmd);//コマンド
            writeSerialLine(1, 0x00);//ダミーデータ
        case _DRD_SIGID://読み込みの場合
            //writeSerialLine(5, cmd);
            //readSerialLine(4, data);
        case _DWD_SIGID:
            writeSerialLine(5, cmd  & 0b00011111);//アドレス
            writeSerialLine(4, data & 0b00001111);//データ
    }
    digitalWrite(_PIN_CS, HIGH);
}
void initCmd(){
    transmit(_CMD_SIGID, _CMD_SYSEN, 0x00);
    transmit(_CMD_SIGID, _CMD_LCDON, 0x00);
    //transmit(_CMD_SIGID, _CMD_TOPTM, 0x00);
    transmit(_CMD_SIGID, _CMD_BIAS2_2C, 0x00);
}
void testPrint(){
    for(unsigned char c=0;32>c;c++){
        transmit(_DWD_SIGID, c, 0b00001111);
    }
}

void setup() {
    pinSetup();
    initCmd();
    testPrint();
}

void loop() {
  // put your main code here, to run repeatedly:

}

m9(^Д^)

しかし動かない… ナンデ?
_Dをシリアルポートに吐き出してみたら数ビット余計に何か出力されてる…?
よく見たらswitchの分岐にbreak入ってないからフォールスルー起こしてらハハハ
入れてみたら正しい値が出力された

しかしそれでも動かない
そもそもロジックが駆動してない…?
シリアルに必死に値吐き続けても全然原因が突き止められない

タイミングは当っているはず…CSも動かしてるしWDもOK…クロックレートも遅いくらい…
コマンドの順番? それともバイアスの設定が悪い?

色々悩んであれやこれややってたらなんか薄らと画面が出てきたような…?

やったか?

もっとデバッグしてもっとミス見つけてた(6bit送信が5bit送信になってたりetc)なんとか駆動できた!!
ヤッター!!嬉しい!!

f:id:lagryz:20170717070234p:plain

最終的にコードはこうなった:

/* HT1621B Controling Test */
//ピン定義
#define _PIN_CS 4
#define _PIN_RD 0
#define _PIN_WD 6
#define _PIN_DA 8
//クロックディレイ(MicroSec,1/2クロック)
#define _CLK_DELAY 20
//シグナルID
#define _CMD_SIGID (unsigned char)0b00000100//100 COMMAND
#define _DRD_SIGID (unsigned char)0b00000110//110 READ
#define _DWD_SIGID (unsigned char)0b00000101//101 WRITE
//コマンドコード
//最後に1bitのダミーデータを噛ませること
#define _CMD_SYSDS (unsigned char)0b00000000//内蔵オシレータ&バイアスジェネレータ無効化
#define _CMD_SYSEN (unsigned char)0b00000001//内蔵オシレータ%バイアスジェネレータ起動
#define _CMD_LCDON (unsigned char)0b00000011//LCDバイアスジェネレータ起動
#define _CMD_RCOCL (unsigned char)0b00011000//内部オシレータ選択
//1/2 BIASモード
#define _CMD_BIAS2_2C (unsigned char)0b00100000//2 commons
#define _CMD_BIAS2_3C (unsigned char)0b00100100//3 commons
#define _CMD_BIAS2_4C (unsigned char)0b00101000//4 commons
//1/3 BIASモード
#define _CMD_BIAS3_2C (unsigned char)0b00100001//2 commons
#define _CMD_BIAS3_3C (unsigned char)0b00100101//3 commons
#define _CMD_BIAS3_4C (unsigned char)0b00101001//4 commons

void pinSetup(){
    //CS初期化
    pinMode(_PIN_CS, OUTPUT);
    digitalWrite(_PIN_CS, HIGH);
    //RD初期化
    //pinMode(_PIN_RD, OUTPUT);
    //digitalWrite(_PIN_RD, HIGH)
    //WD初期化
    pinMode(_PIN_WD, OUTPUT);
    digitalWrite(_PIN_WD, HIGH);
    //DATA初期化
    pinMode(_PIN_DA, OUTPUT);
    digitalWrite(_PIN_DA, HIGH);
}
void writeSerialLine(unsigned char len, unsigned char data){
    unsigned char _D;
    for(unsigned char i=len; i!=0; i--){//ID送信
        _D = (unsigned char)( (data&(1<<(i-1))) >> (i-1) );
        //_D = (unsigned char)( (data>>(len-i)) & 0x01 );
        digitalWrite(_PIN_DA, (_D ? HIGH : LOW));//データセット
        //Serial.print("DATA:");
        //Serial.flush();
        //Serial.print((_D ? HIGH : LOW));
        //Serial.flush();
        digitalWrite(_PIN_WD, LOW);//WD立ち下げ
        //Serial.print("WD:LOW");
        //Serial.flush();
        delayMicroseconds(_CLK_DELAY);//半クロック待つ
        //Serial.println("3us Wait");
        //Serial.flush();
        digitalWrite(_PIN_WD, HIGH);//WD立ち上げ
        //Serial.print("WD:HIGH");
        //Serial.flush();
        delayMicroseconds(_CLK_DELAY);//半クロック待つ
        //Serial.println("3us Wait");
        //Serial.flush();
    }
}
void transmit(unsigned char id,unsigned char cmd,unsigned char data){
    //データ送信開始
    //Serial.println("3us Wait");
    //Serial.flush();
    delayMicroseconds(_CLK_DELAY);//連続して読んだ時のために最初にウェイトを入れておく
    digitalWrite(_PIN_CS, LOW);//CSを下げてデータ転送を開始
    //Serial.print("CS:LOW");
    //Serial.flush();
    delayMicroseconds(_CLK_DELAY);//半クロック待つ
    //Serial.println("3us Wait");
    //Serial.flush();
    writeSerialLine(3, id);//ID送信
    switch(id){//オペランド処理
        case _CMD_SIGID://コマンドの場合
            writeSerialLine(8, cmd);//コマンド
            writeSerialLine(1, 0x00);//ダミーデータ
            break;
        case _DRD_SIGID://読み込みの場合
            //writeSerialLine(5, cmd);
            //readSerialLine(4, data);
            break;
        case _DWD_SIGID:
            writeSerialLine(6, cmd & 0b00111111);//アドレス
            writeSerialLine(4, data & 0b00001111);//データ
            break;
    }
    digitalWrite(_PIN_CS, HIGH);
}
void initCmd(){
    transmit(_CMD_SIGID, _CMD_SYSDS, 0x00);//待機電力でRAMの値を保持するっぽいから一回終了させてやる
    transmit(_CMD_SIGID, _CMD_SYSEN, 0x00);//オシレータ&バイアスジェネレータ起動
    transmit(_CMD_SIGID, _CMD_RCOCL, 0x00);//クロックジェネレータに内部オシレータを指定
    transmit(_CMD_SIGID, _CMD_BIAS3_4C, 0x00);//バイアスモードセット(1/3-4commons)
    transmit(_CMD_SIGID, _CMD_LCDON, 0x00);//LCDのバイアスジェネレータ起動(不要?)
}

void testPrint(){
    for(unsigned char c=0;32>c;c++){
        transmit(_DWD_SIGID, c, 0x00);//塗り潰し
    }
}

void setup() {
    //Serial.begin(9600);
    pinSetup();//ピン設定
    initCmd();//初期化
    testPrint();//ピン書き込み
}

void loop() {//1bitずつ増やしていく
  // put your main code here, to run repeatedly:
    for(int c=0;16>c;c++){
        for(int i=0;16>i;i++){
            transmit(_DWD_SIGID, c, i);
            delay(25);
        }
    }
    for(int c=0;64>c;c++){
        transmit(_DWD_SIGID, c, 0x00);
    }
}

適当実装だから多分色々間違ってる(データ書き込みが逆さになってたりSYS_ENだけで十分っぽかったり)けど
動いたからとりあえず一段落

やっぱり自分で動かすのって楽しい(死んだ目で)

たぶんつづく


参考にしたページ

[失敗]mrubyでAitendo 99円液晶 - Qiita ここで99円液晶の存在を知った
Overview | Introducing Pro Trinket | Adafruit Learning System Adafruit公式のTriknetProのページ
Arduino 日本語リファレンス Arduinoリファレンスの和訳
Arduino のすすめ Arduinoのピンの役割や使い方について
オームの法則 LEDの抵抗について
LEDの抵抗値計算 LEDの抵抗値計算器
morecat_lab » Segment LCDを試す ピンアサインについて
VikiWiki - TS119-5 謎の7番ピンについて
TN液晶8セグモジュールを使う 駆動電圧について(3V3でもOK)
ハム三昧: LCD-TS174をPICによりドライブ クロックサイクルの最低値について
LCDのバイアスについて(pdf注意)
99%おぼえ書き: TS119-3 制御デモ スケッチ モードコマンドの順番について(結局他人のライブラリ見てしまった…)