ちなみに前回のPWMであるが、1ピンだけでよければ、たとえばList 1のように、自分でDigital Outの点滅間隔を細かく調整することで擬似的にエミュレートする、いわば「なんちゃってPWM」も可能である。ここではdisplayLED()という関数を定義し、この中でanalogWrite()に近い動作をさせている。このdisplayLED()では、内部でdelayMicroseconds()(μ秒単位の待ち)を利用することで、0.255ms単位でデューティ比の制御を行っている。ただこのままだとちょっと煩雑なので、これを8回ループさせることで、ほぼ2ms単位という、analogWrite()と同程度の頻度でLEDの輝度調整を行うものだ。こうした使い方をすれば、digitalWrite()を使いながら、あたかもanalogWrite()っぽく動かすこともできるという例である。

List 1:

#define LED_PIN  3

void displayLED( int period )
{
  int lpCnt;
  for(lpCnt = 0; lpCnt < 8; lpCnt++)
  {
    digitalWrite(LED_PIN, HIGH);
    if (period != 0)    delayMicroseconds(period);
    digitalWrite(LED_PIN, LOW);
    if (period != 255)  delayMicroseconds(255-period);
  }
}
void setup()
{
  pinMode(LED_PIN, OUTPUT);
}

void loop()
{
  int  period, lpCnt;

  for(period = 1; period < 256; period++)
  {
    displayLED(period);
  }
  for(period = 255; period > 0; period--)
  {
    displayLED(period);
  }
}

もっともこれ、ある1つのLEDの制御「だけ」をやっている分には差がないのだが、2つ以上のLEDを独立に制御するとか、あるいはLEDの制御以外のこともやらそうとか考えた瞬間に破綻する。というのは、たとえば、

analogWrite(PIN_LED, 127);

を一度実行すれば、その後は何もしなくてもずーっとLEDは定格のほぼ半分の明るさで光り続ける(PWMの制御をMCUがやってくれる)が、List 1の方法を使った場合、2ms毎にdigitalWrite()を呼び出し続けないと、点灯しなくなってしまう。

一般論としてMCUにはタイマー割り込みの機能があり、これを使うことである程度解決できる場合もある。たとえばやはり半分の明るさで点灯させたいと思ったとき、List 1の方法だと図1のような動き方になる。これはタイマー割り込みを使わない場合の典型的な方法で、この結果loop()の処理はdisplayLED()を呼び出す事で埋まってしまい、しかもdisplayLED()は無駄に待機しているという図式だ。

図1

ここで仮にタイマー割り込みが利用できたとすると、プログラムを図2の様な構造に書き換えることができる。これにより、無駄にCPUで待機する必要がないから、間に他の処理をすることも可能で、タイマーが複数利用できるケースならば2つ以上のLEDを「なんちゃってPWM」で制御することも可能だろう。

図2

「ここで仮に」なんて書き出しで始めたからにはお分かりの通り、実際にはArduinoではこの機能は利用できない。Arduino Unoに搭載されているAtmelのATmega328には8bit/16bitの2種類のタイマーが搭載されており、PWM制御に利用されているのだが、PWMに使わなずに内部割込み源として利用することも可能である。ところがArduinoはこのタイマー周りをPWMおよびライブラリ関数であるdelay()/delayMicroseconds()のソースに利用しており、タイマー割り込みをユーザー側で利用することが不可能である。現実問題としてわざわざ「なんちゃってPWM」を使うメリットがあるか? と言われると疑問ではあるのだが、ただこうした形でArduinoには若干の制限が付きまとう事は覚えておいてほしい。

さて、話を進めよう。LEDが1個の場合はまぁ簡単なのだが、もっと数が増えたらどうなるか? とりあえず6個の場合を考えてみる。これを順々に点灯させて行く事をやってみよう。とりあえずPWMの話は措いておき、digitalWrite()を使うタイプの、On/Offだけでよいとする。一番簡単なのは、図3の様な構造だ。要するにピン2~7までにそれぞれLEDを接続するという、あまり頭を使わない構造だ。実態配線図は図4の様な構造になる。

図3

図4

この回路を駆動するSketchとして、とりあえずList 2の様なものを作って見た。これを起動すると、Movie01の様に順に点滅して行くはずだ。

List 2:

void setup()
{
  int lpCnt;

  for(lpCnt=2; lpCnt<8; lpCnt++)  /* Pin 2~8が対象 */
  {
    pinMode(lpCnt, OUTPUT); /* 当該PinをDigital Outに設定 */
    digitalWrite(lpCnt, LOW);  /* 初期値は消灯 */
  }
}

void loop()
{
  int lpCnt;

  for(lpCnt=2; lpCnt<8; lpCnt++)
  {
    digitalWrite(lpCnt, HIGH);  /* 点灯 */
    delay(500);                 /* 500ms待機 */
    digitalWrite(lpCnt, LOW);  /* 点灯 */
  }
}

動画
Movie01:

これはこれで簡単でいいのだが、この方式の場合、LEDの数だけpinが必要という、至極当然の限界につきあたる。前にも書いたがArduinoは最大で20pinのDigital Outを持っており、ところがウチ2本はUSBポートとの共用だから、最大でも18個までしかLEDが付けられない。このあたりもう少し考えましょう、という話は次回。

(続く)