Versetzte Rechtecksignale auswerten, kein drehgeber

Gast #3591607
Lesenswert?

Guten Tag, ich hoffe ich habe das richtige Unterforum erwischt.
Ich lese hier regelmäßig mit, habe bisher aber nichts gepostet.

Ich komme mit einer Programmierung nicht zurecht und möchte mir hier 
Anregungen für die Lösung bekommen.

Es geht darum Interferenzstreifen mit einem Mikrocontroller zu zählen.
Hardware sieht so aus:
Ein Spiegel des Interferometers wird langsam verschoben und entsprechen 
der Verschiebung ändert sich das Interfernzmuster.
Das Muster trift auf zwei Fotodioden (im selben gehäuse direkt 
nebeneinander).
Das Signal der Fotodioden wird mit einem Transimpedanzverstärker 
verstärkt und dann mit einer Komparatorschaltung zu einem 0V -5V 
Rechtecksignal gemacht.
Soweit Funktioniert der Aufbau ganz ordentlich.
Jetzt möchte ich mit einem Atmega (8bit 16mhz, nehme evtl was 
schnelleres) Auswerten wie viele Interferenzstreifen nach links oder 
nach rechts gewandert sind.

Auf dem Foto ist das Ausgangssignal vom Verstärker und das signal vom 
Komparator für eine der Fotodioden.

Wenn sich das muster bewegt, kommt zuerst das signal von einer diode und 
kurz danach das der anderen diode.

Ganz ähnlich zu einem Inkrementalgeber. Mit diesem Artikel bei rn-wissen 
und dem von mikrocontroller.net habe ich mir ein konzept überlegt, aber 
das bekomme läuft nicht so gut wie es soll.
http://www.rn-wissen.de/index.php/%C3%9Cberlegungen_zur_Drehgeber-Auswertung

Ich habe die beiden Signale an die Interrupts 0 und 1 angeschlossen.
Wenn ein Pinchangeinterrupt ausgelöst wird, wird der Zustand der beiden 
Pins eingelesen und in globale variablen gespeichert.
Im Hauptprogramm wird dann nach einer Tablle ausgewertet.

Vorwärts
00 vorher: 01
10 vorher: 00
11 vorher: 10
01 vorher: 11

Rückwärts
00 vorher: 10
01 vorher: 00
11 vorher: 01
10 vorher: 11

Alles andere ungültig.

Meine Probleme:

Leider wackelt/pendelt das signal hin und her wenn der Übergang gerade 
an der kante einer Fotodiode ist.

Mein Code erkennt ignoriert dieses pendeln nicht und gibt offensichtlich 
falsche ergebnisse aus.
Mein Code läuft recht langsam und kann nur sehr langsame bewegung 
erkennen.
Ich weiß nciht ob ich auf dem richtigen weg bin...


Wäre für tipps dankbar. Beispiele die ich mir anschauen kann oder 
Artikel die ich lesen kann wären auch hilfreich.
Angehängte Dateien:
Gast #3591722
Lesenswert?

LarsB schrieb:

> Wenn ein Pinchangeinterrupt ausgelöst wird, wird der Zustand der beiden
> Pins eingelesen und in globale variablen gespeichert.

Überlege dir, wie schnell deine Streifen maximal durchlaufen und dann 
taste die Leds per Timer regelmäßig ab. Über einen kleinen 
Zustandsautomaten kann du dann Streifenwanderrichtung und Streifenanzahl 
bestimmen.

> Jetzt möchte ich mit einem Atmega (8bit 16mhz, nehme evtl was
> schnelleres) Auswerten wie viele Interferenzstreifen nach links oder
> nach rechts gewandert sind.

Nimm einen Arduino...
Der Due kann die Streifen per Hardware auswerten. Etliche tausend 
Wechsel pro Sekunde sind dann kein Problem.
Gast #3591747
Lesenswert?

Mike schrieb:
> Nimm einen Arduino...
> Der Due kann die Streifen per Hardware auswerten. Etliche tausend
> Wechsel pro Sekunde sind dann kein Problem.


Kannst du das "per Hardware auswerten" etwas genauer beschreiben.
Der ist schon viel schneller, das klingt sehr gut, aber was kann der 
Arduino Due mehr?


Danke MaWin, die Seite kannte ich noch nicht. Ist das eine alte 
newsgroup aus dem usenet?
Gast #3591756
Lesenswert?

LarsB schrieb:
> Kannst du das "per Hardware auswerten" etwas genauer beschreiben.

Über zwei Eingänge schließt du deine beiden Rechtecke (aufbereiteten 
Photodiodensignale) an. Der Atmel SAM3X8E liefert dir dann in einem 
Register immer die aktuelle Anzahl der Streifen.
Gast #3591778
Lesenswert?

MaWin schrieb:
> Nun ja, wer Software nicht beherrscht, muss es eben mit Hardwareoverkill
> einer ARM Cortex machen.

Dann möchte ich deine Software mal sehen, wenn ein Drehgeber mit 2048 
Pulsen/Umdrehung mit 10000 rpm dreht und sie nebenher sich auch noch um 
ein paar andere Dinge kümmern soll.

Oder hast du vom TO schon gehört, wie schnell er sein Interferometer 
durchfahren lassen möchte.
Gast #3591797
Lesenswert?

Mike schrieb:
> MaWin schrieb:
>> Nun ja, wer Software nicht beherrscht, muss es eben mit Hardwareoverkill
>> einer ARM Cortex machen.
>
> Dann möchte ich deine Software mal sehen, wenn ein Drehgeber mit 2048
> Pulsen/Umdrehung mit 10000 rpm dreht und sie nebenher sich auch noch um
> ein paar andere Dinge kümmern soll.
>
> Oder hast du vom TO schon gehört, wie schnell er sein Interferometer
> durchfahren lassen möchte.

Das heißt also, wenn man keinerlei Ahnung hat, was der Fragesteller 
eigentlich braucht, dann setzt man automatisch höchstmögliche 
Anforderungen ein, um zu beweisen, daß es einfach nicht geht, sondern 
nur mit maximalem Aufwand.
Gast #3591856
Lesenswert?

ich schrieb:
> Das heißt also, wenn man keinerlei Ahnung hat, was der Fragesteller
> eigentlich braucht, dann setzt man automatisch höchstmögliche
> Anforderungen ein, um zu beweisen, daß es einfach nicht geht, sondern
> nur mit maximalem Aufwand.

Nein, das war ein Beispiel aus der Praxis (bei mir momentan mit zwei 
solcher Drehgeberkanälen) um MaWin von seinem "das mach ich locker mit 
Software" runter zu holen. Wenn du schon mal mit einem Interferometer 
gearbeitet hast, dann weisst du, wie schnell da ein paar 10kHz 
zusammenkommen. Bereits bei 10mm/s hast du je nach Wellenlänge irgendwas 
um typ. 40kHz als Streifenfrequenz.
Gast #3591902
Lesenswert?

Mike schrieb:
> Bereits bei 10mm/s hast du je nach Wellenlänge irgendwas
> um typ. 40kHz als Streifenfrequenz.

Na wegen 40kHz braucht man doch wirklich kein Geschrei machen. Wenn du 
jetzt von mehreren MHz gesprochen hättest, das habe ich noch nicht 
selbst gemacht. Aber bis reichlich 200kHz habe ich das selbst schon oft 
genug im Einsatz. Das sind paar Programmzeilen, kein Problem.
Gast #3592283
Lesenswert?

Eine Schaltung für sin/cos-Signale für ATmega48 findest Du hier:
http://www.mino-elektronik.de/mt12_iic/mt12_iic.htm
Die Schaltung ist zwar für +/-11µA Signale vorgesehen, läßt sich aber 
einfach anpassen. Selbst ein Index wird berücksichtigt.
Sofern Dich nur die Nulldurchgänge interessieren, kannst Du dieses 
Programm verwenden. Beitrag "4-fach Flankenauswertung per Interrupt mit ATmega48/88"

Sofern Du noch eine Interpolation des Signals brauchst, sag Bescheid.
Gast #3592352
Lesenswert?

Danke für die vielen guten Antworten. Da hab ich erstmal einiges 
durchzuschauen.
Ich muss mich noch für die ganzen Rechtschreibfehler entschuldigen. War 
wohl etwas erledigt gestern.


Da noch nach genaueren Anforderungen gefragt wurde:

Der MC hat keine anderen Aufagneb als die Streifen zu zählen und den z 
Wert periodisch über den uart zu schicken. Möchte den Zähler noch mit 
der halben Wellenlänge multiplizieren, das könnte aber auch extern 
passieren.

Die Frequenz wird nicht über 100 kHz hinausgehen. Es sind  keine hohen 
Geschwindigkeiten geplant, 10mm/s ist schon die richtige größenordnung. 
Wellenlänge ist 532nm.
Die Verstärkerschaltung kann auch nicht viel schneller arbeiten wegen 
einem recht hohen rückkoplungswiderstand.

Die 4fach Flankenerkennung mit Interrupt schau ich mir erstmal genau an, 
auf den ersten Blick vielversprechend.

Auch Hardware Quadratur-dekoder sind eine überlegung.
Gast #3592548
Lesenswert?

LarsB schrieb:
> Die 4fach Flankenerkennung mit Interrupt schau ich mir erstmal genau an,
> auf den ersten Blick vielversprechend.

Die .a90-Datei ist im Intel-HEX-Format und kann direkt verwendet werden.
Falls Du noch eine Skalierung brauchst, könnte ich Dir diese ins 
Programm schreiben.
Ob mit 32-Bit float oder 'echten' 64-Bit double - Alles kein Problem.

Ferner kannst Du Dich frei für das Auswerteverfahren entscheiden. Von 
Hause aus ist die Flankentriggerung aktiv, welche Pulsabstände bis 2µs 
erfaßt (125kHz auf auf beiden Kanälen).
Falls Du lieber mit Timerinterrupts die Pegeländerungen auswerten 
willst, muß PD5 auf GND gelegt werden. Die Abtastfrequenz ist dann 
400kHz und bremst den µC konstant aus. Die Datenausgabe kann daher nicht 
so schnell erfolgen.

WIR sind flexibel und können Beides :-)
Gast #3592685
Lesenswert?

Uwe Bonnes schrieb:
> 100 kHz und 4 Flanke Erkennung und damit 400 kHz Interupts hoeht sich
> anspruchsvoll an...

Ja, aber man braucht gar keine Interrupts. Wie die dse faq schreibt 
einfach die Erkennung in die Programmhauptschleife, und damit er seine 
Daten senden kann EINE Instruktion dafür dahinter, dann die Erkennung 
noch mal und die nächste zum Senden nötige Instruktion, auch in 
unterschiedlichen If-Pfaden.
Wer also in Assembler programmieren kann sollte damit 4 Drehgeber 
parallel mit 500ksps auf einem ATTiny auswerten können und seriell 
verschicken.
#3592882
Lesenswert?

@ MaWin (Gast)

>Wer also in Assembler programmieren kann sollte damit 4 Drehgeber
>parallel mit 500ksps auf einem ATTiny auswerten können und seriell
>verschicken.

Dazu braucht es kein Assembler, normales C reicht da vollkommen. Knoff 
Hoff ist hier, wie so oft, gefragt. Der UART/SPI/I2C arebitet parallel 
zur CPU.
Moderator #3593002
Lesenswert?

Die 500 ksps bei 4 Drehgebern auf eine ATtiny dürften schon in Assembler
ziemlich sportlich sein. Bei 20 MHz sind das gerade einmal 40 Taktzyklen
pro Auswertezyklus oder 10 Taktzyklen pro Auswertezyklus und Drehgeber.
Natürlich kann man kann man ein paar Dinge (bspw. das Einlesen und einen
Teil der Bitmanipulation) für alle 4 Drehgeber parallel machen, wenn sie
alle am gleichen 8-Bit-Port angeschlossen sind. Aber das Zählen und die
Entscheidung, in welche Richtung gezählt werden soll, muss für jeden
Geber unabhängig erfolgen. Dazu kommt noch die serielle Kommunikation,
die trotz Hardwareunterstützung mit mindestens 1 I/O-Zugriff (2 Zyklen)
zu Buche schlägt.

Wenn man so etwas in C machen möchte, wird es wohl darauf hinauslaufen,
dass man sich erst überlegt, wie das Ganze in Assembler aussehen könnte,
und dann so lange den C-Code herumdreht, bis irgendwann zufälligerweise
der gewünschte Assemblercode herauskommt. Bei jedem Compiler-Update muss
dann aber überprüft werden, ob der erzeugte Code immer noch optimal ist.
Gast #3593057
Lesenswert?

Yalu X. schrieb:
> Die 500 ksps bei 4 Drehgebern auf eine ATtiny dürften schon in Assembler
> ziemlich sportlich sein.

Frag die Beiden doch einmal nach konkretem Code. Wenn ich danach frage, 
wird immer nur der Finger nach oben in die unendlichen Weiten des 
Sternenhimmels gezeigt und gesagt: "Da steht es doch!"
Abgesehen von den Zählkunststücken, die hier einem ATtiny (8-pol.?) 
angedichtet werden, würden mich auch die Datenausgabekunststücke 
interessieren, mit denen man in 2µs 16 Byte seriell
Falk Brunner schrieb:
> UART/SPI/I2C
ausgeben kann.

Anfangs ist es ja immer lustig, aber je mehr sich die beiden 
Herrschaften hier selbst demontieren, wird die Frage nach einem 
kompetenten Arzt immer dringlicher.
Gast #3593132
Lesenswert?

Der Code aus dem Beitrag "4-fach Flankenauswertung per Interrupt mit 
ATmega48/88"
(Beitrag "4-fach Flankenauswertung per Interrupt mit ATmega48/88") ist mit Vorsicht zu 
genießen: Hier werden die Interrupts zum erkennen der Flanken genutzt, 
das kann ggf. bei kurzen Störpulsen zu Zählfehlern führen. Die 
zuverlässige Methode ist über den Vergleich von altem und neuen Zustand, 
der jeweils nur einmal ausgelesen wird. Ob man die Abtastung in einen 
timer interrupt, Pin-Change Interrupt oder ggf. auch die Hauptschleife 
packt ist dabei relativ egal.
#3593199
Lesenswert?

@ m.n. (Gast)

>> Die 500 ksps bei 4 Drehgebern auf eine ATtiny dürften schon in Assembler
>> ziemlich sportlich sein.

Kann sein, muss nicht. Alles eine Frage des Lösungsansatzes.

>Frag die Beiden doch einmal nach konkretem Code.

Frag ruhig ;-)

> Wenn ich danach frage,
>wird immer nur der Finger nach oben in die unendlichen Weiten des
>Sternenhimmels gezeigt und gesagt: "Da steht es doch!"

Labersack!

>angedichtet werden, würden mich auch die Datenausgabekunststücke
>interessieren, mit denen man in 2µs 16 Byte seriell
> ausgeben kann.

Mamn oh Mann, du hältst dich wohl für den AVR-Programmierer schlecht 
hin, weil deine Homepage mit diversen Kleinprojekten übersäht ist? Schon 
mal über einen gescheiten Systemansatz nachgedacht? Ach ne, ist ja über 
Bastlerniveau . . .

>Anfangs ist es ja immer lustig, aber je mehr sich die beiden
>Herrschaften hier selbst demontieren, wird die Frage nach einem
>kompetenten Arzt immer dringlicher.

Du bist wohl ein Spätpupertierender?

Systemansatz:

Kein Mensch braucht bei 500ksps eine Updaterate von 2us, dann dann wäre 
wenig gewonnen. Denn der Dekoder soll ja die nachfolgende 
Logik/Mirocontroller ENTLASTEN! Also nehmen wir mal 16 Bit/Kanal. Das 
sind 65536 Schritte. Aha! Also dauert es mindesten soviele Takte, bis 
der Zähler Überläuft. Also kann ich mir soviel Takte Zeit lassen, bis 
ich wieder ein Update machen. Macht schlappe 7,6Hz bzw ~130ms. In dieser 
Zeit müssen die Zähler einmal übertragen werden. Macht für 4 Zähler a 16 
Bit ~488 Baud netto. Sagen wie 1200 oder 4800 Baud und wir haben massig 
Luft.

Das Verschachteln der Abtastung und Übertragung ist eine kleine 
Herausforderung, aber nicht unlösbares.

Kleiner Wettbewerb. Wer macht das cleverste Programm. Los geht's!
Gast #3593252
Lesenswert?

Ulrich schrieb:
> Der Code aus dem Beitrag "4-fach Flankenauswertung per Interrupt mit
> ATmega48/88"
> (Beitrag "4-fach Flankenauswertung per Interrupt mit ATmega48/88") ist
> mit Vorsicht zu
> genießen: Hier werden die Interrupts zum erkennen der Flanken genutzt,
> das kann ggf. bei kurzen Störpulsen zu Zählfehlern führen.

Ich erkläre es noch einmal:
Wer in der Lage ist, störungsfreie Signale anzuliefern, kann bedenkenlos 
die max. Eingangsfrequenz nutzen, die sich bei direkter 
Flankenauswertung an INT0 und INT1 ergibt.
So mache ich es, da bei mir in der Regel Sinussignale an den Eingängen 
liegen, deren Verstärkerstufen garkeine Spikes liefern können (begrenzt 
durch Anstiegszeit von OPVs).
Bei nächster Gelegenheit werde ich das Programm auf PCINT testweise 
umstellen; laut Datenblatt haben die PCINTs eine Spike-Unterdrückung per 
Hardware, sodass nur auf Pegeländerungen reagiert wird, die >= 3 
CPU-Takte lang sind.

Sofern mit Störimpulsen zu rechnen ist, kann man diese entweder analog 
(Tiefpass) ausfiltern, oder den Eingängen INT0/INT1 jeweils ein D-FF zur 
Synchronisierung vorschalten. Das 500kHz Taktsignal wird vom µC 
bereitgestellt.
Uns als letzte Möglichkeit kann man, wie oben bereits geschrieben, die 
Auswertung im 400kHz Takt intern synchronisieren und auswerten.

So sollte für jeden ernsthaft Interessierten eine Möglichkeit dabei 
sein.
Falls Jemand jetzt mit Stromausfall argumentiert, der den Zähler ja 
zurücksetzen würde: der hat gewonnen :-)
Gast #3593291
Lesenswert?

m.n. schrieb:
> Ich erkläre es noch einmal:
> Wer in der Lage ist, störungsfreie Signale anzuliefern, kann bedenkenlos
> die max. Eingangsfrequenz nutzen, die sich bei direkter
> Flankenauswertung an INT0 und INT1 ergibt.

Niemand ist in der Lage störungdfreie Signale abzuliefern, schon im 
ersten Beitrag erwähnt LarsB das pendeln seiner Signale.

Vergiss deinen schrottigen Ansatz mit flankengetriggerten Interrupts, er 
ist zudem langsamer.
Moderator #3593553
Lesenswert?

MaWin schrieb:
> Ja. Der Code braucht 24.

Jetzt machst du mich doch ein wenig neugierig :)

> Nein, schon die dse faq erwähnt die mögliche Oarallelbearbeitung.

Es ist schon klar, dass man durch rein sequentielle Abarbeitung der 4
Drehgeber nicht auf die 500 ksps kommen kann. In den DSE-FAQ kann ich
aber keine Informationen zur Parallelbearbeitung finden.

24 Taktzyklen pro Auswertezyklus bedeuten ja, dass bei 500 ksps eine
Prozessortaktfrequenz von 12 MHz ausreichend ist.

Du schaffst du es also, mit einem 12-MHz-ATtiny simultan 4 Drehgeber
unter folgenden Randbedingungen auszuwerten:

- Jeder dieser Drehgeber ändert bis zu 500000 Mal pro Sekunde seinen
  Ausgangszustand.

- Die Inkremente der vier Drehgeber werden jeweils in einem vernünftigen
  Wertebereich bidirektional gezählt. 8 Bit sind dabei m.E. ausreichend,
  mehr sind natürlich besser.

- Alle vier Zählerstände werden über eine serielle Schnittstelle öfter
  als alle 128 Inkremente (bei einer Zählerbreite von 8 Bit) ausgegeben,
  so dass der Empfänger der Daten durch Aufaddieren der Differenzen die
  aktuelle Winkelposition mit einer größeren Wortbreite rekonstruieren
  kann.

- Es werden zu keinem Zeitpunkt Inkremente verschluckt.

Habe ich das richtig verstanden?

Wenn ja, werde ich mich morgen Abend daran machen, das Rätsel zu lösen,
falls du das Geheimnis nicht von dir aus lüftest ;-)

Der Falk scheint ja die Lösung ebenfalls schon zu kennen. Falls ihr den
entscheidenden Kniff unabhängig voneinander gefunden habt, sollte ja
eigentlich auch ein Dritter in endlicher Zeit darauf kommen können ;-)
#3593705
Lesenswert?

Ich benötige für meine unmodifizierte Drehgeberabfrage 15 Takte für 
einen Drehgeber. Nach Optimierung sind es nur noch 8 Takte. Kleiner 
bekomme ich es gerade nicht mehr. Dies könnte man auf 3 Drehgeber 
erweitern. Ab dem 4 Drehgeber steht kein 16-Bit-Register mehr zur 
Verfügung und ich muss 1 Byte immer wieder hin und her schubsen. Sind 4 
Zusatztakte. Sind in der Summe 4*8+4 = 36 Takte

Alle 128 Durchläufe müssten alle 4 Bytes einmal verschickt werden - also 
alle 4608 Takte - Auch hierfür wird Rechenzeit benötigt, so dass ich 
einen 20MHz-Controller nehmen würde. Es würden dann 4340.3 Wertegruppen 
in der Sekunde verschickt werden. Damit ist der µC dann allerdings auch 
ausgelastet.

Ob das sinnvoll ist, sowas so zu lösen, steht auf einem anderen Blatt 
...


Gruß

Jobst
#3593790
Lesenswert?

@ Yalu X. (yalu) (Moderator)

>Habe ich das richtig verstanden?

Ja.

>Der Falk scheint ja die Lösung ebenfalls schon zu kennen.

"Sie öööberschääätzen mich."

Nein, ich kenne die Lösung nicht, es ist nur ein Bauchgefühl und 
sportlicher Ehrgeiz. Und ja, ich hab mich zu der Aussage, "das geht auch 
einfach mit C" wohl etwas vorschnell hinreißen lassen. In Assembler wird 
es anspruchsvoll genug. Mal sehen.

"Seien wir realistisch, versuchen wir das Unmögliche."

Und bitte jetzt keine Krümelkackerei wegen ATtiny und 8 Pins. Es gibt 
auch solche Sachen wie ATtiny2313. Und selbst wenn es einen ATmega88 
oder so brauchen würde, wäre es keine Niederlage. Wenn man mal 
verlgeicht, was für unglaublich perfomanten Dinge mit so einem kleinen 
AVR schon gemacht wurden (Videoterminal, Echtzeitdemo mit Grafig in 
Midisound von diesem Skandinavier, USB-Slave etc.), dann denke ich 
sollte auch das möglich sein.
Gast #3593874
Lesenswert?

Für mich wäre es kein Problem Hardware zu nehmen, die besser ist als 
unbedingt nötig.
Preislich ist z.B. der arduino Due noch im Rahmen.
Ein Vorteil ist, dass der mit seinem hohen Takt (84 MHz) auch mit nicht 
optimalem code noch die Aufgabe erfüllen kann. Was ich in Assembler 
schreiben könnte, wäre wohl kaum dem c-code überlegen, also bleibe ich 
bei c.
Das ist dann technisch wohl nicht die schönste Variante, aber eine die 
funktioniert.

Werde erstmal die verschiedenen vorgeschlagenen Drehgeber Auswertungen 
durchgehen und schauen was mir Ergebnisse liefert.
Mein Versuch von gestern, das Pendeln zu ignorieren, hatte zur Folge 
dass der Zähler auch massenhaft Streifen verpasste, aber ich habe das 
Gefühl dass ich in die richtige Richtung unterwegs bin.

Interpolation und anderes lasse ich zu diesem Zeitpunkt sein, erstmal 
soll das ding "nur" richtig zählen.
Moderator #3593904
Lesenswert?

Jobst M. schrieb:
> Sind in der Summe 4*8+4 = 36 Takte

Ich bin bis jetzt bei 38 Takten (4, Drehgeber, 8-Bit-Zähler, Übertragung
per serieller Schnittstelle), muss das aber noch einmal überprüfen, ob
das wirklich so funktioniert. Bei 20 MHz Taktfrequenz würde man die
500 ksps also erreichen und hätte sogar noch 2 Takte Luft (bei deinem
Code sogar 4 Takte).

Um aber auf die 24 Takte zu kommen, fehlt wohl noch ein entscheidender
Trick. Ich werde heute Abend mal ein wenig Assembler-Puzzle spielen :)

LarsB schrieb:
> Für mich wäre es kein Problem Hardware zu nehmen, die besser ist als
> unbedingt nötig.
> Preislich ist z.B. der arduino Due noch im Rahmen.
> Ein Vorteil ist, dass der mit seinem hohen Takt (84 MHz) auch mit nicht
> optimalem code noch die Aufgabe erfüllen kann.

Klar, das klingt vernünftig. Du musst dann nicht nach jeder kleinen
Code-Änderung überprüfen, ob die Zählerei noch mit der erforderlichen
Frequenz funktioniert.

Edit:

Ich habe mich verzählt. Es sind nicht 38, sondern 37 Zyklen.
Gast #3593983
Lesenswert?

Ungefähr:

LDI R17,0 ; Zähler
IN R16,PORTB
ANDI R16,0xB0
LD R30,R16
LDI R31,HI(tablebase)
loop:
IN  R16,PORTB
ANDI R16,0xB0
OR R30,R16
LPM
ADD R17,R0
SHR R31
:
RJMP loop

Oh, sind doch nur 8 - bei 8 bit Zähler
#3594510
Lesenswert?

Initialisierung:
Drehgebertabelle liegt im RAM (ist schneller als ROM) ab Adresse xx00
In ZH liegt das obere Byte dieser Adresse

1
sbis  DrehgeberA-Pin    ;
2
ori  ZL, 8      ; 2 ; Zusammen immer 2 CLK
3
sbis  DrehgeberB-Pin    ;
4
ori  ZL, 4      ; 4 ; Zusammen immer 2 CLK
5

6
LD  tmp, Z      ; 5 ; Ein Byte aus der RAM Adresse der Tabelle des Z-Registers in tmp geladen
7
ADD  Dout, tmp    ; 6 ; Addiere tmp zu Dout dazu
8
LSR  ZL      ; 7 ;
9
LSR  ZL      ; 8 ; Die 2 alten bits werden nach rechts raus geschoben und die 2 neuen bits werden zu den alten



MaWin schrieb:
> SHR R31

???


Gruß

Jobst
Gast #3594788
Lesenswert?

Jobst M. schrieb:
>> SHR R31

Sollte LSR R30 heissen, dein Trick die Tabelle ins RAM zu holen spart 
einen Takt, aber dein ADD Dout, tmp liefert doch nur 8 bit. Dein SBIS 
ist zwar flexibler, aber 1 Takt mehr.

Wie kommst du auf 16 bit Zähler ?

Die Sign-Extension des Tabellenwerts macht mir Probleme, mir fällt 
nichts kurzes ein.
Gast #3594833
Lesenswert?

Statt sign extension könnte man ggf. ein Sprungtabelle nutzen, und dann 
separaten Code für up, down, nichts und ggf. auch Fehler haben - lohnt 
sich vor allem bei mehr als 16 Bit. Eine weitere Möglichkeit wäre es den 
Hauptcode weiter mit 8 Bit laufen zu lassen, und nur alle 120 (oder so) 
Aufrufe, etwa vor der Ausgabe die oberen Bits zu aktualisieren.

Alternativ könnte man die Richtung auch nicht über eine Tabelle 
bestimmen, sondern per logischen Vergleich: auch das ist nicht so 
kompliziert.
Ein grobes Gerüst (der alte wert ginge auch in ein Register, ggf. auch 2 
Versionen im Wechsel) für einen Kanal sehe etwa so aus:
.equ  Maske = (1<<BitA) + (1<<Bit B)

lds   R16 , alt     ; ggf. auch alten Wert in Register merken
in    R2  , PortA
BST   R16 , BitA     ; Bit A alt merken
eor   R16,R2
andi  R16,Maske
breq  fertig        ; kein schritt, da keine Änderung
 cpi  R16, Maske    ; Optionaler test auf Fehler (beide Bits geändert)
 BREQ Fehler
sts   R2, alt
BLD   R16,BitB       ; Bit A(alt) nach Position B
eor   R16,R2
sbrs  R16,BitB
rjmp  Schritt_vor
      ' Schritt zurück
DEC                 ; Zähler runter
RET
Schritt_vor:
      ' Schritt vor
INC                 ; Zähler hoch

fertig:
RET

Der Code ist nicht ganz so schnell, aber dafür flexibel bei den Pins, 
und kommt mit wenig Registern aus.


Wenn es wirklich zeitkritisch ist, nimmt man halt einen µC mit Hardware- 
Quadraturdecoder, etwa einen Xmega.
Gast #3594843
Lesenswert?

Ulrich schrieb:
> Wenn es wirklich zeitkritisch ist, nimmt man halt einen µC mit Hardware-
> Quadraturdecoder, etwa einen Xmega.

Wie man sieht ist das meistens überflüssig, schon der ATtiny schafft 
fast 1Msps, real braucht LarsB nur 40ksps wie er geschrieben hat, also 
vieeeeel Luft, kann er auch in C screiben und Unmengen weiteren Code 
dazuschreiben.

Es ging vor allem darum, den Unsinn von m.n. hier zu widerlegen, man 
bräuchte Hardware und Software ist sowieso zu langsam, bloss weil er 
nicht programmieren kann.
#3594902
Lesenswert?

MaWin schrieb:
> Dein SBIS ist zwar flexibler, aber 1 Takt mehr.

Stimmt. Aber mit 'unflexiblen' Ports wird für 4 Drehgeber ein µC mit 4 
Ports benötigt. Also ATmega164 aufwärts.

Dein
> ANDI R16,0xB0
verstehe ich allerdings auch nicht so recht. Was macht das 3. Bit?


> Wie kommst du auf 16 bit Zähler ?

Habe ich nie versprochen ...
Würde ich den Empfänger der Daten aufaddieren lassen.
Wenn ich doch 16 Bit in diesem Chip bilden müsste, dann würde ich sie 
nicht jeden Durchlauf bilden, sondern jeden 64. - oder so. Dann hat man 
auch genug freie Takte gesammelt.


> Die Sign-Extension des Tabellenwerts macht mir Probleme, mir fällt
> nichts kurzes ein.

Keine Sign-Extension. 1 für hoch, -1 (=255) für runter. In einem Bereich 
von +127/-128 reicht das. Gut, alle 127 Durchläufe müsste eine 
Datenabfuhr stattfinden. (nicht 128)

So sieht die Tabelle bei mir aus:
Für einen count bei vollem Phasendurchlauf:
.db      0,-1,0,0,1,0,0,0,0,0,0,0,0,0,0,0
Für einen count bei jedem Flankenwechsel:
.db      0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0


Gruß

Jobst
Gast #3595040
Lesenswert?

Jobst M. schrieb:
> Keine Sign-Extension.

Na ja, wenn man eine Tabelle hat wie du sue beschrueben hast und sie in 
der dse faq steht mit 8 bit Werten, dann muss man sign extension 
betreiben um sie auf einen 16 bit zähler addieren zu können.

Bisher ist es am kürzesten, die Tabelle mit 0, 1 und 2 zu füllen und 
jedesmal ein Decrement nachzuschieben.


LDI R18,0 ; Zähler
LDI R19,0
IN R16,PORTB
ANDI R16,0xC0
LD R30,R16
LDI R31,HI(tablebase) 0, 1, 2
loop;
IN R16,PORTB
AND R16,0C0
OR R30,R16
LD R17,Z
ADD R18,R17
ADC R19,R19
SBWI R18,1
LSR R30
LSR R30
:
RJMP loop

macht 11 Takte + 2 für Rücksprung für 16 bit Zähler
Gast #3595128
Lesenswert?

Irgendwie fehlt da bei den hier gezeigten Tabellenlösungen fehlt 
irgendwie die Verknüpfung mit dem alten Wert. So kann das noch nicht 
unktionieren.
Vermutlich ist aber sowieso die Lösung ohne Tabelle schneller, also die 
Richtung direkt aus der XOR Verknüpfung zu berechnen. Einen Schritt vor 
macht man wenn sich Kanal A von alten Wert von Kanal B unterscheidet und 
einen Schritt zurück wenn sich Kanal B von alten Wert von Kanal A 
unterscheidet. Sind beide verschieden macht man keinen Schritt oder halt 
vor und zurück.
Als Code dann etwa so:

CLR   R0            ; nur einmal nötig

mov   R16,R2        ; alten wert merken
                    ; (ggf. auch Wechselweise Register nutzen)
swap  R16           ; A -> B Position
in    R2  , PortA
eor   R16,R2        ; alt EOR neu

ror   R16           ; Bit nach carry -> Bit set für up
adc   R20,R0        ; Kanal 1
adc   R21,R0        ; optional 16 Bit Zähler
ror   R16           ; Bit nach carry -> down
sbc   R20,R0
                    ; optional längerer Zähler
ror   R16           ; Bit nach carry -> Bit set für up
adc   R22,R0        ; Kanal 2
ror   R16           ; Bit nach carry -> down
sbc   R22,R0
usw. für bis zu 4 Kanäle


Das gibt dann einmal 4 (mit Registertausch 3) Zyklen + 4 bzw. 6 (mit 16 
Bit Zählern) Zyklen für jeden der bis zu 4 Kanäle.
#3595189
Lesenswert?

MaWin schrieb:
> Na ja, wenn man eine Tabelle hat wie du sue beschrueben hast und sie in
> der dse faq steht mit 8 bit Werten, dann muss man sign extension
> betreiben um sie auf einen 16 bit zähler addieren zu können.

Ja, das kann man doch machen. Und dies alle <127 Durchläufe zu tun ist 
ausreichend. Man hat also genug Zeit, eine Sign-Extension zu machen.


Gruß

Jobst
Moderator #3595641
Lesenswert?

So ist es.

Wenn man das noch etwas weiter denkt, sollte es mit Ulrichs Idee möglich
sein, auf einem ATtiny 4 Drehgeber mit einer Abtastperiode von 21
Prozessortakten 16 Bit breit auszuwerten und dabei die Ergebnisse
periodisch über einen Hardware- oder Software-UART auszugeben.

Das ergäbe bei 20 MHz Taktfrequenz eine Abtastrate von 952 kHz!

Mich würde aber auch MaWins Code mit den 24 Takten interessieren. Ich
habe mit dieser Taktzahl eine tabellenbasierte Lösung gefunden (und
zusätzlich eine mit 23 Takten für ATmegas mit großem Flash), die aber
bei weitem nicht so elegant ist wie Ulrichs Methode (und dazu noch
langsamer).
Moderator #3595659
Lesenswert?

MaWin schrieb:
> Thread nicht gelesen ?

Doch.

> Ich habe eine mit 11 Takten für 16 bit Zähler beschrieben.

Und mit dem Sprungbefehl am Ende sind es 13 Takte. Das ist aber nur für
einen einzelnen Drehgeber. Ich hatte dich so verstanden, dass die 24
Takte für 4 Drehgeber gelten :)

Ich glaube, andere haben dich ebenso missverstanden, was aber gar nicht
so schlecht war, da diese Vorgabe uns erst so richtig zum Nachdenken
angeregt hat ;-)

Für 1 Drehgeber geht Ulrichs Ansatz in 10 Takten (alles inklusive), für
2 Drehgeber sind es 14 Takte. Tabellenbasiert geht beides noch einen
Takt schneller, also in 9 bzw. 13 Takten. Vielleicht kann man aber auch
Ulrichs Methode noch etwas besser implementieren.
Gast #3595705
Lesenswert?

Im Code oben (Datum: 29.03.2014 10:14) ist noch ein kleiner Fehler: Die 
Reihenfolge in denen die Zähler ausgewertet werden stimmt noch nicht. 
Die ersten 4 Bits die man ins Carry schiebt sind für je eine Richtung 
der 4 Kanäle, also etwa alle mit ADC ... und erst dann kommen die 4 
anderen Richtungen. Die Lösung passt aber wirklich nur so gut für den 
eher theoretischen Fall mit 4 Kanälen, weil es dazu den Swap Befehl 
gibt. Für weniger Kanäle kommt dann für nicht nicht genutzte einfach 1 
Zyklus dazu.

Bei den Taktzyklen sind es dann für 8 Bit Zähler
4 (ggf. auch 3) + 4 je genutzten Kanal und 1 je ungenutztem Kanal.
Für 16  Bit Zähler dannn
4 (ggf. auch 3) + 6 je genutzten Kanal und 1 je ungenutztem Kanal.
Dazu kommt noch die Schleife bzw. der Interrupt overhead.
Für einen Incrementalgeber geht es noch einen Zyklus schneller 
(Alternativen zu 3 Shifts).

Das wären dann also für 1 Kanal 8 Bit minimal wohl 3+4+2= 9 Zyklen 
zuzüglich der Schleife.

Was man allgemein noch machen kann ist eine Art Loop Unrolling: 
Insbesondere kann man 2 Druchläufe hintereinander packen und so das 
Tauschen von Registern für den aktuellen und alten Wert einsparen. Der 
Teil für die Schleife auch auf die Hälfe.
#3595708
Lesenswert?

@ Jobst M. (jobstens-de)

>Ist mir scheissegal, was Du darüber denkst. Ich denke, ich habe hier
>schon häufig genug bewiesen, dass ich solche Kniffelaufgaben drauf habe.

Ein paar Fragmente hinschmeißen kann fast jeder . . .

Auch die andere Herren sind angehalten, eine KOMPLETTE Lösung zu 
präsentieren.
#3595717
Lesenswert?

@ Yalu X. (yalu) (Moderator)

>Wenn man das noch etwas weiter denkt, sollte es mit Ulrichs Idee möglich
>sein, auf einem ATtiny 4 Drehgeber mit einer Abtastperiode von 21
>Prozessortakten 16 Bit breit auszuwerten und dabei die Ergebnisse
>periodisch über einen Hardware- oder Software-UART auszugeben.

Ich glaub dir gehen gerade die Pferde etwas durch. Von einer Routine mit 
4 Drehgebern und nur 21 Takten sind wir MEILENWEIT entfernt, die Ausgabe 
per UART / Whatever hat auch noch keiner im Detail angepackt.

>Das ergäbe bei 20 MHz Taktfrequenz eine Abtastrate von 952 kHz!

Ich glaube SOOOO schnell wird man es nicht schaffen. Ich bin aber 
bereit, mich mit einem vollständigen Code vom Gegenteil überzeugen zu 
lassen.

>bei weitem nicht so elegant ist wie Ulrichs Methode (und dazu noch
>langsamer).

Die Idee ist sehr gut!
#3595732
Lesenswert?

Ok, hier mal meine Version, basierend auf der genialen wie einfachen 
Idee von Ulrich.

Beitrag "Re: Versetzte Rechtecksignale auswerten, kein drehgeber"

Das Dekodermakro braucht 20 Takte für 4 Drehgeber. Dann werden im 
Multitasking die einzelnen Zähler per UART rausgehauen, was bei 8 Bit 
Zählern noch ziemlich hohe Baudraten und Frame rates braucht. Wenn man 
es auf 16 Bit erweitert und dadurch eine minimal geringere Abtastrate in 
Kauf nimmt, kommt man mit 16 Bit Zählern auf SEHR entspannte Baudraten 
und Wiederholfrequenzen.

Siehe Anhang.
Angehängte Dateien:
#3595753
Lesenswert?

Ulrich schrieb:
> Irgendwie fehlt da bei den hier gezeigten Tabellenlösungen fehlt
> irgendwie die Verknüpfung mit dem alten Wert.

Sie ist aber sowohl bei mir, als auch bei MaWin vorhanden.
Und von meinem Code kann ich behaupten, dass er gerade vor meiner Nase 
funktioniert. (Wenn auch nur in einem TimerIRQ mit 5kHz)

Ich finde Deine Lösung sehr elegant!

Edit: erledigt ...

Gruß

Jobst
Gast #3595777
Lesenswert?

Falk Brunner schrieb:
> Von einer Routine mit
> 4 Drehgebern und nur 21 Takten sind wir MEILENWEIT entfernt,

Du vielleicht.

Das ist der Unterschwied zwischen denen, die können, und denem, die nix 
können.

LDI R16,0 ; Zähler 1
LDI R17,0 ; Zähler 2
LDI R18,0 ; Zähler 3
LDI R19,0 ; Zähler 4
LDI R31,0
LDI R0,1
OUT RAMP2,R0

loop:
IN R30,PORTB
ELPM
SBRC R0,0
INC R16,1
SBRC R0,1
DEC R16,1
SBRC R0,2
INC R17,1
SBRC R0,3
DEC R17,1
SBRC R0,4
INC R18,1
SBRC R0,5
DEC R18,1
SBRC R0,6
INC R19,1
SBRC R0,7
DEC R19,1
LD R31,R30
:
RJMP loop

21 Takte + 2 für den RJMP, dafür eine 64k Tabelle im ATmega128.
und eine Instruktion bleibt distributed zum Aussenden der Daten.
#3595805
Lesenswert?

Nun ja, hier mal die 16 Bit Version und die etwas aufgräumte 8 Bit 
Version.
Möge sich jeder selber seine Meinung bilden.

Ich habe auf Timer 1 umgestellt, der ist deutlich flexibler, was die 
Framerate angeht. Und er ist sowieso verfügbar.

Mag sein dass man mit der Brachialmethode, 128kB Flash für ne 
Statemachine zu benutzen ein paar Takte schneller ist, die Lösung hier 
ist auch auf nem kleinen ATtiny wie vor einiger Zeit mal angekündigkt 
lauffähig. Und das bei gerade mal 35% Flashauslastung.

Ach ja, und da gibt es auch kleine Unterschiede zwischen denen, die nur 
mal schnell ein paar Brainstomingfragmente hinwerfen und denen, die ein 
fehlerfrei kompilierbares, und zumindest im Simulator getestetes 
Programm haben. Ein LDI r0,1 knallt dir der Assembler mal ganz fix um 
die Ohren ;-)

Die 8 Bit Version braucht 20 Takte für die Dekodierung und maximal 26 
Takte für einen Dekoderumlauf incl. UART-Übertragung. Macht bei 20 MHz 
maximal 769 kHz Abtastrate. Not bad für so eine Softwarelösung. Die 16 
Bit Variante braucht 28 Takte für die Dekodierung und maximal 34 Takte 
für einen Dekoderumlauf, sprich es sind maximal 588 kHz Abtastrate 
möglich.

Danke an Ulrich für die Inspiration.

MFG
Falk

Edit: Die minimale Framerate ist doppelt so hoch wie im Quelltext 
genannt, denn man muss die Zähler ja vorzeichenbehaftet interpretieren. 
Damit hat man nur noch 127 bzw 32767 Abtasttakte Zeit, um einen Frame zu 
senden.
Moderator #3595815
Lesenswert?

Falk Brunner schrieb:
> Ich glaub dir gehen gerade die Pferde etwas durch. Von einer Routine mit
> 4 Drehgebern und nur 21 Takten sind wir MEILENWEIT entfernt

Nein, gar nicht. Ulrich hat doch schon fast alles erklärt :)

MaWin schrieb:
> Das ist der Unterschwied zwischen denen, die können, und denem, die nix
> können.
> ...
> 21 Takte + 2 für den RJMP, dafür eine 64k Tabelle im ATmega128.
> und eine Instruktion bleibt distributed zum Aussenden der Daten.

Sei nicht so überheblich, denn ganz so arg viel kannst du offensichtlich
auch nicht, sonst hättest du nicht angefangen, die 23-Takte-Lösung mit
dem großen ATmega zu implementieren (die ich in meinem vorletzen Beitrag
schon erwähnt habe, sondern gleich diejenige mit Ulrichs Ansatz, die mit
einem ATtiny auskommt und nur 21 Takte einschließlich RJMP braucht ;-)

Hier ist noch einmal sein vervollständigter Code mit aussagekräftigeren
Registernamen und mit einer kleinen Optimierung am Ende der Schleife:
1
#include <avr/io.h>
2

3
; port connections
4
;
5
; PB7 PB6 PB5 PB4 PB3 PB2 PB1 PB0
6
;  |   |   |   |   |   |   |   |
7
;  B4  B3  B2  B1  A4  A3  A2  A1
8

9

10
; register assignments
11

12
zero     = 0
13
counter1 = 1
14
counter2 = 2
15
counter3 = 3
16
counter4 = 4
17
encbits  = 5
18
work     = 6
19

20
; initialization
21

22
clr  zero
23
in   encbits,PORTB
24

25
; main loop
26

27
loop:
28

29
mov  work,encbits
30
swap work
31
in   encbits,PORTB
32
eor  work,encbits
33

34
lsr  work
35
adc  counter1,zero
36
lsr  work
37
adc  counter2,zero
38
lsr  work
39
adc  counter3,zero
40
lsr  work
41
adc  counter4,zero
42

43
lsr  work
44
sbc  counter1,zero
45
lsr  work
46
sbc  counter2,zero
47
lsr  work
48
sbc  counter3,zero
49
sub  counter4,work
50

51
rjmp loop

Ein Schleifendurchlauf einschließlich des RJMPs wird in den genannten 21
Takten abgearbeitet, zählt aber erst mit 8 Bit und sendet die Ergebnisse
auch noch nicht über den UART.

Mit einem einfachen Trick kosten diese Zusatzfunktionen kosten aber
keine Zeit: Der eigentliche Schleifenblock ohne den RJMP braucht 19
Takte. Schreibt man ihn n-mal hintereinander, können nach jedem Block
(mit Ausnahme des letzten) ein oder zwei zusätzliche Befehle mit
zusammen 1 oder 2 Takten ausgeführt werden, ohne dass die Abtastperiode
von 21 Takten überschritten wird. Auf den letzten Block folgt der RJMP,
der ebenfalls 2 Takte braucht. Bei n Kopien des Schleifenblocks bekommt
man also 2·(n-1) Takte für zusätzlichen Code geschenkt, ohne dass auch
nur ein einziges Abtastintervall die 21 Takte überschreitet.

Mit dieser zusätzlich gewonnenen Zeit und den 25 noch freien Registern
kann man einige lustige Dinge anstellen:

- den Hardware-UART mit Daten beschicken
- die Datenleitung des Software-UART auf high oder low setzen
- die 8-Bit-Zählerstände zu 16-Bit-Werten akkumulieren
- usw.

Zwischen zwei gesendeten Bytes (bzw. Bits beim Software-UART) müssen –
abhängig von der Baudrate – soviele Schleifenblöcke eingefügt werden,
wie die Übetragung des Bytes (Bits) dauert. Jeder dieser eingefügten
Blocke schafft Zeit für andere Dinge.

Die Akkumulation in 16-Bit-Werte muss nicht ständig gemacht werden,
sondern immer nur dann, wenn gerade eine neue Übertragung des
entsprechenden Zählers ansteht, oder nachdem 127 Schleifenblöcke
ausgeführt worden sind, so dass der 8-Bit-Zählerstand mehrdeutig zu
werden droht.

Ein Schleifenblock ist einschließlich der zusätzlichen Befehle 40 oder
42 Bytes lang. In den 16 KiB Flash, die der größte ATtiny mitbringt,
passen also mindestens 390 Schleifenblöcke, so dass in die bis zu
1560 Bytes zusätzlicher in die Gesamtschleife hineingewoben werden kann.
Soviel zusätzlichen Code braucht man aber gar nicht, so dass man auch
mit kleinern ATtinies schon sehr weit kommt.

Also, es bleibt bei 21 Takten mit 16 Bit, HW-/SW-UART und was einem
vielleicht sonst noch so einfällt.
#3595834
Lesenswert?

@ Yalu X. (yalu) (Moderator)

>> Ich glaub dir gehen gerade die Pferde etwas durch. Von einer Routine mit
>> 4 Drehgebern und nur 21 Takten sind wir MEILENWEIT entfernt

>Nein, gar nicht. Ulrich hat doch schon fast alles erklärt :)

Ok, war ein wenig übertrieben.

>Hier ist noch einmal sein vervollständigter Code mit aussagekräftigeren
>Registernamen und mit einer kleinen Optimierung am Ende der Schleife:

>Takten abgearbeitet, zählt aber erst mit 8 Bit und sendet die Ergebnisse
>auch noch nicht über den UART.

Eben, das fehlt noch.

>(mit Ausnahme des letzten) ein oder zwei zusätzliche Befehle mit
>zusammen 1 oder 2 Takten ausgeführt werden, ohne dass die Abtastperiode
>von 21 Takten überschritten wird.

Naja, als Extremoptimierung VIELLEICHT machbar, aber irgendwann ist es 
auch mal wieder gut mit dem Unrolling ;-)

>Zwischen zwei gesendeten Bytes (bzw. Bits beim Software-UART) müssen –
>abhängig von der Baudrate – soviele Schleifenblöcke eingefügt werden,
>wie die Übetragung des Bytes (Bits) dauert. Jeder dieser eingefügten
>Blocke schafft Zeit für andere Dinge.

Ja, dann wächst der Flashbedarf aber auch mal ganz fix. Man muss es 
nicht immer übertreiben. Mag ja sein dass der Igor-USB das so macht.

>Also, es bleibt bei 21 Takten mit 16 Bit, HW-/SW-UART und was einem
>vielleicht sonst noch so einfällt.

Na dann poste mal ein VOLLSTÄNDIGES Beispiel. Oder willst du dich lieber 
auf die geniale Position von MaWin begeben?
Moderator #3595878
Lesenswert?

Falk Brunner schrieb:
> Naja, als Extremoptimierung VIELLEICHT machbar, aber irgendwann ist es
> auch mal wieder gut mit dem Unrolling ;-)

Hier liest man doch öfters den Spruch (kann sein, sogar von dir), dass
man für ungenutzte Flash-Bytes kein Geld von Atmel zurückbekommt ;-)

Falk Brunner schrieb:
> Na dann poste mal ein VOLLSTÄNDIGES Beispiel.

Die Chance, dass ich da einen Fehler einbaue, wäre recht hoch, und zum
intensiven Testen fehlt mir etwas die Zeit.

> Oder willst du dich lieber auf die geniale Position von MaWin begeben?

Unter diesen Umständen: Ja ;-)


Als kleine Entschädigung kommt hier der – ebenfalls unvollständige ;-) –
Code für einen einzelnen Drehgeber mit einer Abtastperiode von 9 Takten:
1
#include <avr/io.h>
2

3
; port connections
4
;
5
; PB7 PB6 PB5 PB4 PB3 PB2 PB1 PB0
6
;  |   |   |   |   |   |   |   |
7
;                          B   A
8

9

10
; register assignments
11

12
counter =  0
13
encbits =  1
14
diff    =  2
15
xl      = 26
16
xh      = 27
17

18
; initialization
19

20
ldi  xh,hi8(table)
21
in   encbits,PORTB
22

23
; main loop
24

25
loop:
26

27
in   encbits,PORTB
28
swap xl
29
andi xl,0xf0
30
or   xl,encbits
31

32
ld   diff,x
33
add  counter,diff
34

35
rjmp loop
36

37
.data
38

39
table:
40
; ... 256 bytes ...

Er verwendet – etwas verschwenderisch – eine Tabelle mit 256 Werten,
deren Elemente die 8-Bit-Werte -1 (0xff), 0 oder +1 haben. Der ATtiny
sollte also mindestens 256 Bytes SRAM haben.

Und als Bonus: So geht's für 2 Drehgeber in 13 Takten:
1
#include <avr/io.h>
2

3
; port connections
4
;
5
; PB7 PB6 PB5 PB4 PB3 PB2 PB1 PB0
6
;  |   |   |   |   |   |   |   |
7
;                  B2  B1  A2  A1
8

9

10
; register assignments
11

12
counter1 =  0
13
counter2 =  1
14
encbits  =  2
15
diff     =  2
16
xl       = 26
17
xh       = 27
18
yl       = 28
19
yh       = 29
20

21
; initialization
22

23
ldi  xh,hi8(table1)
24
ldi  yh,hi8(table2)
25
in   encbits,PORTB
26

27
; main loop
28

29
loop:
30

31
in   encbits,PORTB
32
swap xl
33
andi xl,0xf0
34
or   xl,encbits
35

36
ld   diff,x
37
add  counter1,diff
38

39
mov  yl,xl
40
ld   diff,y
41
add  counter2,diff
42

43
rjmp loop
44

45
.data
46

47
table1:
48
; ... 256 bytes ...
49

50
table2:
51
; ... 256 bytes ...

Diesmal werden – noch verschwenderischer – sogar zwei Tabellen mit
zusammen 512 Byte benötigt. Der ATtiny sollte also mindestens 512 Bytes
SRAM haben.

Die Tabelleninhalte müssen jeweils in der Initialisierungsphase ins SRAM
geschrieben werden. Die genauen Inhalte sollten aus der Art und Weise,
wie die Tabellen genutzt werden, hervorgehen.

Weitere Features (wie 16 Bit und UART) können, wie in meinem letzten
Beitrag skizziert, ohne Einfluss auf die Abtastrate nachgerüstet werden.
Moderator #3595899
Lesenswert?

Ulrich schrieb:
> Man kann noch 1 Zyklus sparen (das mov  work,encbits), wenn man die
> Schleife 2 mal hinschreibt, und dabei die Register-nutzung vertauscht.

Stimmt, das habe ich vergessen. Du hast das ja oben schon vorgeschlagen.

Dann schafft man mit deinem Ansatz 20 Takte für 4, 13 Takte für 2 und 9
Takte für 1 Drehgeber. Die beiden tabellenbasierten Beispiele für 2 und
1 Drehgeber aus meinem letzten Beitrag sind damit obsolet.

Noch gestern hätte ich es kaum für möglich gehalten, 4 Drehgeber in
weniger als 36 Takten abzuarbeiten :)

Auch von mir ein Danke für deinen genialen Tipp!

Ich hatte zwar auch schon eine ganze Weile überlegt, wie man die 8 alten
und 8 neuen Drehgebersignale durch 8 Bit breite Bitoperationen so in ein
einzelnes Byte überführen kann, dass man daraus mit wenig Aufwand die
Inkremente und Dekremente der 4 Zähler ableiten kann, aber leider nur
mit sehr bescheidenem Erfolg: Es blieben immer noch 33 Takte, die man
durch Vereinfachung der Terme vielleicht noch auf 30 hätte reduzieren
können. Aber das wären ja immer noch 50% mehr als mit deiner Methode :)
Gast #3596544
Lesenswert?

Falk Brunner schrieb:
> Oder willst du dich lieber
> auf die geniale Position von MaWin begeben?

Die geniale Position von MaWin lautet:

Du kannst nur fordern, von dir kommt nix ausser Zweifeln,
und selbst mit einer Lösung, die alles erfüllt,
motzt du immer noch unzufrieden rum und willst ANDEREN
Leute Arbeit machen, die du dir lachend sparst.
Spiel weiter deine dummen Spielchen, aber alleine.


Yalu X. schrieb:
> Hier ist noch einmal sein vervollständigter Code mit aussagekräftigeren
> Registernamen und mit einer kleinen Optimierung am Ende der Schleife:

Meiner Meinung nach fehlt dort die Umsetzung zwischen Encoderstand und 
Increment/Decrement-Befehlen, eben meine 64k Tabelle.
Ein A1 EOR B1alt macht nämlich keineswegs richtige up und ein B1 EOR 
A1alt keineswegs richtige down Zählimpulse.

A          000011110000111100001111
B          001111000011110000111100
A EOR Balt  00100010001000100010001 (increment)
B EOR Aalt  01110111011101110111011 (decrement)

richtig     01010101010101010101010 (increment)
wäre        00000000000000000000000 (decrement)
Gast #3596575
Lesenswert?

Wenn man das Increment und Decrement zusammenzählt, heben die sich auf 
wo 2 mal eine 1 steht. Der Witz bei dem Code mit dem XOR ist ja das ggf. 
auch mal hoch und runter gezählt wird um in der Summe nichts zu ändern.
Das gibt dann das gewünschte Muster, nur halt die Andere Zählrichtung - 
aber das ist Geschmackssache.
#3596718
Lesenswert?

@ MaWin (Gast)

Red mal mit deinem Psychiologen, er muss mal deine Medikamente 
kontrollieren.

>Meiner Meinung nach fehlt dort die Umsetzung zwischen Encoderstand und
>Increment/Decrement-Befehlen, eben meine 64k Tabelle.

Tja, das ist der Unterschied mal wieder zwischen "genialen Leuten" und 
Arbeitern, die den Code mal umsetzen und testen und dabei feststellen, 
dass er hervorragend funktioniert, auch ohne "geniale" 64k Tabelle.
Gast #3596839
Lesenswert?

Das man einen fremden ASM Code nicht sofort versteht, ist keine Schande, 
immerhin ist der Code mit den Verknüpfungen auch alles andere als 
offensichtlich. Die Kommentare sind halt bei der suche nach der 
schnellsten Lösung auch eher sparsam ausgefallen. So weit auseinander 
sind die Lösungen ja auch nicht - für die eher realistischen Fälle mit 
nur 1 oder 2 Kanälen hat die Tabelle auch einiges für sich. Und ob es 
dann 20 oder 22 Zykeln sind macht auch keinen so großen Unterschied.

Auch MaWin hat mit der Frage wie man die Sign Extension macht dazu 
beigetragen es mal mit addieren über das Carry zu versuchen.
Gast #3597185
Lesenswert?

Ulrich schrieb:
> Wenn man das Increment und Decrement zusammenzählt, heben die sich auf
> wo 2 mal eine 1 steht.

Stimmt auffallend, habe ich trotzdem nicht bemerkt obwohl ich die 
Zahlenreihen so schön untereinander geschrieben habe. Sehr praktisch.

Nun ja, viele Lösungen für unter 24 Taktzyklen für 4 Drehgeber.
Gast #3606890
Lesenswert?

MaWin schrieb:
> Nun ja, viele Lösungen für unter 24 Taktzyklen für 4 Drehgeber.

So unterschiedlich kann man die Realität wahrnehmen: ich sehe keine 
einzige Lösung. Was ich sehe, ist eine Bitschubserei im virtuellen Raum.
Nirgendwo gibt es gültige Daten, nirgendwo gibt es ein Problem für diese 
'Lösung'.
Stillschweigend wurde die Ein-Prozessor-Lösung verworfen, wobei nicht 
auszumachen ist, wieviele µCs denn noch die Hauptaufgabe des Zählens 
übernehmen müssen. Dass in der realen Welt auch noch Index-Impulse bei 
Drehgebern oder Referenzmarken bei linearen Weggebern ausgewertet werden 
müssen, wird hier völlig verkannt.
Eine zusätzliche Auswertung der Sinussignale? Fehlanzeige!
Synchrones Auslesen mehrerer Kanäle? Fehlanzeige!

Was hier als 'Lösung' propagiert wird, ist ein überstürztes Rennen in 
eine Sackgasse. Aber das merkt man erst, wenn man ein Problem hat, was 
eine richtige Lösung braucht.
Und ich dachte schon, ich könnte noch etwas dazulernen :-(
Gast #4105164
Lesenswert?

Noch ein Takt weniger durch swap 'n' rol...
Für einen einzelnen Encoder reichen so 9 Takte, bzw. 2*8 Takte für den 
von Ulrich vorgeschlagenen Doppeldurchlauf mit vertauschten Registern.

; initialization
clr  zero
in  encbits,PORTB
in  work,PORTB

; main loop

loop:

swap work
in   encbits,PORTB
eor  work,encbits

lsr  work
adc  counter1,zero

swap work
rol work
sbc  counter1,zero

...
...

swap encbits
in work, PORTB
eor encbits, work

lsr encbits
adc counter1, zero

swap encbits
rol encbits
sbc counter1, zero

rjmp loop
#4441280
Lesenswert?

Hier mal ein kleines Update. Die Quelltexte sind aufgeräumt und ein paar 
Zahlen zur Abtastrate und Framerate korrigiert. Ausserdem ist die 
Abtastung nun absolut gleichmäßig und jitterfrei, alle Programmschleifen 
sind exakt gleich lang! Viel Spaß damit.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren