SHIROのIchigoJam日記

IchigoJamの電子工作とプログラミングをメインに

Arduino+8×8マトリックスLEDでコマアニメ表示

Arduinoの作例その2。
秋葉原秋月電子で、8x8のマトリックスLEDが安く売っていたので、ラッチ付きシフトレジスタ74HC595を挟んで、キャラクターをコマアニメで表示させてみました。
74HC595を2個使って、1個は1行分(8個)の表示データを出力、もう1個はマトリックスの行を上→下へスキャンしてスイッチングしています。
動作動画はこちら。

実体写真はこちら。

回路図はこちら。やはり「BSch3V」で描いています。
実体写真には無いのですが、キャラクター表示と共に音を出したかったので、回路図右下でトランジスター2個で発振回路を組んでいます。そこに付いているLEDは確認用なので、無くてもいいです。
もちろん、音が無くてもいいならこの辺りの回路は不要です。

プログラムはこちら。ちょっと長いですが…
キャラクターのパターンは、最初に2進数のフォント配列で4コマ分を定義しています。
ここを変えれば違うパターンが表示できます。
回路図やプログラム内では、1行分のデータ出力ラインを「Row」、行方向のスキャンを「Column」と表記しています。
ディスプレイの見た目ではRowとColumnが逆なのですが、LEDマトリックスでの呼称とピン配置に合わせています。

/*
8x8マトリックスLED
パターンアニメーション
*/

// ポート設定
int Port_C_SClk = 5;
int Port_C_DS   = 2;
int Port_C_LClk = 4;
int Port_C_OE   = 3;
int Port_C_Clr  = 6;

int Port_R_SClk = 10;
int Port_R_DS   = 7;
int Port_R_LClk = 9;
int Port_R_OE   = 8;
int Port_R_Clr  = 11;

int PortBeep   = 12;
int PortSw   = 13;

// インターバル、ループ
long Interval0 = 100;
long Interval1 = 80;
int loop1 = 20;

// 表示用配列

byte Disp[8];

byte Koma[] = {
                B00100000
               ,B11000000
               ,B11000001
               ,B00111110
               ,B00111100
               ,B00111100
               ,B01000100
               ,B10000100
               
               ,B01000000
               ,B11000000
               ,B11000000
               ,B00111111
               ,B00111100
               ,B00111100
               ,B01000100
               ,B01000010
               
               ,B01000000
               ,B11000000
               ,B11000000
               ,B00111111
               ,B00111100
               ,B00111100
               ,B00100010
               ,B00100001
               
               ,B00100000
               ,B11000000
               ,B11000001
               ,B00111110
               ,B00111100
               ,B00111100
               ,B01000100
               ,B01000010
               };

int KomaNum = 4;

// ***--- セットアップ ---***

void setup()
{
  int x;
  
  // シフトレジスターへのポートを出力モードに
  pinMode(Port_R_SClk, OUTPUT);
  pinMode(Port_R_DS  , OUTPUT);
  pinMode(Port_R_LClk, OUTPUT);
  pinMode(Port_R_OE  , OUTPUT);
  pinMode(Port_R_Clr , OUTPUT);

  pinMode(Port_C_SClk, OUTPUT);
  pinMode(Port_C_DS  , OUTPUT);
  pinMode(Port_C_LClk, OUTPUT);
  pinMode(Port_C_OE  , OUTPUT);
  pinMode(Port_C_Clr , OUTPUT);

  // スイッチのポートを入力モードに
  pinMode(PortSw, INPUT);

  // ブザーのポートを出力モードに
  pinMode(PortBeep, OUTPUT);
  digitalWrite(PortBeep, LOW);
}

// ***--- ループ ---***

void loop()
{
  int x;
  int ShiftRow;
  
  // LEDマトリックスをクリアする
  x = disp_clr();
  
  // ボタンが押されるまで停止
  x = disp_kset(0, 0);
  while (digitalRead(PortSw) == HIGH) {
    x = disp_out(Interval0);
  }
  
  // コマでループ
  ShiftRow = 8;
  for (int i = 0; i < loop1; i++){
    for (int k = 0; k < KomaNum - 1; k++){
      if (k == 0) {
        digitalWrite(PortBeep, HIGH);
      }
      x = disp_clr();
      x = disp_kset(k, ShiftRow);
      x = disp_out(Interval1);
      ShiftRow--;
      if (ShiftRow < -8) {
        ShiftRow = 8;
      }
      if (k == 0) {
        digitalWrite(PortBeep, LOW);
      }
    }
  }
}

/* ディスプレイ配列へのコマセット関数 */
int disp_kset(int k, int ShiftRow){

  byte Disp_Char[8];
  int x;
  
  // コマパターンをセット
  for (int i = 0; i <= 7; i++){
    Disp_Char[i] = Koma[k * 8 + i];
  }
  
  // ディスプレイ配列にパターンをセット
  for (int i = 0; i <= 7; i++){
    if (ShiftRow >= 0) {
      Disp[i] = Disp[i] | (Disp_Char[i] >> ShiftRow);
    }
    else {
      Disp[i] = Disp[i] | (Disp_Char[i] << -ShiftRow);
    }
  }

}
/* 8x8マトリックスLED点灯関数 */
int disp_out(long interval){

  long starttime;
  byte mask;
  int x;
  
  starttime = millis();
  
  // 表示時間だけループ
  while (millis() < starttime + interval) {
    
    // Columnループ1度目だけデータをLOWに
    digitalWrite(Port_C_DS, LOW);
  
    // Columnでスキャン
    for (int Col = 0; Col <= 7; Col++){
      
      // Rowにデータをセット
      mask = 128;
      for (int Row = 7; Row >= 0; Row--){
        
        // ディスプレイ配列のビットを転送
        if ((Disp[Col] & mask) != 0) {
          digitalWrite(Port_R_DS, HIGH);
        }
        else {
          digitalWrite(Port_R_DS, LOW);
        }
        
        // シフトクロックを送る
        digitalWrite(Port_R_SClk, LOW);
        digitalWrite(Port_R_SClk, HIGH);
        digitalWrite(Port_R_SClk, LOW);
        
        mask = mask / 2;
      }
      
      // Row出力を一時停止
      digitalWrite(Port_R_OE, HIGH);
      
      // Rowをレジスタ→ラッチへ転送
      digitalWrite(Port_R_LClk, LOW);
      digitalWrite(Port_R_LClk, HIGH);
      digitalWrite(Port_R_LClk, LOW);
      
      // Columnをシフト
      digitalWrite(Port_C_SClk, LOW);
      digitalWrite(Port_C_SClk, HIGH);
      digitalWrite(Port_C_SClk, LOW);
      
      // Columnをレジスタ→ラッチへ転送
      digitalWrite(Port_C_LClk, LOW);
      digitalWrite(Port_C_LClk, HIGH);
      digitalWrite(Port_C_LClk, LOW);
      
      // Row出力を復活
      digitalWrite(Port_R_OE, LOW);
      
      // Columnループ2度目以降はデータをHIGHに
      digitalWrite(Port_C_DS, HIGH);
    }

  }

}

/* 8x8マトリックスLEDクリア関数 */
int disp_clr(){
  
  // Rowシフトレジスタをクリア
  digitalWrite(Port_R_Clr , LOW);
  digitalWrite(Port_R_SClk, LOW);
  digitalWrite(Port_R_SClk, HIGH);
  digitalWrite(Port_R_SClk, LOW);
  digitalWrite(Port_R_Clr , HIGH);
  
  // Rowレジスタ→ラッチへ転送
  digitalWrite(Port_R_LClk, LOW);
  digitalWrite(Port_R_LClk, HIGH);
  digitalWrite(Port_R_LClk, LOW);

  digitalWrite(Port_R_DS, LOW);
  
  // Columnシフトレジスタをクリア
  digitalWrite(Port_C_Clr , LOW);
  digitalWrite(Port_C_SClk, LOW);
  digitalWrite(Port_C_SClk, HIGH);
  digitalWrite(Port_C_SClk, LOW);
  digitalWrite(Port_C_Clr , HIGH);
  
  // Columnシフトレジスタは全て1を出力
  digitalWrite(Port_C_DS, HIGH);
  
  for (int Col = 0; Col <= 7; Col++){
    
    // シフトクロックを送る
    digitalWrite(Port_C_SClk, LOW);
    digitalWrite(Port_C_SClk, HIGH);
    digitalWrite(Port_C_SClk, LOW);
    
  }
  
  // レジスタ→ラッチへ転送
  digitalWrite(Port_C_LClk, LOW);
  digitalWrite(Port_C_LClk, HIGH);
  digitalWrite(Port_C_LClk, LOW);
  
  digitalWrite(Port_C_DS, LOW);
  
  // ラッチからの出力を許可
  digitalWrite(Port_R_OE, LOW);
  digitalWrite(Port_C_OE, LOW);

  // ディスプレイ配列のクリア
  for (int i = 0; i <= 7; i++){
    Disp[i] = 0;
  }
}