もっと沢山のLEDを接続する方法は、Charlieplexingと呼ばれる方法である。これは1995年にMaximに勤めていたCharlie Allen氏が考案した方法で、彼にちなんでこの名前がついている。

Charlieplexingは、n本のピンを使うとn×(n-1)個のLEDを制御することが可能だ。例えばArduinoなら18×17=306個までのLEDを制御することが可能となる。ではどうやって? という話を説明する。

図1は一番基本の2pinの場合だ。この場合2×1=2個のLEDを制御できるわけだが、図2の様にAに5V、Bに0Vを設定すれば、片方だけLEDが点灯する。もう反対側は逆電圧になるから、単に点灯しない。ここで図3の様に電圧をひっくり返すと、今度は反対側のLEDが点灯するわけだ。

図1:

図2:

図3:

もっと多数だと? ということで図4が3pinの場合だ。この場合、LEDは3×2=6個まで制御できることになる。図5~図10が、実際に各pinに電圧設定を行うと、それぞれ個別にLEDが点灯できるという例であるが、ここで新しく出てきたのが「Z」という状態だ。これは何か? というと通常は「ハイインピーダンス」と呼ばれる。電気的に言えば、Highは5Vに繋がっており、Lowは0V(GND)に繋がっている状態と等価だが、ハイインピーダンスは「何も繋がっていない(断線している)」状態に等しいと考えれば良い。このHighとLow以外にZ(High-Zなどと書く場合もある)という状態を持つものをTri-State(状態が3つある)と呼んでおり、この状態を持つことが3pin以上でCharlieplexingの必須条件となっている(そして幸いなことに、Arduinoに搭載されているATMega328のI/Opinは、Tri-stateとなっている)。

図4:

図5:

図6:

図7:

図8:

図9:

図10:

ところで図5~図10を見て、あれ? と思われた読者もおられるかもしれない。例えば図9で、Pin BがZだと事実上無いことになる訳で、等価回路は図11の様になる。ということは、図11で緑の破線で囲んだLEDも点灯しても不思議ではないからだ。実を言うと、これがLEDではなく豆球の様に低い電圧でもそれなりに点灯するものだったら、その考えは正しい(もっとも豆球だと極性がないので、そもそもCharlieplexingが成立しないという話はあるが、それは措いておく)。ところがLEDの場合、点灯するための最低電圧というのが決まっており、これを下回ると何も点灯しなくなる。今回使っているOSDR3133Aの場合、点灯のためには1.8V~2.5V(Typicalで2V)の電圧を掛ける必要があり、これにあわせて抵抗を入れている。だから、電流制限抵抗をすごく少なくすると両方点灯する可能性はあるのだが、普通に抵抗を入れてやると、ここによる電圧降下でLED 1個あたりに掛かる電圧が最低限を下回ってしまい、点灯しないというわけだ。第6回で示したとおり、今回は150Ωの抵抗を使っているから、これにより3Vの電圧降下があり、LEDには2Vしか掛かっていない。ここでLEDを2つ直列につなぐと、LED 1個あたり1Vしか掛からない計算になり、これは最低1.8Vという条件を満たさないので、点灯しないというわけだ。

図11:

さて、ArduinoでこのCharlieplexingを実現する方法だが、これも簡単でpinModeをINPUTに設定すればよい。ここまでのサンプルスケッチでは、setup()の中でかならず、

pinMode(ピン番号、OUTPUT);

を実施していたが、これを行うと出力はHighかLowになる。一方、

pinMode(ピン番号、INPUT);

を行うと、出力としてはハイインピーダンスに設定されるという訳だ。

図12が回路図になる。抵抗が100Ωになっているが、これは図6~10を見れば判るとおり、動作中は1つのLEDを2つの抵抗で挟み込むようになっており、結果として抵抗の直列接続になるので、本来は半分の75Ωにしないと等価にならない。ところが手持ちに75Ωが無かったので、とりあえず100Ωを使っている。この場合、抵抗値は合計200Ωとやや大きくなり、電圧降下3Vに対して200Ωなので、電流は15mAに制限される。定格よりはやや低めだが、問題になるレベルではないだろう。図13に実態配線図も示したが、これを使うよりも図12を見ながら自分でブレッドボードの上で配線を考えたほうがいいだろう。

図12:

図13:

SketchはList 1の通りである。TableはLEDの点灯位置にあわせて、Pin 2~4にHigh(1)、Low(0)、Z(-1)の何を設定するかをパラメータとして持たせたもので、先頭の{-1,-1,-1}は全LED消灯のケースである。今回の場合、pinModeの設定もあとで動的に変えるため、setup()の中身は空であり、その代わりdisp()という関数を定め、この中でパラメータにあわせてPinModeを設定し、必要ならdigitalWrite()で値を設定する処理をまとめておこなっているという仕組みだ。動作の様子をMovie01に示すが、ご覧の通り3pinと3本の抵抗で、6本のLEDを制御できているのがわかると思う。

List 1:

int  Table[7][3]={{-1,-1,-1},{1,0,-1},{0,1,-1},
                  {-1,1,0},{-1,0,1},
                  {1,-1,0},{0,-1,1}};
void disp(int pin, int mode)
{
  switch(mode)
  {
    case 1:
      pinMode(pin, OUTPUT);
      digitalWrite(pin, HIGH);
      break;
    case 0:
      pinMode(pin, OUTPUT);
      digitalWrite(pin, LOW);
      break;
    default:
      pinMode(pin, INPUT);
  }
}

void setup()
{
}

void loop()
{
  int  lpCnt;
  for(lpCnt=0; lpCnt < 7; lpCnt++)
  {
    disp(2, Table[lpCnt][0]);
    disp(3, Table[lpCnt][1]);
    disp(4, Table[lpCnt][2]);
    delay(500);
  }
}

動画
Movie01:

ちなみにこのCharlieplexingを使ったLoL Shield for Arduinoなんてものが販売されているなど、この方式はそれなりに使われている。ただ原理上同時に一つしかLEDを点灯できないし、配線が複雑になるといったデメリットもあるため、利用できる用途が限られるのが難点である。

(続く)