Die Code-Zeilen mit double-Arithmetik kosten ganze 1500bytes. Hat jemand eine Idee zu einer codesparenden und trotzdem genauen Alternative? double adc2Cel(long adcval){ // 10bit ADC > °C 1.5kByte - ganz schön viel #define R25_ntc 10000 // NTC Nennwiderstand #define B_ntc 3988 // NTC-Empfindlichkeit #define Tn_ntc 298 // Bezugstemperatur #define R 10000L // Gegenwiderstand double s=0; long int rntc,tntc; if (adcval<10) adcval=10; // sinnvolle Grenzwerte if (adcval>1010) adcval=1010; // -47 bis 182 °C rntc = ((long)adcval*R)/(1024-adcval); // Rntc s = B_ntc + log((double)rntc/R25_ntc)*Tn_ntc; tntc= (long)1000*B_ntc*Tn_ntc/(long)s; s= (tntc-273150)/1000; return s; // °C } Dank euch! Bernd
Bernd Kohlstrunk schrieb: > Hat jemand eine Idee zu einer codesparenden und trotzdem genauen > Alternative? Dann schreib doch mal auf, was du rechnen möchtest. Oder soll das jetzt jeder hier aus deinem Code reverse-engineeren?
Klarer Fall für eine Wertetabelle. Einfach in Excel (oder LibreOffice) die erwarteten ADC Werte für ganzzahlige Temperaturwerte ausrechnen und als Array (im Flash) speichern. Der Code muss nur die Position in der Tabelle finden, für die Tabelle[x] <= adc_wert <= Tabelle[x+1] gilt, und dann linear interpolieren für einen recht genauen Temperaturwert. Die Tabelle bräuchte allerdings für 230 Temperaturwerte 460 Byte Speicherplatz. Kannst Du den Bereich weiter einschränken? Können Temperaturen >100°C überhaupt auftreten?
Ja Entschuldigung! Es geht um die T-R Beziehung von Heißleitern: T= B*Tn / (B + ( ln (Rt/Rn) ) * Tn) Der Kern der Frage ist, wie man long-Variablen logarithmieren kann ohne arg an Genauigkeit zu verlieren. Eine "eingebaute" Funktion gibt es offenbar dafür nicht.
Wie wäre es mit Festkommaarithmetik? Ich vermute du verwendest einen 8bit Controller (AVR z.B.) und die float-Lib sowie die float/double Rechnerrei frisst schon alleine ziemlich speicher. Also du verrechnest alle Variablen welche eine Kommastelle haben mit einem festen Faktor. Je nach dem wie groß die Auflösung sein soll: 10, 100, 1000. Datentyp muss dann aber am besten int32 oder int64 sein. Dann solltest du die Rechnung zerlegen um zu schauen ob du irgendwo eine integer-Überlauf bekommst. Wenn ja muss du die Berechnung in Teilschritte erledigen.
> long-Variablen logarithmieren
Am schnellsten geht wohl auf die Basis 2 umrechnen und schieben.
Noch schneller geht aber Jims Vorschlag, vorberechnete Werte
interpolieren.
Bernd Kohlstrunk schrieb: > Die Code-Zeilen mit double-Arithmetik kosten ganze 1500bytes. Na und? Reicht der Speicher nicht? Falls Dir sechs Stellen Auflösung reichen, rechne doch mit 'float' ;-)
> ... , rechne doch mit 'float' ;-) Die 8bit AVR Mikrocontroller rechnen eh nur mit "float" auch wenn man "double" im C-Programm schreibt.
Helmut S. schrieb: > Die 8bit AVR Mikrocontroller rechnen eh nur mit "float" auch wenn man > "double" im C-Programm schreibt. Von Hause aus rechnen sie nur mit 8 Bits. Aber man kann damit auch double Berechnungen machen, wenn man den passenden Compiler verwendet.
hab was gefunden. Hier wurde das Thema mit dem Logarithmus schon mal beackert: Beitrag "Code für Logarithmus gesucht" Danke für alle Reaktionen. Bernd
Dank des Beitrages: Beitrag "Algorithmus für Logarithmus-Bererechnung?" bin ich zu einer Lösung gekommen, die auf meinem Tiny 1.5kbyte spart. Die feste Skalierung in ln_m() ist natürlich ein Kompromiss. Im mittleren Temperaturbereich ist aber eine Auflösung von 0.1 °C gut erreichbar. long adc2_mC(long adcval){ // 10bit ADC > T in milli°C Einheiten #define B_ntc 3988L // NTC-Empfindlichkeit in K #define Tn_ntc 298L // 25°C Bezugstemperatur in K #define Rn_ntc 10000L // 10k Widerstand bei Bezugstemperatur #define Tzero 273150L // mK #define R 10000L // 10k Gegenwiderstand der Halbbruecke long rntc,tntc,ne; if (adcval<10) adcval=10; // 194.1°C if (adcval>1010) adcval=1010; // -47.3°C rntc = ((long)adcval*R)/(1024-adcval); // Rntc 1/1000 genau ne = B_ntc + (ln_m((rntc*1000)/Rn_ntc)*Tn_ntc)/1000; // Nenner in K tntc= (1000*B_ntc*Tn_ntc)/ne; // mK= mK*K/K return (tntc-Tzero); // mC } long ln_m(long x){// In/Output als milli-Zahlen von 10(=0.01)..100000(=100) #define SCALE 9 // Erweitern mit 2^SCALE und später SCALE abziehen long lval; // (Logarithmengesetz) uint32_t rval; lval=(x*(1<<SCALE))/1000; // if (lval>0xFFFF) lval=0xFFFF; if (lval<1) lval=1;// Begrenzen rval=fixlog2((uint16_t)lval); // Aufruf mit Typ-Anpassung lval=(rval >> 16)*1000000 + ((rval & 0xffff) * 15625 >> 10) - SCALE*1000000;// ppm genau -9...7 lval=((lval/100)*6931)/100000; // Umrechnung log2->ln -6.238 bis 4.852 return lval ; // Ausgabe als milli-Zahl } uint32_t fixlog2(uint16_t x) { // Input 0...64k Output: Fixkomma 16.16 uint32_t q; // 2.30 uint16_t y = 0; // 0.16 uint8_t intpart = 15, i; // 8.0 while(!(x & 0x8000)) {x <<= 1;intpart--;} // die Ganzen ermitteln for(i=0; i<16; i++) { q = (uint32_t)x * x; // x: 1.15, q: 2:30 y <<= 1; if(q & (uint32_t)1 << 31) // q >= 2.0? y |= 1; // 1-Bit -> y, q *= 2, q: 1.31 else q <<= 1; // 0-Bit -> y, q: 1.31 x = q >> 16; // x = q } return (uint32_t)intpart << 16 | y; // return: 16.16 } // Quelle: Beitrag "Algorithmus für Logarithmus-Bererechnung?"
Bernd K. schrieb: > bin ich zu einer Lösung gekommen, die auf meinem Tiny 1.5kbyte spart. Immerhin wissen wir jetzt, daß es sich um einen AVR handelt. Da aber auch schon kleine ATtiny mit 8 kB ROM verfügbar sind, sehe ich keinen Sinn darin, den notwendigen Code einsparen zu wollen. Vergleicht man die gezeigten Quelltexte, geht bei der nun favorisierten Lösung die Les- und Wartbarkeit völlig in die Hose. Welche Fehler eine Programmänderung mit sich bringt, ist nicht abzusehen. Für mich ist das eine schlechte Lösung!
Laß halt den Logarithnmus weg. Alles was du brauchst steht schon in deinem Code. adcval=10 = 194.1°C adcval=1010 = -47.3°C Und daraus machst du eine einfache Gleichung. Beispiel: http://www.mikrocontroller.net/attachment/127684/msp430x2xxTemperatur.c
Bernd N schrieb: > adcval=10 = 194.1°C > adcval=1010 = -47.3°C > > Und daraus machst du eine einfache Gleichung. Die NTCs sind leider alles andere als linear. So einfach geht es IMHO nicht.
m.n. schrieb: > Na und? Reicht der Speicher nicht? > Falls Dir sechs Stellen Auflösung reichen, rechne doch mit 'float' ;-) Du Scherzbold! Der TO fummelt mit nem AVR herum und dort gibt es sowieso kein double, sondern double wird einfach auf float heruntergebrochen. Mein erster Tip wäre, einen simplen PIC16Fxxx zu nehmen, denn für diese µC hatte ich hier schon mal ne Gleitkomma-Lib gepostet. Mein zweiter Tip wäre, sich mal die Steinhart-Hart-Formel aus dem Netz zu ziehen und zu benutzen. 1,5K für ne GK-Lib ist m.E. zwar relativ viel (wohl schlampig implementiert), aber allemal besser als irgendwelche Integer- Fummel-Varianten, die vor lauter internen Überläufen mehr schlecht als recht funktionieren. Hatte da nicht mal jemand vor einiger Zeit geschrieben, daß man Gleitkomma bei µC seiner Ansicht nach überhaupt nicht braucht??? W.S.
Erst sollte man mal das Problem spezifizieren und nicht gleich die Loesung vorwegnehmen. Also, man moechte einen NTC im Bereich von -47 bis 182 °C betreiben, glaub ich gleich mal nicht. Und dann noch 0.1 Grad anzeigen wollen. Aha. Mit einem 10bit ADC. Sonst noch was ? Woher kommen die -47 Grad ? Von einem Peltier ? Da muss man gut sein. Einen 2 stoeckigen und sehr gute Isolation. Nicht trivial. Die 180 Grad ? Ein Epoyk NTC ist nur bis 150 oder so spezifiziert. Mit 10 bit? Nie. NIE ! Schreib mal eine Simulation der Werte, und der Aufloesung auf dem PC. Aus dem Bauch heraus sag ich mal, die 10 bit reichen nicht mal fuer 1 Grad Aufloesung. Die Genauigkeit lassen wir mal weg.
Jetzt Nicht schrieb: > Und dann noch 0.1 Grad anzeigen wollen. Aha. Mit einem 10bit ADC. War auch mein Gedanke. Die "Auflösung" kann der TE ja bei der Ausgabe haben, was die dann aber mit dem realen Wert zu tun hat ist eine ganz andere Frage.
W.S. schrieb: > m.n. schrieb: >> Na und? Reicht der Speicher nicht? >> Falls Dir sechs Stellen Auflösung reichen, rechne doch mit 'float' ;-) > > Du Scherzbold! > Der TO fummelt mit nem AVR herum und dort gibt es sowieso kein double, > sondern double wird einfach auf float heruntergebrochen. Wie ich sehe, hast Du den Scherz aber nicht verstanden ;-) Mit einem AVR kann man durchaus 'double' mit 64 Bit rechnen und die Lib dafür ist etwas größer als die für 32 Bit 'float'. Für mich bleibt es unsinnig, die Codegröße ohne Not 'optimieren' zu wollen. Auch die Ersatzlösungen brauchen Speicherplatz und haben hohes Fehlerpotential. Jetzt fehlt noch das Totschlagargument, float wäre viel zu langsam. Vorweg: das ist genau so unsinning wie falsch!
Ich rechne auch NTC, allerdings nur 3 mal pro Sekunde. Ich rechne die Linearisierung mit float, 32bit, aus folgenden Gruenden: - Ein approximierendes Polynom muesste fuer die gewuenschte Aufloesung und Genauigkeit 6.Ordnung sein. Der Linearisierungspunkt waere dann aber ohne Float nicht mehr frei waehlbar. - Ich verwende einen 24bit Wandler - ich moechte besser als 5mK Aufloesung, zum Regeln - der NTC ist spezifiziert als 1% - die absolute Genauigkeit ist vielleicht 2 Grad, begrenzt durch den NTC Alle nachfolgenden Berechnungen gehen dann mit 32 bit Integern.
Bernd K. schrieb: > Die Code-Zeilen mit double-Arithmetik kosten ganze 1500bytes. Wo ist das Problem, schon die 8-Pinner AVR haben bis zu 8kB Flash. Mußt Du unbedingt einen 6-Pinner verwenden?
Bernd K. schrieb: > Hat jemand eine Idee zu einer codesparenden und trotzdem genauen > Alternative? Ich würde eine Lookuptabelle und lineare Interpolation vorschlagen. Definiere "genau". Bernd K. schrieb: > if (adcval<10) adcval=10; // 194.1°C > if (adcval>1010) adcval=1010; // -47.3°C Vergiss diese Kommastellen ganz schnell. Vermutlich hat nämlich dein NTC schon 5% Fehler...
SMT-160-30-18 Nicht gerade günstig, aber ne super Auflösung.
Lothar Miller schrieb: > Vergiss diese Kommastellen ganz schnell. Vermutlich hat nämlich dein NTC > schon 5% Fehler... Wobei das nur der einfachere Teil des Problems ist (daß man mit einem anderen Sensor verbessern kann). Wo die Temperatur gemessen wird ist ein viel größeres Problem. Selbst innerhalb eines festen Körpers sind Temperaturgradienten von mehreren Grad, die dann auch noch je nach Wetterlage unterschiedlich sind, nichts ungewöhnliches. Statt aufwändiger Rechnungen kann man auch die Nachkommastellen aus /dev/random lesen. MfG Klaus
Vielen Dank für die Hinweise! Das schöne am NTC ist ja seine Nichtlinearität. Im konkreten Einsatz als Drainback-Steuerung einer Thermischen Solaranlage ist das sehr praktisch. Außerdem werden dort üblicherweise nur PT100 oder NTCs verbaut. Die Anforderung bestand aus zwei Punkten: 1. genaue Messungen um die 0°C (Frostschutzfunktion) und 2. über den ganzen real auftretenden T-Bereich von sagen wir mal -30 bis +150°C überhaupt Meßergebnisse zu bekommen und keinen Over-/Underflow. LUTs sind sicher ein pragmatischer Ansatz. Der Weg über den Festpunkt-Logarithmus funktioniert aber auch. OK, Profis nehmen einfach einen größeren Prozessor und haben die Probleme nicht. Hab mich aber auf den ATtiny85 mit Funk-Interface HM-TRP eingeschossen. Ich finde es immer wieder beeindruckend, was man aus dem Kistchen rausholen kann. Dank dem Bootloader von Peter Dannegger fürs Update via Klinkenbuchse und Debugging via Soft-UART (beides 1-Wire), kommt man trotz der knappen Resourcen schnell zum Erfolg. Man darf nur nicht auf die Idee kommen, dicke Libs einzubinden. 8 Pins sind wenig, aber es reicht mit ein Paar Kniffen für die Ansteuerung/Auslesung von: 1x Ventil 1x Pumpe 1x NTC 1x Messung von Pumpen oder Ventilstrom 3 Tasten 2 LEDs 1x RS232 (1-Wire) 1x Servomotor mit Stromüberwachung ... und der Resetpin ist immer noch aktiv :-) Wen es interessiert, bitte melden.
Jetzt Nicht schrieb: > - Ein approximierendes Polynom muesste fuer die gewuenschte > Aufloesung und Genauigkeit 6.Ordnung sein. Eh? - Der Fehler der Approximination nimmt bei höheren Graden zu - Die Auflösung ist unabhängig vom Grad - Üblicherweise erhöht man die Anzahl der Stützstellen bei konstanten Grad (Siehe Splines, Teillinearisierung, etc) > Der > Linearisierungspunkt waere dann aber ohne Float nicht > mehr frei waehlbar. Quatsch. Natürlich ist er genauso frei wählbar. Float stellt einen Teilbereich der reellen Zahlen (also p/q mit p und q aus den Ganzen Zahlen) dar. Den geforderten Bereich -50 bis +200 lässt sich hervorragend mit < 0.01er Granularität darstellen. Bei konstantem q bleibt reine Integer Arithmetik. Bei gleicher Speichergröße, lässt sich hier sogar eine kleinere Granularität erreichen und damit die Stütz- und Interpolationsstellen freier wählen.
>> Der > Linearisierungspunkt waere dann aber ohne Float nicht > mehr frei waehlbar. >Quatsch. Aber nicht im Feld als Parameter, denn dann wuerde man ja wieder float brauchen um das Polynom zu rechnen.
Jetzt Nicht schrieb: > Ich rechne die Linearisierung mit float, 32bit, aus > folgenden Gruenden: > Alle nachfolgenden Berechnungen gehen dann mit 32 bit > Integern. Das ist dann eigentlich egal, weil die float-LIB schon eingebunden ist und der Code nicht mehr nennenswert größer wird.
Jetzt Nicht schrieb: >>> Der >> Linearisierungspunkt waere dann aber ohne Float nicht >> mehr frei waehlbar. > >>Quatsch. > > Aber nicht im Feld als Parameter, denn dann wuerde man ja wieder float > brauchen um das Polynom zu rechnen. Nein, warum auch?
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.