"Themoelement" aus R8C Wettbewerb

Postby toomuchcoffee23 » Wed Mar 10, 2010 12:00 am

Hallo,

ich habe die Schaltung "Thermoelement" aus dem R8C Wettbewerb nachgebaut und wundere mich über einen seltsamen Effekt: Während bei Temperaturen zwischen 30 und 60°C die Anzeige fast aufs Grad mit einem konventionellen Thermometer übereinstimmt, zeigt die Schaltung bei höheren Temperaturen unplausible Werte an. Bei 77°C sind es 13, bei 88°C 23 und bei 100°C 35. Verschiedene Thermoelemente habe ich schon ausprobiert, also wird es daran nicht liegen. Hat jemand Erfahrungen mit der Schaltung? Für mich sieht es irgendwie nach einem Bug beim auslesen des MAX6675 aus. Ich bin bisher noch nicht tief genug in die C-Programmierung vorgedrungen um einen möglichen Fehler zu sehen.

Grüße,
Klaus
toomuchcoffee23
 
Posts: 49
Joined: Fri Jan 03, 2014 1:52 pm

Postby js222 » Wed Mar 10, 2010 12:00 am

Ich hab hier eine thermo_11.c in der folgende Funktion zu finden ist (prüf mal ob das bei Dir der gleiche Code
ist):

char temperature(void)
{
int i;
unsigned int data;
data=0;
p3_2=0;//CS=Low
for(i=0;i >3);//gib 12-Bit Wert zurück
}

((Verdammt, ich bekomme aus irgendwelchen Gründen den Code hier nur verkrüppelt rein, egal, wichtig sind 1. und letzte Zeile))

Hier werden 16bit vom Max6675 geholt und bei der Rückgabe des Funktionswertes 3 Stellen nach rechts geschoben (also der Zahlenwert durch 8 geteilt).
1. Ergibt 16 Stellen weniger 3 Stellen bei mir immer noch
13 Bit und nicht 12 wie im Kommentar, aber das ist im Grunde nicht der Fehler, sonst würdest Du immer falsche
Werte bekommen.
2. Egal ob 16, 13 oder 12 Bit, es sind immer mehr als 8
und in einen Datentyp "char" gehen nun mal nur 8Bit rein. Die lokale Variable "data" Innerhalb der Funktion hat den richtigen Datentyp (unsigned int, ist hier 16 Bit),
aber am Ende "return (data>>3);" wird der Wert zurückgegeben. Und zwar mit dem Datentyp mit dem der
Rückgabewert definiert ist:
char temperature(void)
^^ und der ist nur 8bittig, damit werden die oberen Stellen einfach abgeschnitten und es kommen nur Werte von 0 bis 255 "aus der Funktion raus". Ab einer bestimmten Temperaur (kannst Du Dir ausrechnen wenn Du Dir die weitere Berechnung Anschaust) wird der ausgegebene Wert plötzlich Null und dann gehts wieder von vorne los 1...2...

Es gibt 2 Möglichkeiten das zu ändern, je nachdem welcher Temperaturbereich wirklich ausgegeben werden soll und was der Rest des Programms kann.
1. Man macht die komplette Temperaturberechnung in der Funktion "temperature". Dann kann man z.B. Temperaturen von 0-255°C ausgeben lassen
(dadrüber hat man dann wieder das gleiche Spielchen)
2. Man ändert den Datentyp vor der Funktion von char in unsigned int. Dann kann die Funktion auch richtig funktionieren

Es tauchen aber an anderer Stelle noch Probleme auf:
So wie ich das sehe können nur 4-Stellige Dezimalzahlen ohne Komma und Vorzeichen wiedergegeben werden,
wenn die Temperatur also negativ wird hat man wieder Unsinn in der Anzeige stehen. Wenn Du lust hast probier doch mal das Teil "Minustauglich" zu machen.
Stichworte wären signed int, Zweierkomplement.
Oder man kann auch noch die Nachkommastelle(n) ausgeben.
js222
 
Posts: 183
Joined: Fri Jan 03, 2014 1:48 pm

Postby toomuchcoffee23 » Thu Mar 11, 2010 12:00 am

Vielen Dank für die ausführliche Erklärung. Wenn ich davon ausgehe, daß in der letzten Zeile eigentlich zwischen dem for (i=0;i und dem Rest noch ein paar Zeilen stehen, haben wir den gleichen Code.
Inzwischen habe ich das Datenblatt des MAX6675 durchgelesen. Nach meinem Verständnis liefert er einen ganzzahligen Wert von 0 bis 1024°C. Damit sollte die Ausgabe einer vierstelligen Dezimalzahl ohne Vorzeichen und Komma, wie vom Autor vorgesehen, hier reichen. Die Ausgaberoutine zu erweitern ist sicher eine interessante Übung, gehe ich aber ein andermal an.

Laut Datenblatt haben die 3 Bits ganz rechts mit dem Temperaturwert nichts zu tun. Also ergibt 3 Stellen nach rechts schieben Sinn und dient hier nicht zur Division. Auf den ersten Blick würde ich nun erwarten, die 16 Bit werden um 3 nach rechts geschoben und von links werden Nullen aufgefüllt und dann werden 8 Bit von der Funktion zurückgegeben. Also sollte es bis 255 gut gehen.
Das Experiment zeigt allerdings einen Sprung bei etwas über 60. Meine Vermutung: Es sind 6 Bit: 1+2+4+8+16+32=63.
Wenn mein Denkfehler darin liegen würde, daß erst 8 Bit genommen und dann geschoben würde, blieben nur 8-3=5 Bit und nach 31 wäre Schluss. Damit stehe ich vor dem nächsten Rätsel?!
Attachments
max6675-output.jpg
toomuchcoffee23
 
Posts: 49
Joined: Fri Jan 03, 2014 1:52 pm

Postby toomuchcoffee23 » Wed Mar 17, 2010 12:00 am

Ich habe nochmal nachgelesen, der MAX6675 liefert doch Nachkommastellen, und zwar eine Auflösung von 0,25°C. Dies entspricht 2 Bit. Nun verstehe ich auch, warum im Code die Temperatur immer mit

t=(temperature()+2)/4;

ausgelesen wird. Es wird gerundet und übrig beiben 6 Bit, was sich mit meiner Beobachtung deckt.

Wie vorgeschlagen habe ich den Datentyp der Funktion temperature in unsigned int geändert und die Anzeige funktioniert einwandfrei. Mit dem Lötkolben kam ich etwas über 300°C und damit auch sicher über 8 Bit hinaus.

Wenn ich nun die volle Auflösung angezeigt bekommen möchte, sollte es doch reichen, die Rundung wegzulassen und die Funktion lcd_integer auf die richtige Anzeige umzuschreiben.
Also auslesen mit

t=(temperature();

und bei der Anzeige gibt t/4 die Vorkommastellen und t&3 die letzten bei Bit, die dann wie folgt ausgeben wären:
0 als ".00"
1 als ".25"
2 als ".50"
3 als ".75"
Der Alarmverarbeitung sollte es egal sein, in welchem Format der Vergleich erfolgt. Die Vorbelegung der Werte wäre um 2 Bit nach links zu schieben oder *4 zu nehmen. Und überall wo die Temperatur oder einer der Alarmwerte auf das Display kommt, wären 3 Stellen an voranstehendem Text zu sparen, weil sonst die 16 Stellen des LCD nicht reichen. Komme ich mit diesen Überlegungen weiter oder habe ich noch einen Denkfehler drin?
toomuchcoffee23
 
Posts: 49
Joined: Fri Jan 03, 2014 1:52 pm

Postby js222 » Wed Mar 17, 2010 12:00 am

Hallo,

ich hatte mir das Datenblatt gar nicht angeschaut, Du hast recht dass die erste "Division" (3faches Rechtsschieben) nicht mit einem kappen von Nachkommestellen zu tun hat und das das Teil ein fixes Vorzeichen und damit nur positive °C-Werte von 0,00 bis 1024,00 in schritten von 1/4°C ausgibt.

toomuchcoffee23
...
Wenn ich nun die volle Auflösung angezeigt bekommen möchte, sollte es doch reichen, die Rundung wegzulassen und die Funktion lcd_integer auf die richtige Anzeige umzuschreiben.
Also auslesen mit

t=(temperature();

und bei der Anzeige gibt t/4 die Vorkommastellen und t&3 die letzten bei Bit, die dann wie folgt ausgeben wären:
0 als ".00"
1 als ".25"
2 als ".50"
3 als ".75"


Ja, so könnte man das machen, wobei man t&3 auch z.B.
so verarbeiten könnte:

switch (data&3)
{
case 0:
lcdtext (".00")
break;
case 1:
lcdtext (".25")
break;
....
}

Je nachdem was man an schon vorhandenen Routinen
hat oder ob man jetzt besonders sparsam mit Prozessorzeit oder Speicher umgehen will.
Das einfachste wäre natürlich auf den ersten Blick einen Punkt auszugeben und dann nochmal die gleiche Funktion aufzurufen, wird aber wegen Ausgabe von
Leerzeichen zur unterdrückung von führenden Nullen nicht funktionieren. Man könnte an die Funktion also einfach o.g. code anhängen. Interessant als Programmierübung und zum späteren wiederverwenden wäre aber auch die Funktion so umzuschreiben das sie eine beliebige 16Bit Integerzahl und die Kommaposition aufnimmt und die Ausgabe entsprechend formatiert.
Man sieht der funktion übrigens jetzt schon an wie man am besten vorgeht: Sie ist zwar einfach zu verstehen, es wiederholt sich aber auch viel. Das könnte man jetzt wo noch mehr Stellen dazukommen vielleicht besser in einer (oder 2) Schleife(n) abhandeln.

toomuchcoffee23
Der Alarmverarbeitung sollte es egal sein, in welchem Format der Vergleich erfolgt. Die Vorbelegung der Werte wäre um 2 Bit nach links zu schieben oder *4 zu nehmen. Und überall wo die Temperatur oder einer der Alarmwerte auf das Display kommt, wären 3 Stellen an voranstehendem Text zu sparen, weil sonst die 16 Stellen des LCD nicht reichen. Komme ich mit diesen Überlegungen weiter oder habe ich noch einen Denkfehler drin?


Ja, im Grunde stimmt das so. Neben den Grenzwerten muss man vielleicht noch andere Werte wie Hyterese ändern oder, um bei Einstellungen im Menü nicht so lange rumdrücken zu müssen die Schrittweite erhöhen.
Man sollte innerhalb des Programms immer streng darauf achten das man einheitliche Skalierung verwendet um nicht falsch zu vergleichen, gerade wenn man mit Grenzwerten arbeitet die ja letztendlich etwas bewirken sollen (Alarm, Abschalten). Dabei hat man es dann oft mit sonst weniger gebräuchlichen Repräsentationen (dezi, centi, hekto oder eben 2er Potenzen) zu tun. Ist halt Fixkommaarithmetik, erst bei der Ausgabe formatiert man dann das Ergebnis in gebräuchliche "runde" Skalierung.
Wenn man nur wenig Platz auf dem Display hat muss man aber auch wieder mit Kompromissen leben.
Du kannst ja mal schauen ob das Display vielleicht internen Speicher hat, so das man vielleicht zwischen 2 Displayseiten Umschalten kann. Normalerweise muss man das aber im Prozessor machen was natürlich Ressourcen kostet. Man würde dann zunächst auf einem Array im Speicher arbeiten und dann zyklisch in einem Rutsch abwechselnd die eine oder andere Seite an das Display ausgeben. Das hätte wiederum den Vorteil das man leicht auf noch mehr Displayseiten erweitern kann, ausserdem wäre der Ausgabezeitpunkt aller Werte einer Seite zusammengefasst und definiert.
js222
 
Posts: 183
Joined: Fri Jan 03, 2014 1:48 pm

Postby toomuchcoffee23 » Thu Mar 25, 2010 12:00 am

Hallo,

mittlerweile funktioniert es. Ich habe den einfachen Weg gewählt und die switch anweisung unten angehängt:

//ein Zahlenwert (16-Bit) wird dezimal auf dem LCD dargestellt **********
void lcd_integer(unsigned int data)
{
unsigned char byt,n; unsigned int i;
n=data&3; // Nachkommastellen
data=data/4; // Vorkommastellen
i=data;
byt = data/1000;
data=data-byt*1000;

... nun kämpfe ich mit der Darstellung des Codes hier. Wichtig ist aber eigentlich nur, daß die switch Anweisung an der Funktion noch dranhängt und nach den ersten beiden Zeichen die Position gesetzt wird, siehe unten.

Beim ersten Versuch bin ich auf Probleme gestoßen, die mit dem Display zusammenhängen. Um auf einer Zeile mit 16 Stellen die um 3 Stellen längeren Zahlenwerte ausgeben zu können, habe ich den Cursor auf Position 6 anstatt 9 gesetzt und natürlich die Texte gekürzt. Ergebnis war eine nach dem Text leere Anzeige. Nach etwas probieren stellte ich fest, daß die Funktion zur Positionierung des Cursors zwischen den ersten und zweiten 8 Zeichen unterscheidet:

// Der Cursor des LCD wird auf eine neue Position 1..16 gesetzt ********
void lcd_pos(unsigned char spalte)
{
if (spalte>8)
lcdctrl (0xB7+spalte);
else
lcdctrl (0x7f+spalte);
delayus(1000);
}

Deswegen habe ich nach der Ausgabe der ersten beiden Stellen lcd_pos(9); eingefügt, seit dem funktioniert es. Nicht besonders schön, denn bei anderen Startpositionen als 6 wird es nicht mehr funktionieren. Wenn ich nun eine Variante schreiben wollte, die unabhängig von der Position und an besten noch mit ein oder zwei Schleifen wie von Dir vorgeschlagen arbeitet, wäre es dann sinnvoll, die Position auf dem Display in einer (globalen) Variable im gesamten Programm mitzuführen? Oder sind an dieser Stelle andere Techniken üblich?

Nebenbei habe ich noch eine Auswertung des Fühlerüberwachungsbits implementiert, in der Weise, daß die Anzeige auf "9999.00" springt. Wichtag dabei war, T- mit Masse zu verbinden, sonst geht es nicht.

Im Grunde genommen macht die Schaltung jetzt was ich will. Mehrzeilige Ausgaben brauche ich bei dieser Anwendung nicht wirklich. Den Gedanken merke ich mir aber gerne für umfangreichere Folgeprojekte.

Für Erweiterungen hätte ich noch folgende Ideen:
Den Temperaturwert würde ich gerne über die serielle Schnittstelle ausgeben, und zwar über die vorhandene UART1 und nicht UART0 wie in den Beispielen hier. Die Temperaturmessung würde ich dann gerne über Interrupt jede Sekunde aufrufen, damit die Ausgabe eine zeitliche Zuordnung bekommt. Außerdem wäre es schön, die Grenzwerte im Flash speichern zu können, damit diese beim nächsten einschalten immer noch gelten. Wo sollte ich am besten zu diesen Themen nachlesen bzw. welche Beispiele sollte ich mir anschauen?
toomuchcoffee23
 
Posts: 49
Joined: Fri Jan 03, 2014 1:52 pm

Postby mr. teflon » Wed May 12, 2010 12:00 am

Hallo,

ich habs lange Zeit schleifen lassen und fange jetzt erst an mit dem MAX6675 am R8C13. Ich guckte mir ebenfalls das Projekt aus dem Wettbewerb an und muß aber sagen, nach dem Schaltplan darf man nicht gehen, der stimmt hinten und vorne nicht. Die Belegung des MAX6675 ist völlig falsch in dessen Schaltplan. Das Thermoelement ist verpolt angeschlossen und Pin 8 ist belegt obwohl der frei ist.
Wofür der 470 Ohm Widerstand da sein soll weis wohl auch niemand zwischen MAX6675 Pin 8 und µC. Sowas kann man doch nicht veröffentlichen für einen Wettbewerb. Das diese Fehler niemand gemerkt hat wundert mich.
mr. teflon
 
Posts: 146
Joined: Fri Jan 03, 2014 1:48 pm

Postby mr. teflon » Sun May 16, 2010 12:00 am

Hallo,

ich bekomme nichts angezeigt, nur Müll. Wenn ich den MAX6675 wie aus dem Wettbewerb mit dessen Code auslese kommen nur kryptische Zeichen auf mein LCD.
Ich vermute es liegt am auslesen des MAX6675, denn mein sonstiges Programm was ich damit modifizieren möchte funktioniert ansonsten mit Ausgaben auf das LCD. Auch zeigt meine Unterroutine womit ich nackte Zahlen 0...9999 auf das LCD ausgeben für Kontrollzwecke "0000" an. ???

Ich habe den MAX6675 laut dessen Datenblatt an den R8C13 angeschlossen. Also nicht laut Schaltplan vom Wettbewerb Bsp. Er ist also laut Pin Namen angeschlossen. Ich hoffe ihr versteht was ich sagen möchte.

MAX6675 Pin 5 "SCK" ... µC P3.1
MAX6675 Pin 6 "CS" ..... µC P3.2
MAX6675 Pin 7 "SO" ..... µC P3.0

Laut Kommentar im Code ist das wohl auch so korrekt?

// Der MAX6675 wird ausgelesen ... zurückgegeben ********
// P3 = x x x x x CS SCK SO
// max_reg=0 b11 b10 b9 b8 b7 b6 b5 / b4 b3 b2 b1 b0 tco 0 0

Nur irgendwas muß falsch sein?

Hinweise? Hilfe?

Tschau
Mr. Teflon
mr. teflon
 
Posts: 146
Joined: Fri Jan 03, 2014 1:48 pm

Postby toomuchcoffee23 » Mon May 17, 2010 12:00 am

Hallo,

die Probleme hatte ich nicht. Soweit ich mich erinnere, habe ich die Schaltung einfach nachgebaut und nur die hier beschriebenen Schwierigkeiten gehabt. Alles weitere hat gepasst.
toomuchcoffee23
 
Posts: 49
Joined: Fri Jan 03, 2014 1:52 pm

Postby mr. teflon » Wed Feb 02, 2011 12:00 am

Hallo,

ich habe eine Frage zur Funktion/Formel beim Aufruf des auslesens

Das hier ist ja quasi die Standardfunktion die jeder verwendet. Ich bekomme den Code nur verstümmelt rein, weis auch nicht warum. Danke jedenfalls nochmal an den Entwickler. Das kann ich nachvollziehen. Am Ende werden die ersten 3 Bits abgeschnitten sodass man nur noch den reinen Temp.wert erhält.

unsigned int read_MAX6675_P33 (void){int i;unsigned int data;data=0;p3_3=0;for(i=0;i >3);}


Bei der Funktion/Formel komme ich nicht ganz mit.
Das geteilt durch 4 wird bestimmt benötigt um die 0.25°C Auflösung herauszunehmen und nur noch ganze Zahlen zubekommen. Nur was bewirkt vorher die Addition +2 ? Das verfälscht doch den ursprünglichen Messwert? Kann mir das jemand bitte erklären?

t=(read_MAX6675_P33()+2)/4;  // Temperatur MAX6675 lesen


Tschau
Mr. Teflon
mr. teflon
 
Posts: 146
Joined: Fri Jan 03, 2014 1:48 pm

Next

Return to Das R8C-Projekt

Who is online

Users browsing this forum: No registered users and 1 guest