● 実験テーマ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へ →