Forum: Mikrocontroller und Digitale Elektronik PIC 18f 24bit Division


von Fabian (Gast)


Lesenswert?

Hallo,

Ich bin dabei eine universelle Ganganzeige für Motorräder zu entwickeln. 
Dazu werden Drehzahl und Geschwindigkeit mit Hilfe der CCP-Module 
gemessen und anschließend dividiert. Dieses Ergebnis wird mit einem 
zuvor abgespeicherten Wert verglichen, wodurch Rückschlüsse auf den 
eingelegten Gang gemacht werden können.
Da das Ganze möglichst universell werden soll, muss ich die Timer um 1 
Byte erweitern und die Überläufe mitzählen.
Somit brauch ich insg. eine 24 / 24 bit Division, wobei auch teilweise 
die 2. Nachkommestelle des Ergebnisses wichtig ist.
Da mich der eigentliche Wert ja nicht interessiert und nur mit einem 
anderen verglichen wird, dachte ich an eine einfache Ganzzahldivision, 
anschließend einen Multiplikation des Rests mit z.B. 100 und erneuter 
Division.

Was meint Ihr? Geht das vll noch einfacher?
Hat jemand eine Divisionsroutine für die 18f PICs? Ich konnte nur für 
die 16er was finden.

Grüße Fabian

von Fabian (Gast)


Lesenswert?

Kann mir keiner mit einer Divisionsroutine weiterhelfen?

von Gerhard (Gast)


Lesenswert?

Hi

warum quälst du dich mit Assembler rum. Hol dir den C18-Compiler von 
Microchip und arbeite mit Float. Der C18 kostet als Studentenversion 
nix.

Gerhard

von Fabian (Gast)


Lesenswert?

Da dachte ich auch schon dran. Ich habe zwar noch nie direkt mit C 
gearbeitet, aber das sollte kein Problem darstellen. Ist wahrscheinlich 
wirklich einfacher.
Das einzige Problem könnte vll sein, dass alles ohne merkbare 
Berechnungszeit geschehen soll.
Ich versuche mich erstmal mit dem C-18 Compiler vertaut zu machen

von chris (Gast)


Lesenswert?

Microchip hat App-notes, wo solche sachen drin sind, einfach App-note 
nach Math durchsuchen.

von Franko P. (sgssn)


Lesenswert?

Hi

es gibt da noch
http://www.piclist.com/techref/piclist/index.htm

Da stehen auch so manche Assembler Routinen für Pics. Die Seite ist aber 
schon älter und beinhaltet wohl mehr Routinen für den PIC16. Aber 
vielleicht nützt das ja was.

Gerhard

von chris (Gast)


Lesenswert?

Wenn ich das machen müsste, würde ich die Division in eine 
Multiplikation umwandeln, zumal der Pic warscheinlich 
HW-Multiplikationen beherrscht.

von Fabian (Gast)


Lesenswert?

Stimmt, das macht die Sache einfacher. Anstatt den Quotienten zu bilden 
und diesen zu vergleichen, vergleiche ich das Produkt aus Quotient und 
z.B. Geschwindigkeit mit der Drehzahl...
Dann brauch ich aber immernoch die einmal die Division, um zu beginn 
einen Referenzwert (somit den einen Multiplikator) zu bekommen.
Das Problem ist halt, dass dieser Quotient rein theoretisch zwischen ca. 
1000 und kleiner 1 liegen kann. Wobei mich die konkreten Zahlen nicht 
interessieren, ich muss sie nur vergleichen.
Somit müsste ich doch z.B. 2 Byte für die Stellen vor dem Komma und 2 
Byte für die Nachkommastellen nehmen können und das Ganze als eine ganze 
("nicht Komma") Zahl nutzen können, oder?

Halt. Ich habe 6 Gänge und will beim Erkennen einen Toleranzbereich 
haben, also einen obere und eine untere Grenze die ich jeweils mit einem 
Wert multiplizieren und dann mit dem anderen Wert vergleichen muss. 
Macht also 12 Multiplikationen... Ob das dann noch ein Vorteil ist?...

Grüße und ein herzliches Dankeschön!

von chris (Gast)


Lesenswert?

Laut mir ist das die falsche Vorgehensweise. Poste mal ein Beispiel, wie 
du die Gänge identifizierst.

von Fabian (Gast)


Lesenswert?

Ich nutze die zwei CCP Module im Capture-Modus um die Zeit zwischen zwei 
Impulsen vom Drehzahl- und Geschwindigkeitssignal zu messen. Da ich 
möglichst flexibel sein möchte und auch noch nicht weiß wieviel Impulse 
pro Umdrehung ankommen (ich will alle Türen offen haben, was die 
Signalquelle angeht) brauche ich eine hohe Taktrate für den Timer (um 
den Fehler bei "schneller Impulsfolge klein zu halten) und andererseits 
muss ich den 16 bit Timer mit einem Register, das die Timer-Überläufe 
zählt, erweitern, um lange Folgen messen zu können.

Ich dachte mir jetzt:
Durch den Quotienten aus Drehzahl und Geschwindigkeit (Einheit und Größe 
sind hier ja egal) wird der Gang identifiziert. In einem 
Programmiermodus werden jeweils die "Referenzquotienten"  mit 
Tolerantzen abgespeichert.
Im Anzeigemodus wird dann wieder der Quotient gebildet und getestet ob 
dieser in die abgespeicherten Bereiche passt.

Vll. wäre es auch geschickt, eine Art automatische Kalibrierung 
durchzuführen, die dann den Takt und Prescaler für die Timer so 
einstellt, dass ich mit dem normalen 16 bit Timer auskomme und so die 
Zahlen kleiner halte. Allerdings soll auch alles recht schnell gehen, 
wodurch lange messzeiten eher schlecht sind.
Ändert jetzt aber an meinem Problem nicht so viel :)

von chris (Gast)


Lesenswert?

Mach bitte ein komplettes Beispiel mit Zahlen. Zudem, der uC rechnet
Binär, nicht Dezimal.

von Fabian (Gast)


Lesenswert?

Timer läuft mit 1Mhz --> alle 10^-6 sek wird der Timer erhöht
2. Gang --> 9000 U/min --> 96km/h=26,7m/sek  (Reifenumfang 1,953m)

Nehmen wir als Beispiel an, das Drehzahlsignal besteht aus 4 H-L-Flanken 
pro Umdrehung und 8 H-L-Flanken pro Radumdrehung(hier können auch andere 
Werte wie 64 bei Drehzahl und 4 bei Geschwindigkeit stehen).

9000 U/min --> 0,0066 sek/Umdrehung --> 0,0016 sek Abstand zwischen zwei 
Impulsen --> Timer zählt 1600 = b'11001000000'

26,7 m/sek  --> 13,7 U/sek --> 0,009 sek Abstand zwischen zwei Impulsen 
--> Timer zählt 9000 = b'10001100101000'

Also haben wir den Quotienten aus 10001100101000 und 11001000000
= 5,625 = B'101' und Rest

Jetzt den Rest mit 1000 = b'1111101000' multiplizieren und erneut 
dividieren und in sep. zwei Bytes schreiben.
Smomit habe ich eine Zahl die aus zwei Byte vor und zwei Byte nach dem 
Komma besteht. Diese Zahl wird als Referenz gesichert

Das ganze wird nun immer wiederholt, wobei der neue Wert mit der 
Referenz verglichen wird.
Den Vergleich will ich mit einer Subtraktion lösen (funktioniert genau 
so auch schon bei einer anderen Sache)

Bitte steinigt mich nicht für meine evtl. unfachliche Art :)
Hilft das weiter?

Ich denke die Sache mit der Anpassung ist schon mal gut, wird's schonmal 
schneller, weil ich nur 16bit Zahlen habe.

von chris (Gast)


Lesenswert?

A=9000 B=1600
A/B=5
A%B=1000
nicht 5.625
>Jetzt den Rest mit 1000 = b'1111101000' multiplizieren und erneut
>dividieren und in sep. zwei Bytes schreiben.
das ist aber Umständlich und entspringt (Volks)Schulmathematik, da
uC nicht auf 10er basis rechnen.

>Smomit habe ich eine Zahl die aus zwei Byte vor und zwei Byte nach dem
>Komma besteht. Diese Zahl wird als Referenz gesichert
Das hast du ja schon vorher, achso verstehe.
24bit / 24bit = 24bit result. Das reduziert man auf 16bit, indem man
es nach rechts schiebt, also gebrochen 2/4/8/16/...
Wenn du aber krumme Werte brauchst, dann multipliziere den  wert
mit einem 16bit wert, als Beispiel, und nimm dann nur die oberen 32bit.
Damit kannst du krumme Werte bekommen. z.B. wenn du umbedingt 100
brauchst, dann multipliziere mit (256*256)/100 = 656,36, also mit 656
Das ergibt einen Fehler von 0.1%, also Ausgangszahl einmal nach rechts
schieben, und addieren, dann hast du einen Fehler von 0%.

Anstatt der Division könntest du auch eine Multiplikation machen,
also 9000 mal 1600 = 219 (mit shift >32) und 2416 als Rest.

Das ganze wird nun immer wiederholt, wobei der neue Wert mit der
Referenz verglichen wird.
Da hast du ja genug Zeit für die Division, würde ich nicht so 
optimieren.

Den Vergleich will ich mit einer Subtraktion lösen (funktioniert genau
so auch schon bei einer anderen Sache)

Bitte steinigt mich nicht für meine evtl. unfachliche Art :)
Hilft das weiter?

Ich denke die Sache mit der Anpassung ist schon mal gut, wird's schonmal
schneller, weil ich nur 16bit Zahlen habe.
Hier fixed point multiplikation/division laut microchip app/note.

24 div 24 sind 420 Instruktionszyklen.
24 mul 16 sind  43 Instruktionszyklen.
40 div 24 sind 130 Instruktionszyklen.

Angenommen, du machst eine Division, sowie eine kombinierte 
Multiplikation/
division, wie sie in vielen Mathematisch orientierten Sprachen vorkommt,
dann sind das 593 Instruktionszüklen, also nicht mal 0.6 uS bei 4Mhz
internem Takt oder 0.3ms bei 8Mhz internem Takt.
Welche Zeitprobleme hast du ?
Einige C-Compiler unterstützen auch fixed point arithmetic.

von chris (Gast)


Lesenswert?

Achso, vergiss nicht den internen OSC eine Temperaturkompensation zu
verpassen, außer du kannst mit 5% Toleranz leben, das kann aber auch
schon zu viel sein. Externen Osc würde ich vermeiden, wegen der 
Vibrationen wegen.

von chris (Gast)


Lesenswert?

Achso, vergiss nicht den internen OSC eine Temperaturkompensation zu
verpassen, außer du kannst mit 5% Toleranz leben, das kann aber auch
schon zu viel sein. Externen Osc würde ich vermeiden, wegen der 
Vibrationen wegen. Die internet Temperaturkompensation macht man mit dem
WDT

von Fabian (Gast)


Lesenswert?

Danke für Deine ausführliche Antwort!

>Angenommen, du machst eine Division, sowie eine kombinierte
>Multiplikation/division, wie sie in vielen Mathematisch orientierten >Sprachen 
vorkommt

Damit meinst Du eine Ganzzahldivision und anschließend die 
Multiplikation des Restes mit irgendeiner Zahl (kann ich ja einfach 
durch Verschiebung nach links lösen) und erneute Division?

In welcher Form habe ich denn dann das edgültige Ergebnis der Division? 
Kann doch dann eine 32bit Zahl sein, bei der die oberen 16bit der ersten 
Ganzzahldivision entsprechen und die unteren 16bit der zweiten Division 
(des Rests) mit vorheriger Multiplikation entsprechen, oder?

An die Temperaturkompensation hätte ich nie gedacht. Aber brauch ich 
die, wenn mein PIC über einen externen Quarz getaktet wird?

Grüße Fabian

von chris (Gast)


Lesenswert?

Du wolltest ja eine Ganzzahldivision, und danach eventuell
mit Fixed Point Float weiterarbeiten, soweit ich dich verstanden haben, 
und dann eine Multiplikation, sowie eine erneute Division.
Aber eigentlich brauchst du es einfacher.

im obigen Beispiel
>24 div 24 sind 420 Instruktionszyklen.
Resultat ist 48 Bit, habe aber angenommen, 24bit reichen als Resultat 
aus
Du kannst es als 16.8 fixed point Zahl sehen, oder auch als 8.16 oder 
12.12
wie du es besser brauchst. Meiner Meinung nach solltest du hier auch die
Möglichkeit einbauen, eines eventuellen Shifts, um die bits auszuwählen,
aufgrund der Bauart, ein Prescaler kann auch dasselbe bewirken, oder / 
und
einen Offset dazuzählen. Nur so als Anregung.
Ich persönlich würde zwischen Periode zählen und Impulse zählen 
automatisch
umschalten, aber das ist eine andere Baustelle.
>24 mul 16 sind  43 Instruktionszyklen.
resultat 40 bit
>40 div 24 sind 130 Instruktionszyklen.
resultat 16bit
Wenn du 24 Bit resultat brauchst, dann mache 40 div 16.

die 2te sowie dritte Operation ist dieser besagte Operator, da du
sonst 40bit Zahlen haben müsstest.
Wenn du nur ein 24bit als Result der Multiplikation hast, und danach
eine Division machst, dann kann das Resultat falsch sein, da du nicht
mit der erforderlichen Genauigkeit rechnest.

Ich habe als Grundlage die APPL NOTE AN617 hergenommen, die 17c ist
ein Pic mit integriertem Multiplizierer, wie auch der 18f. Der Code
läuft auch im 18f, muß eventuell nur ein bisschen angepasst werden.

Externer Osc. Erstens wirst du dich schwer tun, einen OSC zu finden,
der für automotive temperature range freigegeben ist. Zweitens gibt
es da Probleme wegen der Vibrationen und dem Quarz, welche nicht ohne
sind. Wenn du es nicht umbedingt brauchst, was ich derzeit nicht sehe,
würde ich dir den internen OSC nahelegen. Musst im Datenblatt nachsehen,
welche Toleranz er hat, warscheinlich 4%, aber bei den extendet 
temperaturangaben nachsehen, sonst sind die 2% angegeben, welche aber 
nur
unter consumer temperature gelten. Ausrechnen, ob diese Tolleranz das 
Ergebnis verfälscht, oder innerhalb der vorgesehenen hysterie ist.

von chris (Gast)


Lesenswert?

Mit der WDT Calibration bekommst du es besser als 0.5% hin.

von Fabian (Gast)


Lesenswert?

>ein Prescaler kann auch dasselbe bewirken, oder / und einen Offset >dazuzählen. 
Nur so als Anregung.
Stimmt, ich kann das vorher Anpassen, sodass (im interessanten Bereich) 
eine Ganzzahldivision mit der Genauigkeit ausreicht.
Da muss ich mich halt jetzt endgültig mal entscheiden, welcher Weg es 
werden soll.

>Ich persönlich würde zwischen Periode zählen und Impulse zählen automatisch
>umschalten, aber das ist eine andere Baustelle.
Dann kommen allerdings auch wieder Rechungen dazu, um die Werte aus 
Impuls und Periodenmessung zu vergleichen. Das wollte ich mir sparen.

>Resultat ist 48 Bit, habe aber angenommen, 24bit reichen als Resultat
>aus. Du kannst es als 16.8 fixed point Zahl sehen, oder auch als 8.16 oder
>12.12 wie du es besser brauchst.
Hier hab ich mein Verständnisproblem: Wie kann das Resultat denn fixed 
Point sein, wenn es eine Ganzzahldivision ist. Halt, es steht ja Fixed 
Point Routine... Aber was ist dann der Rest?

von Chris S. (schris)


Lesenswert?

>Dann kommen allerdings auch wieder Rechungen dazu, um die Werte aus
>Impuls und Periodenmessung zu vergleichen. Das wollte ich mir sparen.

Wenn die Impulsmessung Fehlschlägt, dann auf Periodenmessung umschalten.
Impulsmessung ist gültig, wenn mehr als ??? Impulse je Zeiteinheit 
ankommen. Keine Vergleiche, sondern das wird Kalibriert, und ist 
eigentlich
vom Sensor dann abhängig, was verwendet wird. Könnte auch im EEprom 
gespeichert werden, was zu nehmen ist und beim Setup ausgemessen werden,
sprich zuerst Impulsmessung alle Gänge durchmachen, dann mit 
Periodenmessung, und besseres Ergebnis gewinnt.

Ein Vorschlag. Mach mal alles mit Float oder Double, und dann optimiere
ich dir den Code. Aber bitte PM.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.