Rotary pulse encoder issue generating pulses

Go To Last Post
19 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello everyone, 

I am working with the rotary pulse generator to create pulse that increases when turned clockwise and decreases other way, but I can't figure out the issue with my code. I tried various way but it doesn't work. lf anyone can let me know the issue that would be really helpful

.include "m328pdef.inc"
.cseg

// configure port pins
cbi DDRD, 2 // signal A - connected to RPG 
cbi DDRD, 3 // signal B - connected to RPG
sbi DDRD, 4 // LED output - connected to LED to GND

// Tracks current & previous state of RPG
.def current_state = r16
.def previous_state = r17

// Duty cycle range
.equ upper_cycle_limit = 200
.equ lower_cycle_limit = 30
.equ half_duty_cycle = 115
.def duty_reg = r18
ldi duty_reg, half_duty_cycle

// Rotational speed
.def rate_reg = r19
ldi rate_reg, 0x02

// Timer registers
.def tmp1 = r23
.def tmp2 = r24
.def count = r25
ldi r30, 0x02
out TCCR0B, r30
ldi r30, 0x00

// RPG signal identification
.equ both_on = 0x00
.equ a_on = 0x01
.equ b_on = 0x02
.equ both_off = 0x03

// Main loop
main:
    nop
    nop
    rcall read_RPG
    nop
    rcall which_direction
    nop

    cbi PORTD, 4

    ldi count, 220
    sub count, duty_reg
    rcall delay

    sbi PORTD, 4

    mov count, duty_reg
    rcall delay
    rjmp main


// Read RPG Input
read_RPG:
    nop
    push r28
    push r29
    ldi r28, 0x01
    ldi r29, 0x02
    mov previous_state, current_state
    ldi current_state, 0x00
    sbis PIND, 2
    add current_state, r29 
    sbis PIND, 3
    add current_state, r28 
    pop r29
    pop r28
    ret

// Which way is RPG is being turned
which_direction:
    nop
    cpi previous_state, both_on
    breq which_end

    cpi current_state, both_off
    breq current_low
    rjmp which_end

    current_low:
    cpi previous_state, a_on
    breq counter_clockwise
    cpi previous_state, b_on
    breq clockwise
    rjmp which_end

    which_end:
    ret

// counter-clockwise turning
counter_clockwise:
    sub duty_reg, rate_reg
    cpi duty_reg, lower_cycle_limit
    brsh end_ccwise
    ldi duty_reg, lower_cycle_limit
    end_ccwise:
    ret

// clockwise turning
clockwise:
    add duty_reg, rate_reg
    cpi duty_reg, upper_cycle_limit
    brsh recover_upper
    rjmp end_cwise
    recover_upper:
    ldi duty_reg, upper_cycle_limit
    end_cwise:
    ret

delay:
    in tmp1, TCCR0B
    ldi tmp2, 0x00
    out TCCR0B, tmp2

    in tmp2, TIFR0
    sbr tmp2, 1<<TOV0
    out TIFR0, tmp2

    out TCNT0, count
    out TCCR0B, tmp1

wait:
    in tmp2, TIFR0
    sbrs tmp2, TOV0
    rjmp wait
    ret

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The first thing is to be clear about what you hope to do:

 the rotary pulse generator to create pulse that increases when turned clockwise and decreases other way  

what exactly does this mean?  What kind of pulses?  Do you want to vary the duty cycle?   

What do you mean by increases---an extra pulse?  A different width?  A higher frequency? A higher voltage level?   Be specific.   If you are blurry in your comments, you will have trouble getting to the sharp answer.

 

Did you draw a flowchart of how all of your pieces of code work together?  That will often point out  your issue.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

Last Edited: Mon. Oct 19, 2020 - 10:04 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes that is what I am trying to do. I am trying to increase duty cycle when turned clockwise and decrease when turned counter-clockwise.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Yes that is what I am trying to do. I am trying to increase duty cycle when turned clockwise and decrease when turned counter-clockwise.

 

Ok then, before you even mess with a knob do you have some code to make the pulses?...that has nothing to do with the knob...you will feed that code a number to get different duty cycles &  the freq you want.

Get that working 100% first.  You give it some different numbers & test it.

 

Later, instead of you giving the number, we will let the knob create the number.  That is a separate activity.  Yes, use a state machine.  You are on the right track.  Forget about the pulse, work on the knob.  Work on that part next...maybe turn on an led when you have 10 "clicks"of the knob.   When the knob is working fine then you combine it with the other code.  Then the knob gives the number to your pulse generator.    Almost like two separate programs...the number joins them.

 

reorder/rethink these

 sub duty_reg, rate_reg
    cpi duty_reg, lower_cycle_limit
    brsh end_ccwise

if duty reg is close to zero, you do a subtract and may get a large value (underflow)   [3-3 is 0,  3-5 is 254 !!].   do a compare first to see if you have room to subtract (or test for negative).

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

Last Edited: Tue. Oct 20, 2020 - 02:38 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I would ask if your "Rotary Pulse Encoder" is in fact an encoder that outputs two square waves in quadrature - meaning 90 degrees out of phase.  If your waveforms look something like this as you rotate it:

 

A:    ____       ____

_____|    |_____|        |____

 

 

B:_        ____            ____

    |___|         |____|         |__

 

 

Then what you have is usually called a quadrature encoder. 

 

Now, in order to increment and decrement, you need to look carefully at each edge.  For example, if traveling from left to right, you will see the falling edge of the A waveform always happens when the B wave is high.  If you are traveling from right to left (the other way), the falling edge of A always happens when the B wave is low.  Therefore, if you detect only the falling edge of A, the signal B at the same time will tell you whether to count up or down.

 

You need an edge trigger.  I'm guessing the various limits for "cycle time" are an attempt at noise reduction, and there will be noise, but get the above working first. 

 

Of course, you can also pick up other edges and count more, but again - get the above working first. 

 

Have fun!  S.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

 

I always think this is a good explanation of encoders:

 

https://www.pjrc.com/teensy/td_l...

 

and this bit of the page:

 

 

is interactive - try clicking on "Counterclockwise" or "clockwise" there (on the linked page) to see what happens and how the encoder count will increase/decrease and what is actually happening to the A/B sensor inputs at the time. Then Paul's code has:

 

https://github.com/PaulStoffrege...

 

has this simple explanation followed by C code:

 

		uint8_t s = state & 3;
		if (digitalRead(pin1)) s |= 4;
		if (digitalRead(pin2)) s |= 8;
		switch (s) {
			case 0: case 5: case 10: case 15:
				break;
			case 1: case 7: case 8: case 14:
				position++; break;
			case 2: case 4: case 11: case 13:
				position--; break;
			case 3: case 12:
				position += 2; break;
			default:
				position -= 2; break;
		}
		state = (s >> 2);

Now you don't really have to understand too much C to understand what is going on here. It's basically implementing a case for every state documented in the comment:

 

		//	new	new	old	old
		//	pin2	pin1	pin2	pin1	Result
		//	----	----	----	----	------
		//	0	0	0	0	no movement
		//	0	0	0	1	+1
		//	0	0	1	0	-1
		//	0	0	1	1	+2  (assume pin1 edges only)
		//	0	1	0	0	-1
		//	0	1	0	1	no movement
		//	0	1	1	0	-2  (assume pin1 edges only)
		//	0	1	1	1	+1
		//	1	0	0	0	+1
		//	1	0	0	1	-2  (assume pin1 edges only)
		//	1	0	1	0	no movement
		//	1	0	1	1	-1
		//	1	1	0	0	+2  (assume pin1 edges only)
		//	1	1	0	1	-1
		//	1	1	1	0	+1
		//	1	1	1	1	no movement

so just implement the same decision logic in your Asm code and it will have the same result.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't think my code is reading the RPG, I tried debouncing the issue with 10k resistor and .1uf capacitor but that didn't change anything. if anyone has an assembly code to read RPG that would be really helpful.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

First question:  Do you actually have a quadrature encoder?

 

Second question:  Do you have some way of seeing that you are getting anything?  An LED would do (PortD,4?), or an o'scope.  How do you get your output, anyhow?

 

Here:  (untested):

(damnit, the code window's broken again)

 

BLINKY:

in r16, PIND         ; Since your A channel is on PortD, 2

lsl r16                  ; We shift it to PortD, 4

lsl r16

out PORTD, r16   ; and put it out again.

rjmp BLINKY

 

The LED should flash as the A waveform goes up and down.

 

Thanks.  S.

 

Edited to add code comments

 

Last Edited: Thu. Oct 29, 2020 - 03:50 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

bira wrote:
I don't think my code is reading the RPG

 

RPG?  Role Playing Game, Rocket Propelled Grande, Red Purple Green.....

 

Gotta love TLA's!

 

bira wrote:
if anyone has an assembly code to read RPG

The way things work here, is we will help you write your own code, but as professionals, we get paid to write code for others.

 

Did any of the responses above help you understand how quadrature encoders work.

 

Jim

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am using Panasonic EVE-KD2F3024M encoder and yes led is showing the pulses that i have generated. All i need to do is read inputs from encoder in order to change the duty cycle. I don't have a o'scope :(

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You need to re-read post #5, and the links from clawson's post #6.  For direction, you need to watch when your signals change from high to low.  Or the other way.  S.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK so (untested) I just wrote:

#include <avr/io.h>
#define F_CPU 1000000UL
#include <util/delay.h>

#define SIG_A           2
#define SIG_B           3
#define LED             4

int position = 0;
uint8_t state = 0;

void init_encoder() {
    DDRD = (1 << LED);
    DDRD &= ~((1 << SIG_A) | (1 << SIG_B));
    if (PIND & (1 << SIG_A)) {
        state |= 1;
    }
    if (PIND & (1 << SIG_B)) {
        state |= 2;
    }
}

void update_encoder() {
    uint8_t s = state & 3;
    if (PIND & (1 << SIG_A)) {
         s |= 4;
    }
    if (PIND & (1 << SIG_B)) {
        s |= 8;
    }
    switch (s) {
        case 0: case 5: case 10: case 15:
        break;

        case 1: case 7: case 8: case 14:
        position++; break;

        case 2: case 4: case 11: case 13:
        position--; break;

        case 3: case 12:
        position += 2; break;

        default:
        position -= 2; break;
    }
    state = (s >> 2);
}

void delay_ms(uint16_t n) {
    while (n--) {
        _delay_ms(1);
    }
}

int main(void)
{
    init_encoder();
    while (1)
    {
        update_encoder();
        PORTD |= (1 << LED);
        delay_ms(position);
        PORTD &= ~(1 << LED);
        delay_ms(position);
    }
}

when I build that the Asm generated is:

000000aa <init_encoder>:

int position = 0;
uint8_t state = 0;

void init_encoder() {
    DDRD = (1 << LED);
  aa:	80 e1       	ldi	r24, 0x10	; 16
  ac:	8a b9       	out	0x0a, r24	; 10
    DDRD &= ~((1 << SIG_A) | (1 << SIG_B));
  ae:	8a b1       	in	r24, 0x0a	; 10
  b0:	83 7f       	andi	r24, 0xF3	; 243
  b2:	8a b9       	out	0x0a, r24	; 10
    if (PIND & (1 << SIG_A)) {
  b4:	4a 9b       	sbis	0x09, 2	; 9
  b6:	05 c0       	rjmp	.+10     	; 0xc2 <init_encoder+0x18>
        state |= 1;
  b8:	80 91 00 01 	lds	r24, 0x0100	; 0x800100 <__DATA_REGION_ORIGIN__>
  bc:	81 60       	ori	r24, 0x01	; 1
  be:	80 93 00 01 	sts	0x0100, r24	; 0x800100 <__DATA_REGION_ORIGIN__>
    }
    if (PIND & (1 << SIG_B)) {
  c2:	4b 9b       	sbis	0x09, 3	; 9
  c4:	05 c0       	rjmp	.+10     	; 0xd0 <init_encoder+0x26>
        state |= 2;
  c6:	80 91 00 01 	lds	r24, 0x0100	; 0x800100 <__DATA_REGION_ORIGIN__>
  ca:	82 60       	ori	r24, 0x02	; 2
  cc:	80 93 00 01 	sts	0x0100, r24	; 0x800100 <__DATA_REGION_ORIGIN__>
  d0:	08 95       	ret

000000d2 <update_encoder>:
    }
}

void update_encoder() {
    uint8_t s = state & 3;
  d2:	80 91 00 01 	lds	r24, 0x0100	; 0x800100 <__DATA_REGION_ORIGIN__>
  d6:	83 70       	andi	r24, 0x03	; 3
    if (PIND & (1 << SIG_A)) {
  d8:	4a 99       	sbic	0x09, 2	; 9
         s |= 4;
  da:	84 60       	ori	r24, 0x04	; 4
    }
    if (PIND & (1 << SIG_B)) {
  dc:	4b 99       	sbic	0x09, 3	; 9
        s |= 8;
  de:	88 60       	ori	r24, 0x08	; 8
    }
    switch (s) {
  e0:	48 2f       	mov	r20, r24
  e2:	50 e0       	ldi	r21, 0x00	; 0
  e4:	40 31       	cpi	r20, 0x10	; 16
  e6:	51 05       	cpc	r21, r1
  e8:	28 f5       	brcc	.+74     	; 0x134 <update_encoder+0x62>
  ea:	fa 01       	movw	r30, r20
  ec:	ec 5c       	subi	r30, 0xCC	; 204
  ee:	ff 4f       	sbci	r31, 0xFF	; 255
  f0:	4a c0       	rjmp	.+148    	; 0x186 <__tablejump2__>
        case 0: case 5: case 10: case 15:
        break;

        case 1: case 7: case 8: case 14:
        position++; break;
  f2:	20 91 01 01 	lds	r18, 0x0101	; 0x800101 <position>
  f6:	30 91 02 01 	lds	r19, 0x0102	; 0x800102 <position+0x1>
  fa:	2f 5f       	subi	r18, 0xFF	; 255
  fc:	3f 4f       	sbci	r19, 0xFF	; 255
  fe:	30 93 02 01 	sts	0x0102, r19	; 0x800102 <position+0x1>
 102:	20 93 01 01 	sts	0x0101, r18	; 0x800101 <position>
 106:	20 c0       	rjmp	.+64     	; 0x148 <update_encoder+0x76>

        case 2: case 4: case 11: case 13:
        position--; break;
 108:	20 91 01 01 	lds	r18, 0x0101	; 0x800101 <position>
 10c:	30 91 02 01 	lds	r19, 0x0102	; 0x800102 <position+0x1>
 110:	21 50       	subi	r18, 0x01	; 1
 112:	31 09       	sbc	r19, r1
 114:	30 93 02 01 	sts	0x0102, r19	; 0x800102 <position+0x1>
 118:	20 93 01 01 	sts	0x0101, r18	; 0x800101 <position>
 11c:	15 c0       	rjmp	.+42     	; 0x148 <update_encoder+0x76>

        case 3: case 12:
        position += 2; break;
 11e:	20 91 01 01 	lds	r18, 0x0101	; 0x800101 <position>
 122:	30 91 02 01 	lds	r19, 0x0102	; 0x800102 <position+0x1>
 126:	2e 5f       	subi	r18, 0xFE	; 254
 128:	3f 4f       	sbci	r19, 0xFF	; 255
 12a:	30 93 02 01 	sts	0x0102, r19	; 0x800102 <position+0x1>
 12e:	20 93 01 01 	sts	0x0101, r18	; 0x800101 <position>
 132:	0a c0       	rjmp	.+20     	; 0x148 <update_encoder+0x76>

        default:
        position -= 2; break;
 134:	20 91 01 01 	lds	r18, 0x0101	; 0x800101 <position>
 138:	30 91 02 01 	lds	r19, 0x0102	; 0x800102 <position+0x1>
 13c:	22 50       	subi	r18, 0x02	; 2
 13e:	31 09       	sbc	r19, r1
 140:	30 93 02 01 	sts	0x0102, r19	; 0x800102 <position+0x1>
 144:	20 93 01 01 	sts	0x0101, r18	; 0x800101 <position>
    }
    state = (s >> 2);
 148:	86 95       	lsr	r24
 14a:	86 95       	lsr	r24
 14c:	80 93 00 01 	sts	0x0100, r24	; 0x800100 <__DATA_REGION_ORIGIN__>
 150:	08 95       	ret

00000152 <delay_ms>:
}

void delay_ms(uint16_t n) {
    while (n--) {
 152:	00 97       	sbiw	r24, 0x00	; 0
 154:	41 f0       	breq	.+16     	; 0x166 <delay_ms+0x14>
	#else
		//round up by default
		__ticks_dc = (uint32_t)(ceil(fabs(__tmp)));
	#endif

	__builtin_avr_delay_cycles(__ticks_dc);
 156:	e9 ef       	ldi	r30, 0xF9	; 249
 158:	f0 e0       	ldi	r31, 0x00	; 0
 15a:	31 97       	sbiw	r30, 0x01	; 1
 15c:	f1 f7       	brne	.-4      	; 0x15a <delay_ms+0x8>
 15e:	00 c0       	rjmp	.+0      	; 0x160 <delay_ms+0xe>
 160:	00 00       	nop
 162:	01 97       	sbiw	r24, 0x01	; 1
 164:	c1 f7       	brne	.-16     	; 0x156 <delay_ms+0x4>
    }
}

int main(void)
{
    init_encoder();
 166:	08 95       	ret

00000168 <main>:
    while (1)
    {
        update_encoder();
 168:	a0 df       	rcall	.-192    	; 0xaa <init_encoder>
 16a:	b3 df       	rcall	.-154    	; 0xd2 <update_encoder>
        PORTD |= (1 << LED);
 16c:	5c 9a       	sbi	0x0b, 4	; 11
        delay_ms(position);
 16e:	80 91 01 01 	lds	r24, 0x0101	; 0x800101 <position>
 172:	90 91 02 01 	lds	r25, 0x0102	; 0x800102 <position+0x1>
 176:	ed df       	rcall	.-38     	; 0x152 <delay_ms>
        PORTD &= ~(1 << LED);
 178:	5c 98       	cbi	0x0b, 4	; 11
        delay_ms(position);
 17a:	80 91 01 01 	lds	r24, 0x0101	; 0x800101 <position>
 17e:	90 91 02 01 	lds	r25, 0x0102	; 0x800102 <position+0x1>
 182:	e7 df       	rcall	.-50     	; 0x152 <delay_ms>
 184:	f2 cf       	rjmp	.-28     	; 0x16a <main+0x2>

00000186 <__tablejump2__>:
 186:	ee 0f       	add	r30, r30
 188:	ff 1f       	adc	r31, r31
 18a:	05 90       	lpm	r0, Z+
 18c:	f4 91       	lpm	r31, Z
 18e:	e0 2d       	mov	r30, r0
 190:	09 94       	ijmp

It seems the compiler chose to implement the switch()/case using a lookup table. It's possibly easier to see that in this version of the Asm:

	.text
.Ltext0:
	.cfi_sections	.debug_frame
.global	init_encoder
	.type	init_encoder, @function
init_encoder:
.LFB6:
	.file 1 ".././main.c"
	.loc 1 12 0
	.cfi_startproc
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
	.loc 1 13 0
	ldi r24,lo8(16)	 ;  tmp53,
	out 0xa,r24	 ;  MEM[(volatile uint8_t *)42B], tmp53
	.loc 1 14 0
	in r24,0xa	 ;  D.1825, MEM[(volatile uint8_t *)42B]
	andi r24,lo8(-13)	 ;  D.1825,
	out 0xa,r24	 ;  MEM[(volatile uint8_t *)42B], D.1825
	.loc 1 15 0
	sbis 0x9,2	 ; ,
	rjmp .L2	 ;
	.loc 1 16 0
	lds r24,state	 ;  state, state
	ori r24,lo8(1)	 ;  D.1825,
	sts state,r24	 ;  state, D.1825
.L2:
	.loc 1 18 0
	sbis 0x9,3	 ; ,
	rjmp .L1	 ;
	.loc 1 19 0
	lds r24,state	 ;  state, state
	ori r24,lo8(2)	 ;  D.1825,
	sts state,r24	 ;  state, D.1825
.L1:
	ret
	.cfi_endproc
.LFE6:
	.size	init_encoder, .-init_encoder
.global	update_encoder
	.type	update_encoder, @function
update_encoder:
.LFB7:
	.loc 1 23 0
	.cfi_startproc
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
	.loc 1 24 0
	lds r24,state	 ;  state, state
	andi r24,lo8(3)	 ;  s,
.LVL0:
	.loc 1 25 0
	sbic 0x9,2	 ; ,
	.loc 1 26 0
	ori r24,lo8(4)	 ;  s,
.LVL1:
.L5:
	.loc 1 28 0
	sbic 0x9,3	 ; ,
	.loc 1 29 0
	ori r24,lo8(8)	 ;  s,
.LVL2:
.L6:
	.loc 1 31 0
	mov r20,r24	 ;  s, s
	ldi r21,0	 ;  s
	cpi r20,16	 ;  s,
	cpc r21,__zero_reg__	 ;  s
	brsh .L7	 ; ,
.LVL3:
	movw r30,r20	 ;  tmp65,
	subi r30,lo8(-(gs(.L9)))	 ;  tmp65,
	sbci r31,hi8(-(gs(.L9)))	 ;  tmp65,
	jmp __tablejump2__
	.section	.progmem.gcc_sw_table,"a",@progbits
	.p2align	1
.L9:
	.word gs(.L8)
	.word gs(.L10)
	.word gs(.L11)
	.word gs(.L12)
	.word gs(.L11)
	.word gs(.L8)
	.word gs(.L7)
	.word gs(.L10)
	.word gs(.L10)
	.word gs(.L7)
	.word gs(.L8)
	.word gs(.L11)
	.word gs(.L12)
	.word gs(.L11)
	.word gs(.L10)
	.word gs(.L8)
	.text
.L10:
	.loc 1 36 0
	lds r18,position	 ;  position, position
	lds r19,position+1	 ;  position, position
	subi r18,-1	 ;  D.1830,
	sbci r19,-1	 ;  D.1830,
	sts position+1,r19	 ;  position, D.1830
	sts position,r18	 ;  position, D.1830
	rjmp .L8	 ;
.L11:
	.loc 1 39 0
	lds r18,position	 ;  position, position
	lds r19,position+1	 ;  position, position
	subi r18,1	 ;  D.1830,
	sbc r19,__zero_reg__	 ;  D.1830
	sts position+1,r19	 ;  position, D.1830
	sts position,r18	 ;  position, D.1830
	rjmp .L8	 ;
.L12:
	.loc 1 42 0
	lds r18,position	 ;  position, position
	lds r19,position+1	 ;  position, position
	subi r18,-2	 ;  D.1830,
	sbci r19,-1	 ;  D.1830,
	sts position+1,r19	 ;  position, D.1830
	sts position,r18	 ;  position, D.1830
	rjmp .L8	 ;
.L7:
	.loc 1 45 0
	lds r18,position	 ;  position, position
	lds r19,position+1	 ;  position, position
	subi r18,2	 ;  D.1830,
	sbc r19,__zero_reg__	 ;  D.1830
	sts position+1,r19	 ;  position, D.1830
	sts position,r18	 ;  position, D.1830
.L8:
	.loc 1 47 0
	lsr r24	 ;  D.1829
	lsr r24	 ;  D.1829
	sts state,r24	 ;  state, D.1829
	ret
	.cfi_endproc
.LFE7:
	.size	update_encoder, .-update_encoder
.global	delay_ms
	.type	delay_ms, @function
delay_ms:
.LFB8:
	.loc 1 50 0
	.cfi_startproc
.LVL4:
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
	.loc 1 51 0
	sbiw r24,0	 ;  n,
	breq .L13	 ; ,
.LVL5:
.L15:
.LBB4:
.LBB5:
	.file 2 "c:\\program files (x86)\\atmel\\studio\\7.0\\toolchain\\avr8\\avr8-gnu-toolchain\\avr\\include\\util\\delay.h"
	.loc 2 187 0
	ldi r30,lo8(249)	 ; ,
	ldi r31,hi8(249)	 ; ,
1:	sbiw r30,1	 ;
	brne 1b
	rjmp .
	nop
.LVL6:
	sbiw r24,1	 ;  ivtmp.25,
.LVL7:
.LBE5:
.LBE4:
	.loc 1 51 0
	brne .L15	 ; ,
.LVL8:
.L13:
	ret
	.cfi_endproc
.LFE8:
	.size	delay_ms, .-delay_ms
.global	main
	.type	main, @function
main:
.LFB9:
	.loc 1 57 0
	.cfi_startproc
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
	.loc 1 58 0
	call init_encoder	 ;
.LVL9:
.L18:
	.loc 1 61 0 discriminator 1
	call update_encoder	 ;
.LVL10:
	.loc 1 62 0 discriminator 1
	sbi 0xb,4	 ; ,
	.loc 1 63 0 discriminator 1
	lds r24,position	 ; , position
	lds r25,position+1	 ; , position
	call delay_ms	 ;
.LVL11:
	.loc 1 64 0 discriminator 1
	cbi 0xb,4	 ; ,
	.loc 1 65 0 discriminator 1
	lds r24,position	 ; , position
	lds r25,position+1	 ; , position
	call delay_ms	 ;
.LVL12:
	rjmp .L18	 ;
	.cfi_endproc
.LFE9:
	.size	main, .-main
.global	state
	.section .bss
	.type	state, @object
	.size	state, 1
state:
	.zero	1
.global	position
	.type	position, @object
	.size	position, 2
position:
	.zero	2

and that is possibly easier to read after processing with: https://github.com/wrightflyer/a... at which point it becomes:

        .text
.Ltext0:
.global init_encoder
        .type   init_encoder, @function
init_encoder:
//==> void init_encoder() {
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
//==>     DDRD = (1 << LED);
        ldi r24,lo8(16)  ;  tmp53,
        out 0xa,r24      ;  MEM[(volatile uint8_t *)42B], tmp53
//==>     DDRD &= ~((1 << SIG_A) | (1 << SIG_B));
        in r24,0xa       ;  D.1825, MEM[(volatile uint8_t *)42B]
        andi r24,lo8(-13)        ;  D.1825,
        out 0xa,r24      ;  MEM[(volatile uint8_t *)42B], D.1825
//==>     if (PIND & (1 << SIG_A)) {
        sbis 0x9,2       ; ,
        rjmp .L2         ;
//==>         state |= 1;
        lds r24,state    ;  state, state
        ori r24,lo8(1)   ;  D.1825,
        sts state,r24    ;  state, D.1825
.L2:
//==>     if (PIND & (1 << SIG_B)) {
        sbis 0x9,3       ; ,
        rjmp .L1         ;
//==>         state |= 2;
        lds r24,state    ;  state, state
        ori r24,lo8(2)   ;  D.1825,
        sts state,r24    ;  state, D.1825
.L1:
        ret
        .size   init_encoder, .-init_encoder
.global update_encoder
        .type   update_encoder, @function
update_encoder:
//==> void update_encoder() {
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
//==>     uint8_t s = state & 3;
        lds r24,state    ;  state, state
        andi r24,lo8(3)  ;  s,
//==>     if (PIND & (1 << SIG_A)) {
        sbic 0x9,2       ; ,
//==>          s |= 4;
        ori r24,lo8(4)   ;  s,
.L5:
//==>     if (PIND & (1 << SIG_B)) {
        sbic 0x9,3       ; ,
//==>         s |= 8;
        ori r24,lo8(8)   ;  s,
.L6:
//==>     switch (s) {
        mov r20,r24      ;  s, s
        ldi r21,0        ;  s
        cpi r20,16       ;  s,
        cpc r21,__zero_reg__     ;  s
        brsh .L7         ; ,
        movw r30,r20     ;  tmp65,
        subi r30,lo8(-(gs(.L9)))         ;  tmp65,
        sbci r31,hi8(-(gs(.L9)))         ;  tmp65,
        jmp __tablejump2__

        .section        .progmem.gcc_sw_table,"a",@progbits
        .p2align        1
.L9:
        .word gs(.L8)
        .word gs(.L10)
        .word gs(.L11)
        .word gs(.L12)
        .word gs(.L11)
        .word gs(.L8)
        .word gs(.L7)
        .word gs(.L10)
        .word gs(.L10)
        .word gs(.L7)
        .word gs(.L8)
        .word gs(.L11)
        .word gs(.L12)
        .word gs(.L11)
        .word gs(.L10)
        .word gs(.L8)
        .text
.L10:
//==>         position++; break;
        lds r18,position         ;  position, position
        lds r19,position+1       ;  position, position
        subi r18,-1      ;  D.1830,
        sbci r19,-1      ;  D.1830,
        sts position+1,r19       ;  position, D.1830
        sts position,r18         ;  position, D.1830
        rjmp .L8         ;
.L11:
//==>         position--; break;
        lds r18,position         ;  position, position
        lds r19,position+1       ;  position, position
        subi r18,1       ;  D.1830,
        sbc r19,__zero_reg__     ;  D.1830
        sts position+1,r19       ;  position, D.1830
        sts position,r18         ;  position, D.1830
        rjmp .L8         ;
.L12:
//==>         position += 2; break;
        lds r18,position         ;  position, position
        lds r19,position+1       ;  position, position
        subi r18,-2      ;  D.1830,
        sbci r19,-1      ;  D.1830,
        sts position+1,r19       ;  position, D.1830
        sts position,r18         ;  position, D.1830
        rjmp .L8         ;
.L7:
//==>         position -= 2; break;
        lds r18,position         ;  position, position
        lds r19,position+1       ;  position, position
        subi r18,2       ;  D.1830,
        sbc r19,__zero_reg__     ;  D.1830
        sts position+1,r19       ;  position, D.1830
        sts position,r18         ;  position, D.1830
.L8:
//==>     state = (s >> 2);
        lsr r24  ;  D.1829
        lsr r24  ;  D.1829
        sts state,r24    ;  state, D.1829
        ret
        .size   update_encoder, .-update_encoder
.global delay_ms
        .type   delay_ms, @function
delay_ms:
//==> void delay_ms(uint16_t n) {
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
//==>     while (n--) {
        sbiw r24,0       ;  n,
        breq .L13        ; ,
.L15:
//==>   __builtin_avr_delay_cycles(__ticks_dc);
        ldi r30,lo8(249)         ; ,
        ldi r31,hi8(249)         ; ,
1:      sbiw r30,1       ;
        brne 1b
        rjmp .
        nop
        sbiw r24,1       ;  ivtmp.25,
//==>     while (n--) {
        brne .L15        ; ,
.L13:
        ret
        .size   delay_ms, .-delay_ms
.global main
        .type   main, @function
main:
//==> {
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
//==>     init_encoder();
        call init_encoder        ;
.L18:
//==>         update_encoder();
        call update_encoder      ;
//==>         PORTD |= (1 << LED);
        sbi 0xb,4        ; ,
//==>         delay_ms(position);
        lds r24,position         ; , position
        lds r25,position+1       ; , position
        call delay_ms    ;
//==>         PORTD &= ~(1 << LED);
        cbi 0xb,4        ; ,
//==>         delay_ms(position);
        lds r24,position         ; , position
        lds r25,position+1       ; , position
        call delay_ms    ;
        rjmp .L18        ;
        .size   main, .-main
.global state

        .section .bss
        .type   state, @object
        .size   state, 1
state:
        .zero   1
.global position
        .type   position, @object
        .size   position, 2
position:
        .zero   2
        .text
.Letext0:

Note that this is for the avr-as assembler, not the avrasm2 you are using so you can't use it directly but it may give an idea of how you can do this?

 

(you would need to lift "__tablejump2__" from the LSS version as it's not in the .s file here)

 

PS and while I haven't tested this simple C version. I have a C++ version of the same thing I wrote at:

 

https://github.com/wrightflyer/s...

 

and I know that works. In fact it allows you to read at least 8 encoders:

 

https://github.com/wrightflyer/s...

Last Edited: Thu. Oct 29, 2020 - 04:09 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here's some cheerful pseudo-code.  Putting it into AVR assembler is left as an exercise for the student:

Begin:

Counter = 0

If A is High, Go to A High Loop else

go to A Low Loop

 

A Low Loop:

  Read A level

 If A is Low, go to A Low Loop else

   ; hey look, A just went high!

 Read B level

 If B is low, Counter = Counter - 1

 If B is high, Counter = Counter + 1

 Send out Counter

go to A High Loop

 

A High Loop:

 Read A level

 If A is high, go to A High Loop else continue

   ; hey look, A just went low!

 Read B level

 If B is low, Counter = Counter + 1

 If B is high, Counter = Counter - 1

 Send out Counter

 

goto A Low Loop

 

repeat ad nauseum.  S.

 

Edited to improve things quite a bit.

Last Edited: Thu. Oct 29, 2020 - 04:49 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Or you could do what clawson said.   cheeky   S.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Edited #13 to make this post irrelevant

Last Edited: Thu. Oct 29, 2020 - 04:46 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you are using one of those super-cheap encocders that sell for less than a dollar/euro/poundSterling each, then I recommend adding this 4 resistor/2 capacitor hardware filter circuit to each one.   This extends the pulse lengths (by tens of microseconds) of the encoder signals.  Especially useful with assembler code.

 

   vcc  ------- /\\//\/-----o-----/\/\/\/------o-----| |------ gnd

                     10k        |       10K          |      0.1uF

                              to encoder        to CPU pin

 

Put one of these circuits on each of the encoder data pins.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I get faultless reading from those cheap encoders with no need for any form of hardware debouncing - I simply sample the pin states in a timer ISR just as you might with any form of bouncy source (in fact the same code does buttons too because many of these encoders are a "click switch" as well).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As well you might - if the timer's slow enough.  Howzabout 800kHz?  Easily done in hardware, if not in an AVR.  S.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

(My AVR designs topped out at about 200kHz.  There may be ways more clever.)