Blog2Viewer

以前、Movable Typeで運用していたブログの内容を表示させるために作成したページです。
esp8266 ArduinoのPROGMEM
160903a0HSES-LCD24での日本語表示を試みる際に、PROGMEMを調べた。

ArduinoのPROGMEMとは、データをROM領域に格納する仕組み。(reference,日本語)

C(C++)言語だとconst修飾子を付ければ、十分な気がするが、AVR等の小型のCPUだと、そうはならない。命令用のROMとデータ用のRAMのアクセス経路を分離し、回路を節約しているためだ。ATmegaとかだとROMのデータを読み込むには、専用の機械語があった気がする。

このためCのプログラムでは、起動前の初期化部(crt.Sとか)でROMにある変数の初期値を一旦RAMにコピーし、その後のプログラムでは全てRAM部をアクセスして処理を行う。これはconst宣言してあっても同じだし、"foo"などのような文字列定数であっても同じ。

RAMの容量が足りていれば問題無いが、RAMの使用を節約したい時や、大きなデータを扱いたい時は問題になる。PROGMEMおよび関連のマクロ、関数を使用するとRAMを消費せず、データをプログラムに格納できるようになる。具体的には、ROMに格納されているデータはpgm_read_byte()等でアクセスするようになるのだが、Arduinoでは、多くのライブラリー関数に ROM上のデータ(多くは文字列)用のインターフェースが準備されているので、若干のマクロをかませる程度で使用できる。

でも、こんなのは、小さなCPUを使っているからで、ESP8266ならconstで十分ではなかろうか、と思っていたのだが甘かった。



まず fontxのデータを .rodataセクションに埋め込んでみる。リンク時にエラー。RAM領域が足りないそうだ。そこでPROGMEMの定義を探す。

./hardware/esp8266/2.3.0/cores/esp8266/pgmspace.h:

[cpp]
#define PROGMEM ICACHE_RODATA_ATTR
[/cpp]

./hardware/esp8266/2.3.0/tools/sdk/include/c_types.h:
[cpp]
#define ICACHE_RODATA_ATTR __attribute__((section(".irom.text")))
[/cpp]

埋込先を .irom.textに変更し、リンクできた。プログラムを実行させると例外が発生。いつものWatchDogTimerかと思ったがそうではない。いろいろ試すと、ROMデータをアクセスするところで発生している。

どうすりゃ良いの?と、いろいろ調べて以下の行を発見。

./hardware/esp8266/2.3.0/cores/esp8266/pgmspace.h

[cpp]
// flash memory must be read using 32 bit aligned addresses else a processor
// exception will be triggered
// order within the 32 bit values are
// --------------
// b3, b2, b1, b0
// w1, w0
[/cpp]

flashメモリーは32bit-alignでないアクセスでないと例外が発生する!? なるほど、そこをケチったか。だったら例外のハンドラを準備すれば対応できそう..とも思うが大変そうなので、pgm_read_byte()を使って、漢字をLCDに表示できるようになった。

プログラムは、整理して、公開したいと思う。うまくAdafruit_GFXに統合できるか?
Adafruit_ILI9341に漢字表示機能追加
160907a0Adafruit_ILI9341クラスを拡張し、fontxの描画機能を組み込み、漢字を表示できるようになった。クラス名はHumblesoft_ILI9341でGitHubで公開している。zipでダウンロードし、Arduino で".ZIP形式のライブラリをインストール"すれば使えるようになるはず。



サンプルとして同梱しているのは、文市(あやち)さんがblogで公開されているILフォント、IPAフォントをfontx化したもの。小さいフォントが多いfontxには珍しく、16,24,32dotのフォントが提供されているのがありがたい。今回は16dotのフォントを使用したが、フラッシュの空き容量からすると24dotぐらいまでは使用できそう。

fontxの使用は、通常の文字描画に使用するprint(),println()メソッドで行う。Printクラスのvirtual methodであるwrite()を拡張し、fontxのフォントが設定されていなければ、親クラスのwrite()を呼び出し、設定されていると自前でutf-8のデコード、unicode-shiftjis変換を行い、fontxの文字の描画を行う。C++だと、こんな感じで機能の拡張を行えて便利だ...と久しぶりに思った。

写真はサンプルプログラムkanjiDisp.inoの実行後の状態。ソースプログラムは、こんな感じ。

[cpp]
#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"
#include "Humblesoft_ILI9341.h"

IMPORT_BIN("fontx/ILGH16XB.FNT", font_h);
IMPORT_BIN("fontx/ILGZ16XB.FNT", font_z);

extern uint8_t font_h[], font_z[];

Humblesoft_ILI9341 tft = Humblesoft_ILI9341();

void setup() {
delay(100);
tft.begin();
tft.setFontx(font_h,font_z);
tft.setRotation(3);
tft.fillScreen(ILI9341_BLACK);

tft.setTextSize(1);
tft.print("Humblesoft_ILI9341 漢字表示デモ\nABCDEFGHIJKLMNOPQRSTUVWXYZ\n");
tft.print("\n");
tft.print("このプログラムはILフォントを使用しています。");
tft.print("ILフォントはIPAフォントを元に作成されたfontx形式のフォントです。\n");

tft.setTextSize(4);
tft.setTextColor(ILI9341_GREEN);
tft.setCursor(0, tft.getCursorY() + 10);
tft.print("Hello世界\n");
tft.setCursor(80, tft.getCursorY() + 10);
tft.setTextSize(3);
tft.setTextColor(ILI9341_YELLOW);
tft.print("ILI9341\n");
}

void loop() {
}
[/cpp]

まぁまぁ上手く実装できたのではなかろうか。

あと、NetLCDも対応済。コードを送るだけで漢字を描画できる。

ILI9341にはあと少し拡張したい機能がある。タッチパネルもなんとかしたい。ほかにもやりたいことがいろいろある。さぁ何をしよう。
生活に小型ディスプレーを!!
160810a6スイッチサイエンスからHSES-LCD24-FRAMEが発売された。これは、HSES-LCD24を100円ショップのフォトフレームに固定するためのアクリル板とネジとスペーサのセット。これがあれば、WiFi接続小型ディスプレーHSES-LCD24を生活空間に自然に置くことができる。見やすい角度に設置するのに苦労したり、USBケーブルに振り回されたりすることも無くなる ... はず。

このフレームの発売はHSES-LCD24自体の企画当初から予定していたのだが、スイッチサイエンスへの委託販売(マーケットプレイス出品)が初めてだったので、まず本体を発売してからと思っていたら、梱包上の問題が発生するなどして遅くなった。



160804d8100円ショップのフォトフレームが使えると言っているが、どれでも使えるわけではないので注意が必要だ。むしろ使えるフォトフレームは少数派というべきかもしれない。最もメジャーなタイプは、裏板から足が生えている。このタイプだと取り付けは可能だが、足がなくなってしまうので、立たない。


160825a0一番お勧めのフォトフレームは、右の写真のもの。きれいに収まるだけでなく、フレーム自体を固定することも簡単そうだ。これが、ずっと入手できると良いのだが... 今回の商品に限らず、表示系の電子回路では重宝しそうなので、買えるうちにどこかの店で確保してくれないだろうか。


160804d2このタイプのフォトフレームしか使えないと、商品の発売が不安だと思っていたら、別の使えるフォトフレームも発見。枠の裏に穴が開いていて、そこに木の棒を刺して足にするタイプ。これだと、割と、どの店でも見かけるし、縦にも使える。

レーザ加工機等でアクリル板を加工できる環境の人は、自分で作っても良いかもしれない。基板の寸法は ここで公開しているので、データは簡単に作れると思う。しかし、ネジ、スペーサ類を自分で揃えると、そこに費用がかかるかも。黒色の超低頭ネジ(頭部0.8mm厚)とか入手が面倒かも、まぁMisumiで買えるんですけど。


160819a4最後の写真は、クリア(透明)アクリルを使った試作品。個人的には、これが気に入っているが、「一般人は基板を見せても喜ばない、喜ぶのはマニアだけ」と言われたことを思い出し、製品は艶消しの黒にした。穴に若干の余裕を持たせてあるので、隙間やタッチパネルのケーブルが目立つ。気になる人は黒色の紙などでマスクして欲しい。希望が多ければ、クリア版の発売も検討したい。

WiFiが自由に使える ESP8266 Arduinoは、なかなか楽しい。ESP8266 Arduinoの入門機としてもHSES-LCD24をお勧めする。
Seeed Grove System
160922a0I2Cのデバイス、基板を扱うことが増えて来た。共通のインターフェース、コネクタが欲しいけど4Pinで有極の使いやすいコネクタはないものかと探していて、SeeedのGroveシステム用のコネクタ(これとかこれ)に行きついた。ケーブル類も安価で売っているし、I2Cデバイスもいろいろあるので、それらを使うこともできる。

自作でいろいろ使う前に、本物を一度使ってみようと、GROVEシールドと温湿度センサを買ってみた。

シールドを見ると、Grove Systemがどういうものなのかよくわかる気がする。GNDと電源とマイコンの信号線2本。信号線はデジタルI/Oか、ADのアナログ入力か、I2Cかの3種類。電源は3.3Vか5V。良くできたシステムだと思う。

あと、Grove Wrapperというものがあって、小さい基板をうまく固定することができる。これは大発明だと思うのだが、スイッチサイエンスでは、何故かまだ扱っていない。早く売って欲しい。まぁ私はSeeedから買ったので良いのですが。
HSES-LCD24のタッチスクリーンを使う


タッチスクリーンを使おうとすると意外に大変だ。HSES-LCD24のLCDモジュールではXPT2046というチップ経由でタッチスクリーンのデータを読み込む。XPT2046のライブラリは複数あり、その点では苦労しないのだが、その次にcalibrationの問題がある。自分ひとりで使う、使い捨てみたいなプログラムであれば、実測値を使い、定数をプログラムに埋め込んで解決だが、人に使いやすいライブラリを提供しようと思うと、それではまずい。LCDの座標系を90度づつ、4方向に設定可能かだから、calibrationも、それに対応する必要がある。毎回、calibrationするのは面倒なので、結果は不揮発性メモリに保存しておきたい。さらに、ちょっと見栄えがするサンプルプログラムを付けようとすると、ボタンやメニューも扱える簡易ウィンドウシステム的なものも欲しくなる。



そこまで作ると、いつ公開できるかわからなので、calibration機能と、そのデータのload/saveができるライブラリを作成し、公開する。名前はLcdTouchscreenでgithubで公開している。データはArduinoのEEPROMライブラリで保存してるが、そのままでは使いづらいので、名前ベースで文字列のsave/loadをできるようにしたライブラリParamsを作成、これもgithubで公開した。

動画は、サンプルプログラムの動作の様子である。このプログラムは、起動時にcalibrationのデータがEEPROMになければ、calibrationを起動する。あれば、calibrationは行わず、タッチされた座標とカーソルの表示を行う。データが既にあってもcalibrationをしたい時のために、起動時に、タッチパネルを押していれば、calibrationを起動する機能も準備した。

calibrationのやり方は、結構、頑張った。LCD上の目標のx,y座標とタッチスクリーンの読出値x',y'の4点分のでデータで、xとx', xとy', yとx', yとy'の4通りの直線回帰を求め、相関係数の値からどちらの座標と対応しているのかを判断している。これでrotationの値によらず直接calibrationの値を決定することができた。また、calibrationでデタラメな場所をクリックした場合、相関係数の値を見て排除できる。

タッチスクリーンを活用した、もっと見栄えがするプログラムを準備したい。それには、自分が普段使いできるプログラムを作って、使いながら完成度を上げていくのがいいのだろうなぁ。となると、時計+温度計になるなぁ。少しづつ、作っていこう。