PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : I²C Bus und LM75



Razer
08.04.2006, 20:29
Hallo an alle

Ich hab wieder ein Problem ;)
Ich hoffe ihr könnt mir helfen :)

Ich hab hier einen LM75 Temperatursensor der über I²C angesteuert wird., Als Prozessor hab ich einen Mega 8.
Als Basisaddresse hat der LM75 90.

Jedoch verstehe ich die Ansteuerung noch nicht wirklich. Ich möchte damit nur Temperaturen auslesen

Als I²C Lib will ich die Procyon Bibliothek verwenden.

Nur leider komme ich damit überhaupt nicht zusammen. Kann mir bitte wer eine Funktions Code zum Auslesen der Temperatur zeigen??
Oder kann mir wer das erklären?

Danke im Voraus

Gruß Robert

Razer
09.04.2006, 21:14
Hallo an alle

Ich hab nun mal etwas geschrieben:



#include <avr/io.h>
#include <stdlib.h>
#include "i2c.h" //Procyon I2C LiB

#define LM75_ADRESS 0x97

int lm75_read(void);

TEMPERATUR struct {
uint8_t high;
uint8_t low;
} temp;

int lm75_read(void)
{
TEMPERATUR struct {
uint8_t high;
uint8_t low;
} temp;

i2cSendStart();
i2cSendByte(LM75_ADRESS); //Sende Adresse
i2cSendByte(0X00); //Temperature Register
i2cSendSart();

temp.high = i2cReceiveByte();
temp.low = i2cReceiveByte();

i2cSendStop();
return(0)
}


Kann das so funktionieren??

Danke im Voraus

Gruß Robert

Razer
11.04.2006, 19:20
Weiß das keiner???

Ich würd mich echt über Hilfe freuen ;)

Gruß Robert

Razer
15.04.2006, 12:38
Hallo an alle

Ich hab das mal geändert:



#include <avr/io.h>
#include <stdlib.h>
#include "i2c.h" //Procyon I2C LiB

#define LM75_ADRESS 0x97

int lm75_read(void);

struct TEMPERATUR {
uint8_t high;
uint8_t low;
} temp;

int lm75_read(void)
{

i2cSendStart();
i2cSendByte(LM75_ADRESS); //Sende Adresse mit write Bit
i2cSendByte(0x00); //Temperature Register
i2cSendStart();

i2cSendByte(LM75_ADRESS | 1); //Sende Adresse mit read Bit
temp.high = i2cReceiveByte(FALSE);
temp.low = i2cReceiveByte(FALSE);
i2cSendStop();
}

int main(void)
{
i2cInit();
i2cSetBitrate(100);
lm75_read();
return(0);
}


Nur wenn ich es compilieren will, dann bekomm ich immer 2 Fehlermeldunden:
lm75.c:23: error: void value not ignored as it ought to be
lm75.c:24: error: void value not ignored as it ought to be

Warum???

Danke im Voraus

Gruß Robert

izaseba
15.04.2006, 13:11
Als Basisaddresse hat der LM75 90.

#define LM75_ADRESS 0x97

Was ist nun richtig ?

Warum benutzt Du nicht die util/twi.h damit geht das wunderbar Beispiel:



void read_data (uint8_t *buffer) {
uint8_t status,twcr,len;

/*Zuerst wird der MASTER in Transmitter Mode betrieben, um
1. Die SLAVE Adresse zu senden
2. UM den Adresspointer zu übergeben, ab den gelesen werden soll

Sende Start Condition */
TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
while ((TWCR & (1<<TWINT)) == 0);

/*Sende Slave Adresse (Hier DS1307 mit WRITE Bit */
TWDR = DS1307 | TW_WRITE;
TWCR = (1<<TWINT)|(1<<TWEN);
while ((TWCR & (1<<TWINT)) == 0);

/*Sende die zu lesende Adresse, als Adresspointer */
TWDR = 0x00;//Stelle den pointer auf adresse 0x00
TWCR = (1<<TWINT)|(1<<TWEN);
while ((TWCR & (1<<TWINT)) == 0);

/*Ab jetzt wird der Master im Reciver Mode versetzt, um die
Bytes zu empfangen
Es wirt die rep. START Condition gesendet*/
TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
while ((TWCR & (1<<TWINT)) == 0);

/*Sende Slave Adresse (Hier DS1307 mit READ Bit */
TWDR = DS1307 | TW_READ;
TWCR = (1<<TWINT)|(1<<TWEN);
while ((TWCR & (1<<TWINT)) == 0);

len = 0;
while (len < 8){
if (len == 6 ){
twcr = (1<<TWINT)|(1<<TWEN);
} else {
twcr = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
}
TWCR = twcr;
while ((TWCR & (1<<TWINT)) == 0);
switch (status = TW_STATUS){
case TW_MR_DATA_NACK:
len = 8;
case TW_MR_DATA_ACK:
*buffer++ = TWDR;
break;
}
len++;
}
TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
}

Damit lese ich 8 Bytes aus dem RTC DS1307 in einem Rutsch aus und speichere in einem Array ab.

Um eventuelle Fehler zu Finden kann man nach jedem Schritt irgendwas über UART ausgeben, dann weißt Du an welcher Stelle es kracht.

Das Beispiel ist nicht Perfekt, Die Endlosschleifen sind nicht gut, aber zum Testen sehr gut.
Gruß Sebastian

Razer
15.04.2006, 14:42
Hallo Sebastian

Basisadresse ist 90. Wenn ich die 3 Pins zum einstellen des 2. Teils der Adresse auf high ege, hab ich 97.

izaseba
15.04.2006, 14:51
Ja gut, Hauptsache die Adresse Stimmt überein :-)

Wie schon gesagt, versuch das so zu schreiben, wie im Beispiel von mir, und nach jedem Schritt gibst Du über UART irgendwas aus z.B. 1 2 3 4 5,
danach siehst Du sofort wie weit das kommt, und wo es ev. ein Problem gibt.
sonst melde Dich nochmal...

Gruß Sebastian

Razer
15.04.2006, 15:28
Hallo

Jap, die Adressen stimmen. Leider bekomm ich es gar nicht erfolgreich compiliert. :( Ich bekomm immer die Fehlermeldung

lm75.c:21: error: void value not ignored as it ought to be

Was heißt das. Kann das mit dem struct zusammenhängen?

Gruß Robert

izaseba
15.04.2006, 15:39
int main(void)
{
i2cInit();
i2cSetBitrate(100);
lm75_read();
return(0);
}


Ist das alles ?

1. es muß heißen return 0; und nicht return(0); ,return ist keine Funktion
2. Irgendwie fehlt mir hier eine Endlosschleife...
Dein Programm beendet sich nach return 0 und was dann?

Gruß Sebastian

Razer
15.04.2006, 15:48
Hi Sebastian

Danke. Das Programm war eigentlich nur ein Test. Ist ja noch nicht fertig. Deshalb springt er nach dem return ins Nirvana.

aber mit Schleife und richtiger Schreibweise von return bekomm ich es noch immer nicht richtig compiliert. Immer noch der gleiche Fehler

Gruß Robert

izaseba
15.04.2006, 16:48
Worauf bezieht sich der Fehler ?
Darauf ?


temp.high = i2cReceiveByte(FALSE);
temp.low = i2cReceiveByte(FALSE);


erwartet i2cReceiveByte ein Argument ?
Weil im erstem Post hast Du die Funktion ohne Parameter aufgerufen...
Und FALSE ? Wo ist Das definiert in i2c.h ?

Naja sonst kann ich da nicht weiter helfen, weil ich die Bibliothek nicht kenne.
So was triviales mache ich "zu Fuß", so wie oben schon beschrieben,es sind
2 oder 3 Register die da mitspielen, aber vielleicht wird hier jemand noch helfen können.

Gruß Sebastian

Razer
15.04.2006, 17:03
Hallo

Ja i2C Erwartet ein Argument. das ist das AckFlag

TRUE -- Ack
FALSE -- Nack

Gruß Robert

uC
15.04.2006, 17:46
Im Zweifelsfall schmeiss das Ding übers Hauseck, und probier nen anderen aus... Habe auch mal damit zu tun gehabt, und hab den nicht zum sprechen gekriegt - obwohl das Mistvieh einen Acknowledge von sich gab...

Gruß
uC

Razer
15.04.2006, 18:00
Hi uC

Das hat leider nichts gebracht. Der Compiler spuckt noch immer die gleiche Fehlermeldung aus :(

Gruß Robert

uC
15.04.2006, 18:25
Ach so Du hast ein Compiler Problem....
Anbei findest Du den Inhalt meiner i2c.h ist selbstgeschrieben, funzt, und
ist im Prinzip das was izaseba meint, einfach mit den armen Registern des
Atmels reden... sollte funzen



//
#define I2C_STA \
do \
{ \
TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN); \
while (!(TWCR&(1<<TWINT))); \
} \
while(0)
//
#define I2C_STO \
do \
{ \
TWCR=(1<<TWINT)|(1<<TWSTO)|(1<<TWEN); \
} \
while(0)
//ADRESSE SENDEN
#define I2C_ADR(X) \
do \
{ \
TWDR=X;\
TWCR=(1<<TWINT)|(1<<TWEN);\
while (!(TWCR&(1<<TWINT)));\
\
}\
while(0)
//DATUM SENDEN
#define I2C_SENDDATA(X) \
do \
{ \
TWDR=X;\
TWCR=(1<<TWINT)|(1<<TWEN);\
while (!(TWCR&(1<<TWINT))); \
}\
while(0)
//DATUM EMPFANGEN
#define I2C_GETDATA(X) \
do \
{ \
TWCR=(1<<TWINT)|(1<<TWEN)|(1<<TWEA);\
while (!(TWCR&(1<<TWINT))); \
X=TWDR; \
}\
while(0)

void ADS1100INIT()
{
I2C_STA;
I2C_ADR(0x96);
//TWDR=0xaa;
//TWCR=(1<<TWINT)|(1<<TWEN);
//while (!(TWCR&(1<<TWINT)));
I2C_SENDDATA(0x0d);
I2C_STO;
}

Razer
17.04.2006, 09:43
Hallo

Hier nun der Protyp aus der Lib



inline void i2cReceiveByte(u08 ackFlag)
{
// begin receive over i2c
if( ackFlag )
{
// ackFlag = TRUE: ACK the recevied data
outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA));
}
else
{
// ackFlag = FALSE: NACK the recevied data
outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT));
}
}


Villeicht hilft das. Ich hoffe ihr könnt mir helfen

Danke im Voraus

Gruß Robert

Razer
17.04.2006, 11:52
Hallo an alle

So es funktioniert jetzt so einigermaßen. Nur die Temperaturs stimmt noch nicht wirklich.

Hier nun die read Funktion:


int lm75_read(void)
{

i2cSendStart();
i2cSendByte(LM75_ADRESS | 1); //Sende Adresse mit read Bit
i2cSendStart();
i2cReceiveByte(FALSE);
temp.high = i2cGetReceivedByte();
i2cReceiveByte(FALSE);
temp.low = i2cGetReceivedByte();
i2cSendStop();
}


Der Prototyp von i2cGetReceivedByte():


inline u08 i2cGetReceivedByte(void)
{
// retieve received data byte from i2c TWDR
return( inb(TWDR) );
}


temp.low ist ja die Kommastelle. Sie steht linksbündig in der Variable temp.low. Wenn ich sie nach Rechts verschiebe und mit 5 Multipliziere, hab ich die Kommastelle oder??

temp.low = (temp.low>>=7)*5;

Soe meine ich das.

Die High Variable macht mir jedoch noch mehr Sorgen. Ich bekomm immer 0xFF (255) empfangen. Den empfangenen Wert muss ich ja durch 2 Teilen, oder??

125°C entspricht 1111 1010 = 0xFA = 250
25°C enspricht 110010 = 32 = 50

Daraus schleiße ich, dass ich den Wert durch 2 dividieren muss, oder?? Muss ich sonst noch etwas mit der Zahl machen??

Danke im Voraus

Gruß Robert

linux_80
17.04.2006, 12:53
Du bist dir sicher das Datenblatt mal auch nur kurz überflogen zu haben ?
Denn da sticht mir gleich eine Tabelle ins Auge (auch wenn man, so wie ich, nicht aus dem engl.sprachigen Raum stammt):
1.4 TEMPERATURE DATA FORMAT
da steht, das letzte Bit (LSB) gibt 0,5 Grad an,
also stimmt das im Prinzip mit dem durch 2 Teilen.

Razer
17.04.2006, 13:25
1.4 TEMPERATURE DATA FORMAT
da steht, das letzte Bit (LSB) gibt 0,5 Grad an,


Bist du dir sicher das Datenblatt überflogen zu haben?? Das LSB ist das erste Bit. Nicht das letzte. (Least Significant Bit). Die Bits werden von Rechts nach Links gezählt ;)

Zuerst muss ich prüfen ob das erste Bit gesetzt oder gelöscht ist. Bzw in eine neue VAriable kopieren.

Dann kann ich doch den low Wert um 1 Bit nach Rechts verschieben. Nun verschiebe ich den High Wert um 7 Stellen nach Links. Danach füge ich beide Variablen mit einer Oder Verknüpfung zusammen. Kann das Funktionieren??



temp.low >>= 1;
temp.high <<= 7;
temp.low = temp.low | temp.high;


Kann das funktionieren??

Gruß Robert

Razer
19.04.2006, 19:59
Hallo an alle

Ich Bitrechnung funktioniert. Ich hab einfach für temp.low und temp.high Zahlen eingesetzt und das ausgeben lassen.

Jedoch bekomme ich wenn ich den LM 75 auslesn will nur falsche Werte. Am Display steht 127.5. Wenn ich low und high und Rechnung ausgebe, liest er immer 0xFF = 255 aus.

Als stimmt da etwas nicht. Was kann den da los sein??

Ich hoffe es kann mir wer helfen

Gruß Robert

Razer
21.04.2006, 13:40
Hallo an alle

Ich hab nun die I2C Routine fertig geschrieben :) Jedoch funktioniert sie noc nicht. Ich finde jedoch keinen Fehler :(

Hier der Code



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

#define TRUE 1
#define FALSE 0
#define ACK TRUE
#define NACK FALSE

#define START 0x08
#define TW_MT_SLA_ACK 0x18
#define TW_MT_DATA_ACK 0x28

void lm75_read(void);
void i2c_init(void);
int i2c_send_start(uint8_t adress);
int i2c_send_byte(uint8_t data);
void i2c_send_stop(void);
int i2c_read(uint8_t adress, uint8_t ackflag);


struct TEMPERATUR {
uint8_t high;
uint8_t low;
} temp;

void i2c_init(void)
{
TWBR |= (1 << 5); //TWBR = 32 = 10 0000
// Prescaler = 1
}

int i2c_send_start(uint8_t adress)
{
TWCR |= ((1 << TWINT) | (1 << TWSTA) | (1 << TWEN)); //Sende start Condition
while(!(TWCR & (1 << TWINT))); //Warte bis ende

if((TWSR & 0xF8) != START)
return -1;

TWDR = adress;
TWCR = (1 << TWINT) | (1 << TWEN);
while(!(TWCR & (1 << TWINT)));

if((TWCR & 0xF8) != TW_MT_SLA_ACK)
return -1;

return 0;
}

int i2c_send_byte(uint8_t data)
{
TWDR = data;
TWCR = (1 << TWINT) | (1 << TWEN);

while(!(TWCR & (1 << TWINT)));

if((TWSR & 0xF8) != TW_MT_DATA_ACK)
return -1;

return 0;
}

void i2c_send_stop(void)
{
TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
while(!(TWCR & (1 << TWSTO)));
}

int i2c_read(uint8_t adress, uint8_t ackflag)
{
if(ackflag == ACK)
TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWEA);
else
TWCR = (1 << TWINT) | (1 << TWEN);

while(!(TWCR & (1 << TWINT)));

return TWDR;
}

void lm75_read(void)
{
i2c_send_stop();
i2c_send_start(0x9E);
temp.high = i2c_read(0x9F,ACK);
temp.low = i2c_read(0x9F, NACK);
i2c_send_stop();
}

int main(void)
{
uint8_t nachkomma, ganzzahl;

i2c_init();
lcd_init(LCD_DISP_ON);
lcd_home();
lcd_clrscr();


lcd_clrscr();
lm75_read();

if(bit_is_set(temp.low, 0))
nachkomma = 5;
else
nachkomma = 0;

temp.low >>= 1;
temp.high <<= 7;
ganzzahl= temp.low | temp.high;

lcd_put_d(ganzzahl);
lcd_putc('A');
lcd_put_d(nachkomma);


return 0;
}


Ich hoffe es kann mir wer helfen.

Danke im Voraus

Gruß Robert

linux_80
21.04.2006, 19:32
Warum sendest Du zu beginn ein STOP, das erzeugt nur Verwirrung,
sollte der Bus nicht frei sein beim START, bekommt man das schon mit, und kann darauf reagieren.
Man sollte aber den Bus wieder freigeben, falls der START nicht geklappt hat,
siehe TWI und TWI_Praxis (https://www.roboternetz.de/wissen/index.php/TWI_Praxis)
ist zwar mit Bascom gemacht, aber die Reihenfolge passt ja trotzdem.

Razer
29.04.2006, 16:54
Hallo an alle

i2c_start() funktioniert. Nur bei der Receive Routine hakt es. Ich hab Acked und Nacked getrennt.

Ich hab nach jedem Befehl eine Lcd Ausgabe eingeführt, um zu sehen wo es hakt.

Hier der Code:



int i2c_read_ack(void)
{
lcd_putc('1');
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA);
lcd_putc('2');
while(!(TWCR & (1<<TWINT)));
lcd_putc('3');

if(!(TWSR & 0x50))
{
lcd_putc('4');
return -1;
}
lcd_putc('5');
return TWDR;

}


Das ist die Funktion mit Acknoledgement. Am Display wird aber nur 12 angezeigt.

Es hängt also bei while(!(TWCR & (1<<TWINT)));

Weiß irgendwer, was da falsch sein könnte. Ich hab je´tzt echt keinen Plan mehr

Danke im Voraus

Gruß Robert

Razer
30.04.2006, 12:16
So, die Ansteuerung funktioniert jetzt :)



oid i2c_init(void)
{
TWBR = (1 << 5) | (1 << 1) | (1 << 3); //TWBR = 11
}

void i2c_send_stop(void)
{
TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
while(!(TWCR & (1 << TWSTO)));
}

int i2c_read_ack(void)
{
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA);
while(!(TWCR & (1<<TWINT)));

if(!(TWSR & 0x50))
return -1;

return TWDR;

}

int i2c_read_nack(void)
{
TWCR = (1<<TWINT) | (1<<TWEN);
while(!(TWCR & (1<<TWINT)));

if(!(TWSR & 0x58))
return -1;

return TWDR;

}

int i2c_send_start(uint8_t adress)
{
TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); //Sende start Condition
while(!(TWCR & (1 << TWINT))); //Warte bis ende

if(!(TWSR & 0x08))
return -1;

TWDR = adress;

TWCR = (1 << TWINT) | (1 << TWEN);
while(!(TWCR & (1 << TWINT)));

if(!(TWSR & 0x40))
return(-2);

return 0;
}

void readfromLM75(uint8_t *temp, uint8_t *nachkomma)
{
i2c_send_start(LM75_ADRESS | 1); //Sende Adresse + Read
*temp = i2c_read_ack();
*nachkomma = i2c_read_nack();
i2c_send_stop();

if(bit_is_set(*nachkomma, 7))
*nachkomma = 5;
else
*nachkomma = 0;
}

linux_80
30.04.2006, 17:43
Eine Anmerkung hätte ich noch,
normalerweise wird beim lesen eines Bytes zuerst das TWDR ausgelesen, und dann erst TWCR gesetzt, sonst kanns vorkommen das sich das nächste Byte schon wieder über den Bus reinschiebt, und die Daten nicht mehr aktuell sind in TWDR.
Denn normalerweise kommt das gesendete Stop-Bit auch wieder bei TWDR an und schiebt die Daten ein Bit weiter !

Und es wird aus jeder funktion bei einem Fehler mit return einfach zurückgesprungen, ohne den Bus wieder freizugeben, das kann auch zu komischen Fehlern führen !
Wenn die Startsequenz zB. keinen Erfolg hatte, muss man trotzdem den Bus freigeben, denn sonst kann ein anderer Master auch nicht weitermachen.