PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Temperaturmessung und Anzeige auf Display



Mr Bean
02.12.2007, 14:48
Hallo

Ich möchte mir gerade eine Temperaturmessung bauen. Bin auch schon soweit dass ich die Temperatur messen kann mit dem AD-Wandler. Wenn ich mir den binären Wert auf meine 8 LED`s ausgeben lasse und dann mit der Hand am Arm umrechne passt die Messung auch schon. Ich möchte die Temperatur nun auf einem zweizeiligen Display ausgeben. Aber das bekomme ich nicht hin. Sehe meistens nur kyrillisch... :-k
Nach einigem lesen hier im Forum und auf mikrocontroller.net weiß ich nun dass ich zum Umrechnen wohl die Funktion itoa brauche, aber wirklich hinbekommen hab ich das so nicht. Vielleicht könntet ihr mal über meinen Code drüber schaun und mir einen Tip geben.


#include <avr/io.h>
#include <inttypes.h>
#include <stdlib.h>
#include <lcd.h>
#define F_CPU = 4000000UL;


uint16_t readADC(uint8_t); //Prototyp von readADC

int main(void)
{
uint16_t result = readADC(0); //Auslesen der analogen Spannungen an Pin 0,
// also ADC0. In result steht das Ergebnis.
result /=5; //result durch 5 teilen um richtiges Ergebnis zu bekommen
DDRD = 0xff; //PORT D als Ausgang definieren (Hier sind 8 LED`s angeschlossen)
PORTD = result; //Den Digitalen Wert mit Hilfe der LED`s anzeigen
DDRB = 0xff;
lcd_init(LCD_DISP_ON_CURSOR); //LCD initialisieren
lcd_gotoxy(0,0);
lcd_puts ("Temperatur:");
char Buffer[20];
itoa(result,Buffer,10);
lcd_gotoxy(0,11);
lcd_puts('Buffer'); //ADC Wert auf Display ausgeben
return 0;
}




uint16_t readADC(uint8_t channel)

{
uint8_t i;
uint16_t result = 0;

// Den ADC aktivieren und Teilungsfaktor auf 32 stellen
ADCSRA = (1<<ADEN) | (1<<ADPS0) | (1<<ADPS2);

// Kanal des Multiplexers waehlen (ADC 0)
ADMUX = (!(1<<MUX0)) | (!(1<<MUX1)) | (!(1<<MUX2));

// interne Referenzspannung verwenden (also 2,56 V)
ADMUX |= (1<<REFS1) | (1<<REFS0);

// Den ADC initialisieren und einen sog. Dummyreadout machen
ADCSRA |= (1<<ADSC);
while(ADCSRA & (1<<ADSC));

// Jetzt 3x die analoge Spannung and Kanal channel auslesen
// und dann Durchschnittswert ausrechnen.
for(i=0; i<4; i++) {
// Eine Wandlung
ADCSRA |= (1<<ADSC);
// Auf Ergebnis warten...
while(ADCSRA & (1<<ADSC));

result += ADCW;
}

// ADC wieder deaktivieren
ADCSRA &= ~(1<<ADEN);

result /= 4;

return result;
}



Ich verwende ein AtMega 8 und ein KTY Element mit 1k Vorwiderstand. Aber das funktioniert soweit ich das beurteilen kann auch schon.
Vielen Dank schonmal!

Grüße!!

Bean

Hubert.G
02.12.2007, 17:32
Wenn du für die LCD-Temperatur lcd_gotoxy(11,0); schreibst, dann kann es funktionieren, mit 0,11 schreibst du in die 11. Zeile. Sonst habe ich nichts angeschaut.

Hubert.G
02.12.2007, 17:41
int main(void)
{

DDRD = 0xff; //PORT D als Ausgang definieren (Hier sind 8 LED`s angeschlossen)

DDRB = 0xff;
lcd_init(); //LCD initialisieren


for(;;){
uint16_t result = readADC(0); //Auslesen der analogen Spannungen an Pin 0,
// also ADC0. In result steht das Ergebnis.
result /=5; //result durch 5 teilen um richtiges Ergebnis zu bekommen
lcd_gotoxy(0,0);
lcd_puts ("Temperatur:");
char Buffer[20];
itoa(result,Buffer,10);
lcd_gotoxy(11,0);
lcd_puts(Buffer); //ADC Wert auf Display ausgeben
PORTD = result; //Den Digitalen Wert mit Hilfe der LED`s anzeigen
}
}


Ändere noch dein main so in etwa ab, ohne Endlosschleife tut sich sonst nicht viel sinnvolles.

Mr Bean
02.12.2007, 21:26
Mh komisch, also ich bin der Meinung dass wenn ich LCD_gotoxy (0,11) schreibe geht der Courser in die oberste Zeile an die 11te Stelle und da soll eigentlich auch das Ergebniss hingeschrieben werden, weil davor "Temperatur:" steht. Darum hab ich das so geschrieben. Die for-schleife habe ich gerade ausprobiert (Kein anderes Verhalten erkennbar), warum soll sich ohne die nicht viel sinnvolles tun? Die Wandlung hat bereits vorhin getan (ohne schleife). Der Wert hat sich auch geändert wenn ich den Temperaturfühler erwärme. Also warum soll sich da nichts sinnvolles tun?
Mein Problem liegt im Moment eher an der Umwandlung des 10Bit Wertes in eine dezimale Kommazahl mit einer Kommastelle.
Denke der Wert in "result" ist im Moment einfach noch zu groß um vom Display verarbeitet werden zu können. Darum auch die kyrillischen Zeichen. Aber kann mich auch irren.

Grüße!!

Bean

T.J.
03.12.2007, 08:17
Mh komisch, also ich bin der Meinung dass wenn ich LCD_gotoxy (0,11) schreibe geht der Courser in die oberste Zeile an die 11te Stelle


Jo, ist ja auch richtig so. Aber (11,0) nicht ;)

Hubert.G
03.12.2007, 08:32
Dann habe ich einen andere LCD-lib oder ein anderes LCD, ich muss jedenfalls lcd_gotoxy(11,0) schreiben, sonst geht es nicht, hab das jetzt extra noch ausprobiert.
Ich habe auch noch in meinem *.h file nachgesehen, dort steht es auch so drinnen, wäre ja auch von der Bezeichnung x y logisch.

Mr Bean
03.12.2007, 09:10
Hallo

Also ich hab das auch noch einmal ausprobiert. Das mit dem lcd_gotoxy passt in jedem Fall. Mein Courser steht dann an der richtigen Stelle. Wie sieht es mit der Umrechnung in eine Dezimale Kommazahl aus? Denke da liegt mein Hauptproblem. Habt ihr da ein paar gute Tipps?

Grüße!!

Bean

robocat
03.12.2007, 10:29
besonders schön ist das nicht, aber hat bei mir immer funktioniert:

void display(int value)
{
char buffer[8];
float f=((float)value*5)/1023; // mit "5" erhält man eine kommazahl von 0,0 bis 5,0
if(f<0)f=0;
itoa((int)f,buffer,10);
lcd_puts(buffer);
lcd_putc(',');
f=(f-(int)f)*1000;
if(f<100)lcd_putc('0');
if(f<10)lcd_putc('0');
itoa((int)f,buffer,10);
lcd_puts(buffer);
return;
}
value ist der 10bit wert, zuerst wird umgerechnet und der ganzzahlige wert ausgegeben, dann wird der rest (die kommastellen) mit 1000 multipliziert und die daraus entstehende dezimalzahl hinter dem komma ausgegeben.

gruesse

Mr Bean
03.12.2007, 11:37
Hallo robocat!

Das sieht schon mehr nach dem aus was ich denke zu brauche. Werde das heut Mittag/Abend nach der Arbeit gleich mal ausprobieren. Melde mich dann wieder.

Grüße!!

Bean

wkrug
03.12.2007, 12:15
Ich nehm für solche Berechnungen eigentlich immer int werte her, da die der Controller schneller bearbeiten kann als float.
Um eine Nachkommastelle zu kriegen nehm ich immer den zehnfachen Wert der tatsächlichen Temperatur.



//Berechnung der Temperatur
si_temp=((si_adwert-ad_nullwert)*si_multiplyer)/si_const;
// Die Ganzen Grade Berechnen
si_ones=si_temp/10;
//Die eine Nachkommastelle Berechnen
si_komma=si_temp%10;
// Die Zehnerstellen ans Display ausgeben
itoa(si_ones,uc_disp);
lcd_puts(uc_disp);
lcd_putchar(",");
itoa(si_komma,uc_disp);
lcd_puts(uc_disp);
Das funktioniert aber nur mit einer Nachkommastelle, da die Funktion itoa führende Nullen unterdrückt.
Die Variable ad_nullwert ist der Wert, den der A/D Wandler bei 0°C ausgibt. Die Variable si_mutiplyer ist der Steilheitsfaktor zur kalibrierung der Temperatur. si_const ist eine feste Teilerkonstante die natürlich von von si_multiplyer und der gewünschten Auflösung abhängig ist.
Vorschlag 1024.

Hubert.G
03.12.2007, 13:29
@Mr Bean Was verwendest du für ein LCD.lib . Ich möchte da keine Grundsatzdiskussion auslösen wegen dem lcd_gotoxy, aber mich würde interessieren ob der Unterschied in der lib oder im LCD ist. Das ist ja eventuell auch für andere interessant das es da Unterschiede gibt.

wkrug
03.12.2007, 15:41
Bin zwar nicht MR.Bean, ich verwende die .lib von Codevision AVR.

Bei mir muss es auch

lcd_gotoxy(10,0); heissen, wenn ich auf das 11te Zeichen der ersten Zeile zugreifen will. (0,1) wäre das erste Zeichen der zweiten Zeile.

Mr Bean
03.12.2007, 18:15
Nabend allerseits!!

Muss mich wohl bei Hubert entschuldigen. Bei mir heißt die Zeile auch lcd_gotoxy(12,0).Weiß auch nicht warum das in dem ersten Code den ich gepostet hab falsch ist. Ich bin überzeugt davon gewesen dass ich da nichtsmehr geändert hab. Muss aber wohl so sein. Tut mir wirklich Leid! 8-[ 8-[

Ich hab meinen Code nun mal nach dem Vorschlag von robocat geändert. Danach hat sich schon etwas mehr getan. Dann hab ich noch etwas an den Werten gespielt um die richtige Temperatur zu errechnen. Aber ich glaube das war der falsche Weg. Jetzt wird zwar die Temperatur richtig angezeigt, aber die Temperturänderung stimmt nichtmehr. Wenn ich den Sensor erwärme ändert sich die angezeigte Temperatur max um 2°. Da kann was noch nicht stimmen... ](*,) ](*,) ](*,)
Hier mal noch mein jetziger Code:


#include <avr/io.h>
#include <inttypes.h>
#include <stdlib.h>
#include <lcd.h>
#define F_CPU = 4000000UL;


uint16_t readADC(uint8_t); //Prototyp von readADC
void display(uint16_t result); //Prototyp von display

int main(void)
{

DDRD = 0xff; //PORT D als Ausgang definieren (Hier sind 8 LED`s angeschlossen
DDRB = 0xff; //PORT B als Ausgang
uint16_t result = readADC(0); //Auslesen der analogen Spannungen an Pin 0,
// also ADC0. In result steht das Ergebnis.
display(result); //Umrechnung und Ausgabe der Temperatur auf Display und LED`s
return 0;
}


void display(uint16_t result)
{
lcd_init(LCD_DISP_ON_CURSOR); //LCD initialisieren
lcd_gotoxy(0,0); //Cursor an erste Stelle setzen
lcd_puts("Temperatur:"); //"Temperatur" ausgeben
lcd_gotoxy(12,0); // Cursor hinter "Temperatur:" setzen
char buffer[8]; //Array für itoa anlegen
float f=((float)result*200)/1023; //
if(f<0)f=0;
itoa((int)f,buffer,10);
lcd_puts(buffer);
lcd_putc(',');
f=(f-(int)f)*1000;
if(f<100)lcd_putc('0');
if(f<10)lcd_putc('0');
itoa((int)f,buffer,10);
lcd_puts(buffer);
PORTD = result; //Den Digitalen Wert mit Hilfe der LED`s anzeigen
return;
}



uint16_t readADC(uint8_t channel)

{
uint8_t i;
uint16_t result = 0;

// Den ADC aktivieren und Teilungsfaktor auf 32 stellen
ADCSRA = (1<<ADEN) | (1<<ADPS0) | (1<<ADPS2);

// Kanal des Multiplexers waehlen (ADC 0)
ADMUX = (!(1<<MUX0)) | (!(1<<MUX1)) | (!(1<<MUX2));

// interne Referenzspannung verwenden (also 2,56 V)
ADMUX |= (1<<REFS1) | (1<<REFS0);

// Den ADC initialisieren und einen sog. Dummyreadout machen
ADCSRA |= (1<<ADSC);
while(ADCSRA & (1<<ADSC));

// Jetzt 3x die analoge Spannung and Kanal channel auslesen
// und dann Durchschnittswert ausrechnen.
for(i=0; i<4; i++) {
// Eine Wandlung
ADCSRA |= (1<<ADSC);
// Auf Ergebnis warten...
while(ADCSRA & (1<<ADSC));

result += ADCW;
}

// ADC wieder deaktivieren
ADCSRA &= ~(1<<ADEN);

result /= 4;

return result;
}



Grüße!!

Bean

T.J.
04.12.2007, 05:58
Wenn ich den Sensor erwärme ändert sich die angezeigte Temperatur max um 2°. Da kann was noch nicht stimmen.

Bist du sicher, dass deine Hardware stimmt? Welche Spannungsänderung hast du auf 10°C ???. Wenn du nämlich bei 100°C Temperaturänderung nur 1V Spannungsänderung hast, nützt dir ein 10bit ADC gaaar nix.


Ich habe noch einen anderen Vorschlag: ich mache das immer in ASM, und zwar so:

man ziehe von der Temperatur (in bin) die Zahl 100 so oft ab bist es negativ ist. dann einmal 100 drauf addieren. Die anzahl wie oft man abgezogen hat bebor es negativ wurde sind dann die hunderter! Wenn man das mit den zehnern auch noch macht bleiben die Einer über und man hat die Temperatur Ziffernweise in 3 Variablen. Geht prima, und supi fix. Ich mache halt alles in ASM, weils spaß macht :)

Mr Bean
04.12.2007, 10:01
Hallo

Ja ich frag mich so langsam auch ob ich nicht doch noch einen Bug in meiner Hardware drin habe.
Ich hab mir das jetzt gestern alles noch einmal überlegt und frage mich ob ich einen OP zum verstärken des Spannungsabfalls über dem KTY element brauche. :-k :-k
Also noch einmal von vorne, ich habe einen 10Bit AD wandler >> 1024

Uref sind 2,56V als entspricht jeder schritt 2,56V/1024 = 0,0025V.

Wenn ich nun mit meinem Multimeter den Spannungsabfall an dem KTY element messe habe ich da bei Zimmertemp. 0,35V also 140 Schritte. Normal sollte sich das doch mit dem AD Wandler gut messen lassen. Auch wenn der Bereich den ich nutze recht klein ist oder sehe ich das jetzt falsch? Wie habt ihr das gemacht? Habt ihr da noch einen OP drin mit dem ihr das Signal verstärkt? Ich werde heute Abend mal noch versuchen die Spannungswerte in Eiswasser und in kochendem Wasser aufzunehmen, damit ich mal ein paar Anhaltspunkte hab.

Grüße!

Bean

wkrug
04.12.2007, 10:35
Ich hab kürzlich auch ein Thermometer mit einem KTY 84 - 130 gebastelt.
Das ging an 5V mit einem 2,2k Ohm Vorwiderstand ohne OpAmp.
Ich krieg aber auch nur eine Auflösung von 1°C zusammen, weil ich bis 300°C messen will.

Ich hab die interne Referenzspannungsquelle des A/D Wandlers mit 2,56V verwendet und den obligatorischen 100nF Kondensator an AREF angebaut.
AVCC muss auch +5V Spannung angeschaltet haben, man sollte in die Zuleitung dieses Pins eine 10µH Drossel einbauen.

Wenn Du das auch so gemacht hast, sollte am AREF Pin eine Spannung von 2,56V anliegen.

Vor ein paar Tagen hatte ich auch einen ATMEGA8 an den ein A/D Eingang defekt war - Teste doch auch mal nen anderen Controller.
Die Spannung an diesem Pin ging bei einer Beschaltung mit einem 10kOhm Widerstand gegen +5V nicht hoch. Legte man dagegen satte 2,5V an, funktionierte auch der A/D Wandler. Der Pin war also irgendwie niederohmig.

Mr Bean
04.12.2007, 10:47
Hallo

Ich möchte auch bis 300°C messen (für meinen Ofen... :-) ). Ich habe allersings einen 1k Widerstand vor meinem KTY-84 130. Werde das heute Abend mal noch mit einem 2K2 ausprobieren.
Es ist doch aber so dass wenn der Widerstand größer wird auch eine größere Spannung über diesem abfällt. :-k Misst Du dann die Spannung über dem Widerstand oder über dem Thermoelement? Oder wie bekommst Du dadurch eine höhere Spannung?

Grüße!!

Bean

Mr Bean
04.12.2007, 11:22
Ach ja, den Kondensator an Aref hab ich noch nicht drin, die Spule auch noch nicht. Werde das aber auch heute Abend versuchen nachzurüsten. Muss mir nur noch eine Spule auftreiben... Aber das dient doch eigentlich nur dazu die Messung zu verbessern oder? Grundsätzlich sollte das ganze doch auch ohne den Kondensator und die Spule funktionieren oder?

Grüße!!

Bean

robocat
04.12.2007, 11:52
result += ADCW;
wahrscheinlich liegt es daran nicht, ich habe "ADCW" nur noch nirgends gesehen. habe da bisher ADC verwendet.

übrigens sind alle anderen methoden zur float-darstellung besser (schneller, kleiner) als meiner. versuch es mal so, wie wkrug oder T.J. es vorschlagen.

gruesse

wkrug
04.12.2007, 15:12
....auch ohne den Kondensator und die Spule funktionieren oder?
Die Spule ist meiner Meinung nach wirklich nur dazu da um HF- Störungen vom A/D Wandler fernzuhalten.
Der Kondensator ist, soweit ich weiß, zur Stabilisierung der Referenzspannungsquelle zwingend erforderlich.

Mr Bean
04.12.2007, 18:52
Hallo

Also ich habe nun den Kondensator eingebaut und mir auch noch einen anderen Spannungsteiler mit einem neuen KTY 84 130 und 2K2 Widersand gemacht. Dann hab ich ein paar Messungen gemacht:

22°C (gemessen mit Multimeter) und 1K Widerstand : 0,326 V am ADW

3°C (gemessen mit Multimeter) und 1K Widerstand : 0,285V am ADW

22°C (gemessen mit Multimeter) und 2K2 Widerstand : 1,12 V am ADW

3°C (gemessen mit Multimeter) und 2K2 Widerstand : 0,998V am ADW

Jetzt bin ich leider so schlau wie vorher, welche Variante ist jetzt besser? Die Spannungsdifferenz ist ja bei der 2K2 Variante größer. Im Datenblatt des KTY steht aber dass man für Temperaturen über ~200° einen Strom >2mA einstellen soll (und in diesem Bereich will ich ja später messen). Und das käme wiederum der 1K Variante zu gute... :-k :-k So langsam weiß ich nichtmehr was ich machen soll... Und die Anzeige stimmt leider auch noch nicht. Habe noch ein paar Kleinigkeiten am Programm verändert (ADCW >> ADC). Das hat aber auch keine Besserung gebracht. Dann hab ich mal noch versucht die Kommastelle zu verschieben, das ist aber auch nicht der richtige Weg. Könnt ihr mir noch irgendwie auf die Sprünge helfe? Wäre klasse... Würde gerne mal meine Zimmertemperatur messen und anzeigen... :-( :-(

Hier mal noch mein jetziger Code:


#include <avr/io.h>
#include <inttypes.h>
#include <stdlib.h>
#include <lcd.h>
#define F_CPU = 4000000UL;


uint16_t readADC(uint8_t); //Prototyp von readADC
void display(uint16_t result); //Prototyp von display

int main(void)
{

DDRD = 0xff; //PORT D als Ausgang definieren (Hier sind 8 LED`s angeschlossen
DDRB = 0xff; //PORT B als Ausgang
uint16_t result = readADC(0); //Auslesen der analogen Spannungen an Pin 0,
// also ADC0. In result steht das Ergebnis.

display(result); //Umrechnung und Ausgabe der Temperatur auf Display und LED`s
return 0;
}


void display(uint16_t result)
{
lcd_init(LCD_DISP_ON_CURSOR); //LCD initialisieren
lcd_gotoxy(0,0); //Cursor an erste Stelle setzen
lcd_puts("Temperatur:"); //"Temperatur" ausgeben
lcd_gotoxy(11,0); // Cursor hinter "Temperatur:" setzen



char buffer[8]; //Array für itoa anlegen
float f=((float)result*5)/1024; //
if(f<0)f=0;
itoa((int)f,buffer,10);
lcd_puts(buffer);
f=(f-(int)f)*1000;
if(f<100)lcd_putc('0');
if(f<10)lcd_putc('0');
itoa((int)f,buffer,10);
lcd_putc(',');
lcd_puts(buffer);


PORTD = result; //Den Digitalen Wert mit Hilfe der LED`s anzeigen
return;
}



uint16_t readADC(uint8_t channel)

{
uint8_t i;
uint16_t result = 0;

// Den ADC aktivieren und Teilungsfaktor auf 32 stellen
ADCSRA = (1<<ADEN) | (1<<ADPS0) | (1<<ADPS2);

// Kanal des Multiplexers waehlen (ADC 0)
ADMUX = (!(1<<MUX0)) | (!(1<<MUX1)) | (!(1<<MUX2));

// interne Referenzspannung verwenden (also 2,56 V)
ADMUX |= (1<<REFS1) | (1<<REFS0);

// Den ADC initialisieren und einen sog. Dummyreadout machen
ADCSRA |= (1<<ADSC);
while(ADCSRA & (1<<ADSC));

// Jetzt 3x die analoge Spannung and Kanal channel auslesen
// und dann Durchschnittswert ausrechnen.
for(i=0; i<3; i++) {
// Eine Wandlung
ADCSRA |= (1<<ADSC);
// Auf Ergebnis warten...
while(ADCSRA & (1<<ADSC));

result += ADC;
}

// ADC wieder deaktivieren
ADCSRA &= ~(1<<ADEN);

result /= 3;

return result;
}


Grüße!!

Bean

wkrug
04.12.2007, 21:53
Nun, ich hab die Werte aus dem Datenblatt in eine EXCEL Tabelle übertragen, ein Diagramm daraus gemacht und dabei festgestellt, das das Temperatur / Werteverhältnis der KTY alles andere als linear war.
Also bin ich hergegangen und hab mit verschiedenen Vorwiderstandswerten mir immer wieder neue Kurven berechnen und zeichnen lassen.
Mit einem 2,2k Ohm Widerstand war die Temperatur / Werte kurve der linearen Funktion am nächsten. Das kommt natürlich auch auf die verwendete Spannung an.

Meine Berechnungsformel Schaut in C so aus.



// Vordefinitionen der Kalibrierungsfaktoren im EEPROM
eeprom signed int ee_tempzero[2]={249,249}; /* (249) A/D Wandlerwert für 0°C "(A/D Wandler - X) *290 / tempfakt" */
eeprom signed int ee_tempfakt[2]={461,461}; /* (461) Teilerfaktor für Temperatursteilheit "(A/D Wandler - tempzero) *290 / X" */
......

si_tempzero[0]=ee_tempzero[0];
si_tempzero[1]=ee_tempzero[1];
si_tempfakt[0]=ee_tempfakt[0];
si_tempfakt[1]=ee_tempfakt[1];
.......

// Berechnung von 2 Temperaturen
li_temp1=((li_temp1-si_tempzero[0])*290)/si_tempfakt[0];
li_temp2=((li_temp2-si_tempzero[1])*290)/si_tempfakt[1];
........

// Einlesen der Temperaturen in einen String ohne Nachkommastellen
ltoa (li_temp1,uc_stringbuffer);
strcat (uc_sendbuffer,uc_stringbuffer);


Die einzelnen Abschnitte sind natürlich in definitionen bzw. Funktionen enthalten, werden aber prinzipiell so abgearbeitet.
Bei mir läuft das einwandfrei.
Versuchs vielleicht mal vorerst ganz ohne Berechnungen und gib mal nur den A/D Wert direkt aus. Und wenn das klappt, dann füge die Berechnungsformel ein.
So kannst DU feststellen ob der A/D Wandler zickt, oder ein Fehler bei der Berechnung gemacht wurde.

Diese KTY Sensoren haben eine krasse Serienstreuung. Um eine Kalibrierung wirst Du also nicht herumkommen.

Ich hab zuerst eine Kalibrierung auf 0°C vorgenommen (Eiswasser) und dann mit 98°C ( kochendes Wasser ) kalibriert und meine es passt so einigermassen.
Von 10 Sensoren, die ich gekauft hab, hatten gerade mal 2 annähernd gleiche Werte.

robocat
05.12.2007, 12:28
void display(uint16_t result)
{
lcd_init(LCD_DISP_ON_CURSOR); //LCD initialisieren
lcd_gotoxy(0,0); //Cursor an erste Stelle setzen
lcd_puts("Temperatur:"); //"Temperatur" ausgeben
lcd_gotoxy(11,0); // Cursor hinter "Temperatur:" setzen



char buffer[8]; //Array für itoa anlegen
float f=((float)result*5)/1024; //
if(f<0)f=0;
itoa((int)f,buffer,10);
lcd_puts(buffer);
f=(f-(int)f)*1000;
if(f<100)lcd_putc('0');
if(f<10)lcd_putc('0');
itoa((int)f,buffer,10);
lcd_putc(',');
lcd_puts(buffer);


PORTD = result; //Den Digitalen Wert mit Hilfe der LED`s anzeigen
return;
}
sicher, dass das so überhaupt kompiliert? weil char buffer[8] und float f mitten im code deklariert sind..
das 10bit result direkt an PORTD auszugeben (der ja nur 8bit hat) wird zu falschen ergebnissen führen.

gruesse

Mr Bean
06.12.2007, 17:24
So also wenn ich folgenden Code in meinen Controller schreibe:

#include <avr/io.h>
#include <inttypes.h>
#include <stdlib.h>
#include <lcd.h>
#define F_CPU = 4000000UL;


uint16_t readADC(uint8_t); //Prototyp von readADC
void display(uint16_t result); //Prototyp von display
char buffer[8]; //Array für itoa anlegen

int main(void)
{

DDRD = 0xff; //PORT D als Ausgang definieren (Hier sind 8 LED`s angeschlossen
DDRB = 0xff; //PORT B als Ausgang
uint16_t result = readADC(0); //Auslesen der analogen Spannungen an Pin 0,
// also ADC0. In result steht das Ergebnis.
display(result); //Umrechnung und Ausgabe der Temperatur auf Display und LED`s
return 0;
}


void display(uint16_t result)
{
lcd_init(LCD_DISP_ON_CURSOR); //LCD initialisieren
lcd_gotoxy(0,0); //Cursor an erste Stelle setzen
lcd_puts("Temperatur:"); //"Temperatur" ausgeben
lcd_gotoxy(0,1); // Cursor in die zweite Zeile setzen
itoa((int)result,buffer,10); //AD-Wert umrechnen
lcd_puts(buffer); //AD-Wert in der zweiten Zeile ausgeben

lcd_gotoxy(11,0); // Cursor hinter "Temperatur:" setzen

//float f=((float)result*5)/1024;
//if(f<0)f=0;
//f=(f-(int)f)*1000;
//if(f<100)lcd_putc('0');
//if(f<10)lcd_putc('0');
//itoa((int)f,buffer,10);
//lcd_putc(',');
//lcd_puts(buffer);


PORTD = result; //Den Digitalen Wert mit Hilfe der LED`s anzeigen
return;
}



uint16_t readADC(uint8_t channel)

{
uint8_t i;
uint16_t result = 0;

// Den ADC aktivieren und Teilungsfaktor auf 32 stellen
ADCSRA = (1<<ADEN) | (1<<ADPS0) | (1<<ADPS2);

// Kanal des Multiplexers waehlen (ADC 0)
ADMUX = (!(1<<MUX0)) | (!(1<<MUX1)) | (!(1<<MUX2));

// interne Referenzspannung verwenden (also 2,56 V)
ADMUX |= (1<<REFS1) | (1<<REFS0);

// Den ADC initialisieren und einen sog. Dummyreadout machen
ADCSRA |= (1<<ADSC);
while(ADCSRA & (1<<ADSC));

// Jetzt 3x die analoge Spannung and Kanal channel auslesen
// und dann Durchschnittswert ausrechnen.
for(i=0; i<3; i++) {
// Eine Wandlung
ADCSRA |= (1<<ADSC);
// Auf Ergebnis warten...
while(ADCSRA & (1<<ADSC));

result += ADC;
}

// ADC wieder deaktivieren
ADCSRA &= ~(1<<ADEN);

result /= 3;

return result;
}


Dann zeigt mir das Display in der zweiten Zeile an erster Stelle den AD-Wert an (438). Was auch der Spannung entspricht die ich am AD-Wandler Eingang mit dem Multimeter messen kann. Das zeigt mir dass die AD-Wandlung korrekt funktioniert. Nun werde ich mich also noch einmal intensiver mit der Umrechnung beschäftigen müssen... :-( :-(

Grüße!!

Bean

T.J.
06.12.2007, 18:00
wenn du direkt den AD wert hast, bist du schon sehr weit!

lustigerweise habe ich genau das gleiche (kty, 2k2 weil linear) vor einiger zeit aufgebaut um ein thermometer im auto zu haben. Aber die genauigkeit(ca. 0,5°C lässt zu wünschen übrig, da ich keinen OP verwende.

Ich rechne so:


W = <aktueller ADCWERT> - <ADC bei 0°C>
T = W / (<ADC bei 100°C> - <ADC bei 0°C>)
T = T * 100

Mr Bean
06.12.2007, 18:15
Hallo

Danke für den Tipp! Ich hab jetzt grad noch den Vorschlag von wkrug implementiert. Dann zeigt mir das Display 1,3 an :-( . Da kann also noch etwas nicht stimmen :-( . Vermute dass es an dem Wert si_multiplyer liegt. Aber weigstens bin ich etwas weiter. Leider hab ich den AD-Wert für 100C nicht. Sonst könnte ich Deinen Vorschlag auch noch versuchen. Was mir auch noch nicht so ganz klar ist wie ich mit den Werten später die Steilheit verändern kann. Das muss ja auch irgendwie gehen. Also mein Code sieht jetzt jedenfalls folgendermaßen aus:


#include <avr/io.h>
#include <inttypes.h>
#include <stdlib.h>
#include <lcd.h>
#define F_CPU = 4000000UL;
#define ad_nullwert 399
#define si_multiplyer 460
#define si_const 1024


uint16_t readADC(uint8_t); //Prototyp von readADC
void display(uint16_t result); //Prototyp von display

char buffer[8]; //Array für itoa anlegen
int si_temp; //Hilfvariable für Temperaturberechnung anlegen
int si_ones;
int si_komma;

int main(void)
{

DDRD = 0xff; //PORT D als Ausgang definieren (Hier sind 8 LED`s angeschlossen
DDRB = 0xff; //PORT B als Ausgang

//Auslesen der analogen Spannungen an Pin 0, also ADC0. In result steht das Ergebnis.
uint16_t result = readADC(0);

display(result); //Umrechnung und Ausgabe der Temperatur auf Display und LED`s
return 0;
}


void display(uint16_t result)
{
lcd_init(LCD_DISP_ON_CURSOR); //LCD initialisieren
lcd_gotoxy(0,0); //Cursor an erste Stelle setzen
lcd_puts("Temperatur:"); //"Temperatur" ausgeben
lcd_gotoxy(0,1); // Cursor in die zweite Zeile setzen
itoa((int)result,buffer,10); //AD-Wert umrechnen
lcd_puts(buffer); //AD-Wert in der zweiten Zeile ausgeben

lcd_gotoxy(11,0); // Cursor hinter "Temperatur:" setzen

//Berechnung der Temperatur
si_temp=((result-ad_nullwert)*si_multiplyer)/si_const;
// Die Ganzen Grade Berechnen
si_ones=si_temp/10;
//Die eine Nachkommastelle Berechnen
si_komma=si_temp%10;
// Die Zehnerstellen ans Display ausgeben
itoa(si_ones,buffer,10);
lcd_puts(buffer);
lcd_putc(',');
itoa(si_komma,buffer,10);
lcd_puts(buffer);

PORTD = result; //Den Digitalen Wert mit Hilfe der LED`s anzeigen
return;
}


uint16_t readADC(uint8_t channel)

{
uint8_t i;
uint16_t result = 0;

// Den ADC aktivieren und Teilungsfaktor auf 32 stellen
ADCSRA = (1<<ADEN) | (1<<ADPS0) | (1<<ADPS2);

// Kanal des Multiplexers waehlen (ADC 0)
ADMUX = (!(1<<MUX0)) | (!(1<<MUX1)) | (!(1<<MUX2));

// interne Referenzspannung verwenden (also 2,56 V)
ADMUX |= (1<<REFS1) | (1<<REFS0);

// Den ADC initialisieren und einen sog. Dummyreadout machen
ADCSRA |= (1<<ADSC);
while(ADCSRA & (1<<ADSC));

// Jetzt 3x die analoge Spannung and Kanal channel auslesen
// und dann Durchschnittswert ausrechnen.
for(i=0; i<3; i++) {
// Eine Wandlung
ADCSRA |= (1<<ADSC);
// Auf Ergebnis warten...
while(ADCSRA & (1<<ADSC));

result += ADC;
}

// ADC wieder deaktivieren
ADCSRA &= ~(1<<ADEN);

result /= 3;

return result;
}

Grüße!!

Bean

T.J.
06.12.2007, 18:39
nimm doch einfach mal n Wasserkocher her, dann hast du den Wert auch ;)

aber du kannst dir doch auch die ADC-Wertänderung pro°C nehmen, den ADC-Wert bei 0°C und das zusammenrechnen!

Mr Bean
06.12.2007, 19:08
Hallo

Vielen Dank!!! Da hätt ich auch selber drauf kommen könne... ](*,) ](*,) :-) Aber wäre ich bestimmt auch noch wenn ich mich angestrengt hätte. Ich hab jetzt noch etwas mit den Werten herumgespielt und jetzt wird eine Temp von 22 °C angezeigt. Nur die "Steilheit" stimmt glaube ich noch nicht so ganz. Vielleicht könnte mir wkrug da noch einmal den genauen Zusammenhang erklären. Das wäre super.
Vielen Dank noch einmal für alle Lösungsvorschläge!! Ihr helft einem wirklich weiter!!! =D> =D> =D>

Grüße!!

Bean
#include <avr/io.h>
#include <inttypes.h>
#include <stdlib.h>
#include <lcd.h>

#define F_CPU = 4000000UL
#define ad_nullwert 399
#define si_multiplyer 460
#define si_const 70


uint16_t readADC(uint8_t); //Prototyp von readADC
void display(uint16_t result); //Prototyp von display

char buffer[8]; //Array für itoa anlegen
int si_temp; //Hilfvariable für Temperaturberechnung anlegen
int si_ones;
int si_komma;

int main(void)
{
DDRB = 0xff; //PORT B als Ausgang

//Auslesen der analogen Spannungen an Pin 0, also ADC0. In result steht das Ergebnis.
uint16_t result = readADC(0);

display(result); //Umrechnung und Ausgabe der Temperatur auf Display und LED`s
return 0;
}


void display(uint16_t result)
{
lcd_init(LCD_DISP_ON_CURSOR); //LCD initialisieren
lcd_gotoxy(0,0); //Cursor an erste Stelle setzen
lcd_puts("Temperatur:"); //"Temperatur" ausgeben
lcd_gotoxy(0,1); // Cursor in die zweite Zeile setzen
itoa((int)result,buffer,10); //AD-Wert umrechnen
lcd_puts(buffer); //AD-Wert in der zweiten Zeile ausgeben

lcd_gotoxy(11,0); // Cursor hinter "Temperatur:" setzen

//Berechnung der Temperatur
si_temp=((result-ad_nullwert)*si_multiplyer)/si_const;
// Die Ganzen Grade Berechnen
si_ones=si_temp/10;
//Die eine Nachkommastelle Berechnen
si_komma=si_temp%10;
// Die Zehnerstellen ans Display ausgeben
itoa(si_ones,buffer,10);
lcd_puts(buffer);
lcd_putc(',');
itoa(si_komma,buffer,10);
lcd_puts(buffer);
return;
}


uint16_t readADC(uint8_t channel)

{
uint8_t i;
uint16_t result = 0;

// Den ADC aktivieren und Teilungsfaktor auf 32 stellen
ADCSRA = (1<<ADEN) | (1<<ADPS0) | (1<<ADPS2);

// Kanal des Multiplexers waehlen (ADC 0)
ADMUX = (!(1<<MUX0)) | (!(1<<MUX1)) | (!(1<<MUX2));

// interne Referenzspannung verwenden (also 2,56 V)
ADMUX |= (1<<REFS1) | (1<<REFS0);

// Den ADC initialisieren und einen sog. Dummyreadout machen
ADCSRA |= (1<<ADSC);
while(ADCSRA & (1<<ADSC));

// Jetzt 3x die analoge Spannung and Kanal channel auslesen
// und dann Durchschnittswert ausrechnen.
for(i=0; i<3; i++) {
// Eine Wandlung
ADCSRA |= (1<<ADSC);
// Auf Ergebnis warten...
while(ADCSRA & (1<<ADSC));

result += ADC;
}

// ADC wieder deaktivieren
ADCSRA &= ~(1<<ADEN);

result /= 3;

return result;
}

izaseba
06.12.2007, 19:39
Hallo Bean,

ich wollte noch eine andere Möglichkeit zeigen, wie man die Temperatur problemlos ermitteln kann, auch wenn der Sensor nicht linear ist und zwar die lookup Tabelle.

Es hat leider einen Nachteil, man muß sich mit einer Messreihe die ADC Werte für die entsprechende Temperatur im Wasserbad ermiteln.

Es geht so, man legt eine Array im Flash an:



const int TEMP_Celsius_pos[] PROGMEM = // Positive Celsius temperatures (ADC-value)
{ // from 0 to 60 degrees
806,796,786,775,765,754,743,732,720,709,697,685,67 3,661,649,
636,624,611,599,586,574,562,549,537,524,512,500,48 8,476,464,
452,440,429,418,406,396,385,374,364,354,344,334,32 4,315,306,
297,288,279,271,263,255,247,240,233,225,219,212,20 5,199,193,
187,
};

Das sind jetzt Werte von 0-60 Grad.

Auslesen kann man es dann so:


for (i=0; i<60; i++)
{

if (ADCresult >= pgm_read_word(&TEMP_Celsius_pos[i]))
{
break;
}
}

in i steht nach verlassen der Schleife die Temperatur drin.

Quelle : AVR Butterfly gccport

Gruß Sebastian

robocat
06.12.2007, 19:41
hi Mr Bean,
ein 16bit integer kann ja (unsigned) nur zahlen bis 2^16=65536 darstellen. wenn ein zwischenergebnis einer berechnung einen groesseren wert ergibt, läuft der 16bit int über (1111 1111 1111 1111 (binär für 65536) + 1 = 0!!). bei..

si_temp=((result-399)*460)/70;
..ist das bereits der fall, wenn result > 541 ist. du solltest also (da es sich eh kürzt) *46 und geteilt durch 7 rechnen. bei uint16_t´s bist du dann auf der sicheren seite.

gruesse von der katz

Mr Bean
06.12.2007, 22:47
Hallo

Danke für die Tips, hab ich gleich umgesetzt. Hab jetzt auch mal versucht meine Schaltung im Eiswasser abzugleichen. Das hat auch funktioniert. Nur werden jetzt bei Zimmertemperatur 34°C angezeigt, und das kann nicht sein. Hab hier vielleicht 21°C. Ich schließe also daraus dass die Steigung noch nicht stimmt. Werde jetzt noch etwas rumprobieren ob ich das ganze zum laufen bekomme.

Vielen Dank auch den Tip mit der Tabelle. Werde das vielleicht auch mal probieren. Mein Problem ist dass ich später im Bereich 200°C messen will. Und da brauch ich erstmal die Werte um in die Tabelle einzutragen. Naja mir reicht bei den Temperaturen auch wenn es auf 2°C genau ist, hoffe das bekomme ich hin.

Grüße!!

Bean

Mr Bean
08.12.2007, 14:12
Hallo

Ich bin immernoch mit der Temperaturmessung beschäftigt. Ich habe das ganze nun zwar so hinbekommen dass die angezeigte Temperatur bei 0°C und bei ~Zimmertemperatur stimmt. \:D/ \:D/
Aber ich habe leider immer noch nicht so ganz verstanden wie die Werte si_multiplyer und si_const zusammenhängen. :-k
Tut mir Leid dass ich da so begriffstutzig bin aber könnte mir das noch einmal jemand erklären? Ich hab das jetzt halt so gemacht dass ich so lange an den Werten herumgespielt habe bis es gepasst hat. Aber die muss man doch auch irgendwie berechnen können... :-k
Außerdem könnte mir vielleicht noch jemand sagen wie ich aus der LCD Lib von Peter Fleury das "°" Zeichen auslese. Das steht ja im LCD Memory drin wenn ich mich nicht täusche. Auch das hab ich schon probiert, aber leider nicht hin bekommen.
Ach ja, nebenbei ist mir noch etwas anderes aufgefallen: Wenn ich nun die Spannung mit dem Multimeter messe die am AD-Wandler anliegt habe ich 0,901V. Wenn ich das durch 0,0025 (2,56/1024) teile müsste ich doch auf den AD-Wert kommen. Das gibt bei mir ~360. Ich lasse mir in dem Display ja auch den ADWert direkt anzeigen, da werden mir aber 341 angezeigt. Woher kann das kommen? :-k Hab ich an dieser Stelle doch noch ein Fehler in meinem Code? :-k

Vielen Dank im Voraus!!

Grüße!!

Bean
#include <avr/io.h>
#include <inttypes.h>
#include <stdlib.h>
#include <lcd.h>

#define F_CPU = 4000000UL
#define ad_nullwert 311
#define si_multiplyer 60
#define si_const 12


uint16_t readADC(uint8_t); //Prototyp von readADC
void display_temp(uint16_t result); //Prototyp von display

char buffer[8]; //Array für itoa anlegen
int si_temp; //Hilfvariable für Temperaturberechnung anlegen
int si_ones;
int si_komma;

int main(void)
{
DDRB = 0xff; //PORT B als Ausgang

//Auslesen der analogen Spannungen an Pin 0, also ADC0. In result steht das Ergebnis.
uint16_t result = readADC(0);

display_temp(result); //Umrechnung und Ausgabe der Temperatur auf Display und LED`s
return 0;
}


void display_temp(uint16_t result)
{
lcd_init(LCD_DISP_ON); //LCD initialisieren
lcd_gotoxy(0,0); //Cursor an erste Stelle setzen
lcd_puts("Temp.:"); //"Temperatur" ausgeben
lcd_gotoxy(0,1); // Cursor in die zweite Zeile setzen
itoa(result,buffer,10); //AD-Wert umrechnen
lcd_puts(buffer); //AD-Wert in der zweiten Zeile ausgeben

lcd_gotoxy(9,0); // Cursor hinter "Temperatur:" setzen

//Berechnung der Temperatur
si_temp=((result-ad_nullwert)*si_multiplyer)/si_const;
// Die Ganzen Grade Berechnen
si_ones=si_temp/10;
//Die eine Nachkommastelle Berechnen
si_komma=si_temp%10;
// Die Zehnerstellen ans Display ausgeben
itoa(si_ones,buffer,10);
lcd_puts(buffer);
lcd_putc(',');
itoa(si_komma,buffer,10);
lcd_puts(buffer);
lcd_putc("°");;
lcd_putc('C');
return;
}


uint16_t readADC(uint8_t channel)

{
uint8_t i;
uint16_t result = 0;

//Den ADC aktivieren und Teilungsfaktor auf 32 stellen
ADCSRA = (1<<ADEN) | (1<<ADPS0) | (1<<ADPS2);

//Kanal des Multiplexers waehlen (ADC 0)
ADMUX = (!(1<<MUX0)) | (!(1<<MUX1)) | (!(1<<MUX2));

//interne Referenzspannung verwenden (also 2,56 V)
ADMUX |= (1<<REFS1) | (1<<REFS0);

//Den ADC initialisieren und einen sog. Dummyreadout machen
ADCSRA |= (1<<ADSC);
while(ADCSRA & (1<<ADSC));

//Jetzt 3x die analoge Spannung and Kanal channel auslesen
//und dann Durchschnittswert ausrechnen.

for(i=0; i<3; i++)
{
// Eine Wandlung
ADCSRA |= (1<<ADSC);
// Auf Ergebnis warten...
while(ADCSRA & (1<<ADSC));

result += ADC;
}


//ADC wieder deaktivieren
ADCSRA &= ~(1<<ADEN);

result /= 3;

return result;
}

wkrug
08.12.2007, 16:38
Das mit deinen Werten bestätigt das, was ich Dir schon in einem früheren Post zu diesem Thema schrieb.

1. Die A/D Wandler der ATMEL Controller sind nicht besonders genau.
Miss mal die Spannung am AREF Ausgang des Controllers und berechne dann den A/D Wert nicht mit den 2,56V die der Pin eigentlich haben sollte, sondern mit dem gemessenen Wert.
Ausserdem sind die A/D Wandler nicht besonders linear.

2. Die KTY Sensoren haben eine ziemliche Serienstreuung. Das kannst Du auch sehen, wenn Du Dir die Tabellen in den Datenblättern anschaust.


Diese Fehlmessungen sind also letztendlich Bauteiletoleranzen.
Da man aber lineare Toleranzen mit Korrekturwerten ziemlich gut rausrechnen kann ist das eigentlich gar nicht so schlimm, erfordert aber einen Abgleich. Und zwar für jeden neuen Controller und jeden neuen Temperatursensor.
Die si_const ist eigentlich ein willkürlich gewählter Wert der 2 Vorraussetzungen erfüllen muss. Er darf nicht dazu führen, das bei einem Maximal A/D Wert die signed int Variable überschritten wird ( 32767 ).
Er muss aber auch mindestens so groß sein, das mit der si_multiplyer vernünftige Schrittweiten einstellen lassen.
Das Ganze hängt damit zusammen, das int Variablen nur Ganze Zahlen darstellen können. 0,5 wird also in einer int Variablen zu 0.

Nichtlineare Fehler, also krumme Kennlinien, erfordern das schon mehr Aufwand. Es kommt also darauf an, welche Genauigkeit man bei der Messung haben will.

Mr Bean
09.12.2007, 12:21
Also das finde ich ja schon etwas komisch. Ich hab jetzt mal an meinem Aref Pin gemessen und da habe ich 1,75V. Wenn ich das durch 1024 teile komme ich auf eine Schrittweite von 0,001709V. Dann teile ich meine AD-Spannung bei 0°C von 1,332V durch diesen Wert und erhalte einen theoretischen digitalten Wert von 779. Tatsächlich habe ich aber 521. Das wäre ja also noch schlechter als wenn ich mit 2,56Vref rechnen würde. Da kann ja was nicht stimmen.
Dass die Sensoren eine große Streuung haben das denke ich mir schon. Das haben auch Messungen gezeigt die ich jetzt mit der gleichen Schaltung aber einem anderen KTY Sensor gemacht habe. Aber wenn ich doch 2,56Vref einstelle, dann sollten die doch auch passen. Oder wo liegt jetzt mein Denkfehler? Sonst wäre es doch einfacher den Aref Pin einfach auf Vcc zu legen. Da hab ich wenigstens halbwegs konstant 5V und weiß mit was ich rechnen kann. Oder sehe ich das falsch?

Grüße und vielen Dank für die Geduld!

Bean

wkrug
09.12.2007, 15:12
@Mr Bean
Also das mit deinen 1,75V am ARef Pin mag ich jetzt fast nicht glauben.
Hast Du da ein Uralt Analog Multimeter verwendet ?
Ist der A/D Wandler des Controller richtig konfiguriert ?
Ich hab da Unterschiede von maximal 0,1V festgestellt.
Klar kannst Du auch deine 5V Als Referenzspannung verwenden. Dazu musst Du den A/D Wandler etwas anders konfigurieren - ADMUX Register.

Ich meine Du solltest mal für die Temperatur Berechnungen eine signed long int Variable vorsehen, sonst passiert das mit dem Überlauf was "robocat" schrieb, wenn die Werte über 32767 bzw. 65535 gehen.
Ausserdem könnten dann die Korrekturwerte etwas größer gewählt werden, was eine feinere Abstufung ergäbe.

Da bei der Temperaturmessung auch negative Werte rauskommen können sollten alle darin verwendeten Variablen als signed angegeben werden.
Sonst könnte es Stress bei der Berechnung geben.

Teil mal die Formel auseinander ( aus der einen Formel 3 machen ) und lass das Programm mal im Simulator laufen. Dann sollte sich zeigen wo die Berechnung nicht stimmt.

Mr Bean
10.12.2007, 17:37
Hallo!

Also ich weiß auch nichtsmehr was da los ist. Ich hab jetzt mal einen neuen Controller genommen und die gleichen Fusebits und das geiche Programm rein geschrieben. Ergo: An Aref weiterhin 1,71V. Wenn ich den Kondensator gegen Masse dann abklemme und den Pin einfach offen lasse messe ich 0V. Weiß jetzt selber nicht ob das so richtig ist. :cry:
Muss ich an den Fusebits auch noch etwas einstellen um den AD-Wandler zu benutzen? Ich denke schon dass ich ihn softwaremäßig richtig initialisiert habe. (ADMUX |= (1<<REFS1) | (1<<REFS0))
An der Umänderung zu signed Variablen bin ich dran. Aber will nicht zu viel Baustellen auf einmal. Möchte eigentlich zuerst das mit der Referenzspg. geklärt haben.
Das Multimeter ist schon etwas älter und war auch ein billiges, aber wenn ich die Spannung meines Labornetzteiles messe stimmt es eigentlich recht gut.

Grüße!!

Bean

wkrug
10.12.2007, 18:07
Ich hab jetzt auch bei meinen beiden ATMEGA 8 nachgemessen.
Bei einem ATMEG 8 L 8 hab ich 2,59V gemessen ( 3,3V Betriebsspannung ), bei dem ATMEGA 8 - 16 hab ich 2,66V gemessen ( 5V Betriebsspannung ).

Welchen Controller hast Du denn genau benutzt ?
Es gibt auch welche mit 1,1V oder 1,76V Referenzspannung.
Der ATMEGA 88 hat z.B. eine Referenzspannung von 1,1V.

Aufschluß darüber gibt das Datenblatt unter der Rubrik "Analog to digital Converter".

Wenn da natürlich eine andere Spannung angegeben ist, musst Du auch mit diesem Wert rechnen - Steht auch im Datenblatt.

Hast Du schon deine Temperatur Variable auf signed long umgebaut ?

Mr Bean
10.12.2007, 18:59
Also ich hab den AtMega8-16PU. Ich bin der Meinung dass im Datenblatt schon 2,56V Uref steht. Finde das schon komisch.
An der Umwandlung in signed long bin ich dran. Hab das jetzt mal soweit gemacht:


#include <avr/io.h>
#include <inttypes.h>
#include <stdlib.h>
#include <lcd.h>

#define F_CPU = 4000000UL
#define ad_nullwert 311
#define si_multiplyer 60
#define si_const 12


uint16_t readADC(uint8_t); //Prototyp von readADC
void display_temp(uint16_t result); //Prototyp von display

char buffer[8]; //Array für itoa anlegen
signed long int si_temp1; //Hilfvariable für Temperaturberechnung anlegen
signed long int si_temp2;
signed long int si_temp3;
signed long int si_ones;
signed long int si_komma;

int main(void)
{
DDRB = 0xff; //PORT B als Ausgang

//Auslesen der analogen Spannungen an Pin 0, also ADC0. In result steht das Ergebnis.
uint16_t result = readADC(0);

display_temp(result); //Umrechnung und Ausgabe der Temperatur auf Display und LED`s
return 0;
}


void display_temp(uint16_t result)
{
lcd_init(LCD_DISP_ON); //LCD initialisieren
lcd_gotoxy(0,0); //Cursor an erste Stelle setzen
lcd_puts("Temp.:"); //"Temperatur" ausgeben
lcd_gotoxy(0,1); // Cursor in die zweite Zeile setzen
itoa(result,buffer,10); //AD-Wert umrechnen
lcd_puts(buffer); //AD-Wert in der zweiten Zeile ausgeben

lcd_gotoxy(9,0); // Cursor hinter "Temperatur:" setzen

//Berechnung der Temperatur
si_temp1= (result-ad_nullwert);
si_temp2= (si_temp1*si_multiplyer);
si_temp3= (si_temp2/si_const);
// Die Ganzen Grade Berechnen
si_ones=si_temp3/10;
//Die eine Nachkommastelle Berechnen
si_komma=si_temp3%10;
// Die Zehnerstellen ans Display ausgeben
itoa(si_ones,buffer,10);
lcd_puts(buffer);
lcd_putc(',');
itoa(si_komma,buffer,10);
lcd_puts(buffer);
lcd_putc('C');
return;
}


uint16_t readADC(uint8_t channel)

{
uint8_t i;
uint16_t result = 0;

//Den ADC aktivieren und Teilungsfaktor auf 32 stellen
ADCSRA = (1<<ADEN) | (1<<ADPS0) | (1<<ADPS2);

//Kanal des Multiplexers waehlen (ADC 0)
ADMUX = (!(1<<MUX0)) | (!(1<<MUX1)) | (!(1<<MUX2));

//interne Referenzspannung verwenden (also 2,56 V)
ADMUX |= (1<<REFS1) | (1<<REFS0);

//Den ADC initialisieren und einen sog. Dummyreadout machen
ADCSRA |= (1<<ADSC);
while(ADCSRA & (1<<ADSC));

//Jetzt 3x die analoge Spannung and Kanal channel auslesen
//und dann Durchschnittswert ausrechnen.

for(i=0; i<3; i++)
{
// Eine Wandlung
ADCSRA |= (1<<ADSC);
// Auf Ergebnis warten...
while(ADCSRA & (1<<ADSC));

result += ADC;
}


//ADC wieder deaktivieren
ADCSRA &= ~(1<<ADEN);

result /= 3;

return result;
}

Wie habt ihr denn euren AD-Wandler beschaltet? So viel kann man da ja eigentlich nicht falsch machen....

Grüße!!

Bean

wkrug
10.12.2007, 19:45
Also das mit deiner Referenzspannung ist definitiv Mist.
Die beiden REFS sollten 1 sein - das müsste stimmen.
An AVCC wirst Du auch +5V haben und An AGND auch GND.
Der Controller hat ach Takt, sonst ginge das Display nicht.

Es wäre jetzt noch möglich, das dein Messgerät einen sehr geringen Einganswiderstand hat und somit die AREF in den Keller zieht.
Miss mal alle Versorgungsspannungen (VCC, AVCC, GND, AGND) am Controller nach, evtl. hast Du ja eine kalte Lötstelle reingebastelt.
Ansonsten fällt mir nicht mehr viel dazu ein.

Der A/D Wandler muss natürlich vor der Messung der Spannung AREF initialisiert sein, aber ich Denke so schlau bist Du auch.

Das Problem mit den zu kleinen Temperaturwerten wird aber vermutlich an der signed long - Geschichte liegen.

Mr Bean
10.12.2007, 21:59
Also

Ich hab nun noch einmal alles durchgemessen. Hier meine Ergebnisse. Die ganze Messung fand statt nach dem der Controller initialisiert war und auch schon eine Temperatur gemessen wurde:

AVcc -> 5V
Aref -> 1,72V
AGND -> 0,03V
Vcc -> 5,05V

Ich hab jetzt in meinen Code noch eine while Schleife eingefügt, brachte aber keine Veränderung. Dachte dass ohne diese Schleife der AD Wandler ja immer wieder neu initialisiert wird und ich dadruch vielleicht nicht die gewollten 2,56V messe (Zugegeben sehr theoretisch aber was will ich machen 8-[ ). Aber dem war nicht so.


#include <avr/io.h>
#include <inttypes.h>
#include <stdlib.h>
#include <lcd.h>

#define F_CPU = 4000000UL
#define ad_nullwert 311
#define si_multiplyer 60
#define si_const 12


uint16_t readADC(uint8_t); //Prototyp von readADC
void display_temp(uint16_t result); //Prototyp von display

char buffer[8]; //Array für itoa anlegen
signed long int si_temp1; //Hilfvariable für Temperaturberechnung anlegen
signed long int si_temp2;
signed long int si_temp3;
signed long int si_ones;
signed long int si_komma;

int main(void)
{
DDRB = 0xff; //PORT B als Ausgang

//Auslesen der analogen Spannungen an Pin 0, also ADC0. In result steht das Ergebnis.
uint16_t result = readADC(0);

display_temp(result); //Umrechnung und Ausgabe der Temperatur auf Display und LED`s
return 0;
while (1)
{
}
}


void display_temp(uint16_t result)
{
lcd_init(LCD_DISP_ON); //LCD initialisieren
lcd_gotoxy(0,0); //Cursor an erste Stelle setzen
lcd_puts("Temp.:"); //"Temperatur" ausgeben
lcd_gotoxy(0,1); // Cursor in die zweite Zeile setzen
itoa(result,buffer,10); //AD-Wert umrechnen
lcd_puts(buffer); //AD-Wert in der zweiten Zeile ausgeben

lcd_gotoxy(9,0); // Cursor hinter "Temperatur:" setzen

//Berechnung der Temperatur
si_temp1= (result-ad_nullwert);
si_temp2= (si_temp1*si_multiplyer);
si_temp3= (si_temp2/si_const);
// Die Ganzen Grade Berechnen
si_ones=si_temp3/10;
//Die eine Nachkommastelle Berechnen
si_komma=si_temp3%10;
// Die Zehnerstellen ans Display ausgeben
itoa(si_ones,buffer,10);
lcd_puts(buffer);
lcd_putc(',');
itoa(si_komma,buffer,10);
lcd_puts(buffer);
lcd_putc(0xdf);
lcd_putc('C');
return;
}


uint16_t readADC(uint8_t channel)

{
uint8_t i;
uint16_t result = 0;

//Den ADC aktivieren und Teilungsfaktor auf 32 stellen
ADCSRA = (1<<ADEN) | (1<<ADPS0) | (1<<ADPS2);

//Kanal des Multiplexers waehlen (ADC 0)
ADMUX = (!(1<<MUX0)) | (!(1<<MUX1)) | (!(1<<MUX2));

//interne Referenzspannung verwenden (also 2,56 V)
ADMUX |= (1<<REFS1) | (1<<REFS0);

//Den ADC initialisieren und einen sog. Dummyreadout machen
ADCSRA |= (1<<ADSC);
while(ADCSRA & (1<<ADSC));

//Jetzt 3x die analoge Spannung and Kanal channel auslesen
//und dann Durchschnittswert ausrechnen.

for(i=0; i<3; i++)
{
// Eine Wandlung
ADCSRA |= (1<<ADSC);
// Auf Ergebnis warten...
while(ADCSRA & (1<<ADSC));

result += ADC;
}


//ADC wieder deaktivieren
ADCSRA &= ~(1<<ADEN);

result /= 3;

return result;
}

Ich werde die das ganze morgen noch einmal mit einem besseren Multimeter im Geschäft messen. Glaube aber nicht dass das andere Werte bringt. Wir werden sehen.

Grüße und gute Nacht!!

Bean

Mr Bean
11.12.2007, 08:10
Guten Morgen!

So ich hatte nun die Möglichkeit meinen Vref Pin im Geschäft mit einem besseren Fluke Multimeter zu messen und siehe da, ich hatte auf einmal 2,473V Am Aref Pin. Ist zwar noch nicht ganz das was ich mir vorgestellt hab, aber ich denke das geht in Ordnung so. \:D/ \:D/
Jetzt bin ich dann schonmal einen großen Schritt weiter. Vielen Dank für die Hilfe!! [-o< [-o<

Grüße!!

Bean

PS.: Das nächste was ich mir kauf wird wohl ein neues Multimeter sein... :-)

wkrug
11.12.2007, 18:08
Hab deinen Code mal auf CodeVision umgestrickt.
Er rechnet mit den vorgegebenen Werten richtig.
Stimmen deine vorgegebenen Konstanten ?

#include <mega8.h>


#include <stdlib.h>
#asm
.equ __lcd_port=0x18 ;PORTB
#endasm



#include <lcd.h>

#define F_CPU = 4000000UL
#define ad_nullwert 311
#define si_multiplyer 60
#define si_const 12
#define ADSC 6
#define ADEN 7

// uint16_t readADC(uint8_t); //Prototyp von readADC
// void display_temp(uint16_t result); //Prototyp von display

char buffer[8]; //Array für itoa anlegen
signed int uint16_t;
signed long int si_temp1; //Hilfvariable für Temperaturberechnung anlegen
signed long int si_temp2;
signed long int si_temp3;
signed long int si_ones;
signed long int si_komma;

void display_temp(unsigned int uint16_t)
{
lcd_init(16); //LCD initialisieren
lcd_gotoxy(0,0); //Cursor an erste Stelle setzen
lcd_putsf("Temp.:"); //"Temperatur" ausgeben
lcd_gotoxy(0,1); // Cursor in die zweite Zeile setzen
itoa(uint16_t,buffer); //AD-Wert umrechnen
lcd_puts(buffer); //AD-Wert in der zweiten Zeile ausgeben
lcd_gotoxy(0,9); // Cursor hinter "Temperatur:" setzen


//Berechnung der Temperatur
si_temp1= (uint16_t-ad_nullwert);
si_temp2= (si_temp1*si_multiplyer);
si_temp3= (si_temp2/si_const);
// Die Ganzen Grade Berechnen
si_ones=si_temp3/10;
//Die eine Nachkommastelle Berechnen
si_komma=si_temp3%10;
// Die Zehnerstellen ans Display ausgeben
itoa(si_ones,buffer);
lcd_puts(buffer);
lcd_putchar(',');
itoa(si_komma,buffer);
lcd_puts(buffer);
lcd_putchar(0xdf);
lcd_putchar('C');
}


int read_adc(unsigned char uc_channel)
{

unsigned char uc_i=0;
unsigned int ui_result;

//Den ADC aktivieren und Teilungsfaktor auf 32 stellen
ADCSRA = 0b10000101; //(1<<ADEN) | (1<<ADPS0) | (1<<ADPS2);

//Kanal des Multiplexers waehlen (ADC 0)
uc_channel &=0b00000111;
uc_channel |=0b11000000;

//interne Referenzspannung verwenden (also 2,56 V)

ADMUX = uc_channel;

//Den ADC initialisieren und einen sog. Dummyreadout machen
ADCSRA |= (1<<ADSC);
while(ADCSRA & (1<<ADSC));

//Jetzt 3x die analoge Spannung and Kanal channel auslesen
//und dann Durchschnittswert ausrechnen.

for(uc_i=0; uc_i<3; uc_i++)
{
// Eine Wandlung
ADCSRA |= (1<<ADSC);
// Auf Ergebnis warten...
while(ADCSRA & (1<<ADSC));

ui_result += ADCW;
}


//ADC wieder deaktivieren
ADCSRA &= ~(1<<ADEN);

ui_result /= 3;

return ui_result;
}

void main(void)
{
DDRB = 0xff; //PORT B als Ausgang


while (1)
{
//Auslesen der analogen Spannungen an Pin 0, also ADC0. In result steht das Ergebnis.
uint16_t = read_adc(0);

display_temp(uint16_t); //Umrechnung und Ausgabe der Temperatur auf Display und LED`s
}
}

Auf deinem C- Compiler wird aber das vermutlich nicht passen.

Mr Bean
11.12.2007, 18:20
Hallo

Denke im Grunde auch dass das ganze so passt. Ich hab jetzt noch einmal versucht das ganze mit Eiswasser abzugleichen. Da ist mir als erstes aufgefallen dass wenn der Wert unter 0°C springt, dann das ganze als "minus füntausendirgendwas...." (Entschuldigt den ungenauen Wert, hab ihn mir leider nicht aufgeschrieben) angezeigt wird. Dann hab ich an si_const und si_multiplyer so lang rumgespielt bis ich genau 0° hatte. Nehme ich den Sensor aber nun aus dem Wasser heraus, so steigt das ganze recht schnell in die Höhe und zeigt mir bei Zimmertemperatur 30° an. Das kann ja nicht sein. Ich denke also dass die Kennlinie nun wieder zu steil eingestellt ist. Versuche das mit "spielen an den Werten" wieder weg zu bekommen.

Grüße!!

Bean

wkrug
11.12.2007, 18:51
Lass doch dein Programm mal durch den Simulator im Einzelschrittmodus laufen und schau welcher Programmteil die Fehler produziert.
Da die Variablen im RAM seinsollten brauchst Du eigentlich nur nach den entsprechenden Rechenschritten die zugehörigen Speicheradressen auslesen und in Dezimal umrechnen.
Dann wird Dir schnell klar wo der Fehler liegt.

Hab noch mal ein wenig rumprobiert.
Der ad_nullwert hab ich als const signed long int definiert, dann berechnete er die negativen Werte richtig.

Ich hab jetzt noch ein kleines Problem mit den Nachkommastellen entdeckt, aber das kriegst Du dann ja noch selber raus.

PCMan
12.12.2007, 15:35
Wieso macht ihr die Float<->char* Umwandlung so Kompliziert?
dtostrf ist dein Freund.

Mr Bean
12.12.2007, 16:34
Hallo

Also bei meiner letzten Version sind das jetzt alles int Werte die berechnet werden. War nur am Anfang als die Berechnung mit float Werten gemacht wurde . :-b Kannst Deine dtostrf Funktion mal genauer beschreiben? Kenne die leider wie so vieles noch nicht. 8-[

Grüße!!

Bean

PCMan
12.12.2007, 20:14
Okay. Typische Anwendung:


char str[6];
float f=1.341;
dtostrf(f,6,3,str);
lcd_puts((char*)str);

Grüße,
Simon