LM75 mit Mega88PA auslesen

Gast #2294778
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.
Angehängte Dateien:
Gast #2294948
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?
Gast #2295728
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?
Gast #2296012
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.
Gast #2296113
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.
#2296785
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.
#2296931
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
Gast #2298048
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?
#2481201
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
}
Persönliche Seite #2481216
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.
#2481230
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)
#2481270
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.
#2481322
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!)?
Persönliche Seite #2481342
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.
#2481349
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?
#2481401
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren