Moin,
Ich bin gerade dabei ein Programm für den Atmega8 zu schreiben, welches
eine gemessene Temperatur auf einer 7-Segment-Anzeige ausgibt. Sprich
ein Thermometer ;-)
Ich hab auch alles mehr oder weniger hinbekommen (soweit ist das mit dem
AVR Studio simulieren kann), nur bei der Umrechnung des ADC-Wertes in
die Temperatur hab ich Probleme.
Zuerst hab ichs mit ner Formel probiert, das hat aber nicht geklappt. Da
hat er mir immer 000 ausgegeben.
Dann hab ich mir ne Liste zusammengestellt, die für jeden ADC-Wert die
Temperatur enthält. Das klappt allerdings nur, wenn ich den ADC-Wert als
Zahl per Hand eintrage, nicht aber, wenn ichs per Variable versuche.
Hier der Ausschnitt aus meinem Programm:
Wenn ich in der letzten Zeile T = tlist[500] schreibe, dann klappt das.
So wies jetzt da steht, bekomme ich, wenn die einzelnen Zahlen angezeigt
werden sollen, die Fehler:
AVR Simulator: Stack Overflow at 0x0090
AVR Simulator: Stack Overflow at 0x00e1
Woran kann das liegen, dass das mit Zahlen funktioniert, nicht aber mit
ADC?
Gruß
koellsch
rechne doch mal nach: int hat 2 byte, deine liste hat 1023 einträge...
->das kann nicht gut gehen.
warum es per "hand" funktionieren könnte:
gcc optimiert dein array weg: da der berechnete wert ja schon zur
compilezeit bekannt ist und das riesen-array nicht gebraucht wird.
verrate doch mal deine formel. eventuell können wir sie so umstellen,
dass sie funktioniert. 0 als Ergebnis klingt verdächtig nach einem
integer Rundungsfehler, weil du eine Weil ein Zwischenergebnis zu 0
gerundet wird.
Was Anonymous (Gast)
da schreibt ist erst mal vernünftig. Solange es keinen wirklich guten
Grund gibt, ist diese Tabelle nicht unbedingt der Weisheit letzter
Schluss.
Schon alleine aus folgender Überlegung heraus:
Deine Tabelle umfasst 1024 int Werte (ich hab jetzt nicht nachgezählt,
ob es wirklich 1024 sind). Ein int benötigt 2 Bytes Speicher. Deine
komplette Tabelle benötigt daher 2KByte SRAM-Speicher. Der Mega8 hat
aber nur 1KByte!
Hab mir schon gedacht, dass das was mit dem Speicher zu tun hat.
Meine Formel sieht folgendermaßen aus:
T =
-(0.668*(10^(-2))*(2.169*(10^9)*adc-3.511*(10^12)+2*sqrt(-3.145*(10^17)*
(adc^2)-8.278*(10^20)*adc+2.165*(10^24))))/(78125*adc-1.265*(10^8))
für adc = 0 kommt -30° und für adc = 1024 100° raus. Das soll auch so
sein. Die Variable adc ist als uint16_t deklariert und nimmt die Werte
von ADC an.
Der Rest bleibt so.
Was die Deklarierung von Variablen betrifft bzw. die einzelnen Typen, da
hab ich eh nicht so die große Ahnung von.
Gruß
koellsch
Dazu kommt, daß das Array als automatische Variable auf dem Stack
angelegt wird.
Da es sich hier um Konstanten handelt, ist es sinnvoll, das Array im
Flash-ROM abzulegen. Dafür ist beim AVR der pgmspace.h - Kram
erforderlich, bei von-Neumann-Maschinen würde ein schlichtes const
genügen.
Wenn du mir jetzt noch sagst, wie ich das anwende...
Scheint ja mit #define PROGMEM _ATTR_PROGMEM_ zu funktionieren, aber
ich weiß nicht, wie ich das anwenden soll.
Wäre aber trotzdem nicht verkehrt, wenn irgendwer die Funktion zum
Laufen kriegen würde.
Der erste (und wahrscheinlich wichtigste) Punkt in deiner Formel ist:
^ ist nicht die Potenzfunktion, sondern bitweises XOR
Was du suchst nennt sich pow() und steckt in math.h (genauso wie auch
sqrt)
Wo hast du die Formel her?
Da scheinen sich einige Fehler beim Abschreiben eingeschlichen zu haben
T =
-( 0.668*(10^(-2))* // 10 ^ -2 ergibt 0.001
(2.169*(10^9)*adc // 10 ^ 9 ergibt aber schon 1000000000
-
3.511*(10^12) // 10 ^ 12 ist irrsinnig hoch, 12 Nullen
+
2*sqrt(-3.145*(10^17)*(adc^2) // und 10 ^ 17 erst
-
8.278*(10^20)*adc
+
2.165*(10^24)
)
)
) / (78125*adc-1.265*(10^8)) // 10^8
vom Zähler bleibt im wesentlichen nur 2.165 * 10^24 übrig. Im Vergleich
zur Größe dieser Zahl, sind alle anderen Terme nur Peanuts.
Im Nenner steht im wesentlichen -1.265 * 10^8. Dass von 100 Millionen
noch 78125*adc abgezogen wird, kann vernachlässigt werden. selbst wenn
adc den Wert 1024 hat, bleiben immer noch -20 Millionen übrig.
Fazit. Mit der Formel dürfte einiges nicht stimmen.
Deine Tabelle sieht interessant aus. Allerdings wird Dich jeder
auslachen, wenn Dein Thermometer -300°C anzeigen können soll.
Ich bezweifle auch, daß es überhaupt Sensoren gibt, die -273 ... +1000°C
abkönnen.
Es gibt haufenweise Sensoren-Datenblätter und da stehen der
Arbeitsbereich und auch die richtige Formel drin.
Da Tiefsttemperatursensoren nicht billig sind, nimmt man da auch keinen
10Bit-ADC mehr sondern 16..24Bit.
Peter
Das sehe ich genauso wie Karl Heinz.
Wenn dein Temperatursensor dennoch ein solch seltsames Verhalten über
den gesamten Temperaturbereich aufweist, würde ich dir zunächst einmal
empfehlen zu schauen welche Kurvenform da rauskommt. Selbst wenn das
Verhalten über den gesamten Bereich nicht linear ist, so könntest du in
bestimmten Bereichen eine erste lineare Annäherung vornehmen. Soll
heißen, von z.B. ADC=0 bis ADC=75 gilt ungefähr f(ADC)=-30 * 0,4*ADC für
ADC=71 bis ADC=223 gilt ungefähr f(x)=0,7*ADC und so weiter. So musst du
nicht so viele Daten hinterlegen und der Rechenaufwand sinkt erheblich,
nur eine Gleitpunkt-multiplikation. Außerdem wird der Code leichter
lesbar:
if(ADC<75) .....
else if(ADC<223)...
usw.
Versuchs mal mit dem Ansatz.
Ups Schreibfehler! Soll natürlich
ADC=0 bis ADC=75 gilt ungefähr f(ADC)=-30 + 0,4*ADC
ADC=71 bis ADC=223 gilt ungefähr f(x)=0,7*ADC und so weiter
heißen.
Erstmal danke für die ganzen Antworten.
Also, der Temperaturfühler (KTY81, welcher genau das ist, weiß ich
nicht, da ich nur die Software mache) ist vorgegeben und kann nicht
verändert werden.
Die Temperaturwerte in der Tabelle sind so groß, weil das die Temperatur
* 10 ist. Sprich die geht von -30 bis 100 in 1025 Schritten. Und das ist
ja realistisch.
Die Formel kommt durch ne Regressionsanalyse zustande. Sprich ich hab
folgende Gleichungen:
1. R(t) = 2000,1 + 15.751*(t-25) + 0.03745*(t-25)^2
2. adc/1024 = -4097 + 0,5187 + 0,5187*4097*((2000+R(t))/2000*R(t))
1 nach t aufgelöst, 2 nach R(t) und eingesetzt. Das ganze mit Maple
durchrechnen lassen und so weit wie möglich vereinfacht (durch Maple).
Also mit pow() anstatt ^ (was ich natürlich nicht wusste, danke Karl
Heinz) klappts fast, für den adc 500 rechnet er 16, anstatt 12 aus.
Evtl. kriegt man die Formel ja auch noch einfacher.
@ Florian K. (koellsch)
>1 nach t aufgelöst, 2 nach R(t) und eingesetzt. Das ganze mit Maple>durchrechnen lassen und so weit wie möglich vereinfacht (durch Maple).
Früher(tm) hat man das BESTENFALL mit einem Polynom 2. Grades angenähert
und gut ist. Heute muss jeder Fliegenschiss mit CAD modeliert werden . .
.
Ok, ich hatte da noch n Fehler drin, jetzt passts mit der Temperatur.
Die fertige Formel sieht so aus:
T = -(0.00668 * (2.169 * pow(10, 9) * adc - 3.511 * pow(10, 12) + 2 *
sqrt(-3.145 * pow(10, 17) * pow(adc, 2) - 8.278 * pow(10, 20) * adc +
2.165 * pow(10, 24))))/(78125 * adc - 1.265 * pow(10, 8))
Mag kompliziert sein, klappt aber. Und ich denke, ich werde das jetzt so
benutzen.
Vielen Dank an alle für die Hilfe.
@Falk Brunner
Wenn mans kann, dann geht das bestimmt. Ich bin zwar recht gut in Mathe,
aber das hab ich dann lieber doch mit Maple gemacht. Wie gesagt, falls
die Formel noch einfacher geht, dann bin ich für jede Hilfe dankbar.
Aber es läuft ja...
Es handelt sich bei der ganzen Aktion um ein Projekt und da haben wir
den Auftrag bekommen, die Formel so zu erstellen. Auch wenn man mit
Kanonen auf Spatzen schießt.
Hallo Florian
Wenn man den KTY81 nicht an einer Stromquelle, sondern an z.B. 5Volt
über einen Vorwiderstand von 5600 Ohm betreibt, wird die Kurve nahezu
linear. Damit habe ich bisher sehr gute Erfahrungen gemacht, und zwar
Bessere, als den Fühler mit Stromquelle zu betreiben.
Obwohl man die Kurve im Bereich von 0 - 50°C als nahezu linear annehmen
kann, hab ich hier mal die Korrektur per Polynom 2ter Ordnung
beschrieben. Die Berechnung findet in (long) statt.
int t = (long)-adc*adc/30941 + (long)1412*adc/1000 - 1554;
Im Ganzzahl-Ergebnis steckt eine Nachkommastelle.
Nachteil:
Der Messbereich erstreckt sich von 910 - 1886mV. Schaltungstechnisch
könnte dies jedoch noch optimiert werden.
Gruß, Bernd
Hi Bernd,
da ich mich mit der Software beschäftigt hab, kenn ich mich nicht so
genau mit der Hardware aus, aber ich denke, der KTY ist über ne
Messbrücke und nem OP mit dem atmega verbunden. Eagleschaltplan häng ich
mal an.