目次

スイッチアダプタ

 ハードウエアで実現した、スイッチのチャタリング除去
 回路を、ソフトウエアで置換してみます。

 ハードウエアを使ったチャタリング除去回路は、以下。



 回路図から、次の3ブロックで構成されていることと
 判断できます。

 各ブロックを、どう実現するのかを考えていきます。

 クロックジェネレータ

  スイッチの状態を記憶するためのタイミングを
  生成しているので、タイマー割込みを使えば
  実現できるはず。

 シフトレジスタ

  レジスタは、変数と同じなので1バイト確保し
  タイマー割込みが入るたびに、1ビットの情報
  を、心太のように変数のLSB(Least Significant Bit)
  から押し込んでいけば実現できます。

  タイミングチャートでは、次のようになっていけば
  よいはず。



 エッジ検出

  回路では、シフトレジスタの2ビットを論理積に
  接続して、出力が1か0になるようにしています。

  シフトレジスタの出力で論理積を取ったときには
  どうなるか、タイミングチャートで見ると以下。



  どういうことなのかは、次の解釈でよいでしょう。

  シフトレジスタの値を、10進数でみると1に
  なっていれば、外部に'H'を出力すれば、それで
  チャタリング除去されたスイッチの状態である
  と見なせます。


 ここまでで動作内容が確定したので、各ブロックの
 処理をコードに落としていきます。

 クロックジェネレータ

  タイマー割込みを生成すればよいので、対応する
  ハンドラを作成。

void interrupt(void)
{
  /* timer0 overflow */
  if ( INTCON.T0IF == ON ) {
    /* clear flag */
    INTCON.T0IF = OFF ;
    /* initialize */
    TMR0 = CNTBEGIN ;
    /* set event flag */
    TFLAG = ON ;
  }
}

  TFLAGは、イベント発生の通知フラグとしておきます。
  割込みでは、なるべく処理することを少なくしておか
  ないと、他の処理の迷惑になるので、次の準備をした
  なら、イベント発生通知フラグをセット。

  ハンドラでは、INTCON.T0IFで、タイマー0が
  オーバーフローしたことを検出し、フラグを
  セットします。

  コンピュータを動かすためには、クロックが必要に
  なるので、内蔵で4MHzを生成。

   /* 4MHz */
   OSCCON = (0x0d << 3) | 0x03 ;

  タイマー0は、このクロックを分周して使うので
  どのくらいの値になっているのかを、知らないと
  次の設定ができないので、重要。

  4MHzのクロックの1/4が、タイマー0に与えられるので
  プリスケーラとカウンタの組みあわせで、割込み周期を
  指定します。

  次のコードで、125Hzで外部の状態を読み込むように設定。

   /* initialize Timer 0 */
   {
     /*
        4MHz/4 = 1MHz -> 1MHz/32 = 31.25kHz prescaler = 1:32
     */
     OPTION_REG = 0x04 ;
     /* 256 - 6 = 250 => 31.25kHz / 250 = 125 Hz */
     TMR0 = CNTBEGIN ;
     /* enable timer0 overflow interrupt */
     INTCON.T0IE = ON ;
   }

  主な内容は決めたので、内蔵されている他モジュールが
  使われないように、まとめます。

void init_usr(void)
{
  /* 4MHz */
  OSCCON = (0x0d << 3) | 0x03 ;
  /* disable D/A converter */
  DACCON0.DACEN = OFF ;
  /* disable compare module */
  CM1CON0.C1ON = OFF ;
  CM1CON0.C1OE = OFF ;
  /* initialize A/D converter */
  ADCON0.ADON = OFF ;
  /* I/O state */
  PORTA = 0 ;
  /* I/O directions */
  TRISA = 0x38 ; /* bit0 - bit 2 as outputs ,  others as input */
  /* initialize Timer 0 */
  {
    /*
       4MHz/4 = 1MHz -> 1MHz/32 = 31.25kHz prescaler = 1:32
    */
    OPTION_REG = 0x04 ;
    /* 256 - 6 = 250 => 31.25kHz / 250 = 125 Hz */
    TMR0 = CNTBEGIN ;
    /* enable timer0 overflow interrupt */
    INTCON.T0IE = ON ;
  }
  /* enable general interrupt */
  INTCON.GIE = ON ;
  /* initialize flags */
  xflags = 0 ;
  /* initialize variables */
  xsft = 0 ;
  ysft = 0 ;
}

 シフトレジスタ

  シフトレジスタは、タイマー割込みが発生したときに
  操作できるように、無限ループ中でイベント発生通知
  フラグを利用して捕捉。

    if ( TFLAG == ON ) {
      /* clear */
      TFLAG = OFF ;
      /* shift register handling */
    }

  シフトレジスタは、2ビットだけを使うので
  変数xsft、ysftをどう扱うのか考えます。

  PICのピンから入力した論理値を、LSBに心太の
  ように、格納すればよいので、次の処理で対応。

      xsft <<= 1 ;
      ysft <<= 1 ;
      /* masking */
      xsft &= MASK03 ;
      ysft &= MASK03 ;
      /* judge */
      if ( PORTA.F5 == ON ) { xsft |= ON ; }
      if ( PORTA.F4 == ON ) { ysft |= ON ; }

  処理を合体させます。

    if ( TFLAG == ON ) {
      /* clear */
      TFLAG = OFF ;
      /* shift register handling */
      xsft <<= 1 ;
      ysft <<= 1 ;
      /* masking */
      xsft &= MASK03 ;
      ysft &= MASK03 ;
      /* judge */
      if ( PORTA.F5 == ON ) { xsft |= ON ; }
      if ( PORTA.F4 == ON ) { ysft |= ON ; }
    }

 エッジ検出

  シフトレジスタの値が1であれば、スイッチが
  押されたとエッジで判断できます。
  そのとき、パルスを出力すればよいので、次の
  ように単純なコードにまとめられます。

    if ( xsft == 1 ) {
      TRGX = ON ;
      TRGX = OFF;
    }
    if ( ysft == 1 ) {
      TRGY = ON ;
      TRGY = OFF;
    }

 全体のソースコードは、以下。

typedef unsigned char UBYTE ;

/* system macro */
#define OFF 0
#define ON  OFF+1

#define CNTBEGIN 6

#define MASK03 3

/* trigger port */
#define TRGX PORTA.F0
#define TRGY PORTA.F1

volatile UBYTE xflags ;

#define TFLAG xflags.F0

volatile UBYTE xsft ;
volatile UBYTE ysft ;

/* function prototype */
void  init_usr(void);

/* interrupt handler */
void interrupt(void)
{
  /* timer0 overflow */
  if ( INTCON.T0IF == ON ) {
    /* clear flag */
    INTCON.T0IF = OFF ;
    /* initialize */
    TMR0 = CNTBEGIN ;
    /* set event flag */
    TFLAG = ON ;
  }
}

void main(void)
{
  /* initialize */
  init_usr() ;
  /* endless loop */
  while ( ON ) {
    /* timer event handling */
    if ( TFLAG == ON ) {
      /* clear */
      TFLAG = OFF ;
      /* shift register handling */
      xsft <<= 1 ;
      ysft <<= 1 ;
      /* masking */
      xsft &= MASK03 ;
      ysft &= MASK03 ;
      /* judge */
      if ( PORTA.F5 == ON ) { xsft |= ON ; }
      if ( PORTA.F4 == ON ) { ysft |= ON ; }
    }
    /* output handling */
    if ( xsft == 1 ) {
      TRGX = ON ;
      TRGX = OFF;
    }
    if ( ysft == 1 ) {
      TRGY = ON ;
      TRGY = OFF;
    }
  }
}

/* define function body */
void init_usr(void)
{
  /* 4MHz */
  OSCCON = (0x0d << 3) | 0x03 ;
  /* disable D/A converter */
  DACCON0.DACEN = OFF ;
  /* disable compare module */
  CM1CON0.C1ON = OFF ;
  CM1CON0.C1OE = OFF ;
  /* initialize A/D converter */
  ADCON0.ADON = OFF ;
  /* I/O state */
  PORTA = 0 ;
  /* I/O directions */
  TRISA = 0x38 ; /* bit0 - bit 2 as outputs ,  others as input */
  /* initialize Timer 0 */
  {
    /*
       4MHz/4 = 1MHz -> 1MHz/32 = 31.25kHz prescaler = 1:32
    */
    OPTION_REG = 0x04 ;
    /* 256 - 6 = 250 => 31.25kHz / 250 = 125 Hz */
    TMR0 = CNTBEGIN ;
    /* enable timer0 overflow interrupt */
    INTCON.T0IE = ON ;
  }
  /* enable general interrupt */
  INTCON.GIE = ON ;
  /* initialize flags */
  xflags = 0 ;
  /* initialize variables */
  xsft = 0 ;
  ysft = 0 ;
}


目次

inserted by FC2 system