Push SWを使用するときに、注意しなければならないことは、チャタリングの防止です。 充分に考慮しておかないとSWを1回押したつもりでも、何度も押したときと同じ動作を指定増します。 以下に、良く使用するプログラムを示します。 ( 2017/7/5 - XC8用にプログラムを書き換えました )
PushSWが押されるとLEDの表示を増加させる内容です。
PushSWが押されるまで while と delay 文で待つ単純な防止プログラム例です。
// File name: Anti-Chattering
// Description: チャタリング防止
// Notes: 4MHz内部クロック
// Language: MPLABX xc8
// Target: PIC18F14K50
#include <xc.h>
#define _XTAL_FREQ 4000000 // delay_ms(x) のための定義
#define LEDs LATC
#define pushSW PORTBbits.RB7 // スイッチを定義
#pragma config FOSC = IRC // 内部クロック
#pragma config USBDIV = OFF, CPUDIV = NOCLKDIV
#pragma config IESO = OFF, FCMEN = OFF, PLLEN = ON
#pragma config BORV = 30, BOREN = OFF, PWRTEN = OFF
#pragma config WDTPS = 32768, WDTEN = OFF
#pragma config MCLRE = OFF, HFOFST = OFF, XINST = OFF
#pragma config BBSIZ = OFF, LVP = OFF, STVREN = ON
#pragma config CP1 = OFF, CP0 = OFF, CPD = OFF, CPB = OFF
#pragma config WRT1 = OFF, WRT0 = OFF, WRTB = OFF, WRTC = OFF
#pragma config EBTR1 = OFF, EBTR0 = OFF, EBTRB = OFF
//----------------------メインプログラム-------------------------
void main(void){
char count = 0;
OSCCON = 0b01010010; // 内部クロック4Mhz
LATC = 0;
TRISC = 0; // PortC 出力に設定
// -------------- 繰返し -----------------
while(1){
while(!pushSW); // SWが押されていないことを確認
__delay_ms(5); // チャタリング防止 5mS遅延
while(pushSW); // SWが押されるのを待つ
__delay_ms(5); // チャタリング防止 5mS遅延
LEDs = ++count; // 表示を1増やす
}
}
Timer0で内部クロック(4MHz)をカウントし、プリスケラで1/4にして、 約 1mS毎に割込みが発生します。 チャタリング防止はこの割込みルーチン内にプログラムされており、pushSWのチャタリングを排除したSWcleanを生成するとともに、SWが押されると「1」になる SWpress がも作っています。 SWpress は、SW入力を処理するメインルーチン内で「0」に戻しています。
// File name: Anti-Chattering
// Description: チャタリング防止
// Timer0で内部クロック(4MHz)をプリスケラで1/4にして、
// カウントアップで割込みが発生する(1024uS毎)
// 割込みごとにKeyの状態を確認する。
// Notes: 4MHz内部クロック
// Language: MPLABX XC8
// Target: PIC18F14K50
#include <xc.h>
#define LEDs LATC
#define pushSW !PORTBbits.RB7 // スイッチを定義
#define MonCnt 5 // チャタリングの防止時間
#pragma config FOSC = IRC
#pragma config USBDIV = OFF, CPUDIV = NOCLKDIV
#pragma config IESO = OFF, FCMEN = OFF, PLLEN = ON
#pragma config BORV = 30, BOREN = OFF, PWRTEN = OFF
#pragma config WDTPS = 32768, WDTEN = OFF
#pragma config MCLRE = OFF, HFOFST = OFF, XINST = OFF
#pragma config BBSIZ = OFF, LVP = OFF, STVREN = ON
#pragma config CP1 = OFF, CP0 = OFF, CPD = OFF, CPB = OFF
#pragma config WRT1 = OFF, WRT0 = OFF, WRTB = OFF, WRTC = OFF
#pragma config EBTR1 = OFF, EBTR0 = OFF, EBTRB = OFF
// ----------------------------------------------
char SWmonit = MonCnt; // チャタリング防止回数定義
char SWclean = 0; // チャタリングを排除したSW信号
char SWpress = 0; // チャタリングを排除した「押した」マーク
char SWreles = 0; // チャタリングを排除した「離した」マーク
//----------------------高優先割込み------------------------
void interrupt Hi_Isr(void){
if(TMR0IF){ //Timer0からの割込みを確認
TMR0IF = 0; // 割込みフラッグをクリア
// チャタリング防止 -----------------------------------------
if(SWmonit == 0){ // SWが安定状態時
if(SWclean != pushSW) // SWの状態が変更されたら
SWmonit = MonCnt; // チャタリング防止をセット
}else{ // チャタリング防止中に
if(SWclean != pushSW){ // SWの状態が変化したままか
SWmonit--; // 変化継続を5回確認
if(SWmonit == 0){ // 防止期間終了で
SWclean = pushSW; // 新しい状態を反映
if(SWclean)SWpress = 1; // ONなら 「押した」を1
else SWreles = 1; // OFFなら「離した」を1
}
}else{ // SWが元に戻ったら
SWmonit = 0; // SWが安定状態を維持
}
}
}
}
//----------------------メインプログラム-------------------------
void main(void){
char count = 0;
OSCCON = 0b01010010; // 内部クロック4Mhz
ANSEL = 0; // Port をデジタルに設定
ANSELH = 0;
LATC = 0;
TRISC = 0; // PortC 出力に設定
T0CON = 0b11000001; // Timer 0 設定(1mSecごと)
// 内部クロックをカウント
// 8BIT モード PS_1/4
TMR0IP = 1; // Timer0 からの割込みを高優先に設定
TMR0IF = 0; // Timer0 からの割込みフラッグをクリア
TMR0IE = 1; // Timer0 からの割込みを許可
RCONbits.IPEN=1; // 2段階の割込みに設定
INTCONbits.GIEH=1; // 高優先割込みを許可
INTCONbits.GIEL=0; // 低優先割込みを禁止
// -------------- 繰返し -----------------
while(1){
if(SWpress){ // SWが押されたら
LEDs = ++count & 0x7; // 表示を1増やす
SWpress = 0; // 処理済みを明示
}
LATC3 = SWclean;
}
}
上記プログラムと下記プログラムとの差はチャタリングを排除したSWcleanを生成するタイミングにあります。
上のプログラムは、SWが押されるとチャタリングが収まるのを待ってから、新しい状態を反映します。
下のプログラムは、状態が変化するとすぐに新しい状態を反映し、その後一定時間はSWの状態が変わってもSWcleanへの反映を禁止し、チャタリングを排除します。
// File name: Anti-Chattering
// Description: チャタリング防止
// Timer0で内部クロック(4MHz)をプリスケラで1/4にして、
// カウントアップで割込みが発生する(1024uS毎)
// 割込みごとにKeyの状態を確認する。
// Notes: 4MHz内部クロック
// Language: MPLABX XC8
// Target: PIC18F14K50
#include <xc.h>
#define LEDs LATC
#define pushSW !PORTBbits.RB7 // スイッチを定義
#define MonCnt 5 // チャタリングの防止時間
#pragma config FOSC = IRC
#pragma config USBDIV = OFF, CPUDIV = NOCLKDIV
#pragma config IESO = OFF, FCMEN = OFF, PLLEN = ON
#pragma config BORV = 30, BOREN = OFF, PWRTEN = OFF
#pragma config WDTPS = 32768, WDTEN = OFF
#pragma config MCLRE = OFF, HFOFST = OFF, XINST = OFF
#pragma config BBSIZ = OFF, LVP = OFF, STVREN = ON
#pragma config CP1 = OFF, CP0 = OFF, CPD = OFF, CPB = OFF
#pragma config WRT1 = OFF, WRT0 = OFF, WRTB = OFF, WRTC = OFF
#pragma config EBTR1 = OFF, EBTR0 = OFF, EBTRB = OFF
// ----------------------------------------------
char SWmonit = MonCnt; // チャタリング防止回数定義
char SWclean = 0; // チャタリングを排除したSW信号
char SWpress = 0; // チャタリングを排除した「押した」マーク
char SWreles = 0; // チャタリングを排除した「離した」マーク
//----------------------高優先割込み------------------------
void interrupt Hi_Isr(void){
if(TMR0IF){ // Timer0からの割込みを確認
TMR0IF = 0; // 割込みフラッグをクリア
// チャタリング防止 -----------------------------------------
if(SWmonit){ // SW操作後の変更禁止期間なら
SWmonit--; // 変更禁止期間を減らす
}else{ // 変更可能期間なら
if(SWclean!=pushSW){ // 状態が変わったなら
SWclean=pushSW; // 変更を反映
SWmonit=MonCnt; // 変更禁止をセット
if(SWclean){ // SWが押されたなら
SWpress=1; // 押されたマークを1
}else{ // SWが離れたなら
SWreles=1; // 離れたマークを1
}
}
}
}
}
//----------------------メインプログラム-------------------------
void main(void){
char count = 0;
OSCCON = 0b01010010; // 内部クロック4Mhz
ANSEL = 0; // Port をデジタルに設定
ANSELH = 0;
LATC = 0;
TRISC = 0; // PortC 出力に設定
T0CON = 0b11000001; // Timer 0 設定(1mSecごと)
// 内部クロックをカウント
// 8BIT モード PS_1/4
TMR0IP = 1; // Timer0 からの割込みを高優先に設定
TMR0IF = 0; // Timer0 からの割込みフラッグをクリア
TMR0IE = 1; // Timer0 からの割込みを許可
RCONbits.IPEN=1; // 2段階の割込みに設定
INTCONbits.GIEH=1; // 高優先割込みを許可
INTCONbits.GIEL=0; // 低優先割込みを禁止
// -------------- 繰返し -----------------
while(1){
if(SWpress){ // SWが押されたら
LEDs = ++count & 0x7; // 表示を1増やす
SWpress = 0; // 処理済みを明示
}
LATC3 = SWclean;
}
}
一般的な処理では、SWclean情報や、SWがOFFになる情報は使用しないことが多くあります。もし、SWが押された ON 情報だけがプログラムで必要なら、多少チャタリング防止は短くすることができます。
チャタリング防止部分のプログラムおよび、そのフローチャートを示します。
//------ SWチャタリング防止 1msループ内 -------
if(SWmonit){ // SWupの確認中
if(pushSW)SWmonit--; // 確認中をー1
else SWmonit = 5; // 確認を最初から
}else if(!pushSW){ // 確認済みでDownなら
SWpress = 1; // SWが押された
SWmonit = 5; // 確認待ちに
}