Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/70.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 旋转编码器防溢出_C_Assembly_Embedded_Microcontroller_Avr - Fatal编程技术网

C 旋转编码器防溢出

C 旋转编码器防溢出,c,assembly,embedded,microcontroller,avr,C,Assembly,Embedded,Microcontroller,Avr,我正在为AVR Atmega328p微控制器编写代码。微控制器应读取编码器,并根据编码器的旋转增加或减少r23。不幸的是,此时,输出只会减小,直到达到0,然后从255开始,与我旋转编码器的方向无关 我的代码相当简单,基于一个表查找值,该值结合了编码器的先前状态和当前状态。如果前一个状态和当前状态不能结合起来创建一个有效的回合,则返回一个错误,代码不执行任何操作。如果发生有效状态更改,则通过r24将1或-1添加到r23 我没有问题让微控制器读取编码器,但我不知道如何防止r23溢出。我的问题是,当我

我正在为AVR Atmega328p微控制器编写代码。微控制器应读取编码器,并根据编码器的旋转增加或减少r23。不幸的是,此时,输出只会减小,直到达到0,然后从255开始,与我旋转编码器的方向无关

我的代码相当简单,基于一个表查找值,该值结合了编码器的先前状态和当前状态。如果前一个状态和当前状态不能结合起来创建一个有效的回合,则返回一个错误,代码不执行任何操作。如果发生有效状态更改,则通过r24将1或-1添加到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
;****一个公共图书馆*************************************
;*
;* 标题:
;* 版本:
;* 最近更新:
;* 目标:AVR龙
;*
;*
;* 描述
;*
;*.设备ATmega328P@1M时钟速度
;* 
;* 这是一个测试光电编码器的简单程序
;***************************************************************************
。包括“m328Pdef.inc”
.org 0x0000
jmp复位;复位手柄
.org 0x0008
jmp中断1;PCINT1处理程序
enc_说:
.db 0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0
重置:
;设置堆栈指针
本地设计院(ldi)温度低(拉芒)
输出SPL,温度
本地设计院(ldi)温度高(拉芒)
外滩
//将端口B引脚设置为输出

ser温度;设置寄存器Rd您只需要为边界情况添加一些测试。我已经多年没有进行任何汇编了,这段代码可能与您的体系结构无关,但它类似于:

    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

“饱和”可以很简单地通过利用进位标志来实现,如:

  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:          
将ISR末尾的
添加r23、r24
替换为上述代码,您应该会没事的。(显然,您可能需要将
\uuuu tmp\u reg\uuu
更改为可以用作临时存储器的某个寄存器。)

考虑到
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

看起来有问题:Z地址的下半部分被忽略,并被索引值覆盖。如果
Low(enc_states*2)
恰好不是
0
,这将导致故障;您可能需要执行
添加ZL,r25
adc ZH,_uuuzero\u reg_uuuu
(16位加法),而不是
mov ZL,r25

另一个可能降低日常工作复杂性的想法:

增量旋转编码器的输出可以解释为某种同步串行数据:一个输出(如“A”)表示“时钟”信号,而另一个输出(“B”)表示“数据”信号

您可以自由选择哪个输出用于哪个信号,以及选择哪个“时钟”极性。因此,算法相当简单:

  • (去抖动和)检测“时钟”信号上的上升(或下降,由您选择)转换
  • 一旦检测到“时钟”边缘,只需读取“数据”信号的电平-此处无需去抖动
  • 然后,读取的单个“数据”位直接指示编码器刚刚进入的方向:“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毫秒执行一次,您已经有了一些“免费”的最低限度的反弹跳


    (顺便说一句,总结一下许多其他人以前的经验教训:不要试图使用一些外部/引脚更改中断来检测任何机械开关或机械编码器产生的(未去抖动的)信号转换。机械元件的反弹性质将确保这永远不会像预期的那样工作。)

    我喜欢你的回答。我更改了代码中的指令,以便在我使用的微控制器中实现它们,但它不起作用。看起来应该能用。谢谢你的帮助。我喜欢你的代码,但我还没有试过。当我将r23连接到r2r网络时,我注意到了另一个问题。我的代码的输出只有一个方向,与我旋转编码器的方向无关。如果我把编码器右转,它就会下降。如果我向左旋转编码器,它也会下降。再次感谢您的帮助。在减法部分中,在进行减法之前,先进行负数运算,得到两个数的补码。这就是为什么添加r23、r24(-1)会给我的原始代码带来问题的原因吗?在我的原始代码中,360度旋转将使我从0旋转到255,但从255旋转到0需要720度。我不明白为什么这是错误的。加法和减法基本上是可互换的。这就是为什么AVRs中只有一条
    SUBI
    指令,而没有相应的
    ADDI
    x+y
    x-(0xff-y)
    之间的差异仅反映在操作后的状态位中。数值结果是相同的。然而,在你的例子中,你实际上混合了有符号和无符号的值,这使得事情变得有点困难;您的“值”(
    r23
    )是无符号的(0…255),而您的“增量”(
    r24
    )是有符号的值
    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
    
    }