Hallo,
ansich sollte das nicht weiter kompliziert sein - trotzdem komme ich
grad nicht weiter.
Ich messe über die Dauer einer C-Entladung einen Potiwert.
Die Entladung wird in der Timer1-OVF-ISR des ATtiny2313 gestartet.
Sinkt die Spannung am C unter die 0.25V externe Referenz des AC löse ich
damit ein Capture Event für den Timer1 aus.
In der Capture-ISR habe ich den Wert aus ICR1 ausgelesen und mir als PWM
ausgeben lassen. Aus der Pulsbreite für linken und rechten Potianschlag
habe ich errechnet, dass der Wertebereich den ICR1 annimmt zwischen
10500 und 35400 (mit etwas Luft) liegen muss.
Diese Grenzen möchte ich nun auf den Bereich 0-255 (also einen uint8_t)
skalieren.
Dazu habe ich mir überlegt, zunächst den unteren Grenzwert (10500)
abzuziehen. Die dann verbleibenden Werte zw. 0 und 24900 teile ich dann
durch einen Faktor x, den ich wie folgt berechnet habe:
24900 / x = 255
24900 = 255 * x
24900 / 255 = x
x = 97,64 ~= 98
1
// ..
2
3
volatileuint8_ticr_8_value=0;
4
volatileuint16_ticr_value=0;
5
6
// ...
7
8
ISR(TIMER1_CAPT_vect){// triggered via ACO change, used for timenet
9
10
// Catch value
11
icr_value=ICR1;
12
13
icr_8_value=(uint8_t)((icr_value-10500)/98);
14
15
}
Rauskommen tuen in 4 Stufen abgestufte Werte, also nicht das gewünschte
Ergebnis. Das ich den tiny2313 mit der Division schon ziemlich fordere
und vermutlich genau da das Problem liegt ist mir bewusst. Das sollte
auch noch optimiert werden.
Ist mein grundsätzlicher Einsatz zur Werteskalierung richtig? Was kann
ich verbessern, um auf fliessende 8-bit Werte zu kommen? Kurz: Wo ist
mein Fehler?
LG :)
> Ist mein grundsätzlicher Einsatz zur Werteskalierung richtig? Was kann> ich verbessern, um auf fliessende 8-bit Werte zu kommen? Kurz: Wo ist> mein Fehler?
Ja, grundsetzlich schon. In der Praxis sieht das ganze aber wie du schon
richtig erkannt hast noch mal anders aus.
Um das definitiv sagen zu können fehlen Infos:
Was ist der Prozessortakt? Wie oft tritt timer1capt auf?
Du wirst in interrupts Abstand von Divisionen nehmen müssen. Das dauert
ewig. Da du einen tiny hast, welcher wiederum keine Multiplikatoreinheit
besitzt und du 16bit Zahlen multiplizieren musst wird schon das allein
sehr lange dauern.
Das einzige, das sehr schnell geht, sind 2er Potenzen bzw
Multiplikationen und Divisionen mit dem Faktor 2. Das lässt sich einfach
durch rechts und links schieben realisieren. Es ist also ein bisschen
Hinrschmalz gefordert.
Ich habe mal ein bisschen gerechnet und die beste Näherung ist folgende:
y = (x shr 2)*5 shr 7
Thor
Hallo Alex,
vielen Dank für deine Hilfe.
> Um das definitiv sagen zu können fehlen Infos:> Was ist der Prozessortakt? Wie oft tritt timer1capt auf?
Takt kommt von einem 8Mhz Quarz. Das Capture Event tritt mit ungefähr
nach je ca. 4ms auf. Während man am Poti dreht weicht das dann ein wenig
ab.
Messen tu ich grundsätzlich mit 250Hz
> Du wirst in interrupts Abstand von Divisionen nehmen müssen. Das dauert> ewig. Da du einen tiny hast, welcher wiederum keine Multiplikatoreinheit> besitzt und du 16bit Zahlen multiplizieren musst wird schon das allein> sehr lange dauern.
Damit habe ich bereits gerechnet. Division und Multiplikation wollte ich
daher ja auch vermeiden. Mit 2er Potenzen kommt man bei dem Wertebereich
leider nicht weit. Ich hätte aber auch kein Problem, den Wert in der ISR
nur auszulesen und in der Mainschleife umzurechnen.
> Das einzige, das sehr schnell geht, sind 2er Potenzen bzw> Multiplikationen und Divisionen mit dem Faktor 2. Das lässt sich einfach> durch rechts und links schieben realisieren. Es ist also ein bisschen> Hinrschmalz gefordert.
Habe ich auch schon probiert, aber auch nicht mit besserem Ergebniss.
> y = (x shr 2)*5 shr 7
y ist 8bit, x ist 16bit? Ich probiers mal aus. ;)
Edit:
> Messen tu ich grundsätzlich mit 250Hz
Mhhhh, dann hast du für die Aufgabe 32000 Takte Zeit. Das reicht auf
jeden Fall. Bist du dir sicher, dass der interrupt auch sicher nicht
mehrmals/öfter auftritt?
Ansosnten liegt das Problem nicht in der Rechenzeit. Wie sieht es denn
mit deiner stack Ausnutzung aus?
Thor
y = (x shr 2)*5 shr 7
icr_8_value = (uint8_t) ((icr_value >> 2) * 5) >> 7
liefert 128 für 10500
und 345 für 35400
8 Bit ???
zieht man noch 128 ab, gibts 0...217. OK.
Einfacher, aber kaum ungenauer:
icr_8_value = (uint8_t) ((icr_value >> 7) - 82);
liefert 0 für 10500
und 194 für 35400
Hmm. Habe den Wertebereich gerade nochmal gecheckt (Werte abfragen und
LED toggeln...). Anscheinend habe ich mich verrechnet: Schient eher
zwischen >0 und <550 zu liegen. Weshalb, dass kann ich gerade noch nicht
nach vollziehen.
Ich hatte die Pulsbreiten vom Oszi abgelesen die 65.535 mal DutyCycle
(<1) genommen und gehofft so auf den richtigen Wert zu kommmen.....
Kanns dran liegen, dass ich den Timer auch noch fürs PWM nutze?
Momentan scheint ein shr 3 gut zu passen, damit bleibe ich leider in
einem recht tiefen Wertebereich.
Das erklärt gerade eine ganze Menge.
> Bist du dir sicher, dass der interrupt auch sicher nicht> mehrmals/öfter auftritt?> Ansosnten liegt das Problem nicht in der Rechenzeit. Wie sieht es denn> mit deiner stack Ausnutzung aus?
Das der nicht öfter auftritt ist sehr sicher. Zumindest wüsste ich
nicht, woher sich der C aufladen sollte wenn ichs nicht im OVF tue...
Wie krieg ich das mit dem Stack raus? Passt da nicht der GCC für auf?
Danke euch nochmal für die Hilfe und entschuldigt meinen Fehler...
LG :)
Habs jetzt so, passt ungefähr:
1
ISR(TIMER1_CAPT_vect){// triggered via ACO change, used for timenet
J. W. schrieb:> Ist mein grundsätzlicher Einsatz zur Werteskalierung richtig?
Das hängt auch von deiner Schaltung ab. Du misst eine Zeit t und willst
daraus einen Widerstand berechnen, d.h. du hast eine Funktion
R = R(t)
Ist R linear, dann ist eine lineare Skalierung wie vorgeschlagen
sinnvoll.
Ist R jedoch nicht linear (abhängig von deiner Schaltung) und hat R z.B.
logarithmische Anteile, dann wirfst du durch eine lineare Skalierung
evtl. die interessanten Bereiche weg, weil die Funktion nicht überall
die gleiche Steigung hat.
Zunächst wäre also zu klären, wie R(t) aussieht.
Alex S. schrieb:> Sicher um zwei nach rechts? Dann kiegst du bei 550 aber nur noch 7 bit> Auflösung.
Hey, du bist echt ne Hilfe. :) Hatte nen Denkfehler drin. Maximalwert
bei 10bit ist ja 1023 und nicht 512 :p. Also nur 1 nach rechts. Du hast
vollkommen recht.
> Ist R linear, dann ist eine lineare Skalierung wie vorgeschlagen> sinnvoll.
R ist ein linieares 10k-Poti. Lineare Skalierung ist also sinnvoll,
nicht nur aus aus Gründen der Rechenzeit ;)
Danke nochmals..
LG :)
> Hey, du bist echt ne Hilfe. :)
Weiß ich :P
Ein Tipp noch:
Ergänze deinen range check noch zur Sicherheit. Sonst gibts irgendwann
eine böse Überraschung:
1
ISR(TIMER1_CAPT_vect){ // triggered via ACO change, used for timenet
> Weiß ich :P
Und ich kann dich da nochmals bestätigen :p
An den Underflow hatte ich nämlich nicht gedacht, und direkt den Wert
für SPEED (GPIOR2) zu schreiben, statt noch zu rechnen ist auch schlau.
Ich für meinen Teil mache nach ca. 10h PC-glotzen jetzt mal ne Pause -
das wird ja langsam peinlich hier :p
Dank again, LG :)
J. W. schrieb:> Alex S. schrieb:>> Ist R linear, dann ist eine lineare Skalierung wie vorgeschlagen>> sinnvoll.>> R ist ein linieares 10k-Poti.
Das war nicht die Frage.
Die Frage ist, ob R linear mit der gemessenen Zeit t geht. Das ist
unabhängig davon, ob das Poti linear oder logarithmisch oder sonstwas
von einer Winkel anhängt. Letzteres ist ja nur ne mechanische
Eigenschaft, spiegelt sich aber nicht in R(t) wieder.
Tach Johann,
du hast natürlich recht. Die RC Ladekurve ist natürlich logarytmisch.
Das sollte man berücksichtigen. Ein logarytmisches Poti wäre hier von
Vorteil gewesen.
Man hat aber auch genügend Rechenzeit über um das in software zu tun.
Thor
Ah, jetzt verstehe ich. Ich habe die ausgelesenen Werte mittlerweile in
eine Geschwindigkeitssteuerung eingearbeitet. Ebenfalls habe ich sie
wieder testweise über PWM "ausgegeben". Das Tastverhältnis ändert sich
durchaus linear, zumindest macht es für mich auf dem Oszi den Anschein,
dass das weitgehend Linear ist.
Auch die Geschwindigkeitssteuerung ist damit recht intuitiv - ich bin
zufrieden.
LG :)