こんにちは、ENGかぴです。
マイコンのソフト開発しているとDIに信号を入力しては処理を判定させることがよくあります。DI信号がスイッチなどであった場合チャタリングによってDI信号が安定しないことがあります。対策としてフィルタを作る方法をまとめました。
この考え方はPICマイコンだけでなく他のマイコンでも応用できます。メインループを周回するごとにDIピンの情報を確認して規定回数一致すると採用する考え方もできますが、今回はタイマ機能を使ってフィルタを作っています。
PIC12F675を使ってマイコンの動きを勉強するためにPIC12F675の機能でできることについてまとめています。
PICマイコン(PIC12F675)で実現できる機能と解説リンクまとめ
PIC12F675のDIのチャタリング防止
マイコンのDIはデジタル入力です。入力された電圧値からHighレベル(1)かLowレベル(0)かの判定を行います。スイッチのチャタリングなどが原因でマイコンが誤動作する可能性があるため注意が必要です。
DI信号のチャタリング
僅か数ms間ではありますが1と0が繰り返されるためフィルタを入れていなかった場合誤動作の可能性があります。リレーの接点などは容量が大きくチャタリングが大きく出ることがあります。
周辺回路にコンデンサを入れたりすることでチャタリングの高周波成分を減衰させることができますが完全に除去できるわけではありません。
【クリエイターズファクトリー】卒業がない!挫折する心配なし!Webスクール説明会申し込み
DIフィルタの効果

DIフィルタはソフトで変化をコントロールする処理を作って実現したものです。DIフィルタの考え方は様々ですが、例の一つとして10ms毎にDIを確認して数回一致した場合ときのDI値を採用する方法を考えていきます。
TMR1を10msでオーバーフローするように実装し10ms毎にDI値を準備したバッファ(値を格納する変数)に入れていきます。buf[0]が初めに格納するバッファとしたとき10ms後にはbuf[1]にDI値を格納します。これを繰り返してbuf[0]からbuf[3]までの4つの値が一致した時にDI信号の値として採用します。
これによってチャタリングしてDI信号が安定しない部分を除いて値が採用できるようになります。
例では4つのバッファで構成していますが、TMR1の間隔を短くしてバッファの値を増やす方法を選択したり、TMR1の間隔を長くしてバッファの数を減らすなど使い方によって調整することによって任意のフィルタを実装することができます。
PR:RUNTEQ(ランテック )- マイベスト3年連続1位を獲得した実績を持つWebエンジニア養成プログラミングスクール
PIC12F675の初期設定
DIフィルタはTMR1割り込みを使って実装することができます。TMR1を10msでオーバーフローするように設定しDI信号を格納するバッファは4つ(40ms程度のフィルタ)とします。
TMR1を実装する
TMR1の初期化と使い方については下記記事を参考にしてください。
PICマイコン(PIC12F675)の割り込みの設定と使い方
TMR1はDI値を格納するタイミングとして使用します。
初期部分を実装する
typedef struct{
char wp;
char buf[ DI_NUM ];
}FILT_DATA;
//--------------変数定義----------------------------
FILT_DATA DiData; //FILT_DATAの型で宣言
//-----------------関数定義---------------------------
void DiFilter(void);
void main(void) {
//各種初期化
//DIフィルタ初期化
DiData.wp = 0;
filtflg = BIT_CLR;
//DIフィルタ初期起動
CntInit = CNT_INIT_MAX;
while(CntInit > 0){ //0になるまでフィルタを実施
CLRWDT();
DiFilter(); //DIフィルタ処理
__delay_ms(10); //10ms遅延させてDIフィルタ処理
CntInit--;
filtflg = BIT_SET;
}
//タイマの初期化
while(1){//メインの無限ループ
CLRWDT();
DiFilter(); //DIフィルタ
}
DIフィルタのデータを格納するための構造体を定義して変数定義を行っています。DI値を格納する変数DiDataの初期化を行います。
メイン関数のループの前にDIフィルタの初期化を行っているのはメイン関数に入る前にDI情報を確定するためです。
TMR1を起動する前にDIフィルタを行う場合のタイミングをdelay関数で作っています。10msのウェイトを10回繰り返してwhileから抜けるようにしています。TMR1を起動して10回割り込みによる処理が終わったらループを抜ける作りでもよいと思います。
例えばプログラム開始時は変数は0になっているため1が入力されていた場合に初期化をしておくことで1を確定してからメイン動作に移ることができます。
PR:スキマ時間で自己啓発!スマホで学べる人気のオンライン資格講座【スタディング】まずは気になる講座を無料で体験しよう!
DIフィルタを実装する
DIフィルタを実装するためにDIピンにスイッチのON/OFFが入力できるようにして動作確認します。
動作確認用の回路図

SW1を押すとGP3の入力に1が入ります。10msの経過の割り込みの4回分の値が1となり一致した時LED2が点灯します。ONからOFFにしたときも同様に10msの経過割り込みが4回分0で一致した時にLED2が消灯します。
DIフィルタを実装例
//割り込み関数
void __interrupt() intr( void){
if( TMR1IE == BIT_SET && TMR1IF == BIT_SET){ //タイマ1の割り込みであるか
TMR1IF = BIT_CLR; //割り込みフラグをクリア
TMR1ON = BIT_CLR;
TMR1H = TIME_START_H; //タイマ初期化
TMR1L = TIME_START_L; //タイマ初期化
TMR1ON = BIT_SET; //タイマ1ON
filtflg = BIT_SET;
}
}
//--------モード切替部のDIフィルタ----------------------------
void DiFilter(void){
if( filtflg ){
filtflg = BIT_CLR;
DiData.buf[ DiData.wp ] = GPIO3;
if( DiData.buf[ 0 ] == DiData.buf[ 1 ] &&
DiData.buf[ 1 ] == DiData.buf[ 2 ] &&
DiData.buf[ 2 ] == DiData.buf[ 3 ] ){
//比較して一致したら値を採用
GPIO2 = DiData.buf[ 0 ];
}
if( ++DiData.wp >= DI_NUM){
DiData.wp = 0;
}
}
}
メイン処理の中にDiFilter()が毎回処理されますが、TMR1がオーバーフローしたときのみDIフィルタの処理をするようにオーバーフローしたときにfiltflgをセットしてDiFilter()の内部処理を行うようにしています。
今回は10msのタイマを作って4回分の値が一致することを確認して一致した場合にGP2の出力として採用するようにしています。
4回分の値が一致してから出力が変更されることから応答時間が最小で約40msとなります。
ソースコード全体
ソースコードは記事作成時点において動作確認できていますが、使用しているライブラリの更新により動作が保証できなくなる可能性があります。また、ソースコードを使用したことによって生じた不利益などの一切の責任を負いかねます。参考資料としてお使いください。
リンクからZIPファイル形式のファイルをダウンロードし、任意の場所に展開していただくとテキストファイルが生成されます。
main.cをコピーすると使用できます。
関連リンク
PICマイコンを使ってマイコンのレジスタの設定やMPLAB X IDEのプラグインであるMCCを使用して動作確認したことについてまとめています。
PICマイコン(PIC12F675)で実現できる機能と解説リンクまとめ
PICマイコン(PIC16F1827)で実現できる機能と解説リンクまとめ
最後まで、読んでいただきありがとうございました。