Hello, I have problem with counting ticks of an encoder. I can achieve about 5 kHz without errors. Above this frequency, I loose ticks. I use second AVR processor as the generator of two signals -- I just test my device, without real encoder. What to do to improve frequency ? Below, I present my program. Processor: AVR ATmega16 You should connect an Encoder in the way: Canal A : PD2 i PD3 Canal B: PD4 #include <stdio.h> #include <avr/io.h> #include <avr/interrupt.h> #include <avr/signal.h> #include "lcd.h" long int licz = 0; int flagaA = 0, flagaB = 0; SIGNAL (SIG_INTERRUPT0) { if (!(inb(PIND) & 16)) /* if 0 on canal B */ flagaA = 1; else /* otherwise, (1 on canal B) */ flagaB = 1; } SIGNAL (SIG_INTERRUPT1) { if (inb(PIND) & 16) { /* if 1 on canal B */ if (flagaA) /* if flagaA is set */ licz++, flagaA = 0; } else if (flagaB) /* if flagaB is set */ licz--, flagaB = 0; } int main(void) { char s[16]; outp(0, DDRD); /* Setting interrupts INT 0 i INT1 -- rising, falling edge */ outp((1<<INT0)|(1<<INT1), GIMSK); outp((1<<ISC01)|(1<<ISC00) | (1<<ISC11), MCUCR); initLCD(); sei(); /* star interrupts */ for (;;) { cursorHome(); sprintf(s, "%ld ", licz); showStringOnLCD(s); } return 0; }
Atmel AVR, too slow encoder tics counting
Started by ●May 6, 2004
Reply by ●May 6, 20042004-05-06
Umpa wrote:> Hello, > > I have problem with counting ticks of an encoder. > I can achieve about 5 kHz without errors. > Above this frequency, I loose ticks. > > I use second AVR processor as the generator of > two signals -- I just test my device, without real encoder. > > What to do to improve frequency ?If you want a lot faster, use hardware, like a CPLD. If the AVR has an Up/Dn counter mode a la 89C52, the PLD needs only do the conditioning to Dirn and Count signals. If you only have UP counters, you can allocate two, and internally take the difference. PLD then generates Clk_UP, and Clk_DN pulse trains. 5KHz sounds about right for LONGINT SW - To speed SW further, try assembler and do cascaded or queued BYTE increments, rather than a full long int INC. Latter is simpler to write, but every INC handles all bytes. In the AVR, register access is much better than DATA access, so either move all the counter to regs, or if that chews too much REG resource, move the LSB byte to one reg, and cascade into pointer/data access only on overflow. For that, you will need to use assembler.... -jg
Reply by ●May 6, 20042004-05-06
How fast are you running the clock on your CPU? What is your clock src? Phil W "Umpa" <����@umpaumpalala.com> wrote in message news:c7efnb$qna$1@atlantis.news.tpi.pl...> Hello, > > I have problem with counting ticks of an encoder. > I can achieve about 5 kHz without errors. > Above this frequency, I loose ticks. > > I use second AVR processor as the generator of > two signals -- I just test my device, without real encoder. > > What to do to improve frequency ? > > Below, I present my program. > Processor: AVR ATmega16 > You should connect an Encoder in the way: > Canal A : PD2 i PD3 > Canal B: PD4 > > #include <stdio.h> > #include <avr/io.h> > #include <avr/interrupt.h> > #include <avr/signal.h> > #include "lcd.h" > > long int licz = 0; > int flagaA = 0, flagaB = 0; > > > SIGNAL (SIG_INTERRUPT0) > { > if (!(inb(PIND) & 16)) /* if 0 on canal B */ > flagaA = 1; > else /* otherwise, (1 on canal B) */ > flagaB = 1; > } > > > SIGNAL (SIG_INTERRUPT1) > { > if (inb(PIND) & 16) { /* if 1 on canal B */ > if (flagaA) /* if flagaA is set */ > licz++, flagaA = 0; > } else if (flagaB) /* if flagaB is set */ > licz--, flagaB = 0; > } > > > int main(void) > { > char s[16]; > > outp(0, DDRD); > > /* Setting interrupts INT 0 i INT1 -- rising, falling edge > */ > outp((1<<INT0)|(1<<INT1), GIMSK); > outp((1<<ISC01)|(1<<ISC00) | (1<<ISC11), MCUCR); > > > initLCD(); > > sei(); /* star interrupts */ > > for (;;) { > cursorHome(); > sprintf(s, "%ld ", licz); > showStringOnLCD(s); > } > > return 0; > } > >
Reply by ●May 7, 20042004-05-07
"Umpa" <����@umpaumpalala.com> wrote in message news:c7efnb$qna$1@atlantis.news.tpi.pl...> Hello, > > I have problem with counting ticks of an encoder. > I can achieve about 5 kHz without errors. > Above this frequency, I loose ticks. > > I use second AVR processor as the generator of > two signals -- I just test my device, without real encoder. > > What to do to improve frequency ?Your interrupt service routines will translate to only a few assembler instructions. There's plenty processing power in the AVR to cope with this at 5 kHz and higher. You may want to check your LCD output routines. Check if it's disabling interrupts somewhere. How do you determine that you loose ticks, not by reading the the LCD I suppose? Rob
Reply by ●May 7, 20042004-05-07
> How do you determine that you loose ticks, not by reading the the LCD I > suppose? >My generator generates known numer of ticks on both chanels with different phase. Yea, I read LCD -- it shows smaller numer at higher frequencies. When I add "cli" at begining of interrupt function and "sei" at the end -- LCD shows "0" at higher frequencies. And it works correctly at smaller frequencies. So, I think, that processor tries to run interrupt function when previous hasn`t finished yet. I think, I must optimalize my code and write it in assembler. The second thing is, when I change type of "licz" to int (not long int), it can count ticks faster. So, I`m almost sure, my code is too slow. Now, I use only INT0 in my program, and I can count ticks with a little bit more frequency -- but not enough still. I need about 10 kHz. unsigned long int licz = 0; unsigned char flagaA = 0, flagaB = 0; SIGNAL (SIG_INTERRUPT0) { // cli(); if (PIND & 4) { (!(PIND & 16)) ? (flagaA = 1) : (flagaB = 1); } else { if (PIND & 16) { flagaA && (licz++, flagaA = 0); } else { flagaB && (licz--, flagaB = 0); } } // sei(); } int main(void) { char s[16]; outp(0, DDRD); outp((1<<INT0), GIMSK); outp((1<<ISC00), MCUCR); sei(); .... }
Reply by ●May 7, 20042004-05-07
> How fast are you running the clock on your CPU? > What is your clock src? >I have ATmega16 -- 16 MHz. Clock source is internal I think, I didn`t connect any quartz. Did I do something wrong? Umpa.
Reply by ●May 7, 20042004-05-07
Thanks for smart advices.> 5KHz sounds about right for LONGINT SW -My processor has 16 MHz. Even if execution of interrupt procedure lasts 1000 cycles (I don`t think so it lasts as long), we should have 16000000 / 1000 = 16 kHz. I think, my program should be able to count with higher frequencies according to high processor speed, and not so slow (I think not slower than e.g. 1000 cycles) interrupt procedure. Umpa.
Reply by ●May 7, 20042004-05-07
This code: #include <stdio.h> #include <avr/io.h> #include <avr/interrupt.h> #include <avr/signal.h> unsigned long int licz = 0; unsigned char flagaA = 0, flagaB = 0; SIGNAL (SIG_INTERRUPT0) { // cli(); if (PIND & 4) { (!(PIND & 16)) ? (flagaA = 1) : (flagaB = 1); } else { if (PIND & 16) { if (flagaA) licz++, flagaA = 0; } else { if (flagaB) licz--, flagaB = 0; } } // sei(); } int main(void) { outp(0, DDRD); outp((1<<INT0), GIMSK); outp((1<<ISC00), MCUCR); sei(); for (;;) ; return 0; } Looks that in assembler: encoder.elf: file format elf32-avr Sections: Idx Name Size VMA LMA File off Algn 0 .text 0000014a 00000000 00000000 00000094 2**0 CONTENTS, ALLOC, LOAD, READONLY, CODE 1 .data 00000000 00800060 0000014a 000001de 2**0 CONTENTS, ALLOC, LOAD, DATA 2 .bss 00000006 00800060 00800060 000001de 2**0 ALLOC 3 .noinit 00000000 00800066 00800066 000001de 2**0 CONTENTS 4 .eeprom 00000000 00810000 00810000 000001de 2**0 CONTENTS Disassembly of section .text: 00000000 <.text>: 0: 0c 94 2a 00 jmp 0x54 4: 0c 94 47 00 jmp 0x8e 8: 0c 94 45 00 jmp 0x8a c: 0c 94 45 00 jmp 0x8a 10: 0c 94 45 00 jmp 0x8a 14: 0c 94 45 00 jmp 0x8a 18: 0c 94 45 00 jmp 0x8a 1c: 0c 94 45 00 jmp 0x8a 20: 0c 94 45 00 jmp 0x8a 24: 0c 94 45 00 jmp 0x8a 28: 0c 94 45 00 jmp 0x8a 2c: 0c 94 45 00 jmp 0x8a 30: 0c 94 45 00 jmp 0x8a 34: 0c 94 45 00 jmp 0x8a 38: 0c 94 45 00 jmp 0x8a 3c: 0c 94 45 00 jmp 0x8a 40: 0c 94 45 00 jmp 0x8a 44: 0c 94 45 00 jmp 0x8a 48: 0c 94 45 00 jmp 0x8a 4c: 0c 94 45 00 jmp 0x8a 50: 0c 94 45 00 jmp 0x8a 54: 11 24 eor r1, r1 56: 1f be out 0x3f, r1 ; 63 58: cf e5 ldi r28, 0x5F ; 95 5a: d4 e0 ldi r29, 0x04 ; 4 5c: de bf out 0x3e, r29 ; 62 5e: cd bf out 0x3d, r28 ; 61 60: 10 e0 ldi r17, 0x00 ; 0 62: a0 e6 ldi r26, 0x60 ; 96 64: b0 e0 ldi r27, 0x00 ; 0 66: ea e4 ldi r30, 0x4A ; 74 68: f1 e0 ldi r31, 0x01 ; 1 6a: 02 c0 rjmp .+4 ; 0x70 6c: 05 90 lpm r0, Z+ 6e: 0d 92 st X+, r0 70: a0 36 cpi r26, 0x60 ; 96 72: b1 07 cpc r27, r17 74: d9 f7 brne .-10 ; 0x6c 76: 10 e0 ldi r17, 0x00 ; 0 78: a0 e6 ldi r26, 0x60 ; 96 7a: b0 e0 ldi r27, 0x00 ; 0 7c: 01 c0 rjmp .+2 ; 0x80 7e: 1d 92 st X+, r1 80: a6 36 cpi r26, 0x66 ; 102 82: b1 07 cpc r27, r17 84: e1 f7 brne .-8 ; 0x7e 86: 0c 94 9a 00 jmp 0x134 8a: 0c 94 00 00 jmp 0x0 8e: 1f 92 push r1 90: 0f 92 push r0 92: 0f b6 in r0, 0x3f ; 63 94: 0f 92 push r0 96: 11 24 eor r1, r1 98: 8f 93 push r24 9a: 9f 93 push r25 9c: af 93 push r26 9e: bf 93 push r27 a0: 82 9b sbis 0x10, 2 ; 16 a2: 0a c0 rjmp .+20 ; 0xb8 a4: 84 99 sbic 0x10, 4 ; 16 a6: 04 c0 rjmp .+8 ; 0xb0 a8: 81 e0 ldi r24, 0x01 ; 1 aa: 80 93 64 00 sts 0x0064, r24 ae: 39 c0 rjmp .+114 ; 0x122 b0: 81 e0 ldi r24, 0x01 ; 1 b2: 80 93 65 00 sts 0x0065, r24 b6: 35 c0 rjmp .+106 ; 0x122 b8: 84 9b sbis 0x10, 4 ; 16 ba: 1a c0 rjmp .+52 ; 0xf0 bc: 80 91 64 00 lds r24, 0x0064 c0: 88 23 and r24, r24 c2: 79 f1 breq .+94 ; 0x122 c4: 80 91 60 00 lds r24, 0x0060 c8: 90 91 61 00 lds r25, 0x0061 cc: a0 91 62 00 lds r26, 0x0062 d0: b0 91 63 00 lds r27, 0x0063 d4: 01 96 adiw r24, 0x01 ; 1 d6: a1 1d adc r26, r1 d8: b1 1d adc r27, r1 da: 80 93 60 00 sts 0x0060, r24 de: 90 93 61 00 sts 0x0061, r25 e2: a0 93 62 00 sts 0x0062, r26 e6: b0 93 63 00 sts 0x0063, r27 ea: 10 92 64 00 sts 0x0064, r1 ee: 19 c0 rjmp .+50 ; 0x122 f0: 80 91 65 00 lds r24, 0x0065 f4: 88 23 and r24, r24 f6: a9 f0 breq .+42 ; 0x122 f8: 80 91 60 00 lds r24, 0x0060 fc: 90 91 61 00 lds r25, 0x0061 100: a0 91 62 00 lds r26, 0x0062 104: b0 91 63 00 lds r27, 0x0063 108: 01 97 sbiw r24, 0x01 ; 1 10a: a1 09 sbc r26, r1 10c: b1 09 sbc r27, r1 10e: 80 93 60 00 sts 0x0060, r24 112: 90 93 61 00 sts 0x0061, r25 116: a0 93 62 00 sts 0x0062, r26 11a: b0 93 63 00 sts 0x0063, r27 11e: 10 92 65 00 sts 0x0065, r1 122: bf 91 pop r27 124: af 91 pop r26 126: 9f 91 pop r25 128: 8f 91 pop r24 12a: 0f 90 pop r0 12c: 0f be out 0x3f, r0 ; 63 12e: 0f 90 pop r0 130: 1f 90 pop r1 132: 18 95 reti 134: cf e5 ldi r28, 0x5F ; 95 136: d4 e0 ldi r29, 0x04 ; 4 138: de bf out 0x3e, r29 ; 62 13a: cd bf out 0x3d, r28 ; 61 13c: 11 ba out 0x11, r1 ; 17 13e: 80 e4 ldi r24, 0x40 ; 64 140: 8b bf out 0x3b, r24 ; 59 142: 81 e0 ldi r24, 0x01 ; 1 144: 85 bf out 0x35, r24 ; 53 146: 78 94 sei 148: ff cf rjmp .-2 ; 0x148
Reply by ●May 7, 20042004-05-07
> You may want to check your LCD output routines. Check if it's disabling > interrupts somewhere. >I didn`t disable interrupts myself in any code. But, after translating code to assembler, I can see many "cli" instructions in my source code. When I cut LCD library and left pure interrupt function and main(), there were no "cli" instructions in source code. Umpa.
Reply by ●May 7, 20042004-05-07
Now, I did loops at the begining of main(): for(i=0;i<10000;i++) for(j=0;j<10000;j++) ; after that I initialize LCD and display counter -- to avoid "cli" instructions (there are no such instructions in C code, but they apear in assembler code generated by winavr) which could be in LCD library. It improved situation (I can count with a little bit more frequencies), but not as much I would like. Umpa.