Arduino初心者編:液晶ディスプレイ、有機ELディスプレイ、アドレッサブルLEDをICで制御

Arduino初心者編:液晶ディスプレイ、有機ELディスプレイ、アドレッサブルLEDをICで制御

これらのデバイス(LCD, OLED, アドレッサブルLED)は、Arduinoで何か表示・表現するときによく使われます。デバイスの出力情報が多い分、入力情報が多くなるため、自分で一から準備すると大変です。そこで、デファクトスタンダードとなっているようなICとライブラリを使用して、簡単に制御する方法を紹介します。

液晶ディスプレイ

私たちは何十年も前から、日常生活の中で液晶ディスプレイ(LCD)に接しています。オリジナルのモノクロデザインで、腕時計やポケット電卓などあらゆるデバイスで誰もが知っています。LEDとは異なり、この素子自体を点灯させるのではなく、ガラス上の偏光フィルターと電場制御の薄い液膜によって光を遮断または透過させることができ、多層ガラス板状の表示装置は、暗い環境でも読みやすいように背景光源(例えばLED)を備えています。

ここでは、16文字のテキストを2行ずつ表示できる標準的なLCDを使用したいと思います。日立が開発したHD44780コントローラは、数十年前からこのような英数字ディスプレイを制御するためのデファクトスタンダードとなっています。

この標準的な液晶ディスプレイは、HD44780を搭載した基板にあらかじめ実装されていて、数多くの信号線が接続しやすいように端子化されています。

しかしデメリットとしては、それを制御するためには最低でも7本のデータピン(プラス電源)が必要になります。やはりArduino経由での制御が可能だとしても、プログラムを少しシンプルにしたい。そのために、液晶基板の裏にHD44780制御用のバックパックをハンダ付けし、I²Cバスを介してArduinoと通信させます。

さらに、店頭で販売されているこのバックパックはLCDのコントラストを調整することができるポテンショメータを持っています。このバックパックを使わなければ、私たちは各々のタスクを実現しなければなりません。

HD44780-LCD用のバックパック
未使用のはんだパッド(緑色のマークが付いている)は、I²Cアドレスに設定のために使用されます。上の3つの銅箔面は各々の入力ピンに接続されており、下の3つの銅箔面は接地電位(GND)を持っています。左側には、非常に小さなサイズの3つの対応するプルアップ抵抗(面実装部品)が見えます。

バックパックの回路基板上には、A0、A1、A2とシルク印刷された3つの未使用のはんだ付けできるパッドが見られます。これらは、基板上のマイクロチップの3つの入力ピンに接続され、I²Cアドレスに設定のために使用されます。

はんだ接合部の左側には、入力ピンをHIGHに保つ3つのプルアップ抵抗がはっきりと見えます。特定のピンをLOWに設定するには、対応する2つのハンダパッド(上の写真ではお互いにHIGHになっている)をハンダでブリッジしてください。そうすると電位はGNDレベル(LOW)になります。この作業をすることで、8つの異なるI²Cアドレスを選択でき、アドレスを衝突させることなく1つのI²Cバスで複数のLCDを使用することができます。

I²CアドレスA0A1A2
32LOWLOWLOW
33HIGHLOWLOW
34LOWHIGHLOW
35HIGHHIGHLOW
36LOWLOWHIGH
37HIGHLOWHIGH
38LOWHIGHHIGH
39 (デフォルト)HIGHHIGHHIGH

I²Cバックパックのアドレス構成デフォルトのアドレスは39ですが、はんだパッドが変更されていない状態では、A0、A1、A2はプルアップ抵抗を介してHIGHに設定されているため、デフォルトのアドレスは39です。

この例では、1つのLCDしか使用していないので、デフォルトのアドレス39を使用することができます。Arduinoでテストしてみましょう。

I²Cバックパックのおかげで、配線が少なく済みます。

バックパックをより簡単に制御するために、”LiquidCrystal_I2C.h”というライブラリをインストールしています。

「LiquidCrystal」ライブラリは、バックパックを介してディスプレイの制御機能を提供します。

次のプログラムでは、最初に挨拶文を5秒かけて、1秒ごとにカウントアップしていく様子が描かれています。最初は何も表示されない場合は、バックパックのポテンショメータをねじ回し・ドライバーで慎重に回して液晶の明るさ・コントラストを変えてみましょう。

#include "Wire.h"     //(1)
#include "LiquidCrystal_I2C.h"    (2)

LiquidCrystal_I2C LCD(39, 16, 2);

void setup()
{
  LCD.init();    //(3)
  LCD.clear();    //(4)
  LCD.backlight();    //(5)
  LCD.setCursor(2, 0);    //(6)
  LCD.print("Hallo, Welt!");    //(7)
  LCD.setCursor(1, 1);
  LCD.print("Hallo, Arduino!");
  delay(5000);
  LCD.clear();    //(8)
}

void loop()
{
  LCD.setCursor(2, 0);
  LCD.print(millis()/1000);    //(9)
  delay(1000);
}
  1. I²Cインタフェースを使用するためのWire.hライブラリはすでにお馴染みですが、LiquidCrystal_ I2C.hライブラリを使用することで、液晶バックパックとの通信に役立ちます。
  2. このライブラリもオブジェクトベースなので、クラスLiquidCrystal_I2C_LCDというオブジェクトを作成します。
    初期化の際、ライブラリは次の3つの引数を必要とします。I²Cアドレス、1行あたりの文字数、行数、この順番で渡せばLCD表示できるようになります。
  3. setup()では、まずLCD.init()でLCDが起動されます。
  4. LCD.clear()で表示内容がクリアされます。
  5. LCD.backlight()でバックライトが点灯します。オフにするには、引数はなしで関数LCD.noBacklight()を使用します。
  6. ディスプレイ上のテキスト出力は、ワープロプログラムと同じようにカーソルで行われます。カーソルは、行と列は同時に配置され、カウントは常に0から始まります。つまり、このプログラムでは1行目(0)の3列目(2)にカーソルを置くことになります。
  7. 実際のテキストの出力は、コンピュータのモニタに出力するための、すでに知られているSerial.print()関数と非常によく似ています。目的のテキストを引数に渡すだけでOKです。
  8. 5秒の一時停止後、再び表示内容が削除されます。
  9. プログラムのメインループでは、常に同じ位置にカーソルを置き、プログラム開始からの経過秒数を表示するようになります。関数 millis() の戻り値は前回のプログラム開始からのミリ秒の整数なので、 1000 で割ったものは経過秒数に相当する。

有機ELディスプレイ

15年ほど前から液晶ディスプレイは、多くの用途で有機ELディスプレイに取って代わられています。これらのディスプレイでは、従来のLEDと同様に、画素自身で光を発生させることができます。この発光機構により、よりシャープなコントラストと優れた読みやすさを実現します。

ここにも、準標準としての地位を確立したコントローラがあります。SSD1306です。コントローラと小型有機ELディスプレイを搭載した非常に安価なプレハブモジュールが市販され、制御はI²Cを介してArduinoと通信されます。以下の例では、幅128ピクセル×高さ64ピクセルのディスプレイを使用しています。メーカーのAdafruitはこのディスプレイの強力なライブラリを提供してます。

有機ELコントローラのライブラリは「Adafruit SSD1306」です。
I²Cバスは2本のデータ線で十分なので、配線は簡単です。A4 は DAT または SDA コネクタに接続し、A5 はクロック信号 CLK または SCL を接続します。

有機ELディスプレイは、バックライトを必要としないため、液晶ディスプレイに比べてコントラストが高いです。

以下のプログラムを使えば、ディスプレイには、額装された挨拶「Hello!」とアニメーション付きの文が表示できます。

#include "Wire.h"    //(1)
#include "Adafruit_SSD1306.h"

Adafruit_SSD1306 display(1);    //(2)

void setup()   {           
  display.begin(SSD1306_SWITCHCAPVCC, 60);    //(3)
}

void loop() {
  display.clearDisplay();    //(4)

  display.fillRect(0, 0, 127, 63, WHITE);    //(5)
  display.fillRect(10, 10, 107, 43, BLACK);    //(6)
  
  display.setTextSize(2);    //(7)
  display.setTextColor(WHITE);    //(8)
  display.setCursor(30,25);    //(9)
  display.print("Hello!");    //(10)
  display.display();    //(11)
  delay(5000);

  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0,20);
  display.println("This is a");    //(12)
  display.println("two-line text."); 
  display.display();
  
  for(byte i = 0; i < 128; i++)    //(13)
  {
    display.drawLine(0,40,  i,40, WHITE);    //(14)
    display.display();
    delay(30);   
  }
}
  1. ここでは、前述したI²CライブラリWire.hと、今回新規登場のAdafruit_SSD1306.hを読み込みます。
  2. ディスプレイ表示させるクラスAdafruit_SSD1306からオブジェクトを作成します。このインスタンスでは、引数に有機ELディスプレイのリセット用ピンを想定しています。I²C経由で接続されているため、このピンは全く使用しません。それにもかかわらず、仮想的に引数を与えられなければなりません。よって今回は任意の第4ピンを使用しています。
  3. 関数begin()でディスプレイを初期化して起動します。最初の引数はタイプで、通常 SSD1306_SWITCHCAPVCC です。(この定数の名前はライブラリによって提供されます) 第2引数はI²Cアドレスです。ここで使用する表示タイプはデフォルトでアドレス60を使用していますが、このアドレスははんだブリッジで修正します。
  4. メインプログラムループで、初めに表示内容が削除されます。
  5. 関数 fillRect() を使用すると塗りつぶされた白い四角形が描画されます。最初の2つの引数は、XとYの位置(それぞれ左上隅の0から始まる)です。次の2つの引数は、ピクセル単位での幅と高さです。最後の引数は、色(ライブラリがすでに定数名を指定している)を表します。今回のようなモノクロディスプレイでは、当然ながら2つの可能性しかありません。黒と白だけです。ここでは白い四角形を描いています。
  6. 白い枠は、白い四角形の中に少し小さめの黒い四角形を覆うように描くことで、作られます。ここでは黒い四角形を描いています。
  7. この行で、フォントサイズ(1は最小フォントを表す)を選択します。
  8. この行で、フォントの色を選択します。
  9. LCDの場合テキストカーソルが設定されますが、このライブラリではピクセル単位で位置が指定されます。ここでは左から31画素目、上から26画素目が指定されています。
  10. テキスト出力は再び print() という名前の関数を使用します。
  11. これまでのコマンドはすべて、表示内容を中間メモリに書き込んだだけです。実際に有機ELディスプレイへ転送するのは関数display()のみです。
  12. 再度削除した後、今度は少し小さめの文字が表示されています。シリアル・インターフェースの使用と同様に、出力後に改行を行うprintln()という関数もあります。
  13. for loopで小さなアニメーションを提供するようになります。
  14. 関数 drawLine() は、最初の 2 つの座標 (x, y) から次の 2 つの座標(x, y)までの間を直線で描画します。第五引数は、またしても色です。この関数はループパスごとに呼び出され、変数iが常に増加するため、行がどんどん長くなっていきます。

ライブラリには他にもたくさんの関数があり、他の多くのライブラリと同様に、独自のプログラム例が付属しているので、簡単に実験できます。ファイル例題Adafruit SSD1306→sd1306_128x64_i2cをクリックするだけです

アドレッサブルLED

アドレッサブルLEDは、データ出力のための興味深いオプションですが、アートプロジェクトのためのものでもあります。さまざまな種類がありますが、今回はホビーの分野でも人気の高いWS2812BのLEDを見ていきましょう。

アドレッサブルLEDの例 – リング、マトリクス、シングルなどの他にも多数のバージョンがあります。

アドレッサブルLEDは個別に、回路基板にあらかじめ取り付けられた格子状の形態や、柔軟な接着剤のストリップとして、円やクリスマスツリーのランタンとしてもさまざまな形態で利用できます。

一見すると、直列接続のようなもので配線されており、制御信号のための配線は1本しかありませんが、個別に制御できます。この点がアドレッサブルLEDの大きな利点です。

アドレッサブルLEDは、必要に応じてカスケード接続が可能です。その仕組みは、シリアルデータ信号(シリアルインターフェースに似ていますが、個々のLEDの通信仕様が若干異なります)が制御ラインを介して送信されることです。緑の色成分の輝度を1バイト(8ビット)、赤の色成分を1バイト、最後に青のLEDの輝度値を1バイトで構成しています。以前紹介したRGB LEDと同じように、0から255までの値から任意の色を混ぜることができます。

WS2812B-LEDには、実際の発光ダイオードの他に小型のマイクロチップがあり、これらの24ビットを受信して動作します。入力端子の他に、次のアドレッサブルLEDにカスケード接続させるための出力端子を備えています。しかし、希望する色値のために上述した3バイトを受信している間は、このデータ通信ではLEDは点灯されません。3バイト(18ビット)以上の通信・ビットが続く場合は、マイクロチップによって無視され、単に転送のみされます。同時に、チップはデータ信号を監視し続けています。50マイクロ秒以上の一時停止がある場合、送信は終了したとみなされ、最後に受信した輝度値がRGB LEDに出力されます。この色は、次の新しい信号を受信するまでラッチ・保持されます。

すべてのアドレッサブルLEDに個別の色を供給するには、各色の値を持つシリアル信号を配線順に直接送信するだけです。

24ビットの送信にかかる時間はわずか30マイクロ秒なので、1秒で約33,000個のLEDにデータを読み込ませることができます。または330個のLEDは、1秒間に100回まで色を変えることができます。LEDの数とフレームレートは互いに制限されていますが、ほとんどの実用的なアプリケーションでは、これらの制限は気にする必要はありません。

それよりも、電源に注意を向けることの方が重要です。RGB LEDはそれぞれ15mA程度の明るさが必要なので、明るく白く表示するためには15mA程度の電流が必要です。

64個のLEDを搭載した8×8マトリックスの場合、使用する電源(たとえばコンピュータのUSBポート)が1アンペアの電流供給できることを確認する必要があります。さらに大きなアレイの場合は、外部電源の使用をお勧めします(。次の例では、64 個のアドレス指定可能な LED を備えたプレハブの 8×8 マトリックスを使用していますが、USB 電源で十分です。

配線は簡単で、どの出力端子もデータ端子として使用できます。

制御にはFastLED.hというライブラリを使用しています。

Daniel Garcia氏によるFastLEDライブラリは、アドレッサブルLEDのための多くの機能を提供しています。

次の例のプログラムは、LEDの上に光の軌跡を描き、徐々に色が変化していきます。

#include "FastLED.h"

#define NUM_LEDS 64    //(1)
#define DATA_PIN 3

CRGB leds[NUM_LEDS];    //(2)
byte Color;
byte Position = 0;

void setup() { 
  FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);     //(3)
}

void loop() { 
  leds[Position++] = CHSV(Color++, 255, 200);    //(4)
  FastLED.show();    //(5)
  delay(50);
  fadeToBlackBy(leds, NUM_LEDS, 20);    //(6)
  if(Position > NUM_LEDS-1)
    Position = 0;
}
  1. 接続されているLEDの総数と使用されている出力端子の数は、読みやすくするためにプリプロセッサの定数として定義しています。この宣言により大きな LED マトリクスを使用した場合など、後からプログラムを適応させることが容易になります。
  2. ライブラリで提供されているオブジェクトデータ型CRGBの配列を作成します。このデータ型は、RGBの色の値を格納できます。配列leds は、各個別の色の値を持つ LED マトリクス全体を表します。
  3. 関数 addLeds() は、3番ピンに WS2812B タイプのリセット可能な LED を使用したいことをライブラリに渡します。丸括弧“(”,“)”内の引数は配列の名前のままになっています。丸括弧内では色の値とその要素の数を指定します。山括弧“<”,“>”の表記は、関数がライブラリ内のいわゆるテンプレートの形で作成されていることを表しています。山括弧内により、ライブラリはさまざまなアプリケーションに対してより柔軟に対応できるようになります。ここではひとまず、関数名の一部と考えてください。
  4. メインループでは、関数 CSHV() の助けを借りて、配列 leds[] の要素に具体的な色が割り当てられています。この関数は、最初の引数として、色を表す0から255までの数値を期待しています。第2引数は彩度、第3引数は明るさです。これらは常にバイト値です。変数Position,変数Colorともにループごとに1ずつ増えていきます。255ステップ後には、バイト型なので、Colorはオーバーフローして再び0に戻ります。Positionは、すべてのLEDが通過したときに、ループ自体の終わりから先頭に戻るif条件で設定されます。もちろん、forを使用したループでも問題ありません。あるいは、同様の関数 CRGB() を使用して、赤、緑、青のそれぞれの明るさの値を指定することもできます。しかし、すべての色を通して連続的に変更したい場合は、CRGB()を使った方法よりもこのプログラムの方が有効です。
  5. 関数 show() は、配列LED[]に格納されている色の値を、出力ピンを介してアドレッサブル LED に送信するようになります。
  6. この関数もライブラリで提供されています。配列leds[] に格納されているすべての明るさの値を、ここでは任意に 20 に設定されて一定量だけ減らしていきます。これによってループが進むごとに徐々に暗くなります。

さて、ライブラリには数多くの事例があります。アドレッサブルLEDを制御するための追加ライブラリのAdafruit_Neo-Pixel.hもその一つで、このライブラリは、芸術的なLED表現機能を提供してくれます。この機能の可能性について、より深く知りたいのであれば実機を使い、実験することをお勧めします。

シミュレーション

これまでの内容をシミュレーションしたいのですが、TINKERCADではそこまでの部品がそろっていないため、今のところできません。その代わりに、Arduinoを2個使用してI2CでLCDを制御するシミュレーションと、Neo-Pixelストライプのシミュレーションを以下に載せておきますので、興味のある方は動かしてみたり、コードを見てください。

Arduino初心者編カテゴリの最新記事