PR

PICマイコン(PIC16F1827)のDIのチャタリング防止

組み込みエンジニア
本記事はプロモーションが含まれています。

こんにちは、ENGかぴです。

マイコンにおいて外部機器の状態をDIに取り込んで判定させることがあります。スイッチやリレーなどで遮断容量が大きいほど瞬時に信号が切り替わらず信号が安定しない期間(チャタリング)があります。ソフトで対策を行う方法をまとめました。

DIのチャタリング防止したフィルタをDIフィルタと表現します。DI情報のサンプリングタイミングはTMR0を使ってソフトウェアタイマを構成して生成します。

PIC16F1827で動作確認したことについてリンクをまとめています。

PICマイコン(PIC16F1827)で実現できる機能と解説リンクまとめ

PIC16F1827でDIフィルタをつくる

マイコンのDI(デジタル入力)は入力信号の電圧によってHighレベル(1)かLowレベル(0)かの判定を行います。DI信号を取り込む際に注意する必要があるチャタリングとDIフィルタについて説明します。

DI信号のチャタリング

チャタリングの説明図

スイッチがONからOFF(又はOFFからON)になるとスイッチの接触抵抗などによって電圧レベルが安定しない瞬間があります。

数ms経過するとDI信号は安定するので安定した状態をDI情報として取り込む必要があります。

僅か数ms間ではありますが1と0が繰り返されるためフィルタを入れていなかった場合誤動作の可能性があります。リレーの接点など遮断容量が大きなものほどチャタリングが大きく出る傾向があります。

周辺回路にコンデンサを入れたりすることでチャタリングの高周波成分を減衰させることができますが完全に除去できるわけではありません。

ソフトでDI信号が安定したことを確認して入力として採用することでチャタリングの影響を抑えることができます。

PR:スキマ時間で自己啓発!スマホで学べる人気のオンライン資格講座【スタディング】まずは気になる講座を無料で体験しよう!

DIフィルタの例

DIフィルタの説明
DIフィルタの説明

wpはbuf[0]からbuf[3]においてデータを格納する位置を示すポインタです。buf[0]から順番にwpを更新しながらbuf[1]・・・buf[3]までデータを格納します。buf[3]にデータを格納するとwpをクリアして次のデータをbuf[0]に格納するようにします。

void DiFilter(void){
    uint8_t i;
    bool    boo = true;

    DiData.buf[ DiData.wp ] = IO_RB0_GetValue();

    for( i = 1; i < DI_NUM; i++){
        if( DiData.buf[i-1] != DiData.buf[i] ){
            boo = false;
            break;
        }
    }

    if( boo ){ //比較して一致したら値を採用
         IO_RA3_LAT = DiData.buf[ 0 ]; 
    }

    if( ++DiData.wp >= DI_NUM){ //wpを更新
        DiData.wp = 0; wpをクリア
    }
}

例ではRB0のDI情報を4回分の格納したデータが一致したときにDIデータとして採用するようにフィルタを構成したものです。ソフトウェアタイマによって10ms毎にDiFilter()の処理を行うようにします。

buf[0]にデータを格納すると10ms後にはbuf[1]にDI値を格納します。これを繰り返してbuf[0]からbuf[3]までの4つの値が一致した時にDI信号の値として採用します。

4回分の値が一致してから出力が変更されることから応答時間が最小で約40msになりますが、チャタリングしてDI信号が安定しない数msの区間を除いて値が採用できるようになります。

タイマーの時限を10msとしているのでボタンを押すタイミングによっては最大で10msの誤差が出ます。これらを考慮して応答時間は40ms~50msになります。

ソフトウェアタイマの構成によってDiFilter()をコールするタイミングが長くなるほどチャタリング防止による誤動作は防ぎやすくなりますが、応答時間が長くなってしまいます。

ソフトウェアタイマの間隔を短くしたりバッファの値を増やしたりなど、仕様によって調整することによって任意のフィルタを実装することができます。

PR:RUNTEQ(ランテック )- マイベスト3年連続1位を獲得した実績を持つWebエンジニア養成プログラミングスクール

DIフィルタを実装する

MCCが作成した関数をmain.cに組み込んでTMR0オーバーフロー割り込みを発生させタイマ管理を行いDIフィルタ処理を行います。

MCCによる設定

MCCを使ってレジスタ設定を行っています。タイマ管理の方法と同様の設定でDIフィルタを構成しています。MCCの設定については下記記事を参考にしてください。

PICマイコン(PIC16F1827)でタイマーを管理する方法

動作確認用の回路

DIフィルタの動作確認用の回路
DIフィルタの動作確認用の回路

タイマ管理によって10ms毎にRB0のDI情報を取得します。DIフィルタによってDIが確定した値をもとにLED3を点灯/消灯します。SW1を押しDIフィルタでLOWが確定できるとLED3を消灯します。

タイマ管理によって250ms毎にRB6をトグル出力(反転しながら出力)させるためLED2を点灯/消灯します。LED1はDIフィルタにおいて状態が不一致の区間を検出して2秒間点灯させます。SW1がONからOFF(OFFからON)に状態変化したとき点灯します。

RB0はウィークプルアップしているため信号レベルが浮くことはありません。ピンの配置は以下の通りです。

PIC16F1827のピン設定(MCC)
PIC16F1827のピン設定(MCC)

PB0はINPUTでウィークプルアップ(WPU)設定しています。WPUを有効にするためにはRegistersのnWPUENをenabledにする必要があります。

Easy SetupでWPUにチェックするのみでは有効にならないので注意が必要です。Notificationsにワーニングで通知されます。

PR:企業で求められる即戦力技術を身に付ける テックキャンプエンジニア転職

DIフィルタの初期化

DIフィルタを使用すると初期起動時に状態変化を検出することがあります。RB0をウィークプルアップを使用していますが初期化時はDIフィルタを構成するバッファがクリアされた状態であるため状態の不一致が発生する期間があります。

メイン処理に遷移したときにDIフィルタが確定していないために誤動作を検出してしまう可能性があります。

void main(void)
{
    DiFilterInit(); //メイン処理前にDIを確定する

    while (1){
        //メイン処理
    }
}
// DIフィルタの初期化 //
void DiFilterInit(void){
	
    CntInit = CNT_INIT_MAX;
    while(CntInit > 0){  //0になるまでフィルタを実施
	DiFilter();      //DIフィルタ処理
	__delay_ms(10);  //10ms遅延させてDIフィルタ処理
        CntInit--;
    }    
}

例のようにメイン処理においてwhite(1)に遷移する前にDiFilterInit()関数によってDI情報を確定しておく必要があります。

DiFilterInit()はメイン処理に遷移する前なのでdelay_ms(10)を使用して10msウェイトを置いています。規定回数(CNT_INIT_MAX)のDIフィルタの処理を行うことでDIが確定した状態でメイン処理に遷移することができます。

スポンサーリンク

ソースコード全体

ソースコードは記事作成時点において動作確認できていますが、使用しているライブラリの更新により動作が保証できなくなる可能性があります。また、ソースコードを使用したことによって生じた不利益などの一切の責任を負いかねます。参考資料としてお使いください。

リンクからZIPファイル形式のファイルをダウンロードし、任意の場所に展開していただくとテキストファイルが生成されます。

ソースコードをダウンロード

main.cをコピーすると使用できます。本ソースコードはMPLAB X IDEにMCCのプラグインをインストールしていることが前提となります。MCCをインストールする方法は下記記事を参考にしてください。

MPLAB Code Configurator(MCC)の追加と使い方

関連リンク

PICマイコンを使ってマイコンのレジスタの設定やMPLAB X IDEのプラグインであるMCCを使用して動作確認したことについてまとめています。

PICマイコン(PIC12F675)で実現できる機能と解説リンクまとめ

PICマイコン(PIC16F1827)で実現できる機能と解説リンクまとめ

広告
マイベスト3年連続1位を獲得した実績を持つ実践型のプログラミングスクール

最後まで、読んでいただきありがとうございました。

タイトルとURLをコピーしました