● 実験テーマ97
「PIC24F_OLEDの表示実験」
(PIC24Fで、モノクロOLED(有機EL表示器)の表示実験をしてみました。)
以下、この実験の顛末記です。
■ 2017.10.17
・WEB上を検索しても、OLEDを使った電子工作の参考資料は少ないが(有っても、Arduinoを使った例が殆ど)
OLED(有機EL表示器)にチャレンジしてみようと思う。
フルカラーの物は高価(3000〜5000円位)なので、ブレイク・アウト基板付の、単色〜2色表示の、128x64dot
の物(1000円前後)にしようと思う。
OLEDの1つのネックは、ロジック用VCCとは別に、パネル表示用のVDD=9V〜が必要と思っていたが、
チャージポンプ内蔵型を選べば、3.3V〜5V単一電源で動くようだ。
・I2C制御か、SPI制御か迷っていたが、スピードの点では、シンプルなプロトコルの、SPIの方が有利なので、
SPI制御の可能な、ブレイク・アウト基板付きの物にしようと思う。
この手のコントローラは、SSD1306が一般的なようだ。
いろいろ調べている内に、Amazonで購入可能な、HiLetgo社製の、0.96"I2C/SPIシリアル128x64OLED
が、I2C/SPIを選べて、コントローラもSSD1306、価格もこの時点で、690円と安価でよさそうである。
発光色については、ブルー単色・ホワイト単色の他、ブルーイエロー2色の、3種類があったが、無難なホワイト
に決めて注文を済ませた。(10月26日現在は、単色のものは売り切れで、ブルーイエロー2色のものしか在庫がないようです。)
■ 2017.10.18
・まず水魚堂で、実験回路図を作成。
そうこうしている内に、早いもので、もうAmazonから荷物(日本郵便ゆうパケット)が届いた。
・とりあえず、OLEDの初期化の部分を、SSD1306のデータシートを見ながら、設定データをSPI送信する
ルーチンを検討・作成。
それに続いて→ 全画面クリア+スタートメッセージ表示まででSTOPして確認する。
Locate関数は未だ組み込まない。(理解不足もあるが・・・)
Fcy=4MHz(Fosc=8MHz FRC)で様子を」見る。後日、最高速16Mzで確認予定。
・ソースでっち上げた。hex書込みok
何と一発で、初期メッセージが出たが、全画面クリアがおかしい。
最初の、page0-col0〜127の、縦128バイト分、つまり最上段の部分はクリアされるが
それ以降は全てゴミが表示される。
たぶん、アドレス設定関係の問題と思われるが、調査は明日以降に持ち越し。
■ 2017.10.19
・昨日の問題は解決した。
Set Memory Addressing Modeレジスタ(20h)の設定が、デフォルト(RESET時)の、A[1:0]=10bの、
Page Addressing Modeになっていた。
それで、PAGE0エリアしかクリアされてなかった。
これを、A[1:0]=00bに変更し、Horizontal Addressing Modeにし、OKとなる。
・次に、Locate関数を検討する。
x座標設定は、Set Column Address レジスタ:0X21を指定し
これに続いて、Column start address(0〜127)をセットすれば良いと思われる。
→ これでは駄目だった。
さらに続けて、end address(固定値=127)をセットでOKになる。
y座標設定は、Set Page Address レジスタ:0X22を指定し
これに続いて、Column start address(0〜127)をセットすれば良いと思われる。
→ これでは駄目だった。
さらに続けて、end address(固定値=7)をセットでOKになる。
・ASCIIテーブルの文字(全192文字)表示テストをやってみた。
最初の1画面:168文字分(横21文字x8行)は、OKだが、次の画面:残り24文字(横21文字x1行+次の行3文字)はNG
■ 2017.10.20
・昨日の問題、調査解決した。
全面クリア関数を別に作ることにした。
現在の、OLED_clear()は、元々ノキア液晶の時の、岩本さん作成のもの。
現在の書込み位置からの指定バイト数分のクリア関数なので、全クリアするためには、OLED_locate(0.0);としてから
OLED_clear(128*64);とする必要があった。(現象は全画面クリアになっていない症状だった)
ところで全画面クリアは頻繁に使うので、1行で書けるように、別関数にした方が使い勝手が良いと考え、
OLED_clear_all()という関数を作ることにした。
これで上手く行く。
とりあえずは、全192文字表示までは、OKになる。
・これからが難題。
ドット描画関数を、まじめに考えないといけない。
その前に、ノキア液晶を使ったグラフィック温度計の、デモ画面(84*48dot)をそのまま、OLED(128*64dot)に表示してみた。
表示エリア等、デファインされているマクロのところを、ノキア液晶のパラメータに数値直で置換え、OKになる。
■ 2017.10.21
・SSD1306で、グラフック表示(dot表示)するのは、そう簡単ではない。
このコントローラは、表示RAM(GDDRAM)に書込むことは出来ても、その内容(過去のdot描画位置)を読出すことは
できない。
なので、GDDRAM上で、過去のdot描画状態に、新しいdotデータを、ORしての描画は出来ない。
つまり上書きされてしまう。
それを回避する方法として、リードも可能なPICの、SRAM上に、表示域と同じ、2次元配列、例えば、
SegmentoBffer[128][8];
(セグメント・バッファ或いは、フレーム・バッファとも言うらしい。)
として用意し、これを表示RAMに見立てて、まずは、ここにOLDデータ or
NEWデータの、OR書込みを行い、
その後、その結果をGDDRAMに転送する方法がある。
これに関しては、このサイトが参考になりました。(OLEDに関する記事が多く掲載されている。)→ 「mgo-tec電子工作」さん
セグメント・バッファに、1024byte取られることになるが、PIC24FJ64GA002
のSRAM容量=8192byteあるので
余裕である。
■ 2017.10.22
・今日から、グラフィック描画にチャレンジ。
最終目標を、オーディオFFTアナライザ(帯域分割レベル表示)としているので、
ドット描画関数・水平線描画関数・垂直線描画関数・汎用的な直線描画関数があれば実現できる。
なのでとりあえず、水平線描画関数から検討し試してみた。→ 結果上手く行く。
昨日も書いたが、グラフィック描画に関しては、表示エリア(128*64dot)と同じ、SRAMエリア(1024byte)
の、セグメント・バッファ[128][8]を用意し、これにor描画してから、それをGDDRAM(表示RAM)へ転送する方式を使う。
原典先は、mgo-tec電子工作」さん→ ESP-WROOM使用(AVRマイコン系/I2C)で記述してるので、これを、
PIC・SPIに移植する形で作業を進めた。
■ 2017.10.23
・今日は水平線描画関数から
@ 左右隅に水平線2本描画
A フルサイズ四角形描画
B 次第に小さくなる四角形描画
はokになる。
使った関数は、以下を参照のこと。
尚、水平・垂直線描画では、Draw_Pixel関数は使ってない。
※ SRAM上に配置したSegmentBuffer[128][8]にOR描画を行ってから、その書込みパターンを
OLEDのGDDRAM(表示RAM)に、
OLED_set_data(&SegmentBuffer[x0][page], 1);という記述により転送し表示させている。
//// 水平線描画関数を試す。 171022
Draw_Horizontal_Line(0,0,127,WHITE); // 最上段0-127(y=0)に水平線描画
Draw_Horizontal_Line(0,63,127,WHITE); // 最下段0-127(y=63)に水平線描画
delay_ms(5000);
OLED_clear_all(); // 全画面クリア
//// 垂直線描画関数を試す。 171023
Draw_Vertical_Line(0,0,63,WHITE); // 左端0-63(x=0)に垂直線描画
Draw_Vertical_Line(127,0,63,WHITE); // 右端0-63(x=127)に垂直線描画
delay_ms(5000);
OLED_clear_all(); // 全画面クリア
//// 四角形描画関数を試す。 171023
Draw_Rectangle_Line(0,0,127,63,WHITE); // FULL SIZE 四角形
delay_ms(5000);
OLED_clear_all(); // 全画面クリア
//// 次第に小さな四角形描画 171023
for(i=0; i<32; i+=2){
Draw_Rectangle_Line(x0+i,y0+i,x1-i,y1-i,WHITE); // 最初は、FULL SIZE 次第に小さな四角形描画
delay_ms(500);
}
・次は斜め線描画。
AVRの例だと、floatを使っているのが気になるし、AVR特有?の丸め関数:round()も使っている。
これは、picの標準ライブラリには無い。
y/xで傾きを求めて、+傾斜なのか、-傾斜なのか判断するアルゴリズムは後閑さん作成のLine文が
使えそうなので、それでやってみることにした。
ただ、dot描画関数は、AVRのものをPICに移植する形で進めようと思う。
・斜め線表示結果であるが、何故か、斜め線を dot描画関数で描画しようとすると本来のラインの周辺に
ゴミが表示されてしまう。
これには見覚えがある。
PIC32MXで初めてGLCDをつかった時と同じ現象。
この時は、ハード的に表示RAMの内容をリード出来たが、これをLATで書いていた為
ラッチポートセットの値がそのまま読み出されしまいゴミとなっていた。
PORTで読出しで解決していた。
■ 2017.10.24
・昨日の症状、一気に斜め線描画だと何が起きているか解りにくいので、x0=0,
y0=63の位置(左下隅)に
1dotだけ描画させてみた。
上は、その実行結果を拡大鏡で見たところを写真に撮ったものである。
一番下の位置に、1dotだけ描画するはずが、8dot描画されている。
過去の、SegmentBuffer[0][7](7はpage7のこと)の内容を読取り表示させたら、255とクリアされてない。
・この件いろいろ調査の結果、何とか解決できた。
ここで、やった事(解った事)の整理をしてみた。
@ SRAM上の2次元配列の全エリアクリアの記述について
メインの頭でしか省略形の、unsigned char
SegmentBuffer[128]8]= {};(又は{0};)は使えない。
A PICの場合この記述をしなくても、RESET後は、0x00にオールクリアされるので書く必要はない。
B メインモジュールの頭で、unsigned char
SegmentBuffer[128]8]; と宣言しただけでは、他のOLEDライブラリ・モジュール
まではリンクしない。スコープ外になる。
その場合は、OLEDライブラリ・モジュールの頭で、この変数を外のモジュールでも使う宣言が必要。
extern unsigned char SegmentBuffer[128][8];
とすることで、外のモジュールでも使える。
C 斜め線で周囲にゴミが出ていたのは、直前の四角形描画中に書いた、SegmentBufferの内容が残っていてそれに
NEWデータを、ORしたから。
ただし、dot関数を使わない水平・垂直線(四角形描画はこれを使っている)描画の時はこの影響を受けない。
D ゴミの対策としては、OLED_clear_all関数の中に、GDDRAMオールクリアの他、SRAM(SegmentBuffer)オールクリア
を追加。但し、ライブラリのそれぞれの関数の中では、省略形の、unsigned
char SegmentBuffer[128]8]= {};は
使えない。
for(i=0; i<128; i++){
for(j=0; j<8; j++){
SegmentBuffer[i][j] = clear_data;
}
}
と明示的に書く必要がある。
これで一番下の位置に、1dotだけ描画することが出来た。
過去の、SegmentBuffer[0][7](7はpage7のこと)の内容も、0となっている。
E 白点を打つ時のSRAMに対するNEW書込みデータの記述で、
b = 0x01 << (y0 % 8);
を見た時、y0 %
8は、そのページ(バンク)内での、y座標に対応する、dot位置を示すことは理解できたが、
(y0 %
8)の結果を、左へ1bitシフトするものだと思い込んでしまった。
冷静に考えるとそうではなかった。
<<
は確かにシフト演算子ではあるが、書式は例えば、X <<
n であり、右辺がシフト数になる。
つまり、0x01を、y0 %
8だけ左にシフトすることになる。
例えば、y0=63の時、y0 % 8= 7なので、1 << 7
となり、1を7bit左へシフトすることになる。
そうすると、ページの上のbitが、b0に相当するので、ここの1を7bit左へシフト(縦方向だと下にシフト)
するので、ページの最下位:bit7に移動することになり、そこが点灯するという理屈だ。
・以下に斜め線描画が上手く行った時の写真をアップしました。
■ 2017.10.25
・円描画をやってみた。
今迄他のプロジェクトで使ってきた、中心点と半径を指定した、Fussyさんのアルゴリズムが、
ほぼそのまま使え描画することができた。
次に、全画面クリアであるが、クリア色(黒 or
白)を、指定できるようにし、白抜き描画も出来る様にした。
また、今後予定の、このOLEDを使った、小型簡易FFTアナライザの、グラフ軸・目盛描画デモも追加した。
・最後に、Fcy= 4MHzから、Fcy= 16MHzに高速化し、両者のVCC=5V入力での消費電流を確認してみた。
Fcy= 4MHz→ 約40mA, Fcy= 16MHz→ 約50mA
だった。
このOLEDは、VCC=3.3Vでも動作可能なようなので試してみた。→ OKだった。
ちなみに、両者のVCC=3.3V入力での消費電流は、以下のように、VCC=5V入力時より、10mAほど少なくてgood。
Fcy= 4MHz→ 約30mA, Fcy= 16MHz→ 約40mA
<感想>
・以下のスペックの通リ、このOLED、0.96インチとかなり小型だが、解像度は、128x64dotあり、
コントラストもクッキリして白が見易くて良い。
また描画の応答も早いように感じた。
消費電流も、バックライトを必要とする液晶に比較し少ないのも魅力である。
<回路図>
・こちらから、どうぞ→ 「PIC24F_OLED実験」
<最終ソース及びヘッダファイル>
・こちらから、どうぞ→ PIC24F_OLED_TEST.c
///
OLEDライブラリ
SSD1306_OLEDlibPIC24F.c
SSD1306_OLEDlibPIC24F.h
///
アスキーフォント
ASCII_font.h
← 実験テーマ1に戻る TOP PAGEに戻る 実験テーマ98へ →