Servus zusammen,
ich möchte einen Drehgeber (oft auch Encoder genannt) mit AB-Ausgang
auswerten. Ganz wie hier beschrieben:
http://www.mikrocontroller.net/articles/Drehgeber
Nun dreht die Welle, an welcher sich der Encoder befindet recht schnell,
sodass ich den Interrupt so kurz wie irgendwie möglich halten möchte um
die Prozessorlast so gering wie möglich zu halten.
Ich habe mich daher an die Arbeit gemacht und mich über inline-Assembler
schlauer gelesen. Leider habe ich ein paar Schwierigkeiten den Code zu
compilieren. Das lesen einer Progmem-Variable scheint mir nicht gerade
DIE Einstiegs-Aufgabe zu sein...
ich scheitere an der Fehlermeldung
error: memory input 1 is not directly addressable
welcher in der Zeile "m" (encoder_tab) auftritt.
Jetzt poste ich einfach meinen ganzen Versuch in der Hoffnung, ihr könnt
mir etwas unter die Arme greifen und vielleicht seht ihr ja sogar noch
etwas Optimierungspotential.
also hier mein Versuch:
Der Code in C:
ich hoffe man muss nicht direkt die Hände über dem Kopf zusammenschalgen
und es besteht noch Hoffnung für mich. Ich habe mich wirklich sehr
bemüht das Beste aus meinem Unwissen zu machen!
Danke schonmal
lg Leo
Der AVR kann nicht nur multiplizieren, sondern auch schieben. Das spart
dir R0,R1 ein.
Der Name eines Arrays ist keine direkt adressierbare Variable sondern
eine Adresskonstante. Daher die Fehlermeldung.
Wenn es wirklich eng zugeht: Viele AVRs stellen im I/O-Bereich ein paar
frei verwendbare Bytes als "General Purpose I/O Register" zur Verfügung,
an die man per IN/OUT schneller ran kommt als per LDS/STS ans RAM.
Etwas härterer Tobak: Tabelle so im Speicher platzieren, dass sie nicht
über eine 256-Byte-Grenze geht, oder noch besser exakt an einer solchen
beginnt. Reduziert die Adressrechnung.
@ Leo B. (luigi)
>ich möchte einen Drehgeber (oft auch Encoder genannt) mit AB-Ausgang>auswerten. Ganz wie hier beschrieben:>http://www.mikrocontroller.net/articles/Drehgeber
Kaum. Denn dann würdest du nicht den Pin Change Interrupt verwenden.
Aber dazu zum 100202034mal zu diskutieren hab ich keine Lust.
Wenn man schon schnelle ISRs braucht, sollte man sie besser komplett in
ASM als eigene Datei aufsetzen, das ist deutlich einfacher und wartbarer
als Inline ASM.
Ihr seid einfach einsame Spitze! Danke
A. K. schrieb:> Der AVR kann nicht nur multiplizieren, sondern auch schieben. Das spart> dir R0,R1 ein.
Haste recht. Zugegeben, ich hab den Compiler meinen C-Code compilieren
lassen und hab mich am lss-File orientiert. Etwas gemogelt, aber für nen
Einsteiger wie mich nicht Falsch glaube ich.
A. K. schrieb:> Der Name eines Arrays ist keine direkt adressierbare Variable sondern> eine Adresskonstante. Daher die Fehlermeldung.
Danke das löst den Fehler. Nur muss ich dann die Adresse oder die
Variable übergeben? also "i" (encoder_tab) oder "i" (&encoder_tab)?
und stimmt der Constraint i dann überhaupt?
A. K. schrieb:> Wenn es wirklich eng zugeht: Viele AVRs stellen im I/O-Bereich ein paar> frei verwendbare Bytes als "General Purpose I/O Register" zur Verfügung,> an die man per IN/OUT schneller ran kommt als per LDS/STS ans RAM.
Hab ich nun umgesetzt denke ich. Danke für den Hinweis!
( enc_delta ist ne globale variable und wär mich auch lieber wenn es
eine solche bleibt )
A. K. schrieb:> Etwas härterer Tobak: Tabelle so im Speicher platzieren, dass sie nicht> über eine 256-Byte-Grenze geht, oder noch besser exakt an einer solchen> beginnt. Reduziert die Adressrechnung.
Da geb ich dir Recht, nur wie ich dem guten Compiler beibringe wo ich
die Daten gerne hätte?! Da hab ich absolut keinen Ansatz wo ich anfangen
sollte google oder Bücher zu bemühen...
Jetzt sieht's folgendermaßen aus:
Kann noch jemand Fehler oder Verbesserungen sehen?
vielen Dank
lg Leo
Edit: hab grad noch ein paar r30 zu r31 und ein r0 zu r 31 gemacht...
Flücktigkeitsfehler, wollte auch schnell sein so wie ihr...
Ja ich entdecke einen Fehler! Nur eine Lösung? wo ist der Fehler
genau???
Der Compiler sollte ja die Adresse der encoder_tab irgendwie so
auflösen, dass später im Code steht:
Ah ok, danke. ja das löst das halbe Problem. Nur wie ich ihm beibringe
das Komplement zu bilden ist mir noch unklar. Er weigert sich
hartnäckig.
Minus klappt nicht, die Schlange ~(...) auch nicht...
Wird wohl auch nicht allzu häufig gebraucht hab ich das Gefühl, da die
allwissende Suchmaschine mich nicht aufklären möchte. Ich hab hier einen
echten Hänger. Sehr nervig!
Danke für jeden Tip!
Genial, Danke!
so ganz 100% klar ist mir zwar gerade noch nicht, wie du das raus
bekommen hast (auch wenn du mir das wohl gerade beibringen wolltest,
danke dafür). aber ich werde da dran bleiben und mir das nochmal genauer
ansehen!
Leo B. schrieb:> so ganz 100% klar ist mir zwar gerade noch nicht, wie du das raus> bekommen hast
A.K. dürfte einfach den gezeigten C-Code compiliert und dann ins
Listfile (.lss) geschaut haben.
troll schrieb:> A.K. dürfte einfach den gezeigten C-Code compiliert und dann ins> Listfile (.lss) geschaut haben.
Das lss-File bringt nix, ich will ja den Code vor dem Assembler
wissen, nicht disassemblerten Code. Also
avr-gcc -S t1.c
und dann ins t1.s schauen.
Leo B. schrieb:> Nun dreht die Welle, an welcher sich der Encoder befindet recht schnell,> sodass ich den Interrupt so kurz wie irgendwie möglich halten möchte um> die Prozessorlast so gering wie möglich zu halten.
Was heisst denn recht schnell in Zahlen?
Wieviele Impulse erzeugt denn der Encoder je Umdrehung?
Das sollte doch ersteinmal ueberschlagen werden, bevor man ueberhaupt
ueber Tricks und Klimmzuege nachdenkt.
fonsana
A. K. schrieb:> troll schrieb:>> A.K. dürfte einfach den gezeigten C-Code compiliert und dann ins>> Listfile (.lss) geschaut haben.>> Das lss-File bringt nix, ich will ja den Code vor dem Assembler> wissen, nicht disassemblerten Code. Also> avr-gcc -S t1.c> und dann ins t1.s schauen.
Ah so, wieder was gelernt. Danke.
troll schrieb:> Ah so, wieder was gelernt. Danke.
Dem schließe ich mich an, Danke.
fonsana schrieb:> Was heisst denn recht schnell in Zahlen?> Wieviele Impulse erzeugt denn der Encoder je Umdrehung?
1000 Striche pro Umdrehung, jeder löst 4 Interrupts/Flanken aus, bei
einer Drehzahl von 1000U/min und mehr. Der aktuelle Interrupt brauch mit
rjmp zum Interrupt und reti 39 Zyklen. Macht 2.600.000 Zyklen pro
Sekunde für den Encoder. Statt den 68 Zyklen den Compilierten C-Codes
por Interrupt, respektive 4.533.333 Zyklen pro Sekunde finde ich ist das
eine starke Verbesserung. Da der µC noch 3 zeitkritische Trigger (so
genau wie möglich, auf jeden Fall aber weniger als 100µs Abweichung vom
Soll) verwalten soll, nebenbei noch SPI-Master an einem Bus mit 3 Slaves
und eine UART->RS232->USB->PC Verbindung verarbeiten darf, ist der µC
doch nicht schlecht ausgelastet. Ich bin also froh um die paar Prozent
gewonnener Prozessorzeit!
Ich glaube ich hab da einen Fehler, kann ihn aber nicht ganz
nachvollziehen. Vielleicht könnt ihr mir nochmal kurz auf die Sprünge
helfen.
Es geht um die Berechnung der Speicheradresse:
Der Compiler produziert aus dem c-Code folgendes:
1
uint8_ttmp;
2
[...]
3
code=tmp;
4
90:e0930201sts0x0102,r30// => r30 = tmp
5
6
tmp=pgm_read_byte(&encoder_tab[tmp]);
7
94:ff27eorr31,r31
8
96:e7fdsbrcr30,7
9
98:f095comr31
10
9a:ec5csubir30,0xCC;204
11
9c:ff4fsbcir31,0xFF;255
12
9e:e491lpmr30,Z
Wenn ich das richtig deute, dann lässt sich der 2. Absatz in folgenden,
für mich leserlicheren "nicht ganz C"-Code zurück übersetzen:
1
uint16tmp=(uint16)(tmp);// r30 = tmp; r31 = 0
2
if(tmp>=128)// sbrc r30, 7
3
tmp-=1;// com r31
4
tmp-=(uint16)(-34);
5
tmp=pgm_read_byte(tmp);// lpm r30, Z
Aber warum das ganze mit dem -1 ?
Entweder ich mach beim übersetzen einen Fehler oder mir ist nicht klar
warum er das macht, der gute Compiler?
Kann mir da grad nochmal jemand helfen bitte?
lg Leo
Leo B. schrieb:> Kann mir da grad nochmal jemand helfen bitte?
Folgenden Hinweis einfach beachten:
Falk Brunner schrieb:> Wenn man schon schnelle ISRs braucht, sollte man sie besser komplett in> ASM als eigene Datei aufsetzen, das ist deutlich einfacher und wartbarer> als Inline ASM.
Piefke schrieb:> Folgenden Hinweis einfach beachten:>> Falk Brunner schrieb:>> Wenn man schon schnelle ISRs braucht, sollte man sie besser komplett in>> ASM als eigene Datei aufsetzen, das ist deutlich einfacher und wartbarer>> als Inline ASM.
Ist für so ziemlich alle Fragen hier völlig irrelevant. Dafür hat man
aber Zusatzkram wie File-Kopf und extern-Deklarationen an der Backe, die
man sich bei inline-Code erspart. Einfacher ist eigentlich nur die
Tipparbeit der Funktion selbst, weil der \n\t Kram entfällt.
A. K. schrieb:> Übersetzt: 8-Bit mit Vorzeichen erweitert auf 16 Bit mit Vorzeichen.
Tatsache... Dann rechnet der GCC offenbar einfach mit einem Vorzeichen
behafteten Index ohne Warnung, Hinweis, o.Ä.... klingt gefährlich!
Folgender Code ist zweifellos der Quellcode zu obigem assemblierten
Ausschnitt, ich hab's nochmal nachgeprüft!
Leo B. schrieb:> Dann rechnet der GCC offenbar einfach mit einem Vorzeichen> behafteten Index ohne Warnung, Hinweis, o.Ä.... klingt gefährlich!
Warum sollte er warnen? Ein Array-Index ist in C immer Vorzeichen
behaftet.
Leo B. schrieb:> Folgender Code ist zweifellos der Quellcode zu obigem assemblierten> Ausschnitt, ich hab's nochmal nachgeprüft!
Schwer zu glauben. Ich würde mich dann mal auf die Suche nach der
Definition von uint8_t machen.
A. K. schrieb:> Ist für so ziemlich alle Fragen hier völlig irrelevant. Dafür hat man> aber Zusatzkram wie File-Kopf und extern-Deklarationen an der Backe, die> man sich bei inline-Code erspart.
Das ist ja nun keine unlösbare Aufgabe.
Wenn man wenig tippen möchte, kann man sich auch die .c-Funktion in eine
Assemblerquelle übersetzen lassen und ggf. optimieren. Dann stimmen
zumindest das Gerüst und die Grundfunktion.
Der obige inline-Text ist doch Spagetti pur!
Stefan Ernst schrieb:> Warum sollte er warnen? Ein Array-Index ist in C immer Vorzeichen> behaftet.
Aber nicht die Erweiterung von uint8_t zu int. Die anschliessende 16-Bit
Addition vom Index zur Basisadresse darf er dann gerne mit Vorzeichen
machen.