我正在为AVR Atmega328p微控制器编写代码。微控制器应该读取编码器并根据编码器的旋转增加或减少r23。不幸的是,在这个时候,输出只会减少,直到它达到0,然后从255开始,无论我转动编码器的方向如何。
我的代码相当简单,基于表查找值,该值结合了编码器的先前状态和当前状态。如果前一个状态和当前状态不能结合起来创建一个有效的回合,则返回一个错误,代码不做任何事情。如果发生了有效的状态更改,则通过r24向r23添加1或-1。
我没有问题让微控制器读取编码器,但我不知道如何防止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
'饱和度'可以很简单地通过利用进位标志来实现,如:
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末尾的add 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)
不恰好是0
,这将引起麻烦;您可能需要执行add ZL, r25
和adc ZH, __zero_reg__
(16位加法)而不是mov ZL, r25
。
另一个可能降低程序复杂性的想法:
增量式旋转编码器的输出可以解释为某种同步串行数据:一个输出(例如'A')代表'时钟'信号,而另一个输出('B')代表'数据'信号。
您可以自由选择哪个输出用于哪个信号,以及您选择的哪个"时钟"极性。那么算法就相当简单了:
- (脱轨和)检测上升(或下降,你选择)转换时钟信号
- 一旦检测到"时钟"边缘,只需读取"数据"信号的电平-这里不需要去噪
- 读取的单个'data'位直接指示编码器刚刚在'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毫秒,你已经有一些简约的"免费"反弹。
(顺便说一下,总结之前许多人学到的教训:不要试图使用一些外部/引脚变化中断来检测由任何机械开关或机械编码器产生的(未脱模的)信号转换。机械元素的弹跳特性将确保永远不会按预期工作)
您只需要为边界情况添加一些测试。我已经很多年没有汇编了,这段代码可能与你的体系结构无关,但它类似于:
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