3

AVR Atmega328p マイクロコントローラーのコードを作成中です。マイクロコントローラーは、エンコーダーを読み取り、エンコーダーの回転に基づいて r23 をインクリメントまたはデクリメントすることになっています。残念ながら、現時点では、エンコーダーを回す方向に関係なく、出力は 0 に達するまでしか減少せず、その後 255 から始まります。

私のコードは非常に単純で、エンコーダーの以前の状態と現在の状態を組み合わせたテーブル ルックアップ値に基づいています。前の状態と現在の状態を組み合わせて有効なターンを作成しない場合、エラーが返され、コードは何もしません。有効な状態変化が発生すると、1 または -1 が r24 を介して r23 に追加されます。

マイクロコントローラーにエンコーダーを読み取らせるのに問題はありませんが、r23 のオーバーフローを防ぐ方法がわかりません。私の問題は、255 をヒットして 1 を追加すると、レジスタがオーバーフローして 0 になることです。レジスタがゼロになるのは望ましくありません。エンコーダーを反対方向に回転させるまで、255 のままにしておきます。0 についても同じ問題があります。レジスタが 0 で -1 を追加した場合、レジスタを 255 にしたくないので、反対方向に回転するまで 0 のままにしておきます。

固定観念にとらわれずに考えることに問題はありません。解決策やアイデアがあれば、お気軽に投稿してください。

;**** A P P L I C A T I O N   N O T E  *************************************
;*
;* Title:       
;* Version:     
;* Last updated:    
;* Target:  AVR Dragon  
;*
;*
;* DESCRIPTION
;*
;*.device   ATmega328P @ 1M  clock speed
;* 
;* This is a simple program to test an optical encoder
;***************************************************************************

.include "m328Pdef.inc"
.org 0x0000
    jmp RESET   ;Reset Handle

.org 0x0008
    jmp Interrupt1 ; PCINT1 Handler

enc_states:
.db 0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0 

RESET:

;Setup stack pointer
    ldi temp, low(RAMEND)   
    out SPL, temp
    ldi temp, high(RAMEND)
    out SPH, temp

//Set Port B pins to output
    ser     temp            ; Set Register Rd <-- 0xff (output)
    out     DDRB,temp       ; set all PORTB bits as output

//Clear analog input pins and enable pull ups on Pin 0 and 1 (Port C)
    clr     temp
    out     DDRC, temp      ;all pins input
    ldi     temp, (1<<PC1)|(1<<PC0)
    out     PORTC,temp      ;Enable pullups on Pin 0 and 1

//Set Port D pins to output
    ser     temp            ; Set Register Rd <-- 0xff
    out     DDRD,temp       ; set all PORTD bits as output

//Enable encoder pins interrupt sources (Encoder 1)
    ldi     temp, (1<<PCINT9)|(1<<PCINT8)
    sts     PCMSK1, temp

//Enable encoder pins interrupt sources (Encoder 2)
//  ldi     temp, (1<<PCINT11)|(1<<PCINT10)
//  sts     PCMSK1, temp

//Enable pin change interrupts
    ldi     temp, (1<<PCIE1)
    sts     PCICR, temp

//Enable global interrupts
    sei

//Lookup table initial value
    ldi     ZL, 0x00   ;lookup table index and initial state

.def temp   = r16
    clr     r25
    clr     r24
    clr     r23

loop:
    out PORTB, r23
    jmp loop

Interrupt1:
// Push SREG, etc
    in      r25, PORTC  ;encoder value from PORTC

    ldi   ZH, High(enc_states*2)   ; setup Z pointer hi
    ldi   ZL, Low (enc_states*2)   ; setup Z pointer lo
    rol     r22     ;remember previous state and shift left twice
    rol     r22
    cbr     r25, 0xFC  ;clear encoder bits 7:2
    mov     r21,r25
    or      r25, r22   ;combine encoder bits with old bits
    cbr     r25, 0xF0  ;clear bits 7:4 for table lookup
    mov     r22, r25   ;save table lookup value
    mov     ZL, r25    ;load index value into table
    lpm     r24, z      ;get result
    add     r23,r24

// Pop SREG, etc.
    reti
4

2 に答える 2

3

「サチュレーション」は、次のようにキャリー フラグを利用することで非常に簡単に実行できます。

  mov __tmp_reg__, r23
  add __tmp_reg__, r24   ; do the addition

  brcs saturated          ; if the carry flag is set we have an overflow and should discard the result

    mov r23, __tmp_reg__ ; there was no overflow so we store the result

  saturated:          

ISRadd r23,r24の末尾にある を上記のコードに置き換えれば問題ありません。__tmp_reg__(明らかに、一時的なストレージとして使用できるレジスターに変更する必要がある場合があります。)

r24が正、負、またはゼロのいずれかになる可能性があることを考慮すると、上記の原則を少し拡張することで、すべてのケースを正しく処理できます。

  mov __tmp_reg__, r23

  tst r24 

  breq doreturn  ; if r24 == 0 we have nothing to do and may just return

  brmi subtract  ; if r24 is 'negative' we need to subtract

    add __tmp_reg__, r24  ; if r24 is not negative we just add
    rjmp store            ; and go to where the result may be stored

  subtract:

    neg r24  ; r24 := -r24
    sub __tmp_reg__, r24   ; do the subtraction

  store:

  brcs doreturn          ; if the carry flag is set we have an overflow and should discard the result

    mov r23, __tmp_reg__ ; there was no overflow so we store the result

  doreturn:

あなたのコードを詳しく見てみると、Z ポインターの計算が完了すると、別の「グリッチ」があるように思えます。

ldi   ZH, High(enc_states*2)   ; setup Z pointer hi
ldi   ZL, Low (enc_states*2)   ; setup Z pointer lo

mov     ZL, r25    ;load index value into table

問題があるように見えます: Z アドレスの下部は単に無視され、インデックス値で上書きされます。Low (enc_states*2)これは、たまたま ではない場合に問題を引き起こします0add ZL, r25の代わりにand adc ZH, __zero_reg__(16 ビット加算)を行う必要があるでしょうmov ZL, r25

ルーチンの複雑さを軽減するためのもう 1 つの考え:

インクリメンタル ロータリー エンコーダの出力は、ある種の同期シリアル データのように解釈できます。一方の出力 (「A」など) は「クロック」信号を表し、もう一方の出力 (「B」) は「データ」信号を表します。

どの出力をどの信号に使用するか、どの「クロック」極性を選択するかを自由に選択できます。アルゴリズムはかなり単純です。

  1. (デバウンスして) 「クロック」信号の立ち上がり(または立ち下がり、選択)遷移を検出します
  2. 「クロック」エッジが検出されるとすぐに、「データ」信号のレベルを読み取るだけです - ここではデバウンスは必要ありません
  3. 次に読み取られた単一の「データ」ビットは、エンコーダがステップしたばかりの方向を直接示します。「0」または「1」、一方の方向または他方の方向です。

疑似コードでは、これは次のようになります。

bit lastClockState;

void readEncoder() {

    bit currentClockState = readClockPin();

    if ( lastClockState == 1 && currentClockState == 0 ) {
        // A (falling) edge was detected...

        // Get the direction of the rotation:
        bit direction = readDataPin();

        if ( direction == 1 ) {
            value++;
        } else {
            value--;
        }
    }

    lastClockState = currentClockState; // Update the lastClockState for the next iteration

}

たとえば、これを 10 ミリ秒ごとに実行すると、「無料」で最小限のデバウンスが既に行われています。

(ちなみに、以前に他の多くの人が学んだ教訓を要約すると、外部/ピン変更割り込みを使用して、メカニカルスイッチまたはメカニカルエンコーダによって生成された(デバウンスされていない)信号遷移を検出しようとしないでください。機械要素の跳ね返りの性質により、これが期待どおりに機能しないことが保証されます。)

于 2013-03-06T13:24:55.640 に答える
2

境界ケースのテストをいくつか追加するだけです。私は何年もアセンブリを行っていません。このコードはアーキテクチャとは関係がないかもしれませんが、次のようなものです。

    lpm     r24, z      ;get result
    cmp     r23, 0
    je      rot_lo      ; if val is 0
    cmp     r23, 255
    je      rot_hi      ; if val is 255
    jmp     rot_add

rot_lo:
    cmp     r24, 0
    jl      rot_ret     ; don't add if delta less than 0
    jmp     rot_add

rot_hi:
    cmp     r24, 0
    jg      rot_ret     ; don't add if delta greater than 0
    jmp     rot_add     ; (or just fall through here)

rot_add:
    add     r23, r24

rot_ret:
    reti
于 2013-03-05T22:26:58.543 に答える