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

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

対局時計を自作(5)「秒読み対戦に対応」

2人用に対応。9999秒の秒読みに対応。

2008/12/26のエントリー『対局時計を自作(4)「2桁の秒読みに対応」』以来、約4ヶ月ぶりに自作対局時計の機能を向上させた。

7セグLED表示とボタンが正式に2人用になった。また、動画では30秒の秒読みとしているが、9999秒まで設定可能だ(後述するソースのTM_LIMITの部分を9999に変更すればOK)。

7セグデコーダ、3to8デコーダ利用

前回のエントリーで懸念事項に挙げたポート数については、

  • 7セグ制御・・・BCD-to-Seven Segment Decoder/Driver HD74HC4511Pを利用
    • 7Pinを4Pinに圧縮
  • コモン制御・・・3-LINE TO 8-LINE DECODERS SN74HC138Nを利用
    • 8Pinを3Pinに圧縮

とすることで、大いに余裕を持って対応できた(写真参考)。

回路組みとコーディングに、苦節4ヶ月を要した・・・わけではなく、前回更新時以来ずっとモチベーションが上がらずノータッチだった。ゴールデンウィークに入りやる気が起き、GW中の2日間でここまで仕上げた。
id:readingknowledgeさんとたまたま開発時期が重なり、競うような形で開発を進めていたわけだが、私の方が急にフェードアウトしてしまう形となり、大変申し訳ないと感じていた。ただ、長くホームページを運営している者同士、この辺りのマイペース感、のらりくらり感は、一方で理解していただいている感覚とも思っている。

余談だが、モチベーションが急激に失われた理由は以下の通り。

  • 年末将棋合宿までに仕上げて持って行くのがそもそもの目的で、それが終了してしまったため。ちなみに、完成度が低すぎたので結局合宿に持って行くのは断念した。参加者の1人は、後で本ブログを見てくれたようだ。
  • 昨年大晦日間近、回路組みを失敗し、過電流で4桁7セグLEDが焼失した。性根がソフト屋さん(最近は仕事でコーディングしていないが)で、「これだからハードは嫌なんだよ・・・」と逆切れ。買い換えたのはこのGW中だった。今回、LEDの色が赤から青に変わっているのはそのためだ。

今後の課題、ToDoリスト

  • 左右各4桁に対応できたので、上位2桁と下位2桁を使って、時・分の時間指定をできるようにする。
  • 時間設定機能を追加する。(現状では、時間設定を変えるにはソフトを書き換える必要がある)

スケッチ公開

参考までに、以下に現状のArduinoのProcessingスケッチを載せておく。

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

// 手番
#define LEFT  0
#define RIGHT 1
volatile int stateTeban = LEFT;
int dispNumArray[8] = 
{ TM_LIMIT/1000, (TM_LIMIT/100)%10, (TM_LIMIT/10)%10, TM_LIMIT%10,
  TM_LIMIT/1000, (TM_LIMIT/100)%10, (TM_LIMIT/10)%10, TM_LIMIT%10 };

// 各セグ制御用(BCD-to-Seven Segment Decoder/Driver HD74HC4511P)
#define SEG_1  6  // A
#define SEG_2  7  // B
#define SEG_3  8  // C
#define SEG_4  9  // D

// コモン制御用(3-LINE TO 8-LINE DECODERS SN74HC138N)
#define COMMON_1  10  // A
#define COMMON_2  11  // B
#define COMMON_3  12  // C

#define BUZZER  13

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

// 各桁のコモン制御用ポート定義
int digitArray[3]={ COMMON_1, COMMON_2, COMMON_3 };

// 各Segment制御用ポート定義
int SegArray[4] = { SEG_1, SEG_2, SEG_3, SEG_4 };

// ブザーOn/Off用。
void buzzer( boolean soundOn ){
  digitalWrite( BUZZER, soundOn );
}
// コモン端子制御用
void ctrlCommon( int port, boolean lightOn ){
  digitalWrite( port, lightOn );
}  

// 数字表示
void dispNum(int num){
  static unsigned long millisBuf = 0;
  static int digit = 0;
  int n;
  
  // 8桁の数字セット
  if( stateTeban == LEFT ){ n = 0; }
  else {                    n = 4; }
  dispNumArray[n  ] = num/1000;
  dispNumArray[n+1] = (num/100)%10;
  dispNumArray[n+2] = (num/10)%10;
  dispNumArray[n+3] = num%10;
  
  // 光らせる桁設定
  if( millis()-millisBuf >= 1 ){  // X msおきにコモン切換
    if(digit>8){digit=0;}
    else        {digit++;}
    millisBuf = millis();
  }
  
  // コモン端子制御。
  ctrlCommon( COMMON_3, digit>>2 );
  ctrlCommon( COMMON_2, (digit>>1) & 1 );
  ctrlCommon( COMMON_1, digit & 1 );

  // 光らせる桁の数字の7セグ出力
  digitalWrite( SEG_4, dispNumArray[digit] >> 3 );
  digitalWrite( SEG_3, (dispNumArray[digit] >> 2) & 1 );
  digitalWrite( SEG_2, (dispNumArray[digit] >> 1) & 1 );
  digitalWrite( SEG_1, dispNumArray[digit] & 1 );
}
void setup(){
  int i;
  
  // 7セグ用Pin初期化
  for(i=0; i<4; i++){
    pinMode(SegArray[i], OUTPUT);
  }
  // コモン制御用Pin初期化
  for(i=0; i<3; i++){
    pinMode(digitArray[i], OUTPUT);
  }
  // ブザー用Pin初期化
  pinMode(BUZZER,OUTPUT);  
  
  // チェスクロボタン押しで割り込み発生
  attachInterrupt(LEFT,  pushButtonLeft,  RISING);
  attachInterrupt(RIGHT, pushButtonRight, RISING);
}
void pushButtonLeft(){  pushButton(LEFT ); }
void pushButtonRight(){ pushButton(RIGHT); }

void pushButton( int button ){
  if( button == LEFT  && stateTeban == RIGHT )  return;
  if( button == RIGHT && stateTeban == LEFT  )  return;
  
  switch(stateCC){
  case CNTDOWN:
    stateTeban = ~stateTeban;
    usingSec = 0;
    countBuf = millis();  // チェスクロボタン押した時の時刻保持
    break;
  case CNTSTOP:
  default:
    break;
  }
}

void loop(){
  switch(stateCC){
  case CNTDOWN:
    usingSec = millis() - countBuf;
    TM_LIMIT - usingSec/1000;

    // 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;
  }
}