Guten Tag zusammen,
kann mir vielleicht jemand helfen?
Ich versuche an meinem Ethernut-kompatiblen Board mehrere LM75
Temperatursensoren zum Laufen zu bringen.
Dafür will ich die TWI-Master von Amtel verwenden, weil auf dem Board
ein RTC-Chip läuft, der die Routinen ebenfalls schon nutzt.
Dabei tritt folgender Effekt auf:
1. Verwende ich nur einen LM75 so wird die Temperatur angezeigt, jedoch
ohne Nachkommastelle. Das könnte ich jedoch verschmerzen.
2. Bei mehreren LM75 wird der Wert des ersten Sensors korrekt angezeigt.
Jedoch nur einmal nach dem Neustart des Boards. Alle weiteren Sensoren
zeigen den Temperaturwert 0 (Null). Wird die Abfrage der Sensoren
wiederholt, so zeigt auch der erste Sensor den Wert 0 (Null) an.
3. Tausche ich die Sensoren (und deren Adressen) so ist es das selbe
Spielchen, was aber zumindest zeigt, daß die Sensoren an sich
funktionieren.
Meine LM75-Routinen in C habe ich mal beigepackt.
Aufruf aus dem Hauptprogramm mit:
1
fprintf_P(stream,PSTR("%d"),LM75_temperature(0));
bzw.
1
fprintf_P(stream,PSTR("%d"),LM75_temperature(1));
für den zweiten Sensor.
Kann da mal bitte jemand einen Blick drauf werfen?
Ich verlieren sonst noch meine eh schon wenigen restlichen Haare.
Vielen Dank für eure Mühen.
pfifferhannes
Hallo Hans,
Problem ist, dass die Funktion die Daten im 16-bit signed Integer format
zurueckgibt. die Funkion muss man wenn man double float will als double
deklariert werden:
double LM75_temperature(unsigned char chip)
Das Half Bit braucht nicht ausgewertet werden, da das LM75 Resultat mit
2 dividiert werden muss um in Celsius direkt ausgertet werden zu können.
Hier Deine etwas umgeaenderte Funktion:
double LM75_temperature(unsigned char chip)
{
double Celsius;
...
Celsius = 0.0;
if ((LM75_temp.tt[1] & 128) == 128) // process Sign-Bit (Byte2)
{
Celsius = LM75_temp.tt[1] - 256; // Temperature < Null
}
else
{
Celsius = LM75_temp.tt[1]; // Temperature >= Null
}
Celsius = Celsius / 2.0;
return (Celsius);
}
Übrigens, wie kommst uaf: "// Temperature *10 from desired..."?
Der LM75 gibt die Werte im positiven Bereich mit 2 multipliziert aus.
Dividieren von (25 Grad) 0x032 mit 2 und Du bekommst 0x19 = 20.0 Grad.
25.5 Grad wird also als 0x033 ausgegeben. Usw.
Ich nehme an dass Du die Addressen Pins am LM75 richtig für jeden LM75
von "000" "001" bis "111" eingestaellt hast. ( Also jeden PIN
entsprechend auf Masse oder Vdd legen)
Sonst kann ich mir eigentlich nicht vorstellen was Du falsch gemacht
haben könntest. Es sei denn dass was falsches in "LM75_addr" drin steht.
Oder dass irgendwas mit der TWI_Start_Transceiver_With_Data() Funktion
nicht stimmt.
Hoffe dass hilft Dir weiter.
Gruss,
Gerhard
Hallo,
ich hatte mal einen ähnlichen Fehler. Abhilfe schaffte ein bisschen
Wartezeit zwischen 2 Abfragen auf den gleichen Sensor, da dieser sonst
immer 0° ausgibt. Ich glaube es war 1s Pause.
Danke, Gerhard & Nils,
für die Antworten.
>Gerhard: "// Temperature *10 from desired.. stammt aus meiner ursprünglichen
Routine. Habe ich anders gelöst und nach Deiner Anmerkung auch bei mir im
Quelltext korrigiert. Danke schön.
>Nils: Die Idee hatte ich schon. Bringta aber keine Lösung zumal die Abfragen
nacheinander auf unterschiedliche Chips laufen. Trotzdem Danke.
Zusatzinfo: Ich habe mal nach der Abfrage den Wert aus dem
Status-Register der LM75 ausgelesen mit
Für die erste Abfrage ist das
248 = 0xF8 // No relevant state information available; TWINT = “0”
Für alle folgenden kommt der Wert 72 zurück.
72 = 0x48 // SLA+R has been tramsmitted and NACK received
Hilft das jemand zur Lösungsfindung?
Grüße
pfifferhannes
Hallo Hans,
vielleicht liegt es daran dass Deine TWI routine nicht weiss wenn
ACK/NACK verwendet werden muss. (Obwohl nach Datenblatt das nicht
wichtig ist):
"...At the end of a read, the LM75 can accept either
Acknowledge or No Acknowledge from the Master (No Acknowledge
is typically used as a signal for the slave that the
Master has read its last byte)."
Es gibt moeglicherweise auch ein Problem wie hier im Datenblatt
beschrieben:
"An inadvertent 8-bit read from a 16-bit register, with the D7
bit low, can cause the LM75 to stop in a state where the SDA
line is held low as shown in Figure 5. This can prevent any
further bus communication until at least 9 additional clock
cycles have occurred.
Alternatively, the master can issue
clock cycles until SDA goes high, at which time issuing a
“Stop” condition will reset the LM75."
Vielleicht solltest Du mit dem Oszillographen die SDA Leitung
ueberwachen um zu sehen ob das obige Problem die Ursache sein koennte.
Bei mir verende ich diese I2C Sequenz wenn ich den CCS Compiler
verwende:
(I2C verwendet nicht die I2C hardware im IC, sondern nur irgendwelche
PORT 2 PORT pins im PIC, da SPI und I2C die gleichen PINS haben)
...
i2c_start(); //
i2c_write(0x90); // Screib Mode ist aktiv
i2c_write(0x00); // Das Pointer register ist auf Temperatur
register gerichtet
i2c_start(); //
i2c_write(0x91); // Lese Modus eingestellt
value.tmp_in[1]=i2c_read(); // Lese MSB mit ACK
value.tmp_in[0]=i2c_read(0); // Lese LSB mit NACK
i2c_stop();
Dem Datenblatt nach ist das die korrekte Kommunikation fuer den LM71 um
ein 16-bit Datenwort zu verlangen.
Du solltest vielleicht nachpruefen inwieweit bei Deiner TWI Funktion die
ACK/NACK individuell fuer jedes Byte eingestellt werden kann. Ich rieche
den Hasen irgendwie hier;:-)
Ohne selber zu experimentieren ist so ein Problem schwierig
diagnostizier bar. Ich habe vor wenn ich Zeit habe einen zweiten LM75
anzuhaengen um zu sehen ob ich aehnliche Problem haben wuerde.
Gruss und viel Glueck,
Gerhard
Hans,
besitzt Du die Source fuer:
TWI_Start_Transceiver_With_Data(...);
TWI_Get_Data_From_Transceiver(...);
TWI_Get_State_Info();
Ich wuerde mir die gerne ansehen,
Gerhard
Guten Tag zusammen,
Danke an alle, die sich so hilfreich meinem Thema annehmen.
Gerhard:
Habe Deine Routine ausprobiert, liefert aber im Moment noch ein falsches
Ergebnis. Reale Temperatur / 2 - warum auch immer. Mein Ansatz hatte
zumindest die annähernd richtige Zahl vor dem Komma.
ACK/NACK: Wird ja offenbar von der Routine in TWI_Master automatisch
generiert, wenn die Anzahl der über den Bus angeforderten Byte erreicht
ist.
Ein Oszi steht mir nicht zur Verfügung. Ich habe aber über Google einen
PC-Oszi an LPT1 gefunden. Wenn nicht anders möglich werde ich mal die
Bauteile zusammensuchen und löten. Letzte Option - ich habe immer noch
Hoffnung, daß mein Problem "nur" Software-Ursachen hat.
Danke Jörg X. - warst schneller als ich mit dem Link.
mfg
pfifferhannes
Hallo Hans,
den Fehler habe ich gemacht
Da habe ich mich aber jetzt schoen blamiert :-( .
Du musst den Teil den ich geloescht habe wieder reingeben. Dann geht's.
if ((LM75_temp.tt[0] & 128) == 128)// process Half-Bit (Byte1, Bit7)
{
Celsius = Celsius + 0.5;
}
Habe aus irgendeinen Grund den LM75_temp.tt[0] nicht beachtet, wo das
Half Bit sitzt.
Das andere Problem ist leider etwas schwieriger. Vielleicht geht es ohne
Oszi. Sollte die SDA Leitung permanent auf Null gehen, brauchst Du nur
eine LED im Pullup der SDA einfuegen, und schon wird es durch staendiges
Leuchten angezeigt.
Um aus diesem Zustand rauszukommen sende dann einfach ein oder zwei
Bytes auf den Bus bis sich die SDA Leitung wieder benimmt.
Lasse mich bitte wissen ob es hilft. Das ist sehr nuetzlich zu wissen.
Wie gross sind die Werte der Widerstaende? Ich verwende meistens 3.9K.
Vielelicht verringere sie etwas. Aber ohne Oszi ist das natuerlich schon
ein Schuss ins Dunkle.
Eine Frage habe ich noch: Was passiert wenn nur ein LM75 dran ist, aber
auf andere Adressen eingestellt wird?
Gruss,
Gerhard
Hi Jörg,
der untengenannte Teil ist doch nicht notwendig, weil das Resultat ja
schon die 0.5 Grad Aufloesung aufweist? Oder bin ich da irgendwie jetzt
blind?
if (temperature >= 0)
temp2 = temperature + 0.5; //wird ja abgerundet
else
temp2 = temperature - 0.5; // nur abrunden wäre verkehrt
Kanns Du mir das erklaeren?
Was mich uebriegens interessiert ist, wie der Compiler die Sache mit dem
Sign Bit beachtet. Z.B. wenn der LM75 den Wert 0xFF80 ausgibt fuer -0.5
Grad, dann muss (signed) int lm75val = 0x7F80 ergeben, damit Deine
Rechnung auch tatsaechlich
(float) -128 / 256.0 = -0.5 Grad ergibt.
Ich nehme an, dass das in der Praxis auch tatsaechlich funktioniert, da
ich es auf diese Weise noch nie gemacht habe.
Ich glaube auch, dass so wie Hans das im Original geschrieben hat,
warscheinlich weniger Speicher Platzaufwendig ist, da keine float
Division gebraucht wird. Die einzige float Rechnung die bei Hans
gebraucht wird, ist die Addition vom Halb-Bit.
Gruss,
Gerhard
> if (temperature >= 0)> temp2 = temperature + 0.5; //wird ja abgerundet> else> temp2 = temperature - 0.5; // nur abrunden wäre verkehrt
Danach stehen nur noch ganze Grad-Zahlen im temp2, hätte ich erwähnen
sollen. Bei der Konvertierung nach int werden die Nachpommastellen
einfach abgeschnitten, deshalb wird die Zahl vorher in die 'richtige'
Richtung korrigiert: z.B. (int)0.6 soll 1 und nicht 0 werden, (int)-0.6
soll -1 werden...
Und der Sensor liefert - wenn man die ganze Zahl betrachtet - das
256fache der Temperatur als Zweierkomplement (das ist kein Hexenwerk,
sondern meistens die normale Darstellung für (negative) int:
http://de.wikipedia.org/wiki/Zweierkomplement )
Wenn der Sensor 0xFF80 liefert, dann ist das -128 (a.k.a -0.5*256). Da
brauchts keine extra Berechnung.
hth. Jörg
Hallo Joerg,
Deine Loesung ist tatsaechlich sehr einfach. Das mit der Aufrundung
verstehe ich jetzt. Danke.
Ich war mir nicht sicher ob man ohne Nachpruefen, sich auf die
automatische Umwandlung von unsigned auf signed immer verlassen kann.
Gruss,
Gerhard
> ... automatische Umwandlung von unsigned auf signed immer verlassen kann...
Kann man ganz sicher nicht - ist hier aber nicht der Fall, weil der
Sensor einen signed Wert liefert ;) (egal was -für einen int-Typ- die
TWI-Funktion liefert)
hth. Jörg
@ Gerhard. (Gast)
>der untengenannte Teil ist doch nicht notwendig, weil das Resultat ja>schon die 0.5 Grad Aufloesung aufweist? Oder bin ich da irgendwie jetzt>blind?
Es wird gerundet. Dabei muss man zwischen positiven und negativen Zahlen
unterscheiden.
>Was mich uebriegens interessiert ist, wie der Compiler die Sache mit dem>Sign Bit beachtet. Z.B. wenn der LM75 den Wert 0xFF80 ausgibt fuer -0.5
Es ist ein ganz normales Zweierkomplement. Damit arbeitet jeder
Compiler.
>Division gebraucht wird. Die einzige float Rechnung die bei Hans>gebraucht wird, ist die Addition vom Halb-Bit.
AHHHH! Was bitte? Halb-Bits?
MFG
Falk
Hallo Falk, Joerg,
danke fuer Eure Erklaerungen.
Das mit dem Halb-bit ist nur ein ungluecklicher Ausdruck fuer das LSB
Bit im LM75 welches einen Wert von 0.5 Grad hat.
Gruss,
Gerhard
Hallo zusammen,
Lieber Gerhard - Pullups sind 4K7.
Ich habe mal einen Logiktester angeklemmt.
SDA Geht am Schluss auf 1 - scheint also nicht zu klemmen.
LM75 mit anderer Adresse funktioniert - sobald ich jedoch ein zweites
anschließe, ergibt sich wieder der selbe Effekt.
Senden von weiteren Bytes auf den Bus bringt ebenfalls keine Lösung.
Bin echt ratlos. Ich hab mal meine Routinen ergänzt und gebe Adressen
etc. auf Seriell aus. Scheint soweit alles i.O. Adressierung stimmt,
Register ebenfalls.
Beim Lesen ist mir allerdings aufgefallen, daß für messageBuf[0] die
Adresse vom LM75 zurückkommt. In messageBuf[1] steht die Temperatur als
(Ganz)-Zahl. messageBuf[2] = ist bisher immer null.
Du sprachst von unbeabsichtigten 8-bit Lesevorgängen auf den Bus.
Wie mache ich denn einen 16-bit Lesevorgang (meine C-Kenntnisse sind
sehr rudimetär...)
mfg
pfifferhannes
Hallo Hannes,
das ist wirklich schlimm. Ich werde mal sehen ob ich dieses Wochenende
bei mir mehr als einen LM75 auf einer Leitung ansteuern kann. Nur
verwende ich bei mir einen PIC und CCS C. Habe bis jetzt auch nur immer
einen LM75 oder TMP101 verwendet.
4.7K sollte OK sein. Wie lange sind Deine Verbindungsleitungen?
Vielleicht brauchen die LM75 0.1uF Abblockkondensatoren.
Ich kann nur bestaetigen dass bei mir mit meinem PIC18F452 ein DS1307,
LM75 oder TMP101, und eine 24ATC1024 EEPROM problemlos angesteuert
werden koennen. Funktioniert 100%.
Vielleicht sollte man das Datenblatt vom AVR in Bezug auf I2C noch
einmal sorgfaeltig durchgehen.
Dein Problem hat bestimmt nichts mit C zu tun. Ich verdaechtige im
Augenblick eher Hardware.
Ueber das Problem mit den 8-bits wuerde ich mir Augenblick keine
Gedanken machen, da das bei Dir anscheinend sowiso kein Problem ist.
Solange Deine TWI Funktion nicht unvollstaendige Zugriffe macht,
passiert das nicht so wie ich das selber verstehe.
Sollte ich was herausfinden werde ich mich in den naechsten paar Tagen
wieder melden. Gib nicht auf - das Problem muss loesbar sein! Muss nur
nachsehen wie viele LM75 ich zuhause habe.
Gerhard
Hallo Hannes,
habe vergessen Deine Frage zu beantworten: Den 16-Bit Lesevorgang macht
Deine TWI Funktion. Ich will mir die Sources fuer diese Funktionen
dieses Wochenende ansehen. Vielleicht hilft es.
"Beim Lesen ist mir allerdings aufgefallen, daß für messageBuf[0] die
Adresse vom LM75 zurückkommt. In messageBuf[1] steht die Temperatur als
(Ganz)-Zahl. messageBuf[2] = ist bisher immer null."
Das ist warscheinlich die TWI Funktion. WIe gesagt ich kenne diese
Funktionen leider noch nicht.
Gruss,
Gerhard
> "Beim Lesen ist mir allerdings aufgefallen, daß für messageBuf[0] die> Adresse vom LM75 zurückkommt. In messageBuf[1] steht die Temperatur als> (Ganz)-Zahl. messageBuf[2] = ist bisher immer null."
Ich hab mir den Code von Atmel noch mal gründlich (hoffe ich ;) )
angeschaut:
1
messageBuf[0]=(LM75_addr)|(1<<TWI_READ_BIT);// Setup for Chip read
2
messageBuf[1]=0;// unnoetig, weil...
3
messageBuf[2]=0;
4
TWI_Start_Transceiver_With_Data(messageBuf,3);/* die ISR auf die Adresse+R
5
reagiert und NACH der adresse 3 Bytes liest- sicherlich ungewollt */
6
7
_delay_ms(LM75_CONVERSION_TIME_MS);/* funktioniert auch nicht: die
8
maximale Wartezeit ist 262.14ms/F_CPU[MHz] -> 262.14/14.7 ? */
9
TWI_Get_Data_From_Transceiver(messageBuf,3);/* liest NUR den Puffer aus,
10
deine Daten wurden schon VOR der warteschleife 'geholt'*/
Der TWI-Code ist irgendwie kompliziert, aber es scheint nicht einfacher
zu gehen, wenn man die TWI-Hardware 'richtig' nutzen will.
hth. Jörg
Hallo Joerg,
ich habe mir die Source vom Hannes angesehen und ich habe eine Frage wie
Zeile 30 zu verstehen ist:
messageBuf[0]=(LM75_addr) | (1<<TWI_READ_BIT); // Setup for Chip read
ISt das nicht falsch? So wie ich das lese, wird TWI_READ_BIT (ist 0) um
ein bit nach links geschoben und zerstoert bit 1 von der Addresse.
Zum Beispiel wenn LM75_addr = 0x92 nach Zeile 30 ist es dann 0x90
Oder nicht?
Ich glaube, dass ist das Problem.
Um einen Schreibvorgang zu setzen muss BIT1 = 1 sein, dann muesste
LM75_addr = 0x93 sein. (0x92 | 0x01)
Gruss,
Gerhard
Joerg,
das war falsch von mir:
Um einen Schreibvorgang zu setzen muss BIT0 = 0 sein, dann muesste
LM75_addr = 0x92 sein. (0x92)
Lesen: LM75_addr = 0x93 sein (0x92 | 0x01)
Sorry!
Gerhard
Hallo Jörg und Hannes,
habe mir noch mal das Datenblatt durchgestöbert und bin (immer noch) der
Ansicht dass die 100+ms Warteschleife unnoetig ist. Solange das Shutdown
bit auf 0 ist, konvertiert der Chip das automatisch und man kan den LM75
so oft ansprechen wie man will. Das würde auch die Funktion schneller
machen.
Ich habe mir meinen eigenen PIC source angesehen und da verwende ich
auch keine Warteschleife. Es gibt ja auch kein "Start Measurement" Bit.
Nur wenn man den LM75 andauerend abschalten möchte im Start/Stop
Betrieb, müsste man ca. 120ms warten.
Übriegens, irgendwie wäre es praktischer, einfachere I2C function wie
I2C_start, I2C_read usw. zur Verfügung zu haben.
Zum Beispiel wenn man den LM75 auf Lese Modus umschalten möchte werden
jetzt 3 Bytes gesendet wenn eigentlich nur die Addresse mit dem Read-Bit
notwendig ist.
Zum Lesen der Temperatur genügt doch eigentlich:
i2c_start(); //
i2c_write(0x91); // Auf Lese Modus eingestellt
value.tmp_in[1]=i2c_read(); // Lese MSB mit ACK
value.tmp_in[0]=i2c_read(0); // Lese LSB mit NACK
i2c_stop();
Was mir auch noch auffällt ist, dass beim Setzen des Pointer Registers
das
messageBuf[2] keinen definierten Wert hat. Ob das was ausmacht weiss ich
nicht. Sollte man das nicht so senden dass nur zwei Bytes gesendet
werden:
...
messageBuf[0]=(LM75_addr) | (0<<TWI_READ_BIT);
messageBuf[1] = 0; // Pointer register auf Temperatur
TWI_Start_Transceiver_With_Data(messageBuf,2 );
// Sende dem LM75 den Read Befehl
messageBuf[0]=(LM75_addr) | (1<<TWI_READ_BIT);
TWI_Start_Transceiver_With_Data(messageBuf,1 );
// Warte auf die Interrupts mit den zwei Bytes
TWI_Get_Data_From_Transceiver(messageBuf,2); // Nur 2 Bytes erwartet
LM75_temp.tt[0]= messageBuf[0];
LM75_temp.tt[1]= messageBuf[1];
...
Jörg, Hannes was meint Ihr?
Gruss,
Gerhard
Die Warteschleife wird irgendwann nötig sein (dann, wenn man neue
Messwerte haben will ;-) ):
> The LM75 can be accessed at any time and reading the Temperature Register> will yield result from the last temperature conversion. When the LM75 is> accessed, the conversion that is in process will be interrupted and it will> be restarted after the end of the communication. Accessing the LM75> continuously without waiting at least one conversion time between> communications will prevent the device from updating the Temperature> Register with a new temperature conversion result. Consequently, the LM75> should not be accessed continuously with a wait time of less than 300 ms
(eine Fußnote aus dem Datasheet)
1
i2c_start();//
2
i2c_write(0x91);// Auf Lese Modus eingestellt
3
/*
4
value.tmp_in[1]=i2c_read(); // Lese MSB mit ACK
5
value.tmp_in[0]=i2c_read(0); // Lese LSB mit NACK
6
*/
7
// mir gefaellt das so aber besser ;)
8
intvalue;
9
value=((int)i2c_read()<<8);
10
value|=i2cread;
11
i2c_stop();
Da spricht nichts dagegen (außer dem Zitat oben). Wenn die aktuelle
I2C-Routine zu komplex ist, geht z.B. die TWI-Lib von Peter Fleury
(www.jump.to/fleury), eben ohne Interrupts.
hth. Jörg
Danke für den Hinweis. Das war mir schon bekannt. Wenn der Sensor nur ab
un dzu abgefragt wird, dann kann man das vernachlässigen. Sonst wäre es
besser, einen Timer Interrupt zu verwenden, der den LM75 im Hintergrund
mit ausreichenden Zwischenpausen anspricht und dann kann man die Wert so
oft lesen wie man will und hat dann immer frische Werte. Das läuft bei
mir so auf einem Gerät mit einem PIC.
Dein umgewandelter Code von mir gefällt mir auch besser; :-)
Ich wünschte nur wir könnten endlich dem eigentlichen Problem vom Hannes
zu Grunde kommen. Das scheint eine wirklich harte Nuss zum Knacken.
Wie schon gesagt, bei mir habe ich schon mindestens vier I2C ICs am PIC
dranhängen und es läuft alles tadellos. Sind aber alles unterschiedliche
Typen. Muss allerdings dazu bemerken, dass bei mir keine Interrupts den
Read unterstützen so wie bei dieser Library.
(Warscheinlich ist das Problem so lächerlich einfach dass man die
Ursache glatt übersieht)
Gruss,
Gerhard
> wie kriegst Du hin, die Farbe der quotierten Texte zu verändern.>> in>>> dem
ich da ">" davor schreibe ;)
@Hannes poste bitte noch mal den aktuell verwendeten Code!
hth. Jörg