目次
前
次
スイッチの状態取得(シフトレジスタ利用)
スイッチには、押したり、離したりするときにチャタリングと
呼ばれる論理値が確定しない状態があります。
チャタリングは、10msから50ms程度でおさまるので
この時間分だけ、間をおいてスイッチ状態を複数回
リードして、判定するという定石があります。
時間差を利用する場合、タイマー割込みを使うのが楽です。
スイッチの状態を記憶するには、1バイトの変数を
利用して、過去から現在までを1ビットの論理値で
記録します。
変数を1ビットの論理値を時系列で記録しているとき
この変数をシフトレジスタと呼びます。
タイマー割込みで、1ビット論理値の記憶タイミング
を作っていると考えます。
変数をシフトレジスタとするには、次のコードを使います。
/* timer interrupt */
if ( TFLAG == ON ) {
/* clear flag */
TFLAG = OFF ;
/* get current switch state */
tmp = PORTB ;
/* shift */
sregx <<= 1 ;
sregy <<= 1 ;
/* mask */
sregx &= 0x03 ;
sregy &= 0x03 ;
/* update LSB */
if ( tmp & 1 ) { sregx |= 1 ; }
if ( tmp & 2 ) { sregy |= 1 ; }
/* judge */
}
タイマー割込みで、希望の周期をつくり、スイッチ状態を
シフトレジスタに入れるので、論理レベルだけに注目する
場合、0→1か1→0の変化に注目する場合のどちらにも
対応できます。
タイマー割込み利用で、時間経過を常に監視する必要が
なくなり、空いた時間を他の処理に振向けることが可能
となります。
2つのプッシュボタンを利用したLEDの点灯、消灯制御の
処理をタイマー割込みを使い、書き換えてみます。
#define OFF 0
#define ON OFF+1
typedef unsigned char UBYTE ;
typedef union {
struct {
unsigned B7:1;
unsigned B6:1;
unsigned B5:1;
unsigned B4:1;
unsigned B3:1;
unsigned B2:1;
unsigned B1:1;
unsigned B0:1;
} BIT ;
unsigned char DR ;
} FLAGSP ;
volatile FLAGSP xflags ;
#define TFLAG xflags.BIT.B0
volatile UBYTE state ;
volatile UBYTE btn1 ;
volatile UBYTE btn2 ;
/* interruption */
void interrupt(void)
{
/* timer0 overflow interrupt 250Hz = 4ms */
if ( INTCON.T0IF == ON ) {
/* clear flag */
INTCON.T0IF = OFF ;
/* initialize */
TMR0 = 6 ;
/* set flag */
TFLAG = ON ;
}
}
/* prototype */
void usr_init(void);
void main(void)
{
/* initialize PORT */
usr_init();
/* endless loop */
while (ON) {
/* timer interrupt */
if ( TFLAG == ON ) {
/* clear flag */
TFLAG = OFF ;
/* shift */
btn1 <<= 1 ;
btn2 <<= 1 ;
/* mask */
btn1 &= 0x03 ;
btn2 &= 0x03 ;
/* get current switch state */
btn1 |= PORTB.F1 ;
btn2 |= PORTB.F2 ;
/* judge */
if ( btn1 == 0x03 ) { state = ON ; }
if ( btn2 == 0x03 ) { state = OFF; }
}
/* impress */
PORTB.F0 = state & ON ;
}
}
void usr_init(void)
{
/* turn off LED */
PORTB.F0 = OFF ;
/* direction */
TRISB = 0xfe ;
/* clear flags */
xflags.DR = 0 ;
/* set first state */
state = OFF ;
btn1 = 0 ;
btn2 = 0 ;
/* initialize Timer 0 */
{
/*
4MHz/4 = 1MHz -> 1MHz/16 = 62.5kHz prescaler = 1:16
*/
OPTION_REG = 0x03 ;
/* 256 - 250 = 6 */
TMR0 = 6 ;
/* enable timer0 overflow interrupt */
INTCON.T0IE = ON ;
}
/* enable general interrupt */
INTCON.GIE = ON ;
}
利用する回路は、以下。
この例では、4msごとにプッシュスイッチの状態を
取得し8msごとに、スタートとストップの動作判定
をします。
判定周期を長くしたい場合、シフトレジスタのLSBに
スイッチの状態を3から5程度入れておき、判定値
を変更します。
言葉の説明では、面倒に思えますが、コードは単純。
/* mask */
btn1 &= 0x0f ;
btn2 &= 0x0f ;
/* get current switch state */
btn1 |= PORTB.F1 ;
btn2 |= PORTB.F2 ;
/* judge */
if ( btn1 == 0x0f ) { state = ON ; }
if ( btn2 == 0x0f ) { state = OFF; }
このコードでは、2変数にそれぞれ過去3回分の
スイッチ状態を記憶したことになります。
過去3回の現在1回の情報となると、4msx8=32msだけ
論理値の1が続いていることになります。マスク値と
判定値を変えるだけで、より長い周期のサンプリング
が可能です。
仕様がかわり、0→1を捉えて状態を変更したいと
なれば、判定値を変えて対応します。
変化を捉えたいので、2回のサンプリングの結果で
判定するようにコードを修正します。
/* mask */
btn1 &= 0x03 ;
btn2 &= 0x03 ;
/* get current switch state */
btn1 |= PORTB.F1 ;
btn2 |= PORTB.F2 ;
/* judge */
if ( btn1 == 0x01 ) { state = ON ; }
if ( btn2 == 0x01 ) { state = OFF; }
この場合、現在とひとつ前のスイッチの状態
だけで考えればよくなります。
仕様変更になった場合でも、修正箇所を極力少なくし
やることを減らすと、バグが入りにくくなります。
仕様変更があっても、使いまわせるコードを日頃から
考えておくと、守備範囲が広がります。
目次
前
次