さくらのジャンク箱ロゴ

Sakura87がお届けするPCや電子工作と写真のブログ @16周年

Arduino Uno でグラフィック液晶にテキストを表示する。

この記事は4年ほど前に投稿されました。内容が古くなっている可能性がありますので更新日時にご注意ください。

IMG_7199_20160124

今回はArduino Unoでグラフィック液晶にテキストを表示してみたいと思います。
前回は画像表示から入りましたが、Arduinoでは正直画像を読み込んでどうこうするよりも、何らかのデータを表示するほうが多いと思いますので。今回はグラフィック液晶に日本語を表示してみたいと思います。

はじめに

液晶の説明等はRaspberry Piで同じ液晶を動かした時に説明していますのでそちらを見てください。
操作方法は基本的に同じ、今回はArduinoの少ないピンを少しでも有効活用をするためにシフトレジスタを使ってデータを転送しています。

お知らせ

以下の記事では、グラフィック液晶にビットマップデータを転送していますが。FONTXというビットマップフォントを扱う記事を少し後に書いていまして、そちらと組み合わせることで、もう少し使い勝手の良い者が作成可能でしょう。よろしければそちらもご覧くださいませ。

Arduino Unoで漢字を扱う(FONTX形式のフォントデータの読み込み・表示)

必要なもの

  • 128×64ドットの白黒グラフィック液晶
    今回は前回と同じTG12864B-02WWBVを使用しました。
  • カーボン抵抗
    10kΩ ←コントラスト調整用1
    15kΩ ←コントラスト調整用2
    470Ω ←R/Wコネクタ用
  • シリアル-パラレルのシフトレジスタ(74HC595)
    74HC595シリーズならなんでもいいので好きなモノを使ってください。
    テキサス・インスツルメンツ製が安くていいでしょう。
  • SDカードシールド
    なんでもいいのでとりあえずArduinoでSDカードが使えるようになるアイテム。
    Arduino標準のSDカードライブラリが使えるもの。

動作説明

この液晶は64x64pxを制御できるコントローラを2つ搭載していて、それを交互に切り替えることによって128x64pxの描画領域を実現しています。

データは8bitパラレル接続でデータを送信し、RSピンを切り替えることによってコマンド・データを、CS1・CS2の2ピンによってデータを転送する液晶コントローラを指定しています。ちなみにこのCSピンは2本ともHレベルにすることによって両方のコントローラに同じコマンド及びデータを送信することが可能です。(2コントローラ同時Hはやって良い処理なのかは知らないが出来るんだから利用させていただく。心配なら片方ずつ書き換えたほうがいいかも。ただし今回は使わないがReadを使うならやめておいたほうがいいかもしれない。)

これを全てIOピンでやろうとすると、13ピン必要になり、20ピンしかないArduinoのIOでは7ピンしか残らないため、SDカードに4ピン、シリアル通信に2ピン使ってしまうともう残るピンがありません。
そこで今回はシフトレジスタを利用してデータ8ピンを3ピンに減らし、更にSPIコマンドが使えるのでSDカードと2ピンを共通化して結果的にデータピンを1ピン相当に減らしています。

これで8ピン空くことになり、色々と使える幅が広がりますね。

接続について

基本的にピン-ピンで接続するだけなので、回路図などは載せません。以下の通りに接続すれば使えるはずです。

液晶側 接続先 接続先のピン
レジスタ選択 Arduino
リードライト切り替え Arduino 抵抗を介してGNDへ
イネーブル信号 Arduino
データビット0-7 シフトレジスタ QA-QH
チップ選択1(CS1) Arduino
チップ選択2(CS2) Arduino
リセット信号 Arduino 10

バックライト関係のコネクタはメーカーの指示通りに半固定抵抗を使用するなり、前回のように以下の様な接続をする事でとりあえず使用することが出来ると思います。

tg12864vo

シフトレジスタのピンは以下のように接続します。

液晶側 接続先 接続先のピン
SI Arduino 11(MOSI用固定 ※SDカードと共通)
RCK Arduino 7(SlaveSelect用・任意)
SCK Arduino 13(SCLK用固定 ※SDカードと共通)
QA-QH 液晶モニタ データビット0-7
G Arduino GND
SCLR Arduino +5V
QH’ なし 今回は使用しません。

SDカードはMOSIを11・MISOを12・SCLKを13・SlaveSelectを4に接続します。

 

フォントの格納

グラフィック液晶に文字を表示する場合、基本的には何らかのフォントを用意する必要がありますが。
Arduinoはメモリが非常に少ない。RaspberryPi2は1GB(1073741824Byte)の超広大なメモリ領域があるのに対してArduinoは2048バイトしかありません。なんと524,288分の1しかない!!これじゃあRaspberry Piでやったような画像一覧ファイルを用意してそれを変数に入れ込んで…なんて言うことはとてもじゃないけど出来ません。

ではどうするのかというと。

1.プログラムにコードを書き込んでIFなどで変数に入れ込む

これは非常に手間です。キャラクタ液晶と同じようにアルファベット+カナ程度ならこれでもいいでしょうが。JIS第一水準だけでも軽く死ねるレベルです。

2.漢字ロムを使う

これが非常に簡単でしょうが、漢字ロムの入手性があまり良くなかったり、色々と制約がありますし。
なんといっても結構これをやる方法は紹介されているので今回は省きます。
恐らく速度面や部品点数から考えるとこれが一番いい方法だと思いますが。
今回はいい感じの漢字ロムが入手できなかったので採用しませんでした。

3.なんとかSDカードに文字一覧画像を入れて読み込む

SDカードって何かと使うじゃないですかぁ?じゃあこいつにフォントを入れちゃえばいいかなーって。

というわけで今回もこの方法で行きます。
ただし、メモリの制約上どうしても1文字ずつの読み込みになってしまうのでファイルシークが発生してしまい少々実効速度は遅めです。(といっても平均40ms程度なので中々実用的です。)

それをやったのが前回のこの記事です。

というかこれをやるために書いたようなものですが。長くなったので分けました。

 

コーディング

今回使用するライブラリはSD.h、SPI.hです。この2つをインクルードしてください。

使用するピン番号を設定します。

//// ピン番号の設定 ////

#define RePin     10 // 液晶リセットピン
#define RSPin     3  // レジスタ選択ピン
#define ENPin     2  // イネーブルピン
#define CS1Pin    5  // CS1ピン
#define CS2Pin    6  // CS2ピン
#define SPI_SSel  7  // シフトレジスタのラッチ

#define SD_SELECT 4          //SDカードのスレーブセレクトピン番号

//SDカードのほかのピンは以下の通りに接続する。
// MOSI:11 ※シフトレジスタのSIピンもココに接続する
// MISO:12
// SCLK:13 ※シフトレジスタのSCLKピンもココに接続する

基本的にSD・シフトレジスタアクセスに使うSPI用の11~13ピン以外は任意で構いません。
実はArduinoのANALOG INピンはデジタルIOとして使用できる(14~19がA0~A5に該当)のでアナログ入力を使用しないのであればこちらに押し込んでしまえばPWMピンが有効活用できます。

次にフォントファイルの仕様を指定します。

今回は上にあげたArduino Unoで漢字を扱う と同じモノを用意します。なお、今回は「左90度回転」させたものを用意します。

//// フォントデータ読み込み関係の設定 ////

//フォントファイルのファイル名(必ず8.3形式のファイル名にする)
#define FNT_FILE  "fontr.bmp"

// BMPファイルの仕様設定
#define BMP_OFSET   62   // BMPファイルのヘッダサイズ
#define BMP_LINEB   96   // 横1行あたりのバイト数
#define FNT_LINEB  768   // 文字コード1区分のバイト数(bmp_lineByte*8)

次に液晶にデータを送る変数名と塗りつぶしデータを指定します。
連続した8箇所なので毎度書き換えてもいいのですが、面倒なので今回はマクロを使って指定する方法を取ります。マクロの使い方については適当にググってください。

//// 液晶転送データの設定 ////

// 塗りつぶしパターンの設定
#define LcdFill 0xFF

// 転送用の変数を設定
// array[i]  <- array変数の内容をそのまま転送するとき
// ~array[i] <- array変数の内容を【反転】させて転送するとき
// 使用例: lcd_cmd(1 , 1, LCD_DATA(0)); は lcd_cmd(1 , 1 , array[0]); と等価
#define LCD_DATA(i) pat[i]

// フォントファイルを開く変数を定義
File font_data;

細かい方法等はコメントを参照してください。

フォントファイルのファイルハンドルを格納する変数もココに定義しておきます。

次にSetup関数に必要な初期化コマンドを書いておきます。

void setup() {
  // put your setup code here, to run once:

  // 使用するピンの初期化
  pinMode(SPI_SSel, OUTPUT);
  pinMode(RePin, OUTPUT);
  pinMode(RSPin, OUTPUT);
  pinMode(ENPin, OUTPUT);
  pinMode(CS1Pin, OUTPUT);
  pinMode(CS2Pin, OUTPUT);

  // シリアルポートの初期化 9600bps
  Serial.begin(9600);

  // シリアル通信が開かれるまで待つ
  while (!Serial);
  Serial.println("Ready!");
  // SDカードの初期化(KanjiRead()を使う前に必ず実施すること)
  if (!SD.begin(SD_SELECT)) return ;

  // SPIの初期化
  SPI.begin();

  // リセット信号送信
  digitalWrite(RePin, 1);
  delay(60);
  digitalWrite(RePin, 0);
  delay(100);
  digitalWrite(RePin, 1);
  delay(60);

  // 液晶初期化コマンド送信
  lcd_cmd(0, 3, 0xC0);
  lcd_cmd(0, 3, 0x3F);
  lcd_cmd(0, 3, 0x3E);
  lcd_cmd(0, 3, 0x40);
  lcd_cmd(0, 3, 0xBF);
  lcd_cmd(0, 3, 0x3F);
}

今回はシリアル通信で入力されたデータを表示するのでシリアル初期化とポート接続待ち、液晶初期化処理を入れます。

loop関数の前に、必要な変数を定義しておきます。(中でもいいがこの方が若干高速化されるっぽい)

// 必要な変数を定義
int line = 0, r = 0,  cs = 2, ume = 4;
unsigned char pat[8];
char r_kanji[64] = {0,};

loop関数の中身を書いていきます。

  r = 0;
  ume = 4;

  // データ受信待ち
  while (Serial.available() == 0)delay(500);

  // 時間計測用
  unsigned long time = millis();

  // 受信データを取り込む
  while (Serial.available())r_kanji[r++] = Serial.read();
  // 32バイト(16文字)以上なら32文字まで出力するために
  // 文字数を32文字に設定する
  if (r >= 32)r = 32;

ループ用変数の初期化からデータ取り込みまでです。
特に説明は不要だと思います。

次に文字を転送する行を塗りつぶします。

 // 使用する行を塗りつぶす
  lcd_cmd(0, 3, 0xBF - (line & 7));
  ume = 64;
  while (ume--)lcd_cmd(1, 3, LcdFill);

今回は両方のコントローラに同時に転送する方法をとっていますが。それでも計64回転送をしないといけません。この後関数を作るので個々のコマンドは1行で済むのですが。裏で9行の命令が待っているので、実は64回命令を書いてしまったほうが処理速度が僅かですが早いです。しかし64行は長いので16行を4回ループにしています。これでも30ms程度で終わるので中々高速です。

と思ったけど今やってみたら殆ど実行時間変わらなかったので変更しました。

次に液晶に文字を転送する位置を決めます。

  //// コード変換し文字パターンを取得して液晶に転送 ////

  // 転送する文字数にあわせて転送位置を設定
  lcd_cmd(0, 1, 0x80 - ((r & 15) << 2) );
  if (r > 16)lcd_cmd(0, 2, 0x40 );
  if (r <= 16) lcd_cmd(0, 2, 0x80 - ((r & 15) << 2) );
  r += 2;

今回は画面を上下反転した状態で使用するのですが。コントローラ1側は9文字目からが表示されるので8文字(16バイト)以上の残りの文字数分オフセットを作っています。

コントローラ2の方は8文字以上の場合は普通にアドレス0に設定してしまえばいいのでそのままコマンドを送ります。8文字以下の場合はコントローラ1で行った処理をコントローラ2にも行ってやります。

最後に今回はfor命令ではなくWhileとデクリメントでループ処理を行います(この方が数ms高速)そのため、ループ用変数に+2(ループ1回分)しています。

次にSDカードをオープンする命令を書きます。
この命令は本来KanjiRead命令の中に入りますが、これを外に出すだけで100msも違うのでループ外に持ってきました。

  ////// ファイルオープン命令は、本来KanjiRead関数内にあるが、
  ////// 今回は高速化のために文字転送ループの枠外にだす。これだけで100ms程度違う
  // SDカード上のフォントファイルをオープンする。
  // 他にSDカードが使われる可能性を考慮して文字転送時に開いて終わったら閉じて…を繰り返す。
  if (!(font_data = SD.open(FNT_FILE, FILE_READ)));

次に文字転送ループを書きます。

  while (r -= 2) {

    // 1文字ずつ取り出す
    unsigned char kanji[2] = {r_kanji[r - 2], r_kanji[r - 1]};

    // Arduinoのシリアルモニタの文字コードがなんでかShiftJISなので
    // SJIS -> JIS 変換を行う。
    // 参考:http://www.tohoho-web.com/wwwkanji.htm

    if (kanji[0] >= 0xE0) kanji[0] -= 0x40;
    if (kanji[1] >= 0x80) kanji[1] -= 1;
    if (kanji[1] >= 0x9e) {
      kanji[0]  = (kanji[0] - 0x70) << 1 ;
      kanji[1] -= 0x7D ;
    } else {
      kanji[0]  = ((kanji[0] - 0x70) << 1) - 1;
      kanji[1] -= 0x1F;
    }

    // 文字パターン取得
    KanjiRead(1, pat, kanji);

    // 液晶コントローラを指定
    cs = 1 + (r < 18);

    // パターン転送
    lcd_cmd(1, cs, LCD_DATA(7));
    lcd_cmd(1, cs, LCD_DATA(6));
    lcd_cmd(1, cs, LCD_DATA(5));
    lcd_cmd(1, cs, LCD_DATA(4));
    lcd_cmd(1, cs, LCD_DATA(3));
    lcd_cmd(1, cs, LCD_DATA(2));
    lcd_cmd(1, cs, LCD_DATA(1));
    lcd_cmd(1, cs, LCD_DATA(0));

  }
  // 実行時間計算用
  time = millis() - time;
  Serial.print("processing time ");
  Serial.print(time);
  Serial.println(" ms");
  

どうしているかはコメントに書いてある通りですが。文字コードはArduinoのシリアルモニタはShift_JISであって。Shift_JISがそこそこ扱いやすいのでShift_JISを使っています。
よってこの命令を使用するときはUTF-8では使用できません。何らかの方法でShift_JISにて送信してやる必要があります。(PCからのデータを表示する場合はPCの文字コードをShift_JISにしてやればいいのですが、Arduino単体や他のArduinoと通信する場合はArduinoのIDEが文字コード指定できないので非常に手間です。)

最後のパターン転送も塗りつぶしと同様に、forやwhileで転送するより8回書いたほうが若干早いのでこうしました。(これは実際にコッチのほうが1msほど早いみたいなのでこのままで行きます。)

最後にSDカードのファイルを閉じて、行をインクリメントして終了です。

  ////// ファイルクローズ命令は、本来KanjiRead関数内にあるが、
  ////// 今回は高速化のために文字転送ループの枠外にだす。これだけで100ms程度違う
  // フォントファイルを閉じる
  font_data.close();

  // 行インクリメント
  line++;

次に液晶制御に必要なコマンド転送命令を書きます。

// コマンド転送用命令
// lcd_cmd(レジスタ,コントローラ,データ);
// 例:lcd_cmd(1,1,0xff); ←コントローラ1にデータ(0xff)を転送する
void lcd_cmd(unsigned char rs, unsigned char cs, unsigned char data) {
  digitalWrite(RSPin, rs);
  digitalWrite(CS1Pin, cs & 1);
  digitalWrite(CS2Pin, cs >> 1);
  digitalWrite(SPI_SSel, 0);
  SPI.transfer(data);
  digitalWrite(SPI_SSel, 1);
  delayMicroseconds(2);
  digitalWrite(ENPin, 1);
  digitalWrite(ENPin, 0);

}

コマンド転送命令です。RSピンを設定、CSピンを設定、シフトレジスタのRCKをLにした後、
SPIコマンドを利用してデータを転送。
シフトレジスタのRCKをHにしてその後2μ秒待ちイネーブルを切り替えてデータを転送しています。

本来ならCSピントシフトレジスタのRCK切り替えの前に0.45μ秒のディレイを置くべきですが、digitalWriteの処理が44サイクル(1サイクル0.0625μ秒@16MHz)SPI転送コマンドに2サイクルを要していて、両方で既に3μ秒経過していますので不要となります。

という事で実はその後のdelayMicrosecondsも不要だったりしますが。こちらは保険として残しておきます。

次に漢字パターンを読み込んで返す命令を作ります。

// 漢字パターン読み込み命令
// 参照→ https://sakura87.net/archives/2575#hw
void KanjiRead(boolean rotate, unsigned char *kanji_pat, unsigned char *kanji_code) {

  //// 今回はデータ転送の高速化のためにこの命令は外に出した。本来の一はココ。
  // SDカード上のフォントファイルをオープンする。
  // 他にSDカードが使われる可能性を考慮して文字転送時に開いて終わったら閉じて…を繰り返す。

  //File font_data; // フォントファイルを開く変数を定義
  //if (!(font_data = SD.open(FNT_FILE, FILE_READ)));

  unsigned long Offset = BMP_OFSET + (FNT_LINEB * ((unsigned long)kanji_code[rotate] - 33)) + (kanji_code[!rotate] - 33) + (BMP_LINEB << 3);

  // 文字コードからファイルの位置を計算して読み込み
  char i = 8;
  while (i--) {
    font_data.seek(Offset -= BMP_LINEB);
    kanji_pat[i] = font_data.read();
  }

  //// 今回はデータ転送の高速化のためにこの命令は外に出した。本来の一はココ。
  // フォントファイルを閉じる
  // font_data.close();

}

前回とだいぶ変わっていますが。前回のコードをいろいろと洗い流した結果。これが一番高速に処理できました。
SD関係の命令は前述のとおり関数外にだしてしまいましたが、一応コメントアウトで残しております。

これを全てつなげたコードが以下のとおりです。

#include <SPI.h>
#include <SD.h>

//// ピン番号の設定 ////

#define RePin     10 // 液晶リセットピン
#define RSPin     3  // レジスタ選択ピン
#define ENPin     2  // イネーブルピン
#define CS1Pin    5  // CS1ピン
#define CS2Pin    6  // CS2ピン
#define SPI_SSel  7  // シフトレジスタのラッチ

#define SD_SELECT 4          //SDカードのスレーブセレクトピン番号

//SDカードのほかのピンは以下の通りに接続する。
// MOSI:11 ※シフトレジスタのSIピンもココに接続する
// MISO:12
// SCLK:13 ※シフトレジスタのSCLKピンもココに接続する


//// フォントデータ読み込み関係の設定 ////

//フォントファイルのファイル名(必ず8.3形式のファイル名にする)
#define FNT_FILE  "fontr.bmp"

// BMPファイルの仕様設定
#define BMP_OFSET   62   // BMPファイルのヘッダサイズ
#define BMP_LINEB   96   // 横1行あたりのバイト数
#define FNT_LINEB  768   // 文字コード1区分のバイト数(bmp_lineByte*8)

//// 液晶転送データの設定 ////

// 塗りつぶしパターンの設定
#define LcdFill 0xFF

// 転送用の変数を設定
// array[i]  <- array変数の内容をそのまま転送するとき
// ~array[i] <- array変数の内容を【反転】させて転送するとき
// 使用例: lcd_cmd(1 , 1, LCD_DATA(0)); は lcd_cmd(1 , 1 , array[0]); と等価
#define LCD_DATA(i) pat[i]

File font_data; // フォントファイルを開く変数を定義

void setup() {
  // put your setup code here, to run once:

  // 使用するピンの初期化
  pinMode(SPI_SSel, OUTPUT);
  pinMode(RePin, OUTPUT);
  pinMode(RSPin, OUTPUT);
  pinMode(ENPin, OUTPUT);
  pinMode(CS1Pin, OUTPUT);
  pinMode(CS2Pin, OUTPUT);

  // シリアルポートの初期化 9600bps
  Serial.begin(9600);

  // シリアル通信が開かれるまで待つ
  while (!Serial);
  Serial.println("Ready!");
  // SDカードの初期化(KanjiRead()を使う前に必ず実施すること)
  if (!SD.begin(SD_SELECT)) return ;

  // SPIの初期化
  SPI.begin();

  // リセット信号送信
  digitalWrite(RePin, 1);
  delay(60);
  digitalWrite(RePin, 0);
  delay(100);
  digitalWrite(RePin, 1);
  delay(60);

  // 液晶初期化コマンド送信
  lcd_cmd(0, 3, 0xC0);
  lcd_cmd(0, 3, 0x3F);
  lcd_cmd(0, 3, 0x3E);
  lcd_cmd(0, 3, 0x40);
  lcd_cmd(0, 3, 0xBF);
  lcd_cmd(0, 3, 0x3F);
}


// 必要な変数を定義
int line = 0, r = 0,  cs = 2, ume = 4;
unsigned char pat[8];
char r_kanji[64] = {0,};

void loop() {
  // put your main code here, to run repeatedly:
  r = 0;
  ume = 4;

  // データ受信待ち
  while (Serial.available() == 0)delay(500);

  // 時間計測用
  unsigned long time = millis();

  // 受信データを取り込む
  while (Serial.available())r_kanji[r++] = Serial.read();

  // 32バイト(16文字)以上なら32文字まで出力するために
  // 文字数を32文字に設定する
  if (r >= 32)r = 32;
  // 使用する行を塗りつぶす
  lcd_cmd(0, 3, 0xBF - (line & 7));
  ume = 64;
  while (ume--)lcd_cmd(1, 3, LcdFill);

  //// コード変換し文字パターンを取得して液晶に転送 ////

  // 転送する文字数にあわせて転送位置を設定
  lcd_cmd(0, 1, 0x80 - ((r & 15) << 2) );
  if (r > 16)lcd_cmd(0, 2, 0x40 );
  if (r <= 16) lcd_cmd(0, 2, 0x80 - ((r & 15) << 2) );
  r += 2;

  ////// ファイルオープン命令は、本来KanjiRead関数内にあるが、
  ////// 今回は高速化のために文字転送ループの枠外にだす。これだけで100ms程度違う
  // SDカード上のフォントファイルをオープンする。
  // 他にSDカードが使われる可能性を考慮して文字転送時に開いて終わったら閉じて…を繰り返す。
  if (!(font_data = SD.open(FNT_FILE, FILE_READ)));

  while (r -= 2) {

    // 1文字ずつ取り出す
    unsigned char kanji[2] = {r_kanji[r - 2], r_kanji[r - 1]};

    // Arduinoのシリアルモニタの文字コードがなんでかShiftJISなので
    // SJIS -> JIS 変換を行う。
    // 参考:http://www.tohoho-web.com/wwwkanji.htm

    if (kanji[0] >= 0xE0) kanji[0] -= 0x40;
    if (kanji[1] >= 0x80) kanji[1] -= 1;
    if (kanji[1] >= 0x9e) {
      kanji[0]  = (kanji[0] - 0x70) << 1 ;
      kanji[1] -= 0x7D ;
    } else {
      kanji[0]  = ((kanji[0] - 0x70) << 1) - 1;
      kanji[1] -= 0x1F;
    }

    // 文字パターン取得
    KanjiRead(1, pat, kanji);

    // 液晶コントローラを指定
    cs = 1 + (r < 18);

    // パターン転送
    lcd_cmd(1, cs, LCD_DATA(7));
    lcd_cmd(1, cs, LCD_DATA(6));
    lcd_cmd(1, cs, LCD_DATA(5));
    lcd_cmd(1, cs, LCD_DATA(4));
    lcd_cmd(1, cs, LCD_DATA(3));
    lcd_cmd(1, cs, LCD_DATA(2));
    lcd_cmd(1, cs, LCD_DATA(1));
    lcd_cmd(1, cs, LCD_DATA(0));

  }


  // 実行時間計算用
  time = millis() - time;
  Serial.print("processing time ");
  Serial.print(time);
  Serial.println(" ms");


  ////// ファイルクローズ命令は、本来KanjiRead関数内にあるが、
  ////// 今回は高速化のために文字転送ループの枠外にだす。これだけで100ms程度違う
  // フォントファイルを閉じる
  font_data.close();

  // 行インクリメント
  line++;

}

// コマンド転送用命令
// lcd_cmd(レジスタ,コントローラ,データ);
// 例:lcd_cmd(1,1,0xff); ←コントローラ1にデータ(0xff)を転送する
void lcd_cmd(unsigned char rs, unsigned char cs, unsigned char data) {
  digitalWrite(RSPin, rs);
  digitalWrite(CS1Pin, cs & 1);
  digitalWrite(CS2Pin, cs >> 1);
  digitalWrite(SPI_SSel, 0);
  SPI.transfer(data);
  digitalWrite(SPI_SSel, 1);
  delayMicroseconds(2);
  digitalWrite(ENPin, 1);
  digitalWrite(ENPin, 0);

}



// 漢字パターン読み込み命令
// 参照→ https://sakura87.net/archives/2575#hw
void KanjiRead(boolean rotate, unsigned char *kanji_pat, unsigned char *kanji_code) {

  //// 今回はデータ転送の高速化のためにこの命令は外に出した。本来の一はココ。
  // SDカード上のフォントファイルをオープンする。
  // 他にSDカードが使われる可能性を考慮して文字転送時に開いて終わったら閉じて…を繰り返す。

  //File font_data; // フォントファイルを開く変数を定義
  //if (!(font_data = SD.open(FNT_FILE, FILE_READ)));

  unsigned long Offset = BMP_OFSET + (FNT_LINEB * ((unsigned long)kanji_code[rotate] - 33)) + (kanji_code[!rotate] - 33) + (BMP_LINEB << 3);

  // 文字コードからファイルの位置を計算して読み込み
  char i = 8;
  while (i--) {
    font_data.seek(Offset -= BMP_LINEB);
    kanji_pat[i] = font_data.read();
  }

  //// 今回はデータ転送の高速化のためにこの命令は外に出した。本来の一はココ。
  // フォントファイルを閉じる
  // font_data.close();

}

動作結果

以上のコードをコンパイルし転送して、以下の文字を転送すると画像のようになります。

〓グラフィック液晶に日本語表示〓
━━━━━━━━━━━━━━━━
8x8のビットマップフォントを使
用して16文字x8行を表示可能!
ABCabc12345六七八九零
アイウエオあいうえお川内那珂神通
○×△□※↑↓→←¥μδθ〓〒☆
━━━━━━━━━━━━━━━━

IMG_7201_20160124

上の文字列を転送した結果、所要時間は以下のとおりでした。

  1. processing time 743 ms
  2. processing time 616 ms
  3. processing time 725 ms
  4. processing time 689 ms
  5. processing time 599 ms
  6. processing time 690 ms
  7. processing time 690 ms
  8. processing time 617 ms

概ね1秒程度で書き換え完了していますのでまぁまぁ実用的だと思います。

終わりに

今回はArduinoにとっては処理が大変なグラフィック液晶を使ってみました。
やはりこれくらいのものになるとArduino Unoでは若干処理が重たいようです。
といっても、実際に一番時間を割いているのはSDカードにアクセする。それもファイルのシーク位置を決めている部分のようなので、そこをどうにかできればもう少し高速化出来ると思います。(今回はRaspberry Piと極力同じ方法で扱えるようにということが重要だったのでこうしましたが、本来ならもう少しArduinoに扱いやすいデータ形式で格納してやる必要がありますね。)

しかし、態々漢字を入れる必要が無く、英数字だけでいいのであればもっと簡単に処理することも可能でしょうが。そこまでして漢字を扱うのなら、もういっそRaspberry Piをメインにしてみたほうがいろいろと楽だと思います。第一世代ならそんなに値段変わらないしね。

というわけで以上でおわります。

“Arduino Uno でグラフィック液晶にテキストを表示する。” への1件のコメント

  1. […] Arduino Uno でグラフィック液晶にテキストを表示する。 […]

コメントを残す

メールアドレスが公開されることはありません。

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください

関連記事