目次

electric dice

 CQ出版の雑誌、エレキジャックの付属基板が
 部屋を掃除していたら出てきました。



 付属基板を見ると、電子サイコロとわかりました。

 PIC16F84の手持ちがあるので、これで電子サイコロの
 プログラムを作成することに。

 18ピンのPICでは、ポートA、ポートBの2ポートの
 構成が大部分で、基板パターンから、LEDとスイッチ
 の接続は、以下とわかりました。

 RB0は、外部割込み端子となっているので
 プッシュスイッチを押すたびに、次の動作
 をすれば、サイコロになります。



 2つの状態を、プッシュスイッチを押すことで
 行き来するようにすれば、動作をエミュレート
 が可能。

 IDLE 表示パターンは変化しない
 RUN  1から6を繰り返し表示

 LEDの配置は、次のようになっています。



 RUNのときの表示パターンを、まとめておきます。



 表示パターンを、配列に格納しておき、RUNのとき
 には、カウンタで配列の添字を使い、パターンを
 引き出して、ポートBに出力すればよいでしょう。

 配列に格納する8ビットは、次のように確定。



 配列を宣言して、点灯パターンを格納するコードは
 以下でよいでしょう。

typdef unsigned char UBYTE ;

UBYTE lpat[6] ;

*(lpat+0) = 0x10 ;
*(lpat+1) = 0x28 ;
*(lpat+2) = 0x92 ;
*(lpat+3) = 0xaa ;
*(lpat+4) = 0xba ;
*(lpat+5) = 0xee ;

 RUNの状態のときは、タイマー割込みで時間経過を
 通知してもらい、次の関数でLEDにパターンを表示
 することに。

#define STATEMAX 6

void dice(void)
{
  /* impress */
  PORTB = *(lpat+state);
  /* update */
  state++ ;
  /* judge */
  if ( state >= STATEMAX ) { state = 0 ; }
}

 カウンタは、変数stateに割当しておきます。

 タイマー割り込みが発生したときは、次のように
 RUNであることをフラグで判定して、関数を呼び
 出す仕様にして対応。

  while (ON) {
    /* timer interrupt */
    if ( tflag == ON ) {
      tflag = OFF ;
      /* dice */
      if ( eflag == ON ) {
        dice();
      }
    }
  }

 フラグtflagは、タイマー割込みが発生したことを
 知らせるイベント通知フラグとします。
 また、フラグeflagはIDLEかRUNのどちらの状態に
 いるのかを通知するとします。

 割込みハンドラは、次のように定義。

void interrupt(void)
{
  /* external interrupt */
  if ( INTCON.INTF ) {
    /* clear flag */
    INTCON.INTF = OFF ;
    /* increment */
    eflag++ ;
    /* adjust 1 bit */
    eflag &= ON ;
  }
  /* timer interrupt */
  if ( INTCON.T0IF ) {
    /* clear flag */
    INTCON.T0IF = OFF ;
    /* initialize */
    TMR0 = 131 ;
    /* increment */
    xcnt++ ;
    /* judge */
    if ( xcnt == 200 ) {
      /* clear */
      xcnt = 0 ;
      /* set flag */
      tflag = ON ;
    }
  }
}

 プッシュボタンを押すと、RB0に'L'が印加されるので
 フラグeflagを計数フラグとして、インクリメント。
 フラグは、1ビットだけの判定で使いたいので、値は
 プッシュスイッチを押すたびに、0→1→0→1と
 反転します。

 タイマー割込みは、タイマー0を使い、カウンタの値が
 131から256まで数えて、255から0に戻るオーバーフロー
 割込みを発生させておきます。

 変数xcntは、LEDの表示パターンが目視できるほどに
 遅くするために使います。

 割込みハンドラを定義したので、初期化を関数に
 まとめていきます。

void usr_init(void)
{
  /* initialize PORT values */
  PORTA = 0x00 ;
  PORTB = 0x00 ;
  /* set directions */
  TRISA = 0x00 ;
  TRISB = 0x01 ;
  /* initialize timer 0 */
  {
    /*
       (10MHz/4)/2 = 1.25MHz ((Fosc/4)/2)
       1 : 256
    */
    OPTION_REG = 0x07 ;
    /* 256 - 125 = 131 */
    TMR0 = 131 ;
  }
  /* enable interrupt */
  INTCON = (ON << GIE) | (ON << T0IE) | (ON << INTE);
  /* others */
  xcnt = 0 ;
  state = 0 ;
  tflag = OFF ;
  eflag = OFF ;
  /* LED pattern */
  *(lpat+0) = 0x10 ;
  *(lpat+1) = 0x28 ;
  *(lpat+2) = 0x92 ;
  *(lpat+3) = 0xaa ;
  *(lpat+4) = 0xba ;
  *(lpat+5) = 0xee ;
}

 割込み利用は、タイマー0のオーバーフロー割み、外部割込みで
 よいですが、PICはグローバル割込みをイネーブルにしないと関数
 interruptが使えません。忘れやすく、割込みが発生しないとなる
 ので、要注意です。

 まとめると、以下。

#define OFF 0
#define ON  OFF+1

#define STATEMAX 6

typedef unsigned char  UBYTE ;

UBYTE xcnt ;
UBYTE tflag ;
UBYTE eflag ;

UBYTE state ;
UBYTE lpat[6];

/* prototype */
void usr_init(void);
void get_flag(void);
void dice(void);

/* interrupt handler */
void interrupt(void)
{
  /* external interrupt */
  if ( INTCON.INTF ) {
    /* clear flag */
    INTCON.INTF = OFF ;
    /* increment */
    eflag++ ;        INTCON.
    /* adjust 1 bit */
    eflag &= ON ;
  }
  /* timer interrupt */
  if ( INTCON.T0IF ) {
    /* clear flag */
    INTCON.T0IF = OFF ;
    /* initialize */
    TMR0 = 131 ;
    /* increment */
    xcnt++ ;
    /* judge */
    if ( xcnt == 200 ) {
      /* clear */
      xcnt = 0 ;
      /* set flag */
      tflag = ON ;
    }
  }
}

void main(void)
{
  /* initialize */
  usr_init();
  /* endless loop */
  while (ON) {
    /* timer interrupt */
    if ( tflag == ON ) {
      tflag = OFF ;
      /* dice */
      if ( eflag == ON ) {
        dice();
      }
    }
  }
}

void usr_init(void)
{
  /* initialize PORT values */
  PORTA = 0x00 ;
  PORTB = 0x00 ;
  /* set directions */
  TRISA = 0x00 ;
  TRISB = 0x01 ;
  /* initialize timer 0 */
  {
    /*
       (10MHz/4)/2 = 1.25MHz ((Fosc/4)/2)
       1 : 256
    */
    OPTION_REG = 0x07 ;
    /* 256 - 125 = 131 */
    TMR0 = 131 ;
  }
  /* enable interrupt */
  INTCON = (ON << GIE) | (ON << T0IE) | (ON << INTE);
  /* others */
  xcnt = 0 ;
  state = 0 ;
  tflag = OFF ;
  eflag = OFF ;
  /* LED pattern */
  *(lpat+0) = 0x10 ;
  *(lpat+1) = 0x28 ;
  *(lpat+2) = 0x92 ;
  *(lpat+3) = 0xaa ;
  *(lpat+4) = 0xba ;
  *(lpat+5) = 0xee ;
}

void dice(void)
{
  /* impress */
  PORTB = *(lpat+state);
  /* update */
  state++ ;
  /* judge */
  if ( state >= STATEMAX ) { state = 0 ; }
}

 サイコロを振るために使うプッシュスイッチは
 次の大きなサイズのデバイスを使います。




目次

inserted by FC2 system