ボタンを押すと、入力端子の信号が似ている:
___---------------------------------___--__--___-_______
^here the press starts ^released^bounces (physically)
接触が固体でない場合、時にはいくつかのバウンスが、でも最初に起こる、または主信号におけるいくつかのノイズも十分な。
それはのような完璧なクリーンなデジタル信号になる場合:
:
_______-------------------------------_______________
その後、あなたがする必要があるだろうすべては、ピンのメイン前の状態に保つことです、ボタンがクリックされたかどうかのチェックは次のようになります
current reading | previous state | action
-------------------------------------------------------------------
0 | 0 | N/A (or refresh "previous")
1 | 0 | previous=1, call click function
1 | 1 | N/A (or refresh "previous")
0 | 1 | previous=0
ボタンの実際のスイッチの物理的なバウンスのために、コード内のよりロバストなロジックをコーディングする必要があります。 "以前の"ビット変更では、一部の "デバウンスタイマー"もリセットされます。 (カウントダウン・カウンタ)がゼロに達すると、「前の」状態eはロックされ、実際のI/Oラインで読み込まれた状態変化は無視されます。それではデバウンスロジックがオンになります:
___---------------------------------___--__--___-_______
^here the press starts ^released^bounces (physically)
へ:
real-time in from button pin:
___-_--_------------------------------___--__--___-____________
"previous" state:
___-----------------------------------_________________________
"debounce timer": (active means "> 0") (preventing change of previous)
___--------------------_______________--------------------_____
action in code:
*1 *2 *3 *4
アクション:
- * 1:前= 1、デバウンス=〜30msのは、*
- のonClickハンドラを呼び出します2:デバウンスがゼロに達した(ここまでは「前回」がロックされていた)
- * 3:前回= 0、デバウンス=〜30ms
- * 4:デバウンスは、(ここまでは「以前は」ロックされていた - 今までクリックが無視され、いずれかのボタンを)ゼロになっ
そして、あなたは「メインの停止ではない」の錯覚を達成したい場合onClickハンドラを非常に短く(ノンブロッキング、遅延なし)保ち、 "メイン"の遅延ロジックを無制限に保持し、必要に応じてタイマー/カウンタを更新する必要があります(それぞれの "debounce"タイマーを含む)タイマーまたは入力状態のいずれかによって起動される特定のイベントに短いクイック関数を実行するために、追加の複雑なロジックを使用します。
EDIT:私が部分的に理解しようとした新しいコードのいくつかの注釈。
その基本的なアーキテクチャに問題があります。そのボタンに関連するすべての値を固定レジスタに保存すると、LOOP_BUTTON
が特定のピン/ボタンに固定され、他の場所では再利用できなくなります。
より一般的な方法でサブルーチンを設計し、サブルーチンのコードではなく引数で特定の機能を設定することをお勧めします。
私は、ボタンオブジェクトのインスタンスのアドレスを登録1に取る方法でそれを設計し、ピンの別のレジスタ値では、のようになります
免責事項:これが有効なAVRのアセンブラであれば、私は確認できませんでした構文は、そのコードが動作するか、あっても、そう(:http://www.avr-tutorials.com/sites/default/files/Instruction%20Set%20Summary.pdf私は命令とその構文を書くために、このリンクを使用):「アイデア」ソースとして主に使用
... main loop code continues with button tests...
; read current state of PINB into R23
in R23,PINB
; button 1 check (data in memory at button1_data, pin: bit 0 of PINB)
ldi ZH,high(button1_data)
ldi ZL,low(button1_data)
ldi R24,0b00000001 ; bit 0 bitmask
rcall BUTTON_HANDLER ; R24 = 0/1 when onClick should be called
sbrc R24,0 ; skip onClick call, when result was 0
rcall BTN_1_ON_CLICK ; BTN1 was clicked, call onClick handler
; ^^ must preserve R23!
; button 2 check (data in memory at button2_data, pin: bit 1 of PINB)
ldi ZH,high(button1_data)
ldi ZL,low(button1_data)
ldi R24,0b00000010 ; bit 1 bitmask
rcall BUTTON_HANDLER ; R24 = 0/1 when onClick should be called
sbrc R24,0 ; skip onClick call, when result was 0
rcall BTN_2_ON_CLICK ; BTN2 was clicked, call onClick handler
; button 3 check (data in memory at button3_data, pin: bit 2 of PINB)
ldi ZH,high(button1_data)
ldi ZL,low(button1_data)
ldi R24,0b00000100 ; bit 2 bitmask
rcall BUTTON_HANDLER ; R24 = 0/1 when onClick should be called
sbrc R24,0 ; skip onClick call, when result was 0
rcall BTN_3_ON_CLICK ; BTN3 was clicked, call onClick handler
... continuation of main loop ...
データとして.dsegで定義されているボタン:
.dseg
button1_data:
.byte 2 ; two bytes of storage per button
button2_data:
.byte 2 ; two bytes of storage per button
button3_data:
.byte 2 ; two bytes of storage per button
おそらくこのように、プログラムの初期化中にそれらをクリアすることを忘れないでください:R24
がボタンのビットマスクで、ボタンのR23
現在の状態になります
main:
; during program init don't forget to clear button data in memory
clr R1 ; R1 = 0
sts button1_data, R1
sts button1_data+1, R1 ; Not sure if this is legal syntax :/
sts button2_data, R1
sts button2_data+1, R1
sts button3_data, R1
sts button3_data+1, R1
最後にボタン入力ハンドラルーチン、(ピン)確認し、Z
はボタンデータを指すはずです。これは、ボタンがクリックされたかどうかの状態でR24 = 0/1
を返します。いくつかのボタンがクリックされたとき
; button input handler:
; R23 = pins state (preserved), R24 = pin bitmask, Z = button data
; returns R24 = 0/1 when onClick should be called
; button data structure: +0 = previous state, +1 = debounce timer
BUTTON_HANDLER:
; check debounce timer first, if > 0, state is locked
ldd R0,Z+1 ; debounce timer is second byte
tst R0 ; is it zero?
breq BUTTON_HANDLER_DEBOUNCE_OK
; debounce timer is > 0, just decrement it and ignore input
dec R0
std Z+1,R0
clr R24 ; R24 = 0 (no click)
ret
BUTTON_HANDLER_DEBOUNCE_OK:
; process input
ld R0,Z ; R0 = previous state of bit
and R24,R23 ; R24 = current state
cpse R0,R24 ; if previous == current, skip change
rjmp BUTTON_HANDLER_CHANGE_DETECTED
clr R24 ; R24 = no click (no change on pin)
ret
BUTTON_HANDLER_CHANGE_DETECTED:
st Z,R24 ; store new state into "previous" data
; EDIT - bugfix added, debounce timer need to be set too!
ldi R0,DEBOUNCE_DELAY ; amount of main_loops to pass
std Z+1,R0
tst R24 ; when new state is zero => released button
breq BUTTON_HANDLER_RELEASED ; return 0
ldi R24,1 ; when new state is non-zero, return 1 (click!)
BUTTON_HANDLER_RELEASED:
ret
次にあなたが特定のボタンのあなたの「onClickの」ルーチンを呼び出します:
; button 1 onClick handler (must preserve R23 (input pins of buttons)).
BTN_1_ON_CLICK:
; TODO anything you wish to do upon BTN1 pressed
ret
を、一定のDEBOUNCE_DELAY
いくつかのデバウンス時間を定義しますボタンが現在の状態に反応し始めるまでに通過するmain_loopsの量です(たとえば、1msごとに1回ループすると、DELAY 30を試すことができます)。
ああ、あなたのコードにコメントするのではなく、私はちょうど私がそれを動作させることさえ確認できないときでも、自分のものを作りました。 :)
(それがうまくいけば、それは非常に効率的なAVRアセンブリではないと思うので、私はいつもAVRがそうであるように、フラグを直接設定するための多くの命令(0 /キャリー/ ...すべて)ですが、それ以外の方法では、フラグにしたがってレジスタを0/1に設定するなど)
あなたが何らかの形で+有効にするために私のコードへの修正を示唆しています。これはちょっと良い答えにしてください(あなたが応答しなければ、間違いなく完全に間違っているかもしれないので、
CPUに2つ以上のコアがないかぎり、ボタン機能の実行中にメインを停止することは避けられません。CPUは1つの命令だけを実行することができます(または、現代の大型CPUの場合は2つだけですが、AVRはおそらく1サイクルにつき1つの命令で実行されます)。もちろん、ボタン機能がマイクロ秒単位で終了するようにコードを整理することは可能ですメインはそこから続くので、人間の知覚のためにはメインが途切れないようになります。 – Ped7g