mikrocontroller.net

Forum: Compiler & IDEs 32-bit Rechnung vereinfachen möglich?


Autor: David (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich suche Anregungen und Ideen zu einer Codeoptimierung bezüglich einer 
Division.
Ich messe an einem Motor eine Rotationsgeschwindigkeit einer Welle. Die 
Drehzahlen können sehr hoch sein bis über 20000U/min, daher ist 
Rechenzeit ein kostbares gut :-)
Mein Prozessor ist ein Atmega8 und läuft mit internem Oszi 8Mhz.
Zur Drehzahlerfassung läuft der 16-bit Timer ohne Prescaler (da er noch 
andere Aufgaben erledigt, die µs genau zeitlich getimed sein sollten). 
In dem 16-bit Timer errechne ich mir die Zeit von Signal zu Signal (1 
Singal pro Umlauf), prinzipiell vereinfacht gesagt mal so:

Anzahl Timer Überläufe*65536+aktuellen Timerwert

Der Prozessor läuft 8 000 000. Die Berechnung der Drehzahl erfolgt nun:
Prozessortakt/gemessener Zeitwert

In den Prozessortakt rechne ich gleichzeitig den Faktor 60 min ein, um 
von Umdrehungen pro Sekunde auf U/min zu kommen. Weiterhin skaliere ich 
das ganze noch mit dem Faktor 100 herunter, um anstelle von 1000U/min 
nur 10 zu erhalten, sodass ich in eine 8-bit Variable, bis 25600U/min 
ablegen kann (entspricht dann 256).

Insgesamt sieht meine Rechnung also wie folgt aus:
32bit Variable aus der gemessenen Timer Rechnung (bei niedrigen 
Drehzahlen, bei höheren würden 16-bit reichen) geteilt durch 
(Prozessortakt*60/100).
Den Divisor habe ich bereits als eine konstante zusammgefasst: 8 000 
000*60/100 = 4 800 000, die konstante ist logischerweise auch 32-bit

Eine Kürzung (cast) auf weniger bits wäre bei der Konstante problemlos 
möglich, ergäbe aber bei niedrigen Drehzahlen eine ziemliche 
Ungenauigkeit, wenn ich die Timerwerte ebenfalls herunter caste.

Lässt sich diese Rechnung noch weiter optimieren? Atmega8 mit 
Hardwaremultiplizierer, allerdings keine Hardware Division. Alle 
Rechnungen unsigned.

Kann mir jemand eine Schätzung geben, wie lange die Rechnung etwa auf 
dem Mega8 dauern würde?

Danke.

Gruß
David

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
David schrieb:

> Kann mir jemand eine Schätzung geben, wie lange die Rechnung etwa auf
> dem Mega8 dauern würde?

Gehe in den Debugger und steuere dein Programm so, dass es auf die 
Berechnung auflaufen wird.
Dann setzt in die Variablen die dich interessierenden Zahlenwerte ein 
und achte im Debugger auf den Cycle-Count. Lass im Debugger die 
Berechnung durchlaufen und sieh dir wieder den Cycle-Count an. Die 
Differenz sagt dir, wieviele Zyklen verbraucht wurden. Und da der 
Debugger auch deine Taktfrequenz kennt, ist unter dem Cycle-Count auch 
dieselbe Angabe in Realzeit umgerechnet zu finden.

Autor: Detlev T. (detlevt)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also "interner Oszillator" und "genau" schließen sich gegenseitig schon 
einmal aus. :D

Was dir vor allem fehlt ist eine Abschätzung, wie genau die Zeitmessung 
wirklich sein muss (Fehlerrechnung). Man muss vorne sicher nicht 32 Bit 
hineinstecken um hinten 8 Bit heraus zu bekommen.

Da du über weitere Anforderungen nichts geschrieben hast, gibt es wohl 
auch keine. ;) Man könnte daher vielleicht eine andere Frequenz für den 
Systemtakt wählen, so dass der Divisor (näherungsweise) 2^n ist, so dass 
die Division per Shiften erledigt werden kann.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dazu kommt dann noch:
Über einer bestimmten Grenzfrequenz schaltet man dann sowieso besser um. 
Weg von Zeit zwischen Pulsen, hin zu: Wieviele Pulse gab es in einer 
definierten Zeit.

Das hat neben der besseren Genauigkeit dann auch den schönen 
Nebeneffekt, dass keine Division benötigt wird.

Autor: David (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Detlev T. schrieb:
> Was dir vor allem fehlt ist eine Abschätzung, wie genau die Zeitmessung
>
> wirklich sein muss (Fehlerrechnung). Man muss vorne sicher nicht 32 Bit
>
> hineinstecken um hinten 8 Bit heraus zu bekommen.

Wenn sich ein Bereich um die +/- 50 U/min bei der Zeitmessung ergibt, 
wäre das ausreichend.

Da nach jedem Impuls bzw. jeder Wellenumdrehung die Drehzahl berechnet 
wird und mit einer Sollvorgabe verglichen wird, folgt im Anschluss über 
eine if-Abfrage, ob ein Grenzwert überschritten ist oder nicht, wenn ja, 
tue etwas.
Daher kann ich nicht die Pulse zählen über einen Zeitraum, wirklich bei 
jedem einzelnen Impuls entschieden wird, muss das Programm etwas regeln 
oder nicht.

Detlev T. schrieb:
> Also "interner Oszillator" und "genau" schließen sich gegenseitig schon
>
> einmal aus. :D

Das war mir etwas bewusst. Ein systematischer Fehler schadet jedoch 
dabei nicht, solange alles verschoben ist. Ich dachte, der interne Oszi 
macht mir letzendlich nur systematische Fehler (also eine Abweichung 
imemr gleich), jedoch keine zufälligen. Liege ich da falsch?

Autor: Klaus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
David schrieb:
> Ich dachte, der interne Oszi
> macht mir letzendlich nur systematische Fehler (also eine Abweichung
> imemr gleich), jedoch keine zufälligen. Liege ich da falsch?

Jain. Der interne Oszillator ist stark Temperaturabhängig. Besonders 
beim Einschalten, oder wenn sich der Strom ändert (wenn z.B. eine LED 
geschaltet wurde) gibt es doch recht schnelle Temperatur- und damit 
Frequenzveränderungen.

Autor: andreas r. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn der wert eh nur für ne if abfrage verwendet wird, könntest du doch 
auch umgekehrt rechnen: timerwert < X ? tu irgendwas.

da das X sich warscheinlich selten ändert, darf die rechnung auch lange 
dauern.

Autor: Detlev T. (detlevt)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
David schrieb:
> Da nach jedem Impuls bzw. jeder Wellenumdrehung die Drehzahl berechnet
> wird und mit einer Sollvorgabe verglichen wird, folgt im Anschluss über
> eine if-Abfrage, ob ein Grenzwert überschritten ist oder nicht, wenn ja,
> tue etwas.

Du könntest natürlich auch aus dem Sollwert die Zeit berechnen, die bei 
einer Umdrehung nicht unterschritten werden darf und die dann mit dem 
gemessenen Zeitwert vergleichen. Dann brauchst du gar nicht mehr 
dividieren.

Autor: Ferkel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich verstehe das Problem nicht.
20000U/min sind rund 333 Ereignisse/s oder 3ms; das ist doch eher 
langsam.
Eine 32 bit Division braucht etwa 20-30µs, was rund 1% von 3ms ist.
Ein Mega8 läuft auch gerne mit 16MHz, was seine Rechenzeit gegenüber 
8MHz noch einmal verdoppelt.

Wo soll denn das Problem nun sein?

Autor: Horst Hahn (horha)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich habe mich an Divisor = Nenner verstolpert.
1/4,8e6 sind etwa *179 / 2^33 ,  aber 4,8e6 steht ja im Zähler= 
Dividend.
// http://de.wikipedia.org/wiki/Division_(Mathematik)

Wie Andreas und Detlev schrieben, es reicht auch mit einem Timerwert 
passend zur Drehzahl zu vergleichen, den kann mal ja einmal vorher 
bestimmen.

Die 20..30 µs wundern mich, ok bei 16 Mhz könnte es hinkommen.
Bei AVR Arithmetik saust die schnelle Assemblerversion 
http://www.mikrocontroller.net/articles/AVR_Arithm... 
32-Bit/32-Bit in maximal 431 Takten durch, bei 8 Mhz 54 µs.
Die normale in maximal 85 µs.

Falls man "sehr schnell" multiplizieren kann:
Es gibt eine Rechenvorschrift zur Bestimmung des Reziproken, wenn man 
eine gute Näherung kennt.( ist das newton raphson bei AMD 3dnow ? )
Dazu müsste 1/x im Format 1.31 gespeichert sein und Du vielleicht alles 
in Assembler schreiben.
Sei k bekannt und 1/k gesucht.
rez(0) ist eine erste Näherung
dann gilt
wiederhole
  rez(n+1) = (2-rez(n)*k ) *rez(n)
bis passend genau.

Dieser rez-Wert muss anschliessend mit den 4,8e6 multipliziert werden.
Diese Vorschrift verdoppelt etwa mit jedem Durchlauf die 
Genauigkeit/Stellenzahl, braucht aber 2 Multiplikationen und ein 
Subtraktion.
Meine Überlegung ist, dass sich bei hohen Drehzahlen die Drehzahl 
während einer Umdrehung nicht mehr so heftig ändert.
Wenn der Motor um 1% schneller wird, hat man nach einem Durchlauf der 
Rechnung bei Benutzung des vorherigen Ergebnisses als Schätzwert rez(0) 
nur eine Abweichung von (1%)^2 also 1e-4.
Also sei timer vorher für 19800 U/min passend und dann eine  Umdrehung 
später 20000 U/min, dann ist die Abweichung bei Dir etwa auf 2 
Umdrehungen bei 20000 U/min.

Aber wozu das:
Man kann nicht schnell 32 x 1.31 rechnen.
Das dauert geschätze 4 x4 x 5..8(Mul Add Übertrag Schleife)  80..108 
Takte und das mindestens 3 mal sind 300 Takte, dann ist eine Division 
auch fast fertig und genau.
Da lohnt es sich, den Dividend = 4,8 Mio klein zu haben, je weniger Bit 
desto besser.

Auf einem PC dauert die Multiplikation in der ALU/FPU einen Takt und die 
Divison ~ 32, da lohnt es für manche Fälle,

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.