2017-11-22 18 views
0

私は、Atmega2560マイクロコントローラを使用して、ボタンを使った簡単なプロジェクトを進めています。私はボタンに問題があります。ボタンをクリックすると、メインループが機能しなくなり、ボタンを押している限り、ボタンの機能は無限に実行され続けます。メインループはボタンを押しても止まらないはずです。ボタン機能は1回だけ実行する必要があります。どうやってやるの ?ButtonClickステートを一度だけ実行しているアセンブリ

.def LEDS = R16 
.def LED_DATA = R21 

.org 0 
    rjmp MAIN 

MAIN: 
    ldi LEDS, 0xFF ; 0xFF = 1111 1111 

    ldi LED_DATA, 0x01 

    out DDRC, LEDS ; PORTC 
    sbi PORTB, 0 
    sbi PORTB, 1 

LOOP_MAIN: 
    sbis PINB, 0  ; If PORTB-0's pin = 1, skip next 
    rjmp BUTTON_CLICK_H 

    sbis PINB, 1 
    rjmp BUTTON_CLICK_Y 

    call DELAY 
    out PORTC, LED_DATA 

    lsl LED_DATA 
    brcc SHIFT_0_IN  ;if carry not set, the MSB was not set, so skip setting the LSB 
    ori LED_DATA, 1 

    SHIFT_0_IN:   ; keep LSB as 0 -> do nothing, just continue 
    rjmp LOOP_MAIN 

    END: 
    rjmp LOOP_MAIN 


BUTTON_CLICK_H: 
    lsl LED_DATA 
    cpi LED_DARA, 0x40 
    breq SPEED_RESET 
    rjmp SPEED_END 
    SPEED_RESET: 
    ldi LED_SPEED, 0x04 
    SPEED_END: 
    rjmp LOOP_MAIN 

EDIT:(2017年11月26日)

私は簡単なボタンコントローラを書きました。このコントローラーの目的は、ボタンが押されているかどうかを確認することです。ボタンコードは一度しか機能しません。しかし、ボタンを押しても機能しません。私はどこでミスをしていますか?

.def BTN_STATE_FIRST = R23 
.def BTN_STATE_CHANGED = R24 
.def BTN_STATE_PINB = R25 
.def BTN_STATE_TEMP = R26 

LOOP_MAIN: 
    out PORTC, LED_DATA   
    call LOOP_BUTTON   
    call DELAY 

    ; ...Codes 

    rjmp LOOP_MAIN 

LOOP_BUTTON: 

    ; (Is the button pressed, not pressed or kept pressed?) Controller 

    in BTN_STATE_PINB, PINB  ; Read PINB data and put it current state in BTN_STATE_PINB 
    mov BTN_STATE_TEMP, BTN_STATE_PINB ; Move it to BTN_STATE_TEMP 
    eor BTN_STATE_PINB, BTN_STATE_FIRST ; XOR BTN_STATE_PINB and BTN_STATE_FIRST. And write result to the BTN_STATE_PINB 
    mov BTN_STATE_FIRST, BTN_STATE_TEMP ; Move it the BTN_STATE_FIRST 
    breq BUTTON_PRESSED 
    brne BUTTON_NOTPRESSED 

    BUTTON_PRESSED: 
    cpi BTN_STATE_PINB, 0x01 ; 1st button 0x01, 2nd button 0x02, 3rd button 0x04 
    breq BUTTON_CHANGED 
    rjmp BUTTON_NOTPRESSED 

    BUTTON_CHANGED: 
    cpi BTN_STATE_CHANGED, 0x01 ; When pressed and held, have been processed before ? 0x01 true, 0x00 false 
    breq BUTTON_UP    ; If yes, branch to BUTTON_UP 
    brne BUTTON_DOWN   ; Otherwise, branch to BUTTON_DOWN 

    BUTTON_UP: 
    dec BTN_STATE_CHANGED  ; Decrement the BTN_STATE_CHANGED to 0x00 

    ldi LED_DATA, 0x40 

    rjmp BUTTON_END 

    BUTTON_DOWN: 
    inc BTN_STATE_CHANGED  ; Increment the BTN_STATE_CHANGED to 0x01 

    ldi LED_DATA, 0x80 


    BUTTON_END: 
    BUTTON_NOTPRESSED: 

    ret 
+0

CPUに2つ以上のコアがないかぎり、ボタン機能の実行中にメインを停止することは避けられません。CPUは1つの命令だけを実行することができます(または、現代の大型CPUの場合は2つだけですが、AVRはおそらく1サイクルにつき1つの命令で実行されます)。もちろん、ボタン機能がマイクロ秒単位で終了するようにコードを整理することは可能ですメインはそこから続くので、人間の知覚のためにはメインが途切れないようになります。 – Ped7g

答えて

3

ボタンを押すと、入力端子の信号が似ている:

___---------------------------------___--__--___-_______ 
^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に設定するなど)

あなたが何らかの形で+有効にするために私のコードへの修正を示唆しています。これはちょっと良い答えにしてください(あなたが応答しなければ、間違いなく完全に間違っているかもしれないので、

+0

例と理論に感謝します。しかし、私はどのようにこれをアセンブリで設定しますか? –

+0

まあ...ほとんどはasm命令にほぼ1:1(1:4比に近い)に変換されます。私はAVRの命令を知らないので、あなたが試してみるだけの簡単なヒントを与えるのは簡単でしょう...しかし、ちょっとはっきりしません。どこに問題がありますか、AVRアセンブリプログラミングを学びたいのですか?はい、これはあなた自身を書くために十分な単純な作業でなければなりません、もしあなたがそれがあなたを助けるように感じるなら、私はそれを細かい詳細な理論ステップに分解しようとするかもしれません。しかし、私は実際にAVRでそれをコード化するのは嫌だが、約30-60分かかるだろうし、AVRを学ぶためにはたくさんのドキュメントを読まなければならないだろう。 – Ped7g

+0

@FurkanTürkal:私は特にこれらのMCのようなCPUに精通していません。通常のCISC/RISC命令とはまったく異なる設計をしています(時にはアキュムレータや間接的なメモリアクセスが限られていることもあります)、ソースが不完全であるように見えますどのような命令がサポートされているのか、ダイレクトI/Oに何があるのか​​、メモリ "変数"(メモリへのアドレス)とは何か、そしてどのような命令がサポートされているのかを推測することはさらに困難になります。コンパイル時に定義される定数です。 – Ped7g

関連する問題