電子回路わからん日記

にゃーんと言いながら電子回路いじってます

バックライト付きI2CキャラクターディスプレイをSTM32で操る

ご無沙汰してます。

 

今回ですが、STM32とバックライト付きI2C制御キャラクターディスプレイ「ACM0802C-NLW-BBW-IIC」を組み合わせて様々な文字列をディスプレイに表示させてみたので、その過程を書いていきたいと思います。

やってて色々とハマりました。


ACM0802C-NLW-BBW-IICについて

秋月電子通商で販売されている、バックライト付きの8文字x2列キャラクターディスプレイ(I2C制御タイプ)です。

 

通販コード:P-13519

akizukidenshi.com

 

だいたいこの手のディスプレイって本来のインターフェースは8bitパラレルだったり4bitパラレルだったりするのですが、接続線が多いと煩わしいので、ディスプレイの更新速度ではやや不利になるものの今回はI2C制御タイプにしました。


最初から怪しい匂いが・・・

このディスプレイですが、データシート上にI2C通信に関する詳細な記述がありません。

秋月の商品ページには「データシートの8ページに記載がある」旨問い合わせに回答されていますが、本当に手順しか記載がなく命令レジスタ等の記載は皆無です。

上記データシートを見ていると、4ページ目に「Contoroller IC: RW1063-0A or compatible」と記載があります。

つまり「RW1063-0A」なるディスプレイ制御ICのデータシートを入手する必要がありそうです。


RW1063-0Aデータシート入手

「これはどうしたものかなー」と思いつつRW1063-0AというICのデータシートを探していたら、同じく秋月電子通商販売の「ACM2004D-FLW-FBW-IIC」というI2C制御キャラクターディスプレイのページにRW1063-0Aのデータシートと思しきものが添付されていました。

 

通販コード:P-17381

akizukidenshi.com

 

これをもとに他のWebサイトでの実装例なんかも見つつ実際に文字列を表示させていきたいと思います。

 

追伸、「ACM2004D-FLW-FBW-IIC」のページにはArduinoのサンプルスケッチを入手することもできます。一度に表示できる文字数もACM2004D-FLW-FBW-IICのほうが多いですからこれからディスプレイを使いたい人はこちらから始めるほうが難易度は低いかもしれません。


バイスアドレスについて

ACM0802C-NLW-BBW-IICですが、コネクタの4番、5番ピンをプルアップまたはプルダウンすることでアドレスを4通りに設定できます。(ACM0802C-NLW-BBW-IICデータシート6ページに記載)

RW1063-0Aのデータシート28ページより抜粋

上図のSA0、SA1がそのアドレスに相当します。つまりデバイスアドレスは16進数(2進数)で

  • 0x78 (0b01111000):SA1 = 0, SA0 = 0
  • 0x7A (0b01111010):SA1 = 0, SA0 = 1
  • 0x7C (0b01111100):SA1 = 1, SA0 = 0
  • 0x7E (0b01111110):SA1 = 1, SA0 = 1

の4通りです。


レジスタアドレス

バイスアドレスの後に送るレジスタアドレスは2通りです(RW1063-0Aデータシート28ページに詳細記載)。

  • 0x00 (0b00000000):ディスプレイに何かしらの設定を送るとき
  • 0x40 (0b01000000):ディスプレイの特定箇所の文字を設定するとき

RW1063-0Aデータシート中の"Control byte"中にあるA0ビットの違いで設定します。


設定データ

設定データについてはRW1063-0Aデータシート17ページに記載があります。

ここに記載された値をレジスタアドレス0x00の後に送ることでディスプレイのオンオフや表示の消去などができます。

RW1063-0Aのデータシート17ページより抜粋

I2Cで制御する場合はRSやRWビットは無視します。(つまり、「Read Busy Flag and Address」の命令はI2C制御時はできません。)

 

AUDIYはこの命令のうち「Set DDRAM Address」にハマりました。後ほど説明します。


文字データ

一般的な半角文字はRW1063-0A内部のROMに格納されていて、少なくともカタカナ以外はレジスタアドレス0x40のあとに対応するASCIIコードを送るだけでその文字が表示されます。

e-words.jp

 

例えば、小文字のaを表示させたい場合は0x40のあとに0x61(10進数97)を送ればよいです。

プログラミングでうまいことやれば文字列をそのまま表示できそうです。


初期化

起動後のデバイスの初期化ですが、RW1063-0Aデータシートの32ページに手順が記載されています。

RW1063-0Aのデータシート32ページより抜粋

待ち時間まで指定されていますね・・・


文字列の表示について

実際にSTM32マイコンでI2C制御ディスプレイを動かすブログ記事があったので参考にしましたが、制御ICが違うためか見事にハマりました。

lawn-tech.jp

 

  1. バイスアドレスを送る
  2. レジスタアドレス(0x40)を送る
  3. 表示したい文字を送る

を繰り返せば文字列が表示できるようなコードに見えますが、実際にやってみても冒頭の1文字しか表示されません。

改めてRW1063-0Aのデータシートを読んでいくと、文字列表示にとても重要なことが12~14ページに記載されていました。

詳しくはデータシートを読んでいただくとして、ざっくり言うと「文字を表示したい場所をDDRAM Addressで指定しなさい」ということです。

今回使用するACM0802C-NLW-BBW-IICは8文字、2列のディスプレイなので、初期化終了時点で1列目のアドレスは0x00 ~ 0x07、2列目のアドレスは0x40 ~ 0x47とのことです。

このアドレスの変更をRW1063-0Aデータシート17ページに記載の「Set DDRAM Address」で行うということです。

 

つまり順序を書くと

  1. レジスタアドレス0x00にSet DDRAM Address命令(0b1xxxxxxx)命令を送って表示位置を指定
  2. レジスタアドレス0x40に表示したい文字を送る

の2つを繰り返すことで文字列表示が実現できます。

 

実現した動画は以下のとおりです。


ライブラリ関数の実装

いくつかのI2Cの送信について関数を作ったので解説します。

といっても設定や文字列表示に関する関数は3つです。

設定書き込み関数ACMWriteInstruction()

レジスタアドレス0x00に対し代入されたデータを送信します。

/* Write Instruction */
uint8_t ACMWriteInstruction(I2C_HandleTypeDef *hi2c, uint16_t AD, uint8_t data) {
    uint8_t buf[2] = {INST_C0_0, data}; // 0x00, data
    uint8_t status = HAL_I2C_Master_Transmit(hi2c, AD, buf, 2, 1000);

    return status == HAL_OK;
}

 

文字書き込み関数ACMPrintChar()

現在指定されているDDRAMアドレスに対し文字*strを書き込みます

やっていることはACMWriteInstruction()関数とほぼ変わりません。

/* Print Single Character to the ACM0802C-NLW-BLW-I2C. */
uint8_t ACMPrintChar(I2C_HandleTypeDef *hi2c, uint16_t AD, const char *cdata) {
    uint8_t buf[2] = {WRITE_C0_0, *cdata}; // 0x40, *cdata
    uint8_t status = HAL_I2C_Master_Transmit(hi2c, AD, buf, 2, 1000);
    HAL_Delay(10);

    return status == HAL_OK;
}

※DDRAM Addressの更新は行いませんので注意してください。

 

文字列書き込み関数ACMPrintStr()

ディスプレイに対し入力引数LNで指定した行に文字列*strを入力します。
関数内部で行指定、文字数計算およびその文字数だけACMPrintChar()関数の繰り返しを行います。

/* Print 1-Line String (under 40 characters) to the ACM0802C-NLW-BLW-I2C. */
void ACMPrintStr(I2C_HandleTypeDef *hi2c, uint16_t AD, uint8_t LN, const char *str) {
    uint8_t LNx = DDRAM_LN1;
    uint8_t str_len = 0;
    uint8_t i = 0;
    const char *startstr = str;

    if (LN == 1) {
        LNx = DDRAM_LN1; // DDRAM Address: 0x80
    } else {
        LNx = DDRAM_LN2; // DDRAM Address: 0xC0
    }

    str_len = strlen(str); // Count String Length.

    if (str_len > 40) {
        str_len = 40; // String Length must be under 40 per Line.
    }

    /* Print Characters in the string */
    for (i = 0; i < str_len; i++) {
        ACMWriteInstruction(hi2c, AD, (LNx | i)); // Specify the DDRAM Address
        ACMPrintChar(hi2c, AD, str); // Write the character the DDRAM.
        str++;
    }

    str = startstr; // Return *str pointer to start address.
}

 

その他にもディスプレイの初期化関数や、ディスプレイに設定されたI2Cデバイスアドレスを検索する関数を実装しています。

github.com


動かしてみる

上記リンク先の関数を組み合わせて文字列を流すプログラムを実装してみました。

流れとしては

  1. CheckACMDeviceAddress()関数でI2Cデバイスアドレスの取得
  2. InitACM()関数でRW1063-0Aの初期化
  3. ACMPrintStr();で文字列を書き込み
  4. ディスプレイの表示を開始
  5. 1秒ごとに文字列を左にシフト
  6. 文字列全てをシフトし終えたら最初の状態で表示し直す
  7. 5と6を繰り返す

という手順です。


まとめ

ということで秋月電子通商で販売されているACM0802C-NLW-BBW-IICを使って文字列を表示させてみました。

まとめとしては

  1. データシートの情報が不足している
  2. DDRAMのアドレスの更新が文字列表示のカギ
  3. ACM2004D-FLW-FBW-IICから始めるほうがオススメ

です。

今回作成したドライバ(HAL使用)はGitHubにアップロードしています。

MITライセンス(2条項BSDライセンス)で公開していますのでご自由にお使いいただければと思います。

github.com

 

また何か進捗があればブログを更新したいと思います。