Rotary encoder uitlezen

Ik ben in het bezit van een 3d-printer, en zou daar graag wat metingen op doen om te kijken of ik hem preciezer kan maken.Zou wil ik op de verschillende assen een rotary encoder bevestigen en deze uitlezen.
Ik heb al flink rond gezocht en ik neem waarschijnlijk een quadrature optische encoder die 250 CPR doet. Dit komt dan neer dat deze 1000 pulsen per omwenteling geeft naar mijn hardware.
Wat rekenen geeft dat dat in mijn geval bijna 4700 pulsen per seconde zijn, maal drie, want een x,y, en z-as. En dat getal ook nog eens maal twee, want een encoder heeft zowel een a- en b-output.

Dat geeft 28000 interrupts(lijkt mij de enige logische manier ) die ik per seconde af moet handelen. Ik heb al een beetje rondgezocht naar de Raspberry Pi, Cubieboard, Arduino uno, Arduino Mega en Arduino Due, maar er is zeer weinig beschikbaar over het aantal interrupts dat ze per seconde af kunnen handelen, ik kwam over de Mega tegen dat men er 10000 per seconde mee haalt, bij de Raspberry had iemand 5000 gehaald.

Ik heb verder gevonden dat er ook quadrature decoders zijn, maar die zijn enorm dun bezaaid. Ik vond bijvoorbeeld http://www.lsicsi.com/pdfs/Data_Sheets/LS7166.pdf deze decoder, maar die lijkt niet makkelijk leverbaar.
Ook een LS7266 lijkt niet meer leverbaar.

Kan iemand mij een hint in de juiste richting geven, om dit uit te lezen? Ik kan mij haast niet voorstellen dat dit iets is wat nauwelijks gedaan wordt.

GJ_

Moderator

Waarom geen P8X32 microcontroller? Die heeft helemaal geen interrupts maar gewoon 8 losse cores. Voor dit soort taken reserveer je gewoon een eigen core.

Pentium-120 MHz: 100k interrupts per seconde.
Pentium2-450 MHz (of zo): 100k interrupts per seconde.

Uit een ver verleden ooit gemeten.

Bij AVR (dus arduino), moet je goed uitkijken hoe je de interrupt routine inricht om "snel" te zijn. Als je hem in C schrijft en je roept een c-functie aan, dan moet de compiler een hele hoop registers bewaren voordat ie aan de interrupt routine kan beginnen. Maar als je geen functies aanroept dan kan ie bijhouden wat ie nodig heeft en alleen die bewaren. Schrijf je het dan nog kort-en-snel, kan je volgens mij richting de 1M interrupts per seconde (20 instructies per interrupt).

Maar ik heb hier wel een ARM bordje die wel 160 instructies per microseconde kan doen. :-) Ik mail je.

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/

Ah,goed om te horen dat het in principe gewoon mogelijk is.
Ik neig op het moment naar een Raspberry, zodat ik een klein webservertje kan maken waar ik wat statistieken op kan plaatsen.
Ik heb al gevonden dat ik een eigen kernel-module kan schrijven, zodat ik niet de onnodige "lagen" heb tussen de hardware en user-space. Dat zou, in theorie, voldoende moeten zijn, zeker omdat ik dan 700MHz tot mijn beschikking heb.

EDIT:
Op zich heb ik niet zo veel instructies nodig per interrupt, enkel een if-je die bepaalt wat de waarde is van het andere kanaal, en aan de hand daar van een counter op/neer laat tellen. Dat zouden dus vrij snelle instructies moeten zijn.

[Bericht gewijzigd door Marcovannoord op woensdag 19 februari 2014 10:15:30 (23%)

Schimanski

Golden Member

Op 19 februari 2014 10:07:17 schreef rew:
Bij AVR (dus arduino), moet je goed uitkijken hoe je de interrupt routine inricht om "snel" te zijn.

De mooiste (en snelste manier) is om in de interrupt routine alleen een flag te zetten welke je dan in het hoofdprogramma (loop) steeds test.
Vroeger (ik programmeer al een tijdje niet meer) was het not done om in een ISR (veel) code uit te voeren of routines aan te roepen.

Je zou in de ISR alleen moeten bepalen of de encoder links-, of rechtsom heeft bewogen en dan met twee flags voor links en rechts te werken welke je set of reset.

What, me worry? // Radiozendamateur - PA2HGJ // Stuff is the junk you keep -- Junk is the stuff you throw away // Tinkeo Ergo Sum

GJ_, ik had al een aantal van dat soort tutorials gevonden, maar meestal maakt men gebruik van digitalRead, wat performance-wise niet bepaald optimaal is. https://code.google.com/p/digitalwritefast/issues/attachmentText?id=2&… digitalWriteFast is al een heel stuk beter, een goede implementatie van dat systeem is bijvoorbeeld http://yameb.blogspot.nl/2012/11/quadrature-encoders-in-arduino-done.h… .

Ik heb er nu even een rPi bij gepakt, om te kijken hoe snel ik daar de interrupts van kan gebruiken. Een arduino zou mogelijk ook werken, maar is waarschijnlijk niet krachtig genoeg voor wat ik er mee wil. Ik zou graag ook een simpele webinterface willen en eventueel de mogelijkheid om later de printer aan te sturen vanaf mijn hardware.

Als het mogelijk is om een klein stukje hardware toe te voegen, kun je je CPU behoorlijk ontlasten. Door de eerste paar LSB's in hardware te tellen in bijvoorbeeld een 8-bits teller, hoeft je interrupt routine al een factor 256 minder te worden aangeroepen. (voorbeeld: HCTL-2017 quadrature counter).

EricP

mét CE

Het is vrij logisch dat er nergens staat hoeveel interrupts er per seconde afgehandeld kunnen worden: dat bepaal jij.

Uitgaande van een AVR: De meeste instructies zijn single-cycle. We werken in assembly - met een compiler weet je nooit precies wat die er van bakt. Dan ga je dus kijken wat zo'n ISR moet doen. Dan weet je welke instructies er voorbij komen. Dan weet je dus ook hoe lang een ISR duurt (uitgedrukt in clock cycles). Vergeet de aanroep en RETI niet.
Daaruit zou je dus kunnen berekenen hoeveel interrupts je per seconde af kunt handelen. Daarbij doe je de aanname dat ze mooi verdeeld in de tijd binnen komen en dat je niks anders doet. Neem daar 30% tot de helft van en dan zou het meestal kunnen werken.

Los het lekker in hardware op: een stel counters wat door je rotary encoders lekker staat te tellen. Als de de actuele waarde wilt weten, dan lees je dat uit je counter. Typisch iets wat je niet in software op moet willen lossen.

GJ_

Moderator

Op 19 februari 2014 14:46:06 schreef EricP:...een stel counters wat door je rotary encoders lekker staat te tellen.

Hoe doe je dat als die encoder een beetje heen en weer staat te wiebelen? Je wil in principe toch het aantal pulsen per omwenteling x4 tellen, hoe los je dat op in de HW tellers?

EricP

mét CE

Ik mis je 'x4' even. De meeste encoders die zoveel pulsen geven per omwenteling, leveren ook fatsoenlijke signalen (je hebt die shaping toch al nodig). Dus geen dender ed. De ene output laat je pulsen genereren, de andere de direction. Heen & weer wiebelen is iets voor mechanische encoders. Voor zover mij bekend, doen optische encoders daar niet aan.

GJ_

Moderator

met "x4" bedoelde ik dat je bij een encoder die bv 1024 delen per omwentelingen geeft natuurlijk wel graag per omwenteling 4096 wil tellen. En een encoder kan twee kanten op draaien, dan wil je wel optellen en aftrekken van je huidige positie. Hoe doe je dat met de teller in een uC? Kunnen die dat? En heen en weer wiebelen: als een een encoder op een positie "stil" staat kan er altijd een van de twee uitgangen knipperen natuurlijk. En half graadje heen en weer wiebelen dus :-)

Als je het allemaal handmatig in software zou willen doen, en dat de enige taak is die draait op je microcontroller, dan wil je juist interrupts vermijden. Waarom? Omdat dat alleen maar overhead oplevert bij de aanroep en return. Je hebt toch altijd een worst-case scenario waarmee je moet rekenen om te uiterste timing vast te stellen. Een interrupt levert in zo'n geval geen voordeel op mbt tijdswinst. Dus doe je alles in een zorgvuldig uitgekiende control loop; gewoon in de main.

Uiteraard kun je ook zonder interrupt-routine binnen je main-loop soms fijn gebruik maken van interrupt FLAGS die door hardware gezet (kunnen) worden, maar dat is een ander verhaal en zadelt je niet op met de overhead van context switching.

MAAR...
Het juiste beestje voor de juiste taak lijkt me hier de oplossing.

Dus een AVR met QDEC of een PICmicro met QEI. In beide gevallen gaat het om een hardwarematige ondersteuning voor een quadrature encoder interface. In je software is het dan een kwestie van een registertje/countertje uitlezen.

If you want to succeed, double your failure rate.
EricP

mét CE

met "x4" bedoelde ik dat je bij een encoder die bv 1024 delen per omwentelingen geeft natuurlijk wel graag per omwenteling 4096 wil tellen.

Waarom zou je dat willen?? Als dat al interessant zou zijn, dan los je dat in software op als je de teller uitleest...

En een encoder kan twee kanten op draaien, dan wil je wel optellen en aftrekken van je huidige positie. Hoe doe je dat met de teller in een uC? Kunnen die dat?

Nee, in discrete hardware. Niks met μC. Gewoon een counter-chippie (zie post Jochem). En ja, die kunnen dat.

En heen en weer wiebelen: als een een encoder op een positie "stil" staat kan er altijd een van de twee uitgangen knipperen natuurlijk.

Als het goed is, dan doen optische encoders dat dus niet. Daar zit een stuk signal-conditioning hardware in. Als het een mechanische encoder is, dan heb je helemaal gelijk.

GJ_

Moderator

per puls op het eerste spoor heb je 4 flanken die je moet tellen, anders is het prutswerk. En een optische encoder waarvan de as niet 100% stilstaat kan knipperen. Dat is onmogelijk met signaalconditionering op te lossen.

Op 19 februari 2014 18:22:00 schreef Jochem:
Als je het allemaal handmatig in software zou willen doen, en dat de enige taak is die draait op je microcontroller, dan wil je juist interrupts vermijden. Waarom? Omdat dat alleen maar overhead oplevert bij de aanroep en return. Je hebt toch altijd een worst-case scenario waarmee je moet rekenen om te uiterste timing vast te stellen. Een interrupt levert in zo'n geval geen voordeel op mbt tijdswinst. Dus doe je alles in een zorgvuldig uitgekiende control loop; gewoon in de main.

Uiteraard kun je ook zonder interrupt-routine binnen je main-loop soms fijn gebruik maken van interrupt FLAGS die door hardware gezet (kunnen) worden, maar dat is een ander verhaal en zadelt je niet op met de overhead van context switching.

Absoluut NIET meet eens! Je draait er altijd wel iets bij want je MOET iets met die signalen willen doen!
De enige manier om dit soort snelle signalen te meten is om interrupts te gebruiken.
De beide ingangen van de encoder (A/B) zo instellen dat ze een interrupt genereren op een flank.

In de code komt dan zoiets (als pseudo code):

code:


ISR-pin A:

  save werkregisters (status/accu + evt andere regs)

  read pin B

  if B = 1
     increment counter
  else
     decrement counter
  endif

  restore registers

  reti

ISR-pin B:

  save werkregisters (status/accu + evt andere regs)

  read pin A

  if A = 0
     increment counter
  else
     decrement counter
  endif

  restore registers

  reti

Bovenstaande code is misschien iets van 10 instructies per ISR. (Afhankelijk van de counter resolutie heb je iets meer nodig)
Op een simpele AVR duurt dat 10 x 250 nS = 2,5 uS.

In de main loop lees je de counter uit.

Hier een HELE belangrijke opmerking:

De counter declareer je in je main C code als volatile.
Als je counter meer dan 1 byte breed is MOET je zorgen dat je ELKE read van de counter atomic maakt.
Het simpelst is om dat met een ISR disable/enable te doen in C:

code:


 cli
 var = counter;
 sti

P.S. Linux op een embedded board is absoluut NIET geschikt want het is niet "hard" realtime!
Wel kun je een multiprocessor board/speciale CPU gebruiken en een tweede core dedicated voor deze taak inzetten.

MAAR...
Het juiste beestje voor de juiste taak lijkt me hier de oplossing.

Dus een AVR met QDEC of een PICmicro met QEI. In beide gevallen gaat het om een hardwarematige ondersteuning voor een quadrature encoder interface. In je software is het dan een kwestie van een registertje/countertje uitlezen

Hier ben ik het wel mee eens, een groot aantal CPU's heeft altijd wel een counter aan boord, en met de juiste configuratie kan die ook gewoon op/af tellen. Misschien dat er extern nog een flipflopje toegevoegd moet worden om de fase/draairichting te bepalen.
In de software maak je dan (weer) een counter overflow/underflow interrupt handler die er voor zorgt dat je meer kunt tellen dan de woordbreedte die de CPU ondersteund.

1-st law of Henri: De wet van behoud van ellende. 2-nd law of Henri: Ellende komt nooit alleen.
GJ_

Moderator

Je moet van het A en het B signaal zowel de stijgende als de dalende flanken nemen om te tellen.

Ik wist dat je zou komen met die opmerking. Ik had het even bewaard voor de oplettende lezer!

Je kunt dat oplossen door in de ISR de flank om te programmeren of interrupt controller zo te programmeren dat deze op beide edges een INT af geeft.

Is de flank NIET de configureren kun je twee poort pinnetjes gebruiken en twee XOR gates tussen de A/B lijn hangen.

In de software moet je wat vlaggetjes bijhouden welke kant je precies op moet tellen.

Nog steeds is dat zeer snel te doen als je dat slim in elkaar zet.
Laat de isr er 20uS over doen, dan kun je nog steeds een kleine 50k gegarandeerd meten. Maar je houdt dan wel niet veel tijd meer over in de main loop.

[Bericht gewijzigd door henri62 op woensdag 19 februari 2014 20:05:12 (13%)

1-st law of Henri: De wet van behoud van ellende. 2-nd law of Henri: Ellende komt nooit alleen.
EricP

mét CE

@henri: je kunt het ook omdraaien. Laat de bemonstering in je main lopen en schrijf een (erg efficiënte / korte) main die je als ISR schopt. Nadeel is dat je timing wat onvoorspelbaar wordt. Maar het kan... Of je het moet willen???

Als je nog iets meer nuttigs in de main loop wilt doen, kun je zeer snel over de minimale periode tijd heen gaan waardoor je pulsen gaat missen.

De de toepassing hierboven neem ik aan dat de POSITIE extreem belangrijk is niet de snelheid dus. Een puls missen is dan funest voor de positionering van bijvoorbeeld je printkop of tafel.

Als je 28000 pulsen MOET kunnen meten is de periode tijd ca 35,7 uS.
Knappe jongen als je kunt garanderen dat je main loop hier NEVER NOOIT overheen komt kwa timing.

Voor een ISR kan ik op maximaal 2 clock cycles EXACT de performance voorspellen en 100% garanderen dat ik geen pulsen mis bij een bepaalde CPU snelheid.

Verder is polling van dit soort signalen in de main loop gewoon NOT DONE. (Zie ander discussie topic).

1-st law of Henri: De wet van behoud van ellende. 2-nd law of Henri: Ellende komt nooit alleen.
EricP

mét CE

Als je 28000 pulsen MOET kunnen meten is de periode tijd ca 35,7 uS.
Knappe jongen als je kunt garanderen dat je main loop hier NEVER NOOIT overheen komt kwa timing.

Daarmee ben je er niet. Je hebt meerdere encoders die allemaal tegelijk bezig kunnen zijn.

Als elke encoder z'n eigen interrupts heeft (en daar ontkom je niet echt aan), dan heb je dat probleem met je delays toch - al zul je een ISR wat beter in de hand hebben dan een main loop.

Dus dat je het op 2 cycles nauwkeurig kunt voorspellen... No way. Dat lukt voor 1 handler. Als die ook nog de enige is. Tenzij je interruptable interrupts maakt. Dat kan. Dan weet je nog steeds niet wanneer de interrupted handler verder kan. Heb je nog steeds hetzelfde probleem en wordt de zaak nodeloos complex. Ofwel: ook met interrupts wordt het tricky. Dus vandaar mijn opmerking: doe het in hardware. Dat werkt asynchroon en autonoom.

GJ_

Moderator

Hardwarematig kan met een paar poortjes geloof ik:
http://www.uploadarchief.net/files/download/encoderdirectiondecoder.jpg
http://www.uploadarchief.net/files/download/encoderdirectiondecoder01.jpg
EricP heeft zeker een punt dat dat verstandiger is.

Het zal duidelijk zijn dat ik de plaatjes niet zelf bedacht heb.

Op 19 februari 2014 20:24:51 schreef EricP:
[...]Daarmee ben je er niet. Je hebt meerdere encoders die allemaal tegelijk bezig kunnen zijn.

Effe niet helemaal gezien, de TS heeft er meer, dan moet je inderdaad wat meer moeite doen.
Als de ene ISR klaar is doet ie meteen de tweede van de andere encoder er achteraan. Zelfs nested interrupts is geen probleem.

Is niet nodeloos complex, ik vind het zelfs veel overzichtelijker.
Je hoeft in de main code niet te knoeien om ergens een of andere functie in te korten of in delen op te splitsen omdat je anders je loop time niet haalt.

Voor EEN encoder is het wel fixed, de ISR van de A en B lijn kunnen nooit gelijktijdig komen.

Maar als je inderdaad wat meer encoders wilt dan ontkom je niet aan extra hardware. Maar met de count up/down alleen ben je er ook nog niet GJ! De ISR rate wordt wel lager, dat scheelt.

Gebruik je externe counters dan heb je er weer een ander serieus probleem bij van het uitlezen van de hardware, je moet iets verzinnen om de counters dubbel te bufferen zodat je het HELE WORD atomic kunt lezen. En dat valt niet mee om dat correct te doen!! Vandaar dat er speciale (nogal exotische) IC's voor zijn.

[Bericht gewijzigd door henri62 op woensdag 19 februari 2014 21:01:37 (10%)

1-st law of Henri: De wet van behoud van ellende. 2-nd law of Henri: Ellende komt nooit alleen.
EricP

mét CE

Als de ene ISR klaar is doet ie meteen de tweede van de andere encoder er achteraan. Zelfs nested interrupts is geen probleem.

Je weet alleen absoluut niks meer van response tijden...

Is niet nodeloos complex, ik vind het zelfs veel overzichtelijker.

Dat is het niet. Je moet op de een of andere manier zien dat je stack niet ontploft als het wat drukker wordt. Nested interrupts is vragen om ellende. Houd je ISR kort. Is nesten nergens voor nodig.

Je hoeft in de main code niet te knoeien om ergens een of andere functie in te korten of in delen op te splitsen omdat je anders je loop time niet haalt.

Dat hoef je bij non-nested interrupts ook niet.

Maar als je inderdaad wat meer encoders wilt dan ontkom je niet aan extra hardware. Maar met de count up/down alleen ben je er ook nog niet GJ! De ISR rate wordt wel lager, dat scheelt.

Je hebt helemaal geen interrupts nodig. Laat de hardware lekker naar die decoders kijken. Lees de hardware uit als je de actuele positie wilt weten. Er zijn dedicated chippies die dat kunnen! Dataprotocolletje. Klaar. Enige is dat je niet 3 assen atomic uit kunt lezen. Ik *denk* dat dat niet zo'n punt is.

Gebruik je externe counters dan heb je er weer een ander serieus probleem bij van het uitlezen van de hardware, je moet iets verzinnen om de counters dubbel te bufferen zodat je het HELE WORD atomic kunt lezen. En dat valt niet mee om dat correct te doen!! Vandaar dat er speciale (nogal exotische) IC's voor zijn.

Inderdaad. Als het een simpele counter is, dan gaat dat fout. Vroeg of laat.

Op 19 februari 2014 21:27:13 schreef EricP:
[...]Je weet alleen absoluut niks meer van response tijden...

Voor dit geval hoeft dat ook niet, als de totale tijd van de isr routines maar binnen je kritische window valt. En dat is zeer goed voorspelbaar.

...Dat is het niet. Je moet op de een of andere manier zien dat je stack niet ontploft als het wat drukker wordt. Nested interrupts is vragen om ellende.

Dat wordt (vaak) bepaald door de architectuur van de gebruikte CPU.
Sommige ondersteunen dat, andere niet, soms is het een feit dat het zo is.
Normaal laat ik de interrupts op dezelfde prioriteit lopen, dan wordt er niks genest. Per CPU type zoek ik dat uit hoe het precies in elkaar zit.

Houd je ISR kort. Is nesten nergens voor nodig.[...]Dat hoef je bij non-nested interrupts ook niet.[...]

Ik heb absoluut niet beweerd dat een ISR niet kort hoeft te zijn, in tegendeel in de ISR MOET de code zo kort mogelijk zijn. In de voorbeeld pseudo code is ie ook maar iets meer dan 10 instructies. Kortom hier heb je gelijk in.

Je hebt helemaal geen interrupts nodig.

Zonder context is dit een gevaarlijke uitspraak.

Laat de hardware lekker naar die decoders kijken. Lees de hardware uit als je de actuele positie wilt weten. Er zijn dedicated chippies die dat kunnen! Dataprotocolletje. Klaar. Enige is dat je niet 3 assen atomic uit kunt lezen. Ik *denk* dat dat niet zo'n punt is.Vroeg of laat.

Kijk, nu haal je de context er wel bij: Laat de hardware het doen, net beweerde je nog dat je alles in de main-loop moet doen en dat werkt hier nu juist net niet.

1-st law of Henri: De wet van behoud van ellende. 2-nd law of Henri: Ellende komt nooit alleen.