Forum: Mikrocontroller und Digitale Elektronik LM75 mit Mega88PA auslesen


von Anonymus (Gast)


Angehängte Dateien:

Lesenswert?

Ich versuche den Temperatursensor LM75 auszulesen. Allerdings klappt es 
nicht ganz, weil ich nur ein Byte anstatt 2 empfangen kann. Das heißt, 
wenn ich das 2. Byte mittels i2c_readNak() auslesen möchte, hängt das 
Programm ewig fest. Das erste Byte scheint das richtige Ergebnis (26) zu 
haben. Ich hab das ganze in einer Endlosschleife implementiert, welche 
jede Sekunde die Temperatur ausliest, dabei aber erstmal nicht das 2. 
Byte anfordert. Der 1. Durchlauf funktioniert problemlos, beim nächsten 
Durchlauf hängt das Programm aber schon beim Lesen dess 1. Bytes.

Hier der Code:
1
  i2c_init();
2
  delayms(1000);
3
  i2c_stop();
4
5
  while(1) {
6
    uchar addr = 72;
7
  
8
    i2c_start(addr, TW_READ);
9
    uchar byte1 = i2c_readAck();
10
    uchar byte2 = 0;    // Hier sollte i2c_readNak() stehen
11
    i2c_stop();
12
    
13
    strcpy(strTemp, "Bytes: ");
14
    num2Char(byte1);
15
    strcat(strTemp, numChar);
16
    strcat(strTemp, " ");
17
    num2Char(byte2);
18
    strcat(strTemp, numChar);
19
20
    LCD_clear(0xFFFF);
21
    LCD_ShowString(75, 12, 1, 1, LCD_FONTCOLOR, strTemp);    // Ausgabe
22
23
    delayms(1000);
24
25
    LCD_clear(0x0F);
26
  }

Ich habe mal ein Bild vom Mega88PA angehängt. Der Temperatursensor ist 
direkt an den 2 roten Pins angeschlossen. Ich hab gehört, das Pullups 
nötig sind, aber tuns die internen Pullups auch (sind aktiviert)?

Bin für jede Hilfe dankbar.

von Oliver J. (skriptkiddy)


Lesenswert?

Anonymus schrieb:
> Ich hab gehört, das Pullups
> nötig sind, aber tuns die internen Pullups auch (sind aktiviert)?
Nein.

Die internen Pullups sind viel zu hochohmig. Häng extern 4,7k dran.

von Anonymus (Gast)


Lesenswert?

Ok, ich habe zwischen +5 - SDA und +5 - SCL jeweils ein Pullup 
eingebaut. Das Problem besteht jedoch weiterhin... Kann es sein das ich 
den Sensor geschädigt habe, weil ich vorher keine Pullups drinnen gehabt 
habe?

von Stefan W. (wswbln)


Lesenswert?

Hmm, eine kleine google Suche nach LM75 und AVR bringt massig Beispiele 
in div Programmiersprachen - ist Dein Google kaputt?

Ich habe mal etwas auf der Platte gekramt: Als ich mal mit dem LM75 
'rumspielte', hatte ich die Routinen von Chris Efstathiou benutzt. Auch 
danach kann man suchen. Die funzten auf Anhieb.

von Anonymus (Gast)


Lesenswert?

Habe schon gegoogelt und sehr viele Beispiele gefunden und getestet - 
überall das selbe Problem. Kann also nur an der Hardware liegen.

von Ich (Gast)


Lesenswert?

Wieso kann es nur an der Hardware liegen?

Ich kenne mich mit "C" nicht aus, glaube aber erkennen zu können, du 
initialisierst i2c um dann 1 Sekunde später wieder abzuschalten.

Dann folgt deine Abfrageschleife innerhalb du wieder 1 Sekunde wartezeit 
eingebaut hast. Warum? Bremst du dich bzw das Programm damit nicht aus?

von Anonymus (Gast)


Lesenswert?

Die Funktion "i2c_stop" sendet nur ein Stopsignal an den Slave, also sie 
schaltet TWI nicht ab. Ich habs natürlich auch ohne Stop versucht, 
ändert sich aber nichts am Verhalten. Hab mal gelesen dass es hilfreich 
sein soll. Das mit der Wartezeit in der Schleife liegt daran, das ich 
die Temperatur im Sekundentakt auslesen möchte.

Also um das Problem jetzt deutlicher zu formulieren: Ich kann maximal 1 
Byte mittels 'i2c_readAck()' auslesen. Wenn ich das nächste Byte 
auslesen will (egal ob ich danach stoppe und den Slave erneut mit seiner 
Adresse anspreche) hängt das Programm an der stelle ewig fest. An den 
while - Schleifen die in der Datei 'twimaster.c' sind liegt das nicht, 
denn auch wenn ich diese raus nehme hängts weiter. Erst wenn ich den 
Controller resette kann ich wieder ein Byte lesen.

von Simon K. (simon) Benutzerseite


Lesenswert?

Betreibst du den Bus zu schnell?

von Anonymus (Gast)


Lesenswert?

Mit 100kHz, laut Datenblatt und den Beispielen die ich gefunden hab ist 
das auch in ordnung. Hier die Formel:
1
TWBR = ((F_CPU / SCL_CLOCK) - 16) / 2

Demnach ist 'TWBR = 32' und 'TWSR = 0'. Ich hab den Wert von TWBR auch 
probeweise mal auf 16 und TWSR auf 0 gesetzt, was aber keinen 
Unterschied gemacht hat.

von Marco F. (m8_killer)


Lesenswert?

Hi,

versuch mal folgendes:
1
    I2C_Start();
2
    I2C_Write(TEMP_W);                           / DEVICE ADDRESS Write
3
    I2C_Write(0);                                / Pointer ADDRESS
4
    I2C_Start();                                 / RESTART
5
    I2C_Write(TEMP_R);                           / DEVICE ADDRESS Read
6
    HighByte = (I2C_Read_ACK());
7
    LowByte = (I2C_Read_NACK());
8
    I2C_Stop();

funktioniert bei mir einwandfrei. du musst zuerst den pointer 
anschreiben (hier "0") um beide Temp bytes auslesen zu können.

von Marco F. (m8_killer)


Lesenswert?

Hi nochmal,

hab grad gesehen du benutzt die lib von Peter Fleury, da gehts dann 
etwas anders:
1
#define LM75  0x90                         // Device Adresse
2
3
    i2c_init();                            // init I2C interface
4
    ret = i2c_start(LM75+I2C_WRITE);       // set device address and write mode
5
   if ( ret ) {
6
        /* failed to issue start condition, possibly no device found */
7
        i2c_stop();
8
   }else {
9
        /* issuing start condition ok, device accessible */
10
        i2c_write(0x00);                    // write Pointer address = 0
11
        i2c_rep_start(LM75+I2C_READ);       // set device address and read mode
12
        HighByte = i2c_readAck();           // read Hi byte
13
        LowByte  = i2c_readNak();           // read Low byte
14
        i2c_stop();
15
        }

ggf. noch ein wenig anpassen, den define mit der richtigen adr( von 0x90 
- 0x9E) und den code um die verarbeitung der rückgabe werte ergänzen

von Anonymus (Gast)


Lesenswert?

Danke für das Beispiel. Aber auch hier ist das selbe Problem :(. Ich 
habe letztens mal den Sensor mit dem Messgerät gecheckt und 
festgestellt, dass durch SDA permanent 4,2V fließen. Das dürfte 
eigentlich nicht sein, oder?

von Nils S. (kruemeltee) Benutzerseite


Lesenswert?

Anonymus schrieb:
> Danke für das Beispiel. Aber auch hier ist das selbe Problem :(. Ich
> habe letztens mal den Sensor mit dem Messgerät gecheckt und
> festgestellt, dass durch SDA permanent 4,2V fließen. Das dürfte
> eigentlich nicht sein, oder?

Willst du da was anderes sehen? Mit nem Messgerät geht vieles, aber die 
Bits siehst du nicht ;)

von H. G. (ledi)


Lesenswert?

Hallo!

Ich verwende die I2C-Lib von Peter Fleury und habe eine Frage zum 
Auslesen des Temperaturregisters.

Ich gebe das High-Byte und das Low-Byte direkt am PortC und PortB aus 
und lasse mir das Ergebnis mit LED´s anzeigen.

Das Ergebnis bei Raumtemperatur (rund 22°C):

High Byte: 0010 0000 = 0x20h
Low-Byte:  0001 0111 = 0x17h

Lt. Datenblatt sollte das Ergebnis im High-Byte rund 0x32h betragen.
So wie ich das verstanden habe, ist das MSB im High-Byte für das 
Vorzeichen (+-), die restlichen 7 Datenbits stellen den Temperaturwert 
dar und das LSB ist in dem Fall das MSB vom Low-Byte und steht für die 
Nachkommastelle.

Keine Ahnung, wie ich diese Werte erhalte?

Hier mein C-Code:
1
#include <avr/io.h>
2
#include <util/delay.h>
3
#include "i2cmaster.h"
4
5
#define DevLM75  0x90      // device address of EEPROM 24C02, see datasheet
6
7
int main(void)
8
{
9
    unsigned char ret, High_Byte, Low_Byte;
10
    
11
    DDRB  = 0xFF;                           // use all pins on port B for output 
12
  DDRC  = 0xFF;              // use all pins on port B for output
13
    PORTB = 0x00;                           
14
  PORTC = 0x00;
15
16
    i2c_init();                             // init I2C interface
17
18
  while(1)
19
  {
20
      /* write 0x75 to eeprom address 0x05 (Byte Write) */
21
      ret = i2c_start(DevLM75+I2C_WRITE);       // set device address and write mode
22
      if ( ret ) 
23
    {
24
          /* failed to issue start condition, possibly no device found */
25
          i2c_stop();
26
          PORTB=0xFF;                           // activate all 8 LED to show error */
27
      }
28
  
29
    else 
30
    {
31
          /* issuing start condition ok, device accessible */
32
          i2c_write(0x00);                       // write pointer address
33
       
34
          i2c_rep_start(DevLM75+I2C_READ);       // set device address and read mode
35
36
      High_Byte = i2c_readAck();          // read hih byte
37
38
          Low_Byte = i2c_readNak();              // read low byte
39
          
40
      PORTC = High_Byte;             // output high byte on the LED's 
41
          PORTB = Low_Byte;                      // output low byte on the LED's      
42
   
43
44
      i2c_stop();
45
      }
46
    _delay_ms(500);
47
  }
48
}

von Nils S. (kruemeltee) Benutzerseite


Lesenswert?

0x32 sagt das Datenblatt für +25°C, nicht für ungefähr 22 ;)

Rechne den Wert mal in Temperatur um und lass ihn dir anzeigen.

Beispiel:
1
unsigned short get_lm75(unsigned char address) {
2
  char buffer[2];
3
  unsigned short return_value;
4
5
// hier einlesen, buffer[0] = high byte, buffer[1] = low byte
6
7
  return_value = (buffer[0]<<8);
8
  return_value |= (buffer[1]);
9
  return (return_value>>7);  
10
}
11
12
double get_lm75_celsius(unsigned short lm75val) {
13
  float ret = (int)(lm75val&~0x80);
14
  ret /= 2;
15
  if (lm75val&0x80) ret *= -1;
16
   return ret;
17
}

//edit: Der Code ist sehr ungünstig für Controller, sinnlose 
Arrayzugriffe und Teilen statt schieben, wurde aber auch für Linux und 
nicht für AVR/... geschrieben. Würde ich vielleicht nochmal 
überarbeiten, wenn ich den auf einem Controller haben wollen würde.

von Karl H. (kbuchegg)


Lesenswert?

> Das Ergebnis bei Raumtemperatur (rund 22°C):
>
> High Byte: 0010 0000 = 0x20h
> Low-Byte:  0001 0111 = 0x17h

High-Byte und Low Byte vertauscht?

Wenn das was du da geschrieben hast stimmt, dann behauptet dein LM bei 
dir sind 32°C. Dreht man aber die beiden Bytes um, dann hat man 23°C

Das High-Byte gibt direkt die Grad als ganze Zahl an (0x17 == 23). Erst 
wenn du die 0.5 Grad auch noch haben willst, brauchst du das andere 
Byte. (Aber bitte nicht so brutal, wie Nils das gemacht hat)

von Nils S. (kruemeltee) Benutzerseite


Lesenswert?

>(Aber bitte nicht so brutal, wie Nils das gemacht hat)
Danke :)

von Simon K. (simon) Benutzerseite


Lesenswert?

Karl Heinz Buchegger schrieb:
> (Aber bitte nicht so brutal, wie Nils das gemacht hat)

Das sieht tatsächlich sehr stark nach "von hinten durch die Brust ins 
Auge" aus.

von Karl H. (kbuchegg)


Lesenswert?

Nils S. schrieb:
>>(Aber bitte nicht so brutal, wie Nils das gemacht hat)
> Danke :)

:-)

Das H-Byte (noch als Byte) zuerst auf signed char umcasten, dann auf 
signed int (damit die Vorzeichenerweiterung gemacht wird), dann mal 2 
(um Platz für das fehlende Bit vom L-Byte zu bekommen) und das L-Bit zb 
mit einer Abfrage des MBIT vom LByte einodern.

    ((int16_t)(int8_t)High_Byte)*2 + LowByte & 0x80 ? 1 : 0;

Oder

  int16_t Temperatur = ((int16_t)(int8_t)High_Byte)*2;
  if( LowByte & 0x80 )
    Temperatur++;


Der WErt ist dann jeweils das Doppelte der tatsächlichen Temperatur.

von Simon K. (simon) Benutzerseite


Lesenswert?

Die Ausgabe kann man dann so machen:

1. Schreibe Temperatur/2
2. Wenn Temperatur & Bit0 Schreibe ".5", ansonsten schreibe ".0"

 ;-)

von H. G. (ledi)


Lesenswert?

Karl Heinz Buchegger schrieb:
>
> High-Byte und Low Byte vertauscht?
>
> Wenn das was du da geschrieben hast stimmt, dann behauptet dein LM bei
> dir sind 32°C. Dreht man aber die beiden Bytes um, dann hat man 23°C

Und genau so ist es auch ! :-)
Vielen Dank!

Warum aber?
Wird normalerweise nicht das High-Byte zuerst ausgelesen?

von Karl H. (kbuchegg)


Lesenswert?

H. G. schrieb:
> Karl Heinz Buchegger schrieb:
>>
>> High-Byte und Low Byte vertauscht?
>>
>> Wenn das was du da geschrieben hast stimmt, dann behauptet dein LM bei
>> dir sind 32°C. Dreht man aber die beiden Bytes um, dann hat man 23°C
>
> Und genau so ist es auch ! :-)
> Vielen Dank!
>
> Warum aber?

Beim Ablesen der LED die Ports falsch zugeordnet (kann auch ein 
optischer Irrtum sein!)?

von Nils S. (kruemeltee) Benutzerseite


Lesenswert?

Karl Heinz Buchegger schrieb:
> ((int16_t)(int8_t)High_Byte)*2 + LowByte & 0x80 ? 1 : 0;

Wie ich schon schrieb, auf einem Controller ist mein Code nicht sehr 
brauchbar, aber auf einem Rechner mit mehreren hundert mhz oder mehr, 
Multitasking und einem Betriebssystem ist mir das egal ;)
So hab ich meine Zahl, fertig mit Kommastelle und Vorzeichen für printf 
usw. und zum Rechnen.

von H. G. (ledi)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Beim Ablesen der LED die Ports falsch zugeordnet (kann auch ein
> optischer Irrtum sein!)?

Ja, Ports vertauscht! -> jetzt funzt alles!

Das ganze werde ich im nächsten Schritt auf eine 7-Segment-Anzeige 
bringen.
Positive Temperaturen darzustellen ist mir klar.
Wie zeige ich aber die negativen Temperaturen richtig an?
Hast du da einen Tip für mich?

von Nils S. (kruemeltee) Benutzerseite


Lesenswert?

Wenn das Bit für negativ gesetzt ist, dann setze den Pin, der ein Minus 
auf der 7seg leuchten lässt.

von H. G. (ledi)


Lesenswert?

Nils S. schrieb:
> Wenn das Bit für negativ gesetzt ist, dann setze den Pin, der ein Minus
> auf der 7seg leuchten lässt.

Ok! Dann habe ich z.B. für -25°C folgendes:

1 1100 1110 = -206

von Karl H. (kbuchegg)


Lesenswert?

H. G. schrieb:
> Nils S. schrieb:
>> Wenn das Bit für negativ gesetzt ist, dann setze den Pin, der ein Minus
>> auf der 7seg leuchten lässt.
>
> Ok! Dann habe ich z.B. für -25°C folgendes:
>
> 1 1100 1110 = -206

Nö.
Du hast 2-er Komplement.

Das linkste Bit sagt dir, dass es sich um eine negative Zahl handelt.
Daher muss zur Ausgabe die Zahl an sich erst mal positiv gemacht werden.
Alle Bits umdrehen und 1 dazuzählen

   1 1100 1110
   0 0011 0001

+1
   0 0011 0010


das ist 0x32 oder dezimal 50. 50 halbe Grade sind 25.0°C

Also muss auf der Anzeige -25.0 stehen.

Aber um den ganzen 2-er Komplement Kram musst du dich nicht kümmern. Um 
aus einer negativen Zahl eine positive zu machen, schreibst du einfach

    Zahl = -Zahl;

und der Compiler erledigt die Drecksarbeit.

Edit: D.h. wenn du (wie weiter oben), die beiden Bytes korrekt zu einer 
16 Bit 2-er Komplement Zahl zusammengesetzt hast. Wenn du das nicht 
gemacht hast, dann musst du dich natürlich selber drum kümmern. Ich 
würde dir aber raten, erst mal eine korrekte 16 Bit Zahl aus den beiden 
Bytes zu machen. Schon alleine deswegen, weil dann auch vergleiche auf 
wärmer/kälter, die du vielleicht irgendwann mal machen wirst, (zb für 
Minimum/Maximum) wieder korrekt funktionieren.
1
void Ausgeben( int16_t Anzeigewert )
2
{
3
   if( Anzeigewert < 0 ) {
4
     LED für  '-' einschalten
5
     Anzeigewert = -Anzeigewert
6
   }
7
   else
8
     LED für '-' ausschalten
9
10
   Anzeigewert / 10  auf die linke 7-Segment
11
   Anzeigewert % 10  auf die rechte 7-Segment
12
}

wie du dann den Teil 'auf die xx 7-Segment' realisierst, musst du 
wissen. Ich schätze mal, dass da ein Multiplex beteiligt sein wird.

von Simon K. (simon) Benutzerseite


Lesenswert?

Man benötigt keine Sonderbehandlung des Vorzeichens, wenn man das so wie 
Karl Heinz macht.

von Nils S. (kruemeltee) Benutzerseite


Lesenswert?

Man muss das aber ja auch irgendwie auf die 7 Segment Anzeige bekommen, 
das erkennen und anzeigen vom Minus.

von H. G. (ledi)


Lesenswert?

> Karl Heinz Buchegger schrieb:
>> ((int16_t)(int8_t)High_Byte)*2 + LowByte & 0x80 ? 1 : 0;

Danke für die ausführliche Anleitung!

Eine Frage habe ich noch zur Typumwandlung.

((int16_t)(int8_t)High_Byte)*2 + LowByte & 0x80 ? 1 : 0;

bis auf den letzten Teil verstehe ich die Zeile!

Was aber bedeutet ? 1 : 0;

von Nils S. (kruemeltee) Benutzerseite


Lesenswert?


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
Noch kein Account? Hier anmelden.