ピン変更割り込みの正しい使用法


10

ピンの変更割り込みを使用して、押されたボタンを検出しようとしています。これまでは、この種の割り込みを扱ったことはなく、いくつかの問題があるので、これが正しい使い方かどうかを確認したいと思います。

データシートが正しい場合、ピン変更割り込みを使用するには、次のことを行う必要があります。

  1. 制御するPINをPCMSKレジスタに設定します
  2. ピン変更割り込み制御(PCICR)のPINレジスタを有効にする
  3. 割り込みを有効にする
  4. 対応する割り込みベクターを使用する

プロジェクト:シンプルなムードランプ、4つのボタンで色を制御。

セットアップ:

  • Atmega168A-PU
  • 4つのミニプッシュボタンスイッチ
  • 私の3ワットRGB LEDを制御するMOSFET

これが私が使用しているコードで、期待どおりに機能していません:

#include <avr/io.h>
#include <stdint.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#define BUTTON1 (1<<PC5) 
#define BUTTON2 (1<<PC4) 
#define BUTTON3 (1<<PC3) 
#define BUTTON4 (1<<PC2) 

#define GREEN   (1<<PB1) 
#define BLUE    (1<<PB2) 
#define RED     (1<<PB3) 

void init() {

        // enable LED
        DDRB |= GREEN;
        DDRB |= BLUE;
        DDRB |= RED;

        // button pullups
        PORTC |= BUTTON1;
        PORTC |= BUTTON2;
        PORTC |= BUTTON3;
        PORTC |= BUTTON4;

        // pin change interrupts for buttons
        PCMSK1 |= PCINT13;
        PCMSK1 |= PCINT12;
        PCMSK1 |= PCINT11;
        PCMSK1 |= PCINT10;

        // enable pin change for buttons
        PCICR |= PCIE2;

        sei();

}

ISR(PCINT2_vect) {

                PORTB = BLUE;
}


void ledTest() {

                PORTB ^= RED;
                _delay_ms(250);
                PORTB ^= RED;
                _delay_ms(250);
                PORTB ^= RED;
                _delay_ms(250);
                PORTB ^= RED;


                PORTB ^= BLUE;
                _delay_ms(250);
                PORTB ^= BLUE;
                _delay_ms(250);
                PORTB ^= BLUE;
                _delay_ms(250);
                PORTB ^= BLUE;

                PORTB ^= GREEN;
                _delay_ms(250);
                PORTB ^= GREEN;
                _delay_ms(250);
                PORTB ^= GREEN;
                _delay_ms(250);
                PORTB ^= GREEN;
}

int main() {

        init();
        ledTest();

        _delay_ms(500);
        PORTB |= GREEN;

        while(1) {
                _delay_ms(100);
        }
}

注:ボタンはデバウンスする必要があります。私は一歩一歩これを試みており、それがLEDをオンにするために合わないはずなので、ここではそれを無視しました。

質問:割り込みの使用方法は正しいですか?

私のセットアップに関する問題:

  • ボタン1〜3は完全に無視されます。
  • Button4がatmegaのリセットをトリガーしています

私がチェックしたもの:

  • ボタンがリセットPINに接続されていない
  • ボタンが押された場合、ボタンは正しくGNDに接続されています
  • ボタンが押されていない場合、ボタンはGNDに接続されていません
  • ボタンは、私が中断することなくそれらを利用する場合、うまく機能します。例:

    if(!(PINC&BUTTON4)){PORTB ^ =ブルー; }

  • 16MHZ外部クリスタル/内部クリスタル
  • ルーティングのエラー
  • AtmegaでPWRとGNDの間に100nFのコンデンサーを使用しています
  • VCC(7)、GND(8)、GND(22)、AVCC(20)が接続されています(AREFは必要ないため、接続されていません)

あなたはPCIE1フラグ(ないPCIE2)とPCINT1_vect(ないPCINT2)が必要
microtherion

PCIE1を選ぶ理由 Cレジスタを使用しているので、数えるとA(PCIE0)、B(PCIE1)、C(PCIE2)になりますか?とにかく、PCIE1とPCINT1_vectで試してみましたが、ボタンを押しても反応がありません。
echox 2013

1
そのような割り当てで直交性を仮定することは少し危険かもしれません。この特定のケースでは、ATmega168にポートAがないことを除いて、あなたはほぼ正しいでしょう。いずれにせよ、私はデータシートとピン配置を調べました。もう1つのヒントは、PCIE2を使用していたが、PCMSK1でビットを設定していたことです。正しくない可能性があります(残念ながら、修正したスケッチがまだ機能しない理由はわかりません)。
microtherion

ありがとう、セルフビルドハードウェアに依存するデバッグソフトウェアの組み合わせはそれほど簡単ではないことも理解しています;-)
echox

回答:


14

ピン変更割り込みは通常、ボタン操作を検出するための良い方法ではありません。これは、メカニカルボタンが跳ね返るため、無意味な割り込みがたくさん発生し、それでもデバウンスを実行する必要があるためです。

より良い方法は、1 ms(1 kHzレート)ごとなど、定期的な割り込みを行うことです。これはほとんどのプロセッサで長い時間であるため、割り込みに費やされる時間の割合は小さくなります。割り込みごとにボタンの状態をサンプリングするだけです。新しい状態を50ミリ秒続けて確認した場合は、新しいボタンの状態を宣言します。50ミリ秒は、ほとんどのボタンのバウンドよりも長くなりますが、それでも十分に短いので、人間はラグに気付かず、気にしません。

この方法では、同じ周期の1ミリ秒の割り込みで複数のボタンを処理することもできます。必要なのは、ボタンごとに1つのカウンターです。

デバウンス時間の詳細:

この場合のように、50ミリ秒ではデバウンス時間が長すぎると誰かが言うこともあります。これは、人間が押す通常のボタンには当てはまりません。ストップウォッチのようなタイミングが非常に重要なアプリケーションでは問題になる可能性がありますが、今のところ、これに遭遇したことはありません。私はこれについて1980年代初頭にテストしましたが、他の多くの人もテストしました。

典型的なプッシュボタンのバウンス時間は約10ミリ秒であり、ほぼすべての整定時間は25ミリ秒です。デバウンス時間の制限要因は人間の知覚です。50ミリ秒は、遅延を探していないときに人々が遅延に気づき始める場所よりも少し短いです。それでも、それが迷惑になるのにはるかに長い時間がかかります。人間が50 msと0 msの遅延の違いを具体的に探している場合、人間がそれを検出できる場合がありますが、これはボタンを押して何かが発生し、遅延について考えていないのとはかなり異なります。

したがって、遅延は通常のアプリケーションの知覚限界を下回り、煩わしさの限界をはるかに下回り、ほとんどのスイッチのバウンス時間をはるかに上回るため、50 msは適切なデバウンス時間です。ほぼ同じ時間バウンドしたスイッチを見つけたので、緩むものが何もないので、知覚の限界にプッシュすることもできます。

50ミリ秒のデバウンス時間を使用して、ファームウェアデバウンスボタンで多くの製品を実行しました。顧客が遅延に気づいてさえ言及したことは一度もありません。ボタンはすべて問題なく機能するものとして受け入れられました。


1
場合によっては50ミリ秒は長すぎるかもしれません(通常、10〜20ミリ秒は人間の知覚の限界であり、デバウンスにはそれで十分です)。しかし、ここで説明されている方法が適切です。
Laszlo Valko 2013

1
@Laszlo:いいえ、通常の場合、50 msは長すぎません。私の答えに加えて参照してください。
Olin Lathrop、2013

50ms試してみましたが、うまく動作します:-)なぜピンチェンジ割り込みが機能しないのか(バウンスのほかに)まだ気になりますが、これは動作します:-)ありがとう。
echox 2013

1

ピン変更割り込みは、ポーリングよりもデバウンスに優れた方法です。割り込みは通常、DフリップフロップやDラッチなどのロジックを通過します。これは事実ですが、このデバウンスルーチンをより高いレベルのコンパイラで実装することは困難です。割り込みが発生すると、割り込みフラグはクリアされず、遅延が発生するまで割り込みイネーブルはクリアされます。遅延が発生すると、ピンの状態がチェックされ、それがまだ割り込みをトリガーした所定の状態にある場合、ボタンの状態が変更され、割り込みフラグがクリアされ、割り込みイネーブルが設定されます。開始を引き起こした状態でない場合、割り込みイネーブルが設定され、状態は同じままです。これにより、プロセッサが他のタスクに解放されます。定期的な割り込みはプログラムの時間を浪費します。


-1

「ピン交換割り込みは通常、ボタン操作を検出するための良い方法ではありません。」

違う。PC INTが最適なオプションです。ポーリングを使用してボタンの状態を確認する場合、ほとんどの場合何も実行されません。多くの貴重なCPU時間を浪費します。PC INTでは、要求に応じてのみアクションを実行できます。

「これは、メカニカルボタンがバウンドするためであり、多くの意味のない割り込みが発生し、それでもとにかくデバウンスを実行する必要があります。」

バウンスについて修正します。ただし、割り込みルーチン内でボタン/スイッチをデバウンスしないでください(同じ理由:CPU時間の浪費)。ISRは、実際には短くて効率的であり、コード的にも意味があります。ハードウェアデバウンスを使用するだけです。ソフトウェアをクリーンに保ってください!

ハードウェアデバウンスの方が便利です。ここを参照してください/ RCデバウンス+シュミットトリガーを参照してください。PC INTで何度も使用しましたが、失敗することはありませんでした。

したがって、はい、PC INTを使用してボタンの状態を取得できます(そうする必要があります)。ただし、適切なハードウェアデバウンスも使用する必要があります。


2
ソフトウェアデバウンスは有効なアプローチであり、ほとんどの場合、少しの余分なCPUオーバーヘッドは無関係です。通常、ハードウェアでデバウンスする必要があると言うことは、せいぜい疑わしいだけです。すべてのケースでハードウェアデバウンスを使用する必要があると言うのは、まったく間違いです。
Olin Lathrop 2017年

ほとんどのアプリケーションでは、コントローラーはほとんどの場合ほとんどの場合アイドル状態で実行され、メインループが実行されます。また、IO状態チェックを実行し、変数をインクリメントする可能性があるために必要なCPU時間は最小限です。ソフトウェアでの単純なデバウンスの実装は、ほぼ「無料」で実現し、ハードウェアは費用がかかります。また、数セントで笑わないでください。組み立てにはお金もかかります。中量から大量の製品を稼働させても無視できません。ISRの時間を短くする必要があることは事実ですが、この場合はほとんど議論になりません。PC INT ISRがバウンスのために連続して50回発火する場合は、おそらくより重要です。
Rev1.0

@Nelson、「CPU時間の浪費」は一部のアプリケーションでは重要であり、他の多くのアプリケーションでは重要ではありません。CPU時間が重要な状況では、回答を修飾する必要があります。
user1139880 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.