将棋の神様〜0と1の世界〜

「三間飛車のひとくちメモ」管理人、兼「フラ盤」&「チェスクロイド」作者がおくる、将棋コラム

対局時計を自作(4)「2桁の秒読みに対応」

99秒までの秒読みに対応


開発が思うように進まず、前回(12/14のエントリー参照)からの進歩としては、2桁の秒読みに対応できただけ。
「デンシ電気店 ヤフーショップ」というところから、ダイナミックドライブ用配線済みの4桁7セグLED「LED/E3-4040CHR2」を入手した。
上記動画で数字がちかちか点滅して見えるのは、各桁を短い周期で交互に光らせる「ダイナミック点灯」を行なっているから。ただし人間の肉眼であれば、常に光っているように見える。
左側のボタンとLEDはまだ未使用。

今後の課題

7セグデコーダドライバ、または3to8デコーダ/デマルチプレクサ(Demultiplexer。コモン端子制御用に利用したい)が無い現状では、ポートが足りない(はず)。

  • ブザー用・・・1Pin
  • 7セグ用・・・7Pin(7セグデコーダがあれば4Pinで表現可能)
  • ボタン用・・・1ボタンにつき1Pin
  • コモン制御用・・・1桁につき1Pin(3to8デコーダ/デマルチプレクサがあれば、8桁までを3Pinで表現可能)

つまり、2ボタン+4桁を単純に表現すると、1+7+2+4=14Pinとなり、ArduinoのDigital In/Out Pin13個を超えてしまう。

ウケるのか?

明日から、1泊2日の某温泉将棋合宿に参加する。対局時計の数は足りているのだが、笑いをとるだけのために本対局時計を持っていく予定。というか実は、本開発の根本的な目的の1つは、この合宿に持っていく(そして笑いをとる)ことだった。もっと完成度を上げたかったのだが・・・無念。

スケッチ公開

参考までに、以下に現状のProcessingスケッチを載せておく。
主な変更点は、数字を表示するdispNum関数。その他、millis()で取得した時間データをうっかりInt型変数で受け取っていたバグ(前回は時間が短くバグが表面化していなかった)を修正したりしている。アノード→カソード変換など、どうもスマートじゃないところが散見かも。

#define TM_LIMIT 30  // 持ち時間
#define CATHODE // カソードコモンLED利用時。アノード時はコメントアウト
 
// 状態 stateCC(CCはChessClockの略)
// 1 (CNTDOWN): 秒読み状態
// 2 (CNTSTOP): 時間切れ状態
#define CNTDOWN 0
#define CNTSTOP 1
volatile int stateCC = CNTDOWN;

unsigned long countBuf = 0;  // 着手時のmillis時間保持用
int usingSec = 0;  // 使用時間

// 各桁のコモン制御用ポート定義
int digitArray[2]={9,10};

// 各SegmentとPin対応。2,3Pinは割り込み用に空ける
int SegArray[7] = {0,1,4,5,6,7,8};

// 0〜9の7セグ表示パターン アノードコモンLED用
boolean LEDArray[10][7]={ {0,0,0,0,0,0,1},
                          {1,0,0,1,1,1,1},
                          {0,0,1,0,0,1,0},
                          {0,0,0,0,1,1,0},
                          {1,0,0,1,1,0,0},
                          {0,1,0,0,1,0,0},
                          {0,1,0,0,0,0,0},
                          {0,0,0,1,1,0,1},
                          {0,0,0,0,0,0,0},
                          {0,0,0,0,1,0,0} };

// カソードコモンLEDに変換する際に用いる関数。各ビット反転
void changeCommonAtoC(void){
  int i,j;
  for(i=0; i<10; i++){
    for(j=0; j<7; j++){
      LEDArray[i][j]= !LEDArray[i][j];
    }
  }
}
// ブザーOn/Off用。13Pin利用
void buzzer( boolean soundOn ){
  digitalWrite(13,soundOn);
}
// コモン端子制御用
void ctrlCommon( int port, boolean lightOn ){
  digitalWrite(port, lightOn);
}  

// 数字表示
void dispNum(int num){
  static unsigned long millisBuf = 0;
  static int digit = 1;
  int num1;  // 光らせる桁に表示させたい数字設定用
  
  // 光らせる桁設定 現状2桁だけ。1→2→1→・・・
  if( millis()-millisBuf >= 10 ){  // 10msおきにコモン切換
    if(digit>=2){digit=1;}
    else        {digit++;}
    millisBuf = millis();
  }  
  // 光らせる桁の数字取得
  for(int n=digit; n!=1; n--){ num=num/10; }  // 右へ桁シフトし下位桁削除
  num1 = num%10;  // 上位桁の数字クリア
  
  // コモン端子制御。光らせる桁のコモンのみHIGH
  for(int i=0; i<2; i++){ ctrlCommon( digitArray[i], digit==i+1 ); }
  // ↑digitとi+1が等しいときのみtrueとなる

  // 光らせる桁の数字の7セグ出力
  for(int j=0; j<7; j++){
    digitalWrite(SegArray[j], LEDArray[num1][j]);
  }
}
void setup(){
  int i;
  
  //カソードコモンLED使用ならばカソード用に変換
#ifdef CATHODE
  changeCommonAtoC();
#endif
  
  // 7セグ用Pin初期化
  for(i=0; i<=7; i++){
    pinMode(SegArray[i], OUTPUT);
  }
  // ブザー用Pin初期化
  pinMode(13,OUTPUT);
  
  // コモン制御用Pin初期化
  pinMode(9,OUTPUT);
  pinMode(10,OUTPUT);
  
  // チェスクロボタン押しで割り込み発生
  attachInterrupt(0, pushButton, RISING);
}
void pushButton(){
  switch(stateCC){
  case CNTDOWN:
    usingSec = 0;
    countBuf = millis();  // チェスクロボタン押した時の時刻保持
    break;
  case CNTSTOP:
  default:
    break;
  }
}
void loop(){
  switch(stateCC){
  case CNTDOWN:
    usingSec = millis() - countBuf;

    // 7セグ関連
    dispNum(TM_LIMIT - usingSec/1000);  // カウントダウン表示

    // ブザー音関連
    if(usingSec <= 50){  // ボタン押し直後は鳴らさない
      buzzer(false);
    }      
    else if(TM_LIMIT - usingSec/1000 <= 5){  // ラスト5秒
      buzzer(true);
    }
    else if(TM_LIMIT - usingSec/1000 <= 10){  // ラスト10秒
      if(usingSec%1000 >= 0 &&
         usingSec%1000 <= 50){
        buzzer(true);
      }
      else{buzzer(false);}
    }
    else if( (TM_LIMIT*1000-usingSec)%10000 >= 9950 ){  // それ以前は10秒毎
      buzzer(true);
    }
    else{buzzer(false);}

    // 時間使い切った場合
    if(usingSec == TM_LIMIT*1000){
      stateCC = CNTSTOP;
    }    
    break;
  case CNTSTOP:
    dispNum(0);
    buzzer(false);
    break;
  default:
    break;
  }
}