2013-04-30 6 views
5

私は比較的新しくcとRaspberry Piを使い、簡単なプログラムを試しています。ボタンが押されていても(ラッチのようなものであっても)、ボタンが再び押されるまで、printfは一度押されてからもう一度printfされません。私はおそらく2番目のwhileループを追加するとこれを修正すると思ったが、時にはまだボタンプレスを検出しない。Cプログラムは、押したときにタスクを1回実行するためのボタンです。

#include <bcm2835.h> 
#include <stdio.h> 
#define PIN RPI_GPIO_P1_11 

int main() 
{ 
    if(!bcm2835_init()) 
     return 1; 

    bcm2835_gpio_fsel(PIN, BCM2835_GPIO_FSEL_INPT); 

    while(1) 
    { 
     if(bcm2835_gpio_lev(PIN)) 
     { 
      printf("The button has been pressed\n"); 
     } 

     while(bcm2835_gpio_lev(PIN)){} 
    } 

    bcm2835_close(); 
    return 0; 
} 
+3

「ボタンデバウンシング」のGoogle検索が役立つかもしれません。 –

+2

その技術用語を知ることで、私はそれを調べることができます。ありがとうございました – Marmstrong

+0

幸いです - 私はそれが答えを保証するとは思わなかったが、検索するために適切な用語を持っていることは時々本当に助けになる! –

答えて

1

このような単純なプログラムの場合、実行したようなビジーなループを使用すると問題ありません。しかし、私はおもちゃのプロジェクト以上のものでは受け入れられないことが多いので、習慣から脱却することを提案します。

ボタンをデバウンスするには、コードを書く人がいるのと同じくらい多くの方法があります。ハードウェアで行うことは、場合によっては行こうとするかもしれませんが、欠点がないわけではありません。いずれにしても、これはプログラミングサイトなので、ハードウェアを変更できない(またはしたくない)と仮定しましょう。

メインループ内のボタンを定期的にチェックし、変更された場合にのみ行動します。Cや組み込みプログラミングに慣れていないので、私はタイマーや割り込みを避けますが、コードを理解すればコードを理解しやすく保守しやすくすることができます。

#include <bcm2835.h> 
#include <stdio.h> 
#define PIN RPI_GPIO_P1_11 

// A decent value for the number of checks varies with how "clean" your button is, how 
// responsive you need the system to be, and how often you call the helper function. That 
// last one depends on how fast your CPU is and how much other stuff is going on in your 
// loop. Don't pick a value above UINT_MAX (in limits.h) 
#define BUTTON_DEBOUNCE_CHECKS 100 

int ButtonPress() 
{ 
    static unsigned int buttonState = 0; 
    static char buttonPressEnabled = 1; 

    if(bcm2835_gpio_lev(PIN)) 
    { 
     if(buttonState < BUTTON_DEBOUNCE_CHECKS) 
     { 
      buttonState++; 
     } 
     else if(buttonPressEnabled) 
     { 
      buttonPressEnabled = 0; 
      return 1; 
     } 
    } 
    else if(buttonState > 0) 
    { 
     buttonState--; 
     // alternatively you can set buttonState to 0 here, but I prefer this way 
    } 
    else 
    { 
     buttonPressEnabled = 1; 
    } 

    return 0; 
} 

int main() 
{ 
    if(!bcm2835_init()) 
     return 1; 

    bcm2835_gpio_fsel(PIN, BCM2835_GPIO_FSEL_INPT); 

    while(1) 
    { 
     if(ButtonPress()) 
     { 
      printf("The button has been pressed\n"); 
     } 

     // the rest of your main loop code 
    } 

    bcm2835_close(); 
    return 0; 
} 
+0

このコードを実装しようとしました。私は "BUTTON_DEBOUNCE_CHECKS"を変更したので、まだ少しバウンスがあった。私は良い値が約300であることが分かった(私のボタンはおそらく "クリーン"ではない)。多くの場合、バウンスの原因となったボタンを放していました。 (また、 "BUTTON_DEBOUNCE_CHECKS" = 5000、楽しい、目立った遅れのために試みたが、バウンスはない)。ハードウェアソリューションの信頼性は向上していますか?ご協力いただきありがとうございます! – Marmstrong

+0

この解決策の問題点は、 'BUTTON_DEBOUNCE_CHECKS'に必要な値は、スイッチの特性だけでなく、プロセッサの速度、および「メインルックコードの残りの部分」の実行時間にも依存するということです。また、 "ビジーウェイト" - メインループ内で他のブロッキングコールがない場合は、CPU使用率が100%になり、他のプロセスではシステムが全体的に遅くなります。ボタンを監視しながら他の作業をしたい場合は、CPU負荷を最小限に抑えるために 'usleep()'コールを使って別のスレッドを使うほうが良いかもしれません。 – Clifford

+0

私はBUTTON_DEBOUNCE_CHECKS値の壊れやすい性質について絶対に同意し、テキストとコードの両方で多くを述べました。しかし、私は、デバウンスを学ぶだけの人のタイマー、割り込み、スケジューリングについての議論を避ける方が良いと感じました。私は待っているコードについては同意しない。 「コードの残りの部分」が降伏または寝るのを妨げるものは何もありません。書かれているように、**純粋に協調的なマルチタスクOSであるか、他のタスクがない場合に限り、すべてのプロセッササイクル**を吸い上げます。 RTOSが何であれ(もしあれば)何を使用しているのかを知るために、私はRaspberry Piにはあまり馴染んでいません。 – jerry

2

あなたのロジックは正しいですし、ボタンが完璧だったらこれはうまくいくでしょう。しかし、彼らはそうではありません。あなたはdebounceボタンの信号を持っている必要があります。それを達成するために、2つの方法(組み合わせたときに最高の作品):

I.は、ボタンの二つのピンの間にコンデンサを追加(またはan even more sophisticated button debouncer circuitryを試してみてください)、および/または

II。ソフトウェアデバウンス(擬似C)を使用します。

while (1) { 
    while (!button_pressed) 
     ; 

    printf("Button pressed!\n"); 


    while (elapsed_time < offset) 
     ; 
} 

などを

編集:@jerryが指摘したように、ボタンが保持されている場合、上記の「正しく」動作しません。ここには、すべての要件を満たすために使用できるmore professional code snippetsのカップルがあります。

+0

ボタンが押されている場合、このコードはprintfを無期限に繰り返します。 – jerry

+0

@jerryそうです。それだけでなく、より良い選択肢へのリンクも追加しました。 –

+0

while(elapsed_time jerry

1

次の関数は、名目上1ミリ秒の間隔でボタンをポーリングし、状態が20回の連続したポーリングに「リリース」されたままであることを要求します。それは応答時間を維持しながら、ほとんどのスイッチのデバウンスをで実行するのに十分です。

while(bcm2835_gpio_lev(PIN)){}ループをwaitButtonRelease()の呼び出しで置き換えます。

#include <unistd.h> 
#define DEBOUNCE_MILLISEC 20 

void waitButtonRelease() 
{ 
    int debounce = 0 ; 

    while(debounce < DEBOUNCE_MILLISEC) 
    { 
     usleep(1000) ; 

     if(bcm2835_gpio_lev(PIN)) 
     { 
      debounce = 0 ; 
     } 
     else 
     { 
      debounce++ ; 
     } 
    } 
} 

ボタンプレスとリリースのデバウンスも必要な場合があります。これは、同じ方法で行われますが、反対の状態カウント:ループがどのように見えるかもしれないが、この最後の機能を考えると、あなたのメインを

#include <stdbool.h> 

void waitButton(bool state) 
{ 
    int debounce = 0 ; 

    while(debounce < DEBOUNCE_MILLISEC) 
    { 
     usleep(1000) ; 

     if(bcm2835_gpio_lev(PIN) == state) 
     { 
      debounce++ ; 
     } 
     else 
     { 
      debounce = 0 ; 
     } 
    } 
} 

void waitButtonPress() 
{ 
    int debounce = 0 ; 

    while(debounce < DEBOUNCE_MILLISEC) 
    { 
     usleep(1000) ; 

     if(!bcm2835_gpio_lev(PIN)) 
     { 
      debounce = 0 ; 
     } 
     else 
     { 
      debounce++ ; 
     } 
    } 
} 

それとも、単一の関数は、どちらかの状態をデバウンスします:あなたはデジタル・ストレージ・オシロスコープへのアクセス権を持っている場合は

while(1) 
    { 
     waitButton(true) 
     printf("The button has been pressed\n"); 

     waitButton(false) ; 
    } 

は、あなたが見て、直接スイッチ信号を探るかもしれないまさにスイッチバウンスのように見えます。問題を理解したり、特定のスイッチの特性に合わせてデバウンスを調整したりするのに役立ちます。

+0

'usleep(1000)'呼び出しはwhileループ内にあるはずです。さもなければ、あなたは多くのデバウンスをするつもりはありません。 – jerry

+0

@ジェリー:良い点 - ありがとう。 – Clifford

関連する問題