Skip to main content

TFT液晶LCD+AVRマイコンで自作フォントを表示

秋葉原でTFT液晶LCDモジュール(ZY-FGD1442701V1、キャリーボード付き)を購入して動かしてみました。キャリーボードが搭載されていますので試作は楽でした。

DSC_0205_2 DSC_0209_2

接続

キャリーボードのピンが液晶モジュールのピンと番号が一つずれているので、キャリーボードの2番がLCDモジュールの1番となります。配線の際にご注意ください。

TFT液晶LCDモジュールのデータシート:ダウンロード(PDF)

circuit

ピン説明:

  • RSTB  − ソフトウェアリセット
  • CS    − 設備選択(通信有効化)
  • CD   − コマンド/データの切り替え
  • WR   − 書き込みモード
  • RD   − 読み込みモード
  • D0~D7 − 8ビットデータピン

D0〜D7をマイコンのPD0~PD7に接続すると8ビットのデータを一括送信できます。

制御

このLCDモジュールのコントローラチップはST7735です。AVRマイコンからコマンドベースでの制御が可能です。

ST7735のデータシート: ダウンロード(PDF)。

設備選択(通信有効化)

一般的なSPI通信と同じように、CSピンをLOWにしてLCDモジュールを通信対象にします。

#define SELECT_LCD()   (PORTC &= ~CS)
#define DESELECT_LCD() (PORTC |=  CS)

送信の種類(コマンドとデータ)

CD(DCS)ピンをLOWに設定するとコマンド送信、HIGHに設定するとデータ送信となります。種別によってCD(DCS)ピンの切り替えが必要です。

コマンド

コマンドリストはデータシートのP76からご参照ください。コマンドは8ビット、パラメータはコマンドによってビット数が違います。8ビットと32ビット2種類のパラメータが多いです。

コマンドを送信するにはCD(DCS)ピンをLOWに設定します。そしてPORTDを送信コマンドに書き込んで、WRXピンを一旦LOWにして、HIGHに設定するとデータが渡されます。

コマンド送信プログラム:

#define SWITCH_CMD()   (PORTB &= ~DCS)
#define SWITCH_DATA()  (PORTB |=  DCS)

#define START_WRITE()  (PORTC &= ~WRX)
#define END_WRITE()    (PORTC |=  WRX)

inline void send_cmd(byte cmd)
{
  SWITCH_CMD();
  PORTD = cmd;
  START_WRITE();
  END_WRITE();
}

データ

パラメータをデータとして送信しますので、CD(DCS)ピンをHIGHに設定します。1バイトのパラメータの送信プログラムは以下の通りです。

inline void send_data(byte data)
{
  SWITCH_DATA();
  PORTD = data;
  START_WRITE();
  END_WRITE();
}

16ビット(2バイト)のパラメータを送信したい場合、send_dataを2回呼び出します。

send_data(0x00);
send_data(0x03);

ST7735初期化

ST7735コントローラチップとの通信の前、画面サイズやピクセルフォーマットなどの初期設定を行う必要があります。使うコマンドを定数として定義します。

#define CMD_RESET         0x01
#define CMD_SLEEP_OUT     0x11
#define CMD_COL_SET       0x2A
#define CMD_ROW_SET       0x2B
#define CMD_MEM_WRITE     0x2C
#define CMD_MEM_READ      0x2E
#define CMD_MEM_DATA      0x36
#define CMD_PIXEL_FORMAT  0x3A

ハードウェアリセット

RSTBピンを一旦LOWにして、HIGHにすると、LCDモジュールをハードウェアリセットできます。

#define RESET _BV(PORTC2)

PORTC &= ~RESET;  _delay_ms(100);
PORTC |= RESET;   _delay_ms(120);

ソフトウェアリセット

コマンドRESETを送信するとソフトウェアリセットができます。

send_cmd(CMD_RESET);              // Software Reset
_delay_ms(120);

send_cmd(CMD_SLEEP_OUT);          // Sleep out
_delay_ms(120);

メモリの制御方式を設定

初期化の際にLCDモジュールのメモリ制御方式を設定します。コマンドCMD_MEM_DATAの送信の後、値0xC8をパラメータとして送信します。0xC8は左上から右下、順次描画の設定です。他の設定値はデータシートのP109をご参照ください。

send_cmd(CMD_MEM_DATA);          // Memory data access control
send_data(0xC8);

ピクセルフォーマット(色の通信モード)を設定

ST7735は8ビット、16ビット、18ビットのデータバスに対応していますが、このLCDモジュールには8ビットのデータピンしか実装されておらず、かつ65k色までしか対応しないため、8ビットデータバスで16ビットの色データを渡すようにピクセルフォーマットを設定します。(データシートP38を参照)

send_cmd(CMD_PIXEL_FORMAT);      // 色データの通信モード
send_data(0x05);                 // 8ビットデータバスと16ビットの色データ

8ビットデータバスで16ビットの色データを送信するには、下図のように三つのRGB値を2バイトに分けて送信します。

RGB_data

R(赤)は5ビット、G(緑)が6ビット、B(青)が5ビット、合計16ビット(2バイト)のデータを渡します。

65k色以外、ST7735は4kと262kにも対応していますが、今回は使いません。

  • 4k colors, RGB 4,4,4-bit input
  • 65k colors, RGB 5,6,5-bit input (←今回の利用フォーマット
  • 262k colors, RGB 6,6,6-bit input

行列メモリ範囲を設定

LCDモジュールは128×128ですのでメモリの範囲を128×128まで確保します。以下のコードは行列メモリの範囲を128×128に設定します。

send_cmd(CMD_COL_SET);  // 列アドレス
send_data(0x00);        // 開始 02h = 2
send_data(0x02);
send_data(0x00);        // 終了 81h = 129
send_data(0x81);

send_cmd(CMD_ROW_SET);  // 行アドレス
send_data(0x00);        // 開始 0003h = 3
send_data(0x03);
send_data(0x00);        // 終了 0082h = 130
send_data(0x82);

表示処理

LCDモジュールのメモリにピクセルの色データを書き出しすると画面の色が順次変わります。列のピクセルが129に達した場合自動的に次の行の先頭から表示します。

lcd_flow
(図はデータシートから)

画面を真っ黒にクリアしたい場合、先頭から合計128×128=16384ピクセルの色が0のデータを送信します。プログラムは以下のようです。

void clear_black() 
{
  // 色データメモリの書き込み
  send_cmd(CMD_MEM_WRITE);

  for (int i = 0; i < 128; i++)
  {
    for (int j = 0; j < 128; j++)
    {
      send_data(0);
      send_data(0);
    }
  }
}

RGB値が0のデータを行列(128×128=16384)のピクセルデータに送信すると、画面が真っ黒に変わります。

描画範囲を設定

毎回画面の最初から最後まで送信することもできますが、より効率的に一部のみの範囲を描画したい場合、描画の範囲の指定もできます。

draw_range

描画範囲の指定は行と列に対してそれぞれ行います。行範囲指定のコマンドは以下の通りです。

void set_col_range(short start_col, short end_col)
{
  send_cmd(CMD_COL_SET);  
  send_data(start_col >> 8);
  send_data(start_col);
  send_data(end_col >> 8);
  send_data(end_col);
}

列範囲指定のコマンド:

void set_row_range(short start_row, short end_row)
{
  send_cmd(CMD_ROW_SET);
  send_data(start_row >> 8);
  send_data(start_row);
  send_data(end_row >> 8);
  send_data(end_row);
}

上記関数を呼び出して描画範囲を指定します。

// 描画範囲の指定
set_col_range(<開始位置>, <終了位置>);
set_row_range(<開始位置>, <終了位置>);

色データの送信

上記の出力範囲設定の後、コマンドCMD_MEM_WRITE(0x2C)を送信し、色データを1ピクセルにつき2バイト送信します。

send_color

フォントの定義

フォントの全ての文字を幅6ピクセル、高さ12ピクセルとして固定します。文字毎にピクセル情報を事前に定義します。以下は大文字Aの定義です。

font_A

全ての文字のサイズを幅が6、高さが12に統一します。

#define FONT_WIDTH  6
#define FONT_HEIGHT 12

フォントのサイズを統一すると、128×128の画面に出力可能な行列数も計算できます。出力可能の列数が16、行数が10を定数として定義します。

#define SCREEN_COLS 16
#define SCREEN_ROWS 10

文字データを16進数として定義

フォントの文字毎のピクセルデータを定義するには、まずピクセルの有無を0と1に区別して定義します。0は色データを出力しない、1は出力するという定義です。バイナリの0と1を16進数の1バイトにまとめると、1行の定義情報を1バイトとして保存できます。

font_A_hex

さらに全ての行のデータをbyte配列に保管できます。文字Aのプログラム定義は以下の通りです。

byte font_A[] = { 0x1e, 0x21, 0x21, 0x21, 0x3f, 0x21, 0x21, 0x21, 0x21, 0x0, 0x0, 0x0 };

色データを送信

RGB値を作成するためのマクロを定義します。

#define RGB(r,g,b) (((r & 0x1f) << 11) | ((g & 0x1f) << 6) | (b & 0x1f))

文字を描画

文字は幅6、高さ12のサイズしかないため、文字の色データの出力には先に描画範囲を指定する必要があります。

// 文字の出力範囲を設定
set_col_range(pos_x, pos_x + FONT_WIDTH);
set_row_range(pos_y, pos_y + FONT_HEIGHT);

統一した文字の幅と高さのサイズの範囲内で、文字定義の16進数の中から0と1を探します。1の場合色データを出力します。fcolorは前景色、bcolorは背景色として色データを一時保存します。

static ushort fcolor = RGB(31, 31, 31), bcolor = 0;

void draw_char(char ch)
{
  ...
  send_cmd(CMD_MEM_WRITE);
  ...
  for (int y = 0; y < FONT_HEIGHT; y++)
  {
    for (int x = 0; x <= FONT_WIDTH; x++)
    {
      ushort k = (*fc) & (1 << (FONT_WIDTH - x));  // 0か1をチェック
    
      // 文字のピクセルを出力する
      send_data(k ? (fcolor >> 8) : (bcolor >> 8));
      send_data(k ? fcolor : bcolor);
    }
    fc++;
  }
}

ASCII表の定義

一つの文字を出力できましたら、次は文字列の出力です。C言語の文字列の文字を簡単に定義されたフォントの文字にマッピングするには、ASCII準拠のアルファベット表を作成します。

例:数字0と小文字aのフォントの定義:

const byte fc_0[] = { 0x1e, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x1e, 0x0 };
const byte fc_a[] = { 0x0, 0x0, 0x0, 0x1c, 0x22, 0x2, 0x1c, 0x22, 0x22, 0x26, 0x1b, 0x0 };

0-128のASCII表を定義し、全ての文字の定義を表に入れます。数字「0」のASCIIコードは48、小文字「a」は97、それぞれの文字を該当位置に設定します。使わない文字のところには0を設定すればよいです。

/* 自作ASCII表の一部 */
const byte* fc_tab[] = {
  0, fc_soh, fc_stx, 0, 0, 0, 0, 0, 0, 0, // 0~9
  ...
  0, 0, 0, 0, fc_common, 0, fc_dot, 0, fc_0, fc_1, // 40~49
  fc_2, ..., fc_9, fc_colon, fc_semi, // 50~59
  fc_great, fc_equal, fc_less, fc_quest, fc_at, fc_AA, ... // 60~69
  ...
  fc_ZZ, 0, 0, 0, 0, 0, 0, // 90~96
  fc_a, fc_b, fc_c, fc_d, fc_e, fc_f, fc_g, // 97
  ..., fz_z
};

文字列出力プログラム:

void draw_str(const char* str)
{
  uint len = strlen(str);

  for (int i = 0; i < len; i++)
  {
    draw_char(str[i]);
  }
}

文字列を出力

希望表示色、fcolor(前景色)とbcolor(背景色)にRGB値を設定し、文字列出力の関数を呼び出します。

fcolor = RGB(0, 31, 0); bcolor = 0;
draw_str("this is a lcd display test.\n\n");

サンプル結果は下図のようです。

output_sample

フォント編集ツール

ネコ技術は、1個ずつの文字をより簡単に編集し、HEXに生成する自動化ツールを作成しましたので、ご自由にご利用ください。

font_editor

このエディタでピクセルをクリックすると自動的にバイナリと16進数の値を作成してくれます。

漢字の作成

行列15×15に設定、値をushortに格納して漢字の自作もできます。

font_moji

但し、たくさんの漢字データを格納するには大量のメモリ領域が必要ですので、AVRマイコンの内蔵メモリでは厳しいです。SDカードなどの外部ストレージが必要かもしれません。フォントライブラリファイルとしてフォントデータを保存し、出力する際にライブラリファイルから読み取りして出力します。

ツールのダウンロード

EXEファイル:SymbolBinary-20141106-Binary.zip

<ご注意>

  1. 実行できない場合.NET 4.5をインストールしてください。
  2. 警告が出た場合は、無視してファイルを開いてください。

ソースコードダウンロード

ソースコード一式:TFTLCD.zip (4kb)

試作機

DSC_0205_2
正面

DSC_0209_2
マイコン基板との接続

DSC_0208_2
AVRマイコン基板(水晶振動子とATMEGA328Pのみ実装)


(最後更新:2014年11月6日)

Jingwood

北海道の田舎で暮らしているプログラマーです。最近山登りにハマりました。

2 thoughts to “TFT液晶LCD+AVRマイコンで自作フォントを表示”

  1. 初めまして。マイコンで液晶モジュールを制御する方法を調べていたらこのページを発見しました。
    コントローラチップの制御方法を非常にわかりやすく解説されていて、とても参考になります。
    それと、SymbolBinalyダウンロードさせていただきました。ありがたく使わせていただきます。

    1. コメントありがとうございます!お役に立てたようでうれしいです。SymbolBinaryも含めてもし改善点やアドバイスがありましたらぜひ教えてください。今後もよろしくお願いします。

Leave a Reply

Your email address will not be published. Required fields are marked *