niszetの日記

アナログCMOS系雑用エンジニアが頑張る備忘録系日記

キャラクタLCD「PLCD1602」で Bad Apple!! をYouTubeに公開しました。

これで私もYouTuber(ちがう)

ということで、キャラクLCDを色々といじっていたら出来てしまった、Bad Apple!!、ツイートにも書いた通りでYouTubeの方にも公開しています。

www.youtube.com

Twitter、いつの間にか2分くらいの動画上げられるようになっててすごい。でも動画は3分40秒あるので、フルバージョンはそちらをご覧ください。 おそらくTwitterよりもYouTubeの方が(圧縮されない分)画質が良いはずで、より動きがなめらかになっていると思います。

これをやるための技術は今まで書いた、文字をスクロールさせる技術と地続きで、表示領域を規定し、そこにデータを適切なタイミングで送ればそのドットの変化がアニメーションしているように見える、ということです。

niszet.hatenablog.com

niszet.hatenablog.com

結局のところ、字形をあらわすドット列も画像を表すドット列も、捉え方次第、というそういう感じですかね?

右側の文字列はキャラクLCD本来の使い方で文字を表示しています。つまりプリセットの半角カナを呼び出して表示しています。左側の画像データのフレーム番号とあわせて歌詞データを管理しておけば同じタイミングで表示させられますよね、という。

というわけで、これを作るための技術は特殊なものは特になく、データも非圧縮でXIAOのSRAMに載せているだけです。難しいことをしないで短期間で(実働2日)作り上げるために、完成とは何か、そのために何が必要で何が不要かを選別して、無理なく作るというのも大事だな(やる気が持続しないからね…)と思った次第です。

技術的な説明を書かないのにだらだら文章続けても良くないので、一旦切ります。

面白そうだなと思った方、是非、チャレンジしてみてください。

キャラクタLCDで高速で文字列をスクロールする技術

まずは文字表示領域の話からはじめよう。

新章突入です。これまでの話を踏まえて書いていますので、よくわからんところあるな?という場合は前の章の1話からご覧ください(↓こちらから)

niszet.hatenablog.com

なんの話?

今回は、キャラクLCDで文字をスクロールさせる動画の第二弾、こちらのtweetに貼った動画の内容の解説です。

前回の動画、

と比べると、かなり高速でスクロールさせているのに文字が見えることが分かると思います(残像が気になりはじめる速度なので、ちょっと読みづらいですが)

では、はじめます。

ひとつひとつ見ていく

今回使用しているキャラクLCD、PLCD1602(クレイン電子)は16x2の文字表示領域を持つ液晶ですが、液晶(コントローラ)の表示モードを切り替えることで上下の文字表示領域をまとめて1つの文字表示領域として使うことが出来ます。

言葉だけだとわかりづらいので図で見ていきましょう。サンプルコードにあった図形の例を使うと、2列で表示する場合に次のように表示されている記号が、

f:id:niszet:20210420162523p:plain

次のように上下の表示領域を使うイメージです。

f:id:niszet:20210421185254p:plain

このように、各ドットが上下2マス使うように引き延ばされて表示されます。これはCGRAM(あるいはDDROM)に入っている字形データそのものには変更なく、このモードに入るとこのように引き延ばされて表示されます。なので、上下に連続しないように表示ということは出来なさそうです(データシートの内容完全に理解した!とまでは言えないので、あったらすみません)

字形が縦に伸ばされているのは動画の右側、niszet0の文字列を確認すると分かりやすいでしょう(スクロールしないので) 余談ですが、文字単位ならDDRAMにどの文字を表示するのかの指定を変えていけばいいので、niszet0の文字列もスクロールさせることは可能のはずです(未確認)

さらに余談ですが、この使い方ではカーソルが表示できませんでした。これはまたいつか書きましょうかね…。

ちなみに、1列表示にしたとき、2列表示のときに下段に表示されていた文字列は表示されなくなります。2列のデータが1列に引き延ばされるイメージで、もし液晶の表示領域が右にずーっとのびているものであれば、20文字目に出てくるのかなと思います。結局、ここにデータがあっても表示されないだけなので、適当な文字を入れても動作には問題ありません。

話を戻しましょう。

美咲フォントを使った場合です。美咲フォントは8x8の領域を使うので、char型の8つ要素を持つ配列であらわせるのでした。このときの変数名cg0として(宣言時はcg0[8])、このデータを普通に変数cg0にデータを入れた場合、

f:id:niszet:20210421185353p:plain

のようになります。

一方、液晶ディスプレイの方を考えると、文字表示領域には横5ドットしかないため、美咲フォントの1文字は液晶の1つの文字表示領域には収まりません。

表示のさせ方として、今回は文字表示領域間に1マス文字が見えなくなる柱があると思うこととして、となりの文字領域にまたがる場合にその柱の分を加味することとします。ここで、見えないドットを灰色ドットで示すとすると、2列表示モードで一番上の文字表示領域にこの文字を表示する場合は

f:id:niszet:20210421185523p:plain

のようになります。これを、1列モードで表示する場合は、縦に引き延ばしたものを考えれば良いわけですから、次のようになるわけですね。

f:id:niszet:20210421191351p:plain

さて、動画では単純に1列モードで表示するのではなく、字形としてはもう一つ工夫を入れています。それが、横方向への引き延ばしです。

あんまり詳しくないのでふわっとした話ですが、横方向に文字がスクロールするため、液晶の応答(ON->OFFもOFF->ONも)と目が感じる残像(感じるのは脳?)の影響で、この文字が1文字幅だと速度を上げていくと段々見づらくなっていきます。

あと単純に字形も縦長で美しくないですね。元の字形を保ちたいという気持ちがわいてきます。

そこで、元々の字形(再掲)

f:id:niszet:20210421191740p:plain

を横方向に引き延ばした字形として以下のようなデータを作成します。

f:id:niszet:20210421191750p:plain

これは縦方向の引き延ばしと同じように、横方向にも2マス使うようにデータを入れていけば良いわけです。

一点、注意しないといけないのは、一番上に番号を振ったとおり、幅8だったものが幅16になるので、もはやchar型ではデータを持つことが出来ないという点です。char型の変数2つで表現しても良いですが、ここは素直にint型などのより多くのデータ量を持てる型を使いましょう(機種(正確にはコンパイラ依存のはず)によってバイト数が違うとかある気がするのですが、今は16bit=2byte持つことが出来ればよいので、ここは最低限保証されているはず、です)

これを液晶画面に表示するときは、こちらで横に伸ばしたこの字形がさらに縦に引き延ばされ、結果として次のような字形になることが期待できます。

f:id:niszet:20210421192146p:plain

実際には横方向には柱があるという工夫が出来ますが、縦方向の上下に切れる部分はLCDコントローラが勝手にやるので上下にぶった切られてちょっとだけ縦長のバランスになります。ここは妥協するポイントですね。表示しないドットを灰色で示すと、最終的には次のようになります。

f:id:niszet:20210421192851p:plain

ということで、モードの設定と前処理によって表示する字形を制御し、結果として視認性が向上、スクロール速度を上げてもみやすくできる、というお話しでした。

実際にはこれにコントラストやリフレッシュレートなどちょっと細工があるのですが、文字を大きく表示した点が見た目で一番インパクトが出るので、まずはここを押さえると良いでしょう(そう?)

ということで、動画第二弾のための要素技術の話でした。

キャラクタLCDで文字列をスクロールするやつの解説(5回目)

ようやく文字列がスクロールします

これまでのあらすじ

過去4回、約1万1千字を費やして、CGRAMに文字の字形データを入れればどのように表示されるかというレベルまで見てきました。 一旦の区切りとして、今回は文字をスクロールさせるための話です。

前回の記事はこちら。

niszet.hatenablog.com

本題。

さて、あとはスクロールするだけです。前回までにCGRAMに対して変数cg0からcg7までのデータを書き込むことでCGRAMの内容を更新することを説明しました。

察しがついている方もいると思うのですがやるべきことは次の通りです。

  1. 一定時間ごとにcg0からcg7までの変数の中身を横に動くように書き換える
  2. 書き換えたcg0からcg7までの変数を使ってCGRAMの内容を更新する
  3. 結果、書き換えた内容が画面に反映される

これだけです。3つ目はLCDが勝手にやってくれるし、2つ目は前回書いたようにCGRAMへ書き込む処理をもう一度行うだけです。ということで、1つめの「変数を書き換えたら文字が動くのはそうなんだろうけど実際どうやれば文字が動くように変更できるの?」が分かれば出来そうですね!

ということで、やっていきましょう。

余談?

あ、そうだ…。今回、美咲フォントを読み出す話の詳細は書いてませんが、処理はArduino-misakiUTF16ライブラリに依存しているので詳細はReadmeを参照してください…。というかそれも含めてサンプルコード書けばいいんですよね…わかる…。

変数の中身を変更して文字がスクロールするように見せる

変数沢山ありますが、やることは同じなのでcg0を中心に見ていきます。字形はbit列によってあらわされていたので、このbit列を横に動かす方法があれば良いわけですね。

C/C++(他の言語にもあるか?)には丁度良いことにシフト演算子(<<, >>それぞれ左に1つ、右に1つ、bitの並びを動かす。右に動かす場合には算術と…とかはいいか。詳しくは仕様を見てね)があるのでこれでbit列を移動させれば良さそうです。

今、cg0に8'b0000_0100というビットの並びが入っていたとすると、cg0 << 18'b0000_1000になります。簡単ですね!

そういえば、8'b0000_0100という書き方はC/C++ではできません(Verilog風の書き方)が、説明の便宜上使っていますのでご注意。

もうちょっと例を考えます。

シフトして、左側に行き過ぎたらどうなるのか。移動前が8'b0001_0000だった場合には移動後に8'b0010_0000となります。

前回書いたようにCGRAMの上位3bitは画面に表示されないので、CGRAMにコピーする元になる変数cg0も気にせずにシフトすれば良さそうです。

一番左に1があっても1bit左にシフトするとその1は消えてしまいます(もちろん0でも)。これがぐるっと回って右からまた1が現れるといったこともないので、気にせずに左に1つシフトすればよさそうです。反対側、一番右は1bit左にシフトすると常に0になります。

この一番下のbitに右隣の文字からデータを持ってきたいですね。

お隣から拝借

となりの字形データもイメージしたいので図を。隣に動かす場合、右のb4から持ってきたい気もしますが、そうすると字がつながってしまいます。

f:id:niszet:20210420162224p:plain

ここは、間に縦に空白があると思って、b5から持ってくると文字がつながらなくて丁度良い感じになります。

実際、画面の表示はこんな感じで文字の表示領域の間に1ドット分の空きがあります。その分を考慮して(そこに柱があるとおもって、そこに隠れていると考える)作るとより自然な動きになります(最初の動画もこの方法でやっています)

f:id:niszet:20210420162523p:plain

さて、cg1のb5からcg0のb0に値を入れたいわけですが…ひとまずb5の値を一番右に持っていくことにします。これはさっきの逆で右に4つシフトすればよいですね。(cg1 >> 4)こんな感じで。b7, b6には何が入っているかわからないですが、不要なのでここは捨てておきたいですね。こういう時はbit同士の演算で0にするとよいですね。

bit同士の演算には&(and)や|(or)が使えます。0と&を取ると相手が何であっても結果は0になり、1と&をとると相手が0なら0、1なら1、相手のbitがそのまま残ります。 なので、cg1のb5を右端に持ってきたあとに8'b0000_0001と&をとればよさそうですね。8'b0000_0001を16進数の0x01に書き換えれば、

(cg1 >> 4) & 0x01

これでよし。あとはこれをcg0の1桁目に入れてあげるだけ。

それにはこの2つをorで計算すればよいです。orの場合は1とorを取ると相手が何であっても結果は1に、0の場合は相手が1なら1、0なら0で相手のbitがそのまま残ります。 今、cg0の1桁目は0、cg1の1桁目以外は0になっているので、それぞれの欲しかったbitだけが残ってくれるということですね。

ということで、実装例としてはこんな感じになります。cg0やcg1は配列で、長さが8だったのでその分をfor文で処理します。

  for (int i = 0 ; i < 8; i++){
    cg0[i] = (cg0[i] << 1)| (((cg1[i] >> 4) & 0x01);
  }

サンプル表示されていた記号の場合を例に今回のことを図示するとこんな感じになります。イメージ掴めるでしょうか?

f:id:niszet:20210420161038p:plain

さて、これで画面の一番右以外は解決です。一番右はフォントデータから字形を読みだして、それを持ってくる必要があります。…なのですが、これはArduino-misakiUTF16ライブラリの解説になってしまうのでここではざっくりと。やることは上に書いたこととそう変わらないのですが。

フォントデータを持ってくるのはgetFontData()です。これにstatic unsigned char font[8];のように宣言した変数fontを与えるとこの中に字形データが入ります。これの一番左をcg7のb0に入るようにコードを書けば良いわけです。

と、いうことでひとまず一連の説明が終わりです。疲れた…(一度に書くものではないな

いずれ、これらのコード全文を公開すると思いますが(整理中)、中身を理解して、実際に手元で書いてみると色々工夫が出来て楽しいのではと思います。

DDRAMは更新しなくても良い

さて、CGRAMだけを更新するだけでいいの?DDRAMは更新しなくていいの?ということを気にする人がいるかもしれません。 今回の使い方ではDDRAMの更新は不要です。液晶画面の文字座標に対して表示する文字コード自体は変わっていないので、あらためてこの座標にはこの文字コードだよと指定する必要はないのです。文字座標に紐づいた文字コードの字形の入っているCGRAMが書き変わることで、その文字座標の文字も一緒に書き変わる、という仕組みです。

実のところこの更新タイミングをちゃんと制御したいという気持ちがあったのでデータシートも見てみたんですが、関連するパラメータはなさそうです。I2Cだとそこまで制御できないだろうというのもありますが。I2Cの話も追ってかきたいところですね。

液晶ディスプレイの話になりますが、画面の更新、リフレッシュをどのくらいの頻度でやるのか(リフレッシュレート)を制御することが可能で、おそらくそのタイミングで書き換えが出来ていると思うのですが、そのあたりの仕様は書かれていなかったと思います。あったらごめんなさい(教えてね)

と、書いている間に動画をもう一つ上げたので…それも解説しましょうか…。

キャラクタLCDで文字列をスクロールするやつの解説(4回目)

CGRAMの中身の話だよ。

データシートの図をコピペすればよいのではと思うこともあるけども、横着してDS読みに行かない人が出たらどうしようと考えてしまって出来ていないniszetですこんにちは。

さて、このお話も4回目、折り返しです(まだ折り返しか…)

前回の記事

niszet.hatenablog.com

CGRAMはどのように字形を保存しているか

PLCDは文字領域が16x2あるキャラクタ液晶ディスプレイで、各文字は横5dot縦8dotで構成され、CGRAMに外字を登録するということで、今回はCGRAMの中身についての話です。

データシートの17ページ目に書いてあることが全てではあるのですが、ここでは改めてサンプルコードも交えて説明します。

液晶の各ドットのオン/オフによって字形を表現することが出来ます。このオン/オフの状態を数字の1と0にそれぞれ割り当てることでプログラム内で扱うことが出来ます。1と0の状態を表現するデータのことをbit、8bitをまとめたものを1byteと呼んだりしますね。

CGRAMでは行ごとにアドレスを付けていて、上から0,1,2 ... と 7までの8つが与えられています。この行方向にデータをまとめてあげて、それを縦8つ分まとめてあげればCGRAM用のデータの出来上がりです。行単位で1byte、これが8列あって1つも文字分の表示領域の字形データの表現に8byte必要という計算になります。

実際の液晶画面は横に5ドット分しかないので、行方向の1byteのデータのうち上位3bit分は使用されず下位の5bitだけが使われます。この領域にデータを書き込んでも画面の表示に変更はありません。

データを詰め詰めにして5byteにすることも可能でしょうが、このLCDではそうなっていませんね。もちろんそういう実装もありでしょうし、縦方向にすることも考えられますね。でも扱いも煩雑になるので使う側としてはやめてほしいけども。

話がそれたぞ。

Arduinoで(…に限らず、CやC++で)最小単位のデータ型はchar型で1byte、8bitになります。組み込み用のプログラムだと使用できるメモリ量がとても少ないのでデータ量は少しでも減らしておきたいことから、char型をよく使います。今回も丁度良いですね。

サンプルコードではchar cg0[8] ... の8つの配列です。配列の話は…まぁいいか。データの並びを表現するためのものです(割愛)

1点注意としては、このcgなんたらは画面表示のために使いますが、これがCGRAMそのものではないです。CGRAMにこの値を反映するためにはサンプルコードで言うところのplcd_cgram関数で書き込むことが必要です。

さて、そのCGRAMに字形情報を登録する関数、plcd_cgram()ですが今は関数内部実装には立ち入らないことにして、引数(関数に渡しているパラメータ)の意味だけ押さえておきましょう。

定義をみると、

void plcd_cgram(char adr, char *d, int len)

で、それぞれ

  • adr : 文字コードアドレス(外字を登録するDDRAMの文字アドレス。指定できるのは0-7の8つ)
  • *d : char型のポインタ…ここではcg0みたいなデータ列を受け取るもの、までの理解で行きましょう。ポインタの説明は面倒くさいので。
  • len : 配列の長さ。cg0たちはcg0[8]のように全部で8要素入っているので8を与えます。

です。具体的な呼び出し方は

  plcd_cgram(0, cg0, 8);

こんな感じで、文字アドレス0にchar型の配列cg0のデータが送られ、それは要素数が8ですよ、と伝えていますね。この関数を実行するたびに文字アドレス0にcg0で設定したデータが書き込まれます。

では、cg0のデータについて見ていきましょうか。

  char cg0[8] = {0x04, 0x04, 0x04, 0x1f, 0x00, 0x0e, 0x00, 0x04};

0x00みたいな書き方は0xがこれは16進数の表記だよという接頭辞で、うしろの2文字が16進数で2文字で0から255までを表します。10がA、11がB、12がC、13がD、14がE、15がFで0x00-0xFFまでの表現、最大値は16x15+15=255。

配列にデータを与えて初期化する場合は{}で囲います。この辺りはC/C++の文法の話だしいいか。

ここでそれぞれの16進数が10進数で何を表しているか見てもいいんですけど、実際にはこれは液晶の各ドットのON/OFFを便宜的に1と0であらわしたものを束ねているだけなのであまり意味はないです。よって割愛。

逆?に、これらが液晶のドットとどのように対応しているのかを見ましょう。cg0のそれぞれの16進数をbit(2進数)形式であらわすと、それぞれ

8'b0000_0100 8'b0000_0100 8'b0000_0100 8'b0001_1111 8'b0000_0000 8'b0000_1110 8'b0000_0000 8'b0000_0100

となります。頭の8は8bitだよという意味、bはバイナリ(binary、2進数、bitでもいいのかな?)、_は見やすさのための区切りで意味はないです。

0と1で書くとうっすら字形が見える気がしますね?

では実際にお絵かきしてみます。ここでは万能方眼紙であるエクセルを使って描いていますが、エクセルは本来(以下省略)

一応、下に10進数の値も書いてみました。Cって12だから8と4で…1100だな…みたいに計算するために使ってください(?)

cg0の字形は…

f:id:niszet:20210419220713p:plain

サンプルコード下にある写真の左上の図形と一致しましたね!

ということで、CGRAMにどういうデータを入れればどういう字形が表示されるのかがわかりましたね。もちろん、CGRAMにデータを書き込むだけでは液晶画面に表示されないので、サンプルコードのplcd_cgramで字形データを送った後のplcd_data文字コードを所定の座標に書き込むことが必要です。

  plcd_cmd(0x80 | 0); //Set Cursor (0,0)
  for (int i = 0; i < 8; i++) {
    plcd_data(i);
    plcd_data(' ');
  }

このコードでは最初に書き込むためのカーソル位置を0,0に設定し、順に文字コードを0から7まで(8は含まれないので注意)書き込んでいますね。DDRAMに文字を書きこんだらカーソルが1つ後ろにずれるので、これは文字コード0、空白を書き込んで次のループ、文字コード1、空白を書き込んで次のループ…でひとつずつ右に書き込む座標がずれていきます。結果として、CGRAMに登録した図形が1文字おきに表示され、サンプルコード下の写真のように表示されるのでした。

気になる人はCGRAMの他の字形データも写真のようになってるか確認してみましょう。

ということで、CGRAMに字形を書き込んで液晶に表示するまでの話が一通り終わります。

次回、ようやく文字列がスクロールします。長かった…。 察しの良い人はもう気づいている気もしますが…

次の記事

niszet.hatenablog.com

キャラクタLCDで文字列をスクロールするやつの解説(3回目)

ようやくハードウェアの階層まで降りてこれそう。

前回の記事

niszet.hatenablog.com

ある程度背景を説明したので、ようやくハードウェアレベルの話が始められそうです。

が、その前に…。

まえがき(いまさら?)

本シリーズの内容はPLCD1602以外にも当てはまる話も含まれますが、話が発散するので以降はPLCD1602の~みたいな書き方をします。 が、そこは適宜読み替えてください(仕様が共通しているものは他のものにも置き換えられるという話です。しかし、適宜読み替えられる人には別にこの情報必要なくない?というツラミ)

また、用語もPLCD1602/AQM1602Y-RN-GBW/ST7032i固有の名称を使うことがありますが、これも適宜読み替えてください。

あと、それなりにカッチリ書くつもりですがある程度表記ゆれや曖昧な点があるところはご容赦を。 実行の際には自己責任でよろしくお願いしますね(いつものやつ)

文字コードと字形の対応

さて、外部から字形を8つまで登録できる(外字)という話までは書きました。が、そもそもこの液晶ディスプレイはどうやってあらかじめ登録されている文字(プリセットの文字)と外字を区別しているの?という話をしていきます。

実際には、液晶の表示領域の16x2文字のそれぞれに対してどの文字コードを指定するかという観点では両者に差異はなく、「違う文字なのでアドレスが違う」というだけです(後で説明します)

登録した字とプリセットの字は文字コードとして連続した領域にあるので、登録した字を使いたいなという時はそのアドレスを指定すればプリセットの文字列と同じように使えるのです。

実は公開されているサンプルコードにヒント/答えが載っている

書きそびれていましたが、実は(?)製造元のクレイン電子さんのページにはArduino UNOでそのまま動かせるサンプルコードが載っています。

crane-elec.co.jp

コードを実行したときのPLCDの表示例の写真が載っていますが、これの上段は登録された外字が使われていていろんな記号が表示されています。

これらの文字についてはサンプルコード中段のsetup CGRAMで字形を登録して、それを表示しています。

このサンプルコードを実行するとわかりますが、右下の文字がぐるぐると順に変わっていきます。これはloop()の中のfor文の処理で登録されている字形を順に表示しています。

これが、先の「プリセットの文字と外字については文字の表示について扱い上の差異はない」ということです。

ところでこのサンプルコードですが、MITでライセンス付与されているので使いまわしがきくのも何気にありがたいですね。

…ということで、これを下地にコードを書いていくと良いのではないかと思います。ただし、データシートはちゃんと読んで各コードの意味が何かは把握しておきましょう(自戒)

え、それも書けばいい…?じゃあいずれ書きます…。

各ROM/RAMについて

さて、サンプルコードは「同じ座標で表示する文字を置き換える」ことは出来ますが、これだと動画のように1dotずつ横に文字をスクロールすることはできませんね。

しかし、次のようにやれば文字を1文字ずつ横にシフトしていくのは可能そうです。

  1. 文字座標を指定(サンプルコードの//Set Cursorの部分)して文字を書く(plcd_data()
  2. 同じ文字を文字座標を左にずらして再度文字を書く

これを繰り返せば、1文字単位ですが文字がスクロールして見えるはずです。これらの関数についてはI2Cとは何ぞやという話が必要なので、これもまたいずれ書きますか…。

では、動画のように1dotずつシフトするにはどうすればよいでしょうか?そのためには外字として登録している字形情報がPLCD(正確にはコントローラ)内でどのように扱われているのかを知る必要があります。

ようやく、ハードウェアの話っぽくなってきました(ワクワク

と、その前に写真を一枚。これがPLCDの実物で、すべてのdotを■に表示するようにしています。2段構成で横に16字分の領域、各字の間には1dotくらいの領域があり(上下はもうちょっと広い)、各字の領域は横に5dot、縦に8dotの領域があります。文字単位といった場合はこの5x8dotの領域1つ、列といった場合は2つに分かれた上下のそれぞれの単位、を指します。あとは雰囲気で…。

f:id:niszet:20210419183432j:plain

CGROM

ST7032のデータシートをご覧ください(いきなりセミナー形式かな?) なお、データシートは秋月の液晶のページからダウンロードできます。

このページにはブロック図(Block Diagram)が載っていますが、この中にCGROMとうブロックが見つかります。

ブロック図というのはこの場合、コントローラチップ(ST7032)の中の機能毎にまとまった部分をブロック(□)であらわし、データのやり取りがどの機能ブロック間で行われているかということを図示しているものです。といっても見てもよくわからんな…。

CGROMについてはp.16に字を生成するROMであることが記されています。ROMとはRead Only Memoryの略で読み取りのみの記憶領域なので、ここに登録された文字の字形は変更できません。

もう一つ、ブロック図には似た名称のCGRAMというのがあります。

CGRAM

これがつまるところ外字登録領域です。ユーザが作った字形はCGRAMというRAM(書き換え可能なメモリ)領域に書き込まれます。 これはCGROMの領域を一部間借りして使っているため、元々そこのCGROMのアドレスに登録されている字は使えません(代わりにCGRAMで作った字形が使われます)

このCGROMとCGRAMの領域についてはOPR1,2の設定に依存します(データシートのAppendix. のTable A2)が、秋月電子通商 AQM1602Y-RN-GBWのFAQを見ると、

OPR1=0、OPR2=0固定で出荷されており変更できません。

との記載があります。

akizukidenshi.com

データシートのp.57などを見るとこれらは端子としては出ているようで、それを秋月の方でGNDにつないでいるということでしょうから、他の型番の液晶を使う場合はこの設定にも注意しましょう。

DDRAM

さて、CGRAMとCGROMはひとまとまりになっているので、文字を指定する場合は文字コードにてそれらを区別することなく連続的に指定できます(PLCD1602のサンプルコード、右下で文字が変化する部分もそうなっていますね)

液晶の各文字領域に対してどの字形を表示するかを指定にするには、

  1. どの文字座標か?
  2. どの文字コードか?

をそれぞれ指定する必要があります。

画面の文字座標それぞれに対して、文字コードを保持するDisplay data RAM(DDRAM)のアドレスを紐づけることで、ある画面の座標の文字を更新したければ対応するDDRAMが持っている文字コードを変更すればよいわけで、実際にとあるDDRAMを更新すれば、それに対応した画面の文字座標の文字図形が更新されます。

画面上の文字を置き換える場合はその文字座標を指定しますが、これはアドレスカウンタ(AC)が持ちます。ACは文字を書きこむと1つ後ろにずれます。サンプルコードのfor文中で文字書き込みの前にカーソル位置を設定していますが、その座標にデータを書き込むとカーソルが隣に移動してしまうので、元の座標を指定しなおす必要があったわけですね。

余談ですが、コントローラはカーソル位置を点滅させる機能もあります(動画では非表示にしています)

画面の文字座標とDDRAMのアドレスの対応関係はデータシートのp.14以降にありますが、左上がアドレス0となっています。

さらに余談ですが、液晶自体の表示領域は2x16文字が最大ですが、コントローラとしてはもっと広い領域までもつことが出来るようで最大で80文字分をカバーします。ただしモードによってどの文字座標がどのアドレスコードになるかは変わりますので、実際に使用するモードでどのアドレスコードとなるかはデータシートを確認しましょう。これもまたサンプルコードをより詳細に説明する際にやりましょうか…。

ICON

ちなみに、ICONという領域もありますが、これは液晶画面に既にアイコン形状の表示領域があるタイプにのみ使用でき、PLCD1602にはその表示領域がない(16x2の文字表示領域しかない)のでここの値をいじっても何も起こりません(この領域は他に使えないのかと試行錯誤して無理だったので多分使いまわしできない…モッタイナイなぁ)

これの使用例は"ST7032i ICON" で検索エンジンで検索するとでてきますね。

また長くなってきたので今回はこの辺で。次回はようやく文字を1dotずつ移動させる話です。

次の記事

niszet.hatenablog.com

キャラクタLCDで文字列をスクロールするやつの解説(2回目)

フォント/字形周りの話です。

前回の記事

niszet.hatenablog.com

HWの話を先に書こうと思ったのですが、ちょっと書き出したら深みにはまって初見だと難しい話になってきたので、先にフォント/字形周りの話をします。HWの話も次回以降に。

とりあえず難しいことはさておいて、キャラクタ液晶というのは基本的にROM(読み取り専用の領域)に書かれている文字を使い、もしそこにないのであればRAM領域に字形データを登録してそれを使うということでした。

実際に文字として認識されるdotの並び、つまり字形(フォント)を全部自作しても良いのですが、それでは大変です。

なので、今回は公開されているフォントとArduinoで使用できるライブラリがないかを探し、それぞれ見つけたのでほぼそのまま利用しています。

使用したフォントとライブラリについて

フォントの話も深みにはまりそうなので、ここでは単にその字形データを扱いたいなという話に留めましょう。

フォントデータについて、個人の趣味で使うだけならあまり気にしなくても良いかもしれませんが、ライセンスまわり(利用条件)が結構ややこしいのでフリーで使えるもので、出来れば字形が高さ8bot分で収まっているもの(PLCDの液晶の文字表示領域は幅5高さ8です)がないかを探しました。

この条件に該当するのが美咲フォントでした。

littlelimit.net

私は名前は聞いたことがあるレベルでしたが、界隈?ではポケコン(ポケットコンピュータ)向けのフォントとして有名らしいですね。 なお、8x8の美咲フォント以外にもバリエーションがあり、表示領域の条件によっては他のフォントが選択肢に入るかもしれませんので見ておくと良いでしょう。今回は高さ8で良いので美咲フォントを選択します。

このフォントデータからArduino上で扱うために字形情報を取り出せば良いわけですが、これについては既にTamakichiさんのArduino向けの実装がありましたので今回はこちらを使わせていただいています。

github.com

文字データについては文字コードに対していくつか実装パタンがあるので、詳しくはGitHubリポジトリやTamakichiさんのブログを読みましょう。

nuneno.cocolog-nifty.com

注意点としては全ての字形が登録されているわけではないので所望の文字が出ない場合があります(トーフになるとのことだけど手元ではなっていない)JISの字形全部入れるとArduinoには入れられないので、他のコトもするとなるとバランスが取れている実装だなと思います。

美咲フォントは1文字あたり8x8領域を使用しますが、実際には縦横外周1行1列が真っ白なので連続して文字を表示したときにくっつかないようになっています。Tamakichiさんの実装では上1行分のデータを取り除いて読み出し時に補完しています。8x8 bitなので、ちょうどchar型で収まるというのも取り回し的には便利ですね。

が、PLCD1602は文字領域の横幅が5dotしかありません。そのため、この文字データを1文字が1文字の表示領域にそのまま送り出すと3bit分表示が出来ません。これについてはST7032iがどのように字形データを取り扱っているか?という話になるので、次回に回します。

ここではとりあえず、丁度良いフォントデータとそれをArduinoで読むライブラリがあり、入力した文字に対して対応する字形データを出すことができそうだぞ、というところまでわかればよいでしょう。

なお一応書いておきますが、今回の私の実装に関連する問い合わせについてはそれぞれの作者様ではなく私にお願いします。

余談:文字コードのツラミ

ArduinoではUTF8で文字列を扱いますが、なんと日本語の処理についてはArduinoのString等ではうまく扱えません(文字列長とか) また、文字データも連番で入っているわけではないので、ここからここまでの文字コードで対応しているよ~という処理で弾くことも出来ません。上記のライブラリは登録済みデータかどうかを文字のコードがindexに入っているかというチェックをしているため、字形データ+登録済み文字コードのデータ領域が使用されます。 手元で少しずつチェック用のコードを書いていますが、とても面倒くさい…。既に実装があれば使いたいなぁ…というお気持ち(お気持ち表明文)

ようやく概略が終わったので、次こそはHWの中の話が出来る・・・はず。

しかし、話の内容が文字のスクロールに一向に近づいている気がしないぞ…?

次の記事

niszet.hatenablog.com