TNKソフトウェア的ArduinoでドットマトリックスLED操作

マイコンボードArduinoでLEDマトリックスのプログラムが完成したので、メモがてらその開発過程を紹介します。

Arduino入門書に掲載されている電子パーツと全く同じ型番を買ってくると、プログラミングの勉強の余地が狭まりますし、費用も割高になります。と、いうわけで、今回は一番安かったからという理由で選んだ、およそ100円のデジット(共立電子)で売っていた5x7ドットマトリックスLEDです。

どこかの記事に「5x7のドットマトリックスLEDの仕様はどれも同じ」とありましたが、一般的なデータシートを参考に配線すると表示がおかしくなったので、総当たりでピンの属性を調べなおしました。

結果はこんな感じ。XはColumn、YはRowを表し、Columnはアノード、Rowはカソードのようです。
この情報を元にArduinoへ配線をします。X方向は数が少ないのですべてアナログピン0から、y方向はデジタルピン2から1.0kΩ抵抗を介して順番につなげていきました。写真の場合、寒色系がY、暖色系がXです。
続いてプログラミングです。XがHIGHかつYがLOWのLEDが通電します。ただし、XがLOWかつYがHIGHだと逆方向に電流が流れることになりますが、今回は対策は考慮していません。
led.ino
#define XNODE_0 A0
#define XNODE_LEN 5

#define YNODE_0 2
#define YNODE_LEN 7

void led(int x, int y, bool o){
  if(o == true){
    digitalWrite(XNODE_0 + x, HIGH);
    digitalWrite(YNODE_0 + y, LOW);
  }else{
     digitalWrite(XNODE_0 + x, LOW);
     digitalWrite(YNODE_0 + y, HIGH);   
  }
}
void ledOn(int x, int y){ led(x, y, true); }
void ledOff(int x, int y){ led(x, y, false); }

void setup() {
  for(int i = XNODE_0; i < XNODE_0 + XNODE_LEN; i++){
    pinMode(i, OUTPUT);
    digitalWrite(i, LOW);
  }

  for(int i = YNODE_0; i < YNODE_0 + YNODE_LEN; i++){
    pinMode(i, OUTPUT);
    digitalWrite(i, HIGH);
  }
}
一度に複数のLEDを表示させるには、対象のLEDを1個1個高速で順繰りに表示させるのが一般的です。列単位でまとめて処理したいところですが、電圧が一定だと、同時に点灯するとその分明るさが落ちるので、見栄えが悪くなります。

別に35バイトの配列を用意してもいいのですが、組み込みであることを意識して、マトリックスの表示データは5バイトの配列とビット演算を組み合わせてみました。
matrix.ino
// およそ1/60sですべてのLEDを処理する
#define LED_ALL_MS (1000 * 1000 / 60)
#define LED_WAIT_MS (LED_ALL_MS / (XNODE_LEN * YNODE_LEN))

unsigned char matrix_bit[5] = {0};

void setMatrix(int x, int y, bool o){
  int pos = y * XNODE_LEN + x;
  int bytepos = pos / 8;
  int bitpos = pos % 8;
  if(o == true){
    matrix_bit[bytepos] |= (1 << bitpos);
  }else{
    matrix_bit[bytepos] &= ~(1 << bitpos);
  }
}

void clearMatrix(){
  for(int i=0;i<5;i++) matrix_bit[i] = 0;
}

void showMatrix(int interval){
  if(interval <= 0) return;
  int ms = millis();
  while(true){
    for(int y = 0; y < YNODE_LEN; y++){
      for(int x = 0; x < XNODE_LEN; x++){
        int pos =y * XNODE_LEN + x;
        int bytepos = pos / 8;
        int bitpos = pos % 8;
        bool sw = (matrix_bit[bytepos] & (1 << bitpos)) != 0;
        led(sw, x, y);
        delayMicroseconds(LED_WAIT_MS); 
        ledOff(x, y);
      }
    }
    
    int cm = millis();
    if(cm < ms || cm - ms >= interval) return;
  }
}
点灯例。指定に位置にある2つの点を交互に表示させます。
sample.ino
void loop() {
  clearMatrix();
  setMatrix(1, 0, true);
  setMatrix(2, 2, true);
  showMatrix(1000);

  clearMatrix();
  showMatrix(500);

  clearMatrix();
  setMatrix(3, 5, true);
  setMatrix(4, 2, true);
  showMatrix(1000);
  
  clearMatrix();
  showMatrix(500);
}
最後に文字列を表示するコードを追加してみましょう。あらかじめ用意した文字のデータをマトリックスデータにコピーするだけです。文字データはプログラムにそのまま転用できるものが見つからなかったので、海外の掲示板にあったコードを、自作のコンバーターを使って変換することにしました。
chartest.ino
void setMatrixFromChar(char code){
  if(code < ' ') code = ' ';
  else{
    code -= ' ';
    if(code > 95) code = ' ';
  }
  for(int i =0;i<5;i++) matrix_bit[i] = chr57[code][i];
}

void showChar(char c, int interval){
  setMatrixFromChar(c);
  showMatrix(interval);
}
こちらの動画の元となった実行部分はこのようになります。コンバーターを含めたサンプルコードはこちらからどうぞ。
loop.ino
void loop() {
  showChar('T', 500); 
  showChar('N', 500); 
  showChar('K', 500); 
  showChar('S', 500); 
  showChar('O', 500); 
  showChar('F', 500); 
  showChar('T', 500); 
  showChar(' ', 1000); 
}
参考文献ボクのArduino工作ノート(鈴木哲哉・著)
2015/12/30