さて、今回はloop()の中身である。動作の概略は、第370回図1で紹介した通りである。

いくつかの変数の初期化の後、最初に呼び出すのがSerial.available()である。これは、RS232C(というか、USB)経由でPCからデータが何バイト届いているかを取得するFuncrionである。ここで届いていれば1以上が戻るので、そのまま続く処理を行う。一方何も届いていない場合には0が返ってくるので、while()の中を飛ばしてLED表示ロジックに移る形だ。

Serial.available()が1以上の場合は続いてSerial.read()を呼び出すと、1文字読み込んで、これを返す(今回はreadBufでこれを受けている)形だ。文字だからchar型では? と思われるかもしれないが(というか、char型にしてもSketchはbuild出来たのだが)、ArduinoのReferenecを読むと、Serial.read()をintで受けるサンプルとなっているので、ここではそのまま踏襲した形だ。

さて、受け取った値はプロトコル(これも370回で説明した)にあわせて変換を行う。まず最初に'z'が来た場合を処理しているが、ここはちょっと後にしてまずはelse節の後である。今回の場合、'0'~'D'の範囲の文字が来たら0~20の範囲の値として保存するが、それ以外の場合はどうするか?というととりあえず0として扱う、という実装をしている。これが7セグメントLEDならば'0'という表示になるが、LEDバーだと「0=何も光らない」だから、これで問題なかろうという判断だ。そこでとにかく受け取ったらまず桁送りをする。桁送りとは何か?ということだが、例えば370回で例に挙げた6/4/3/4という表示を行う場合、プログラムの動き方としては、

Receive Receive Receive Receive
Buf[0] Buf[1] Buf[2] Buf[3]
1Bytes目受信 6
2Bytes目受信 4 6
3Bytes目受信 3 4 6
4Bytes目受信 4 3 4 6

という具合になる。勿論、

Receive Receive Receive Receive
Buf[0] Buf[1] Buf[2] Buf[3]
1Bytes目受信 6
2Bytes目受信 4 6
3Bytes目受信 3 4 6
4Bytes目受信 4 3 4 6

という風に逆順で保存する方法もある。正常に動いている場合、これはどちらの実装でも問題はない。ただ何か問題があって、5Bytes目の'z'が正しく受け取れなかった場合に差が出てくる。筆者の実装の場合、例えば一回'z'を受け取り損なった場合、次の'z'を受け取るまでの間、常に古いデータを捨てて新しいデータをReceiveBuf[]に書き込むことになるため、次に表示された時には最新の結果が表示される。対して逆順で書き込んだ場合、古いデータが残って新しいデータが捨てられてしまうため、次に'z'を受信したときに表示されるのは過去のデータとなる。どちらが振る舞いとして正しいかというのは半ば好みの問題だが、筆者は前者を取った。

またバッファにしても、こんなカタチではなくポインタで管理する方法もある。一種のリングバッファ的な扱いだが、これだとコードは、

      /* 桁送り */
      ReceiveBuf[n++] = readBuf-48;
      if (n > 3) n = 0;

といったカタチになる。一見スマートなのだが、条件分岐が入る分、マシンコードはやや増える事になる。これは桁送り側のみならずDisplayBufへの変換部でも必要になるため、トータルすると変数が1個余分に増え、トータルとしてのコード量は殆ど変わらず、であれば(やや愚直ではあるが)コピーによる桁送りが無難、と判断した。これに続く部分は、ASCIIコードを実際の値に変換する部分で、170回で説明した通り受信したコードから48(0x30)を引くだけだ。

さて一方'z'が来た場合である。この場合は表示更新となるので、ReceiveBufをDisplayBufに変換となる。DisplayBufは、

DisplayBuf[0]: 1本目のLEDの下10個の点灯個数
DisplayBuf[1]: 1本目のLEDの上10個の点灯個数
DisplayBuf[2]: 2本目のLEDの下10個の点灯個数
DisplayBuf[3]: 2本目のLEDの上10個の点灯個数
DisplayBuf[4]: 3本目のLEDの下10個の点灯個数
DisplayBuf[5]: 3本目のLEDの上10個の点灯個数
DisplayBuf[6]: 4本目のLEDの下10個の点灯個数
DisplayBuf[7]: 4本目のLEDの上10個の点灯個数

をそれぞれ格納している。例えば1本目のLEDが80%(16個)、2本目のLEDが35%(7個)の表示であれば、

DisplayBuf[0]: 10
DisplayBuf[1]: 6
DisplayBuf[2]: 7
DisplayBuf[3]: 0

となる。そんなわけでまず値を判断し、それが10以下であれば上側バッファは無条件で0、逆に11以上なら下側バッファは無条件で10になる計算だ。

(続く)

List2:

void loop()
{
  int lpCnt, lpCnt2;
  int readBuf;         /* 受信データを一時的に受けるバッファ */

  while( Serial.available() > 0)  /* データ到着なら受信を行う */
  {
    readBuf = Serial.read();  /* まず1Byte読み込み */
    if (readBuf == 122)  /* 小文字の"z"が来た */
    {
      for ( lpCnt = 0; lpCnt < 4; lpCnt++ )
      {
        if(ReceiveBuf[lpCnt] < 11)
        {
          DisplayBuf[lpCnt*2] = ReceiveBuf[lpCnt];
          DisplayBuf[lpCnt*2+1] = 0;
        }
        else
        {
          DisplayBuf[lpCnt*2] = 10;
          DisplayBuf[lpCnt*2+1] = ReceiveBuf[lpCnt] -10;
        }
      }
    }
    else
    {
      /* 桁送り */
      ReceiveBuf[3] = ReceiveBuf[2];
      ReceiveBuf[2] = ReceiveBuf[1];
      ReceiveBuf[1] = ReceiveBuf[0];

      if ((readBuf > 47 ) && ( readBuf < 69 ))  /* 読み込んだのは0~'D'の範囲か? */
      {
        ReceiveBuf[0] = readBuf-48;  /* 数字ならその値を格納 : atoi()の代わり */
      }
      else
      {
        ReceiveBuf[0] = 0;  /* それ以外なら無条件で0 */
      }
    }
  }

  /* LED点灯 */
  for (lpCnt = 0; lpCnt < 8; lpCnt++)
  {
    digitalWrite( PosY[lpCnt], LOW );
    for (lpCnt2 = 0; lpCnt2 < DisplayBuf[lpCnt]; lpCnt2++)
    {
      digitalWrite( PosX[lpCnt2], HIGH );
    }
    delayMicroseconds( WAIT_LED ); 
    for (lpCnt2 = 0; lpCnt2 < DisplayBuf[lpCnt]; lpCnt2++)
    {
      digitalWrite( PosX[lpCnt2], LOW );
    }
    digitalWrite( PosY[lpCnt], HIGH );
  }
}