Forum: Mikrocontroller und Digitale Elektronik BH1750 per I²C an ATmega32 - low Byte immer nur 255


von Matthias S. (matze11)


Angehängte Dateien:

Lesenswert?

Hallo!

Ich möchte einen Lichtsensor über die I²C-Schnittstelle auslesen.
Der BH1750 liefert einen 16-Bit Messwert, wobei zuerst das high-Byte
und dann das low-Byte übertragen wird.

Das high-Byte kann ich verarbeiten, das low-Byte ist hingegen aber
immer nur 255. Auf dem Oszilloskop sieht es auch so aus, als dass
der Wert nur mit 255 übertragen wird.

Ich vermute, dass ich einen Fehler mit der I²C-Schnittstelle im Bereich 
ack/nack habe. Hat von euch vielleicht jemand eine Idee, warum das so 
nicht funktioniert?

Anbei das Datenblatt in dem auf Seite 7 unten das Übertragungsschema 
gezeigt ist.

Danke im Voraus!

Gruß,
Matze

P.S.: Die I²C-Funktionen stammen nicht von mir.
1
#define F_CPU 16000000UL
2
3
#include<avr/io.h>
4
#include<util/delay.h>
5
#include<inttypes.h>
6
#include "lcd4b.h"
7
#include <stdint.h>                      // für my_uitoa(uint32_t zahl, char* string)
8
9
void my_uitoa(uint32_t zahl, char* string);
10
void TWI_start(void);
11
void TWI_repeated_start(void);
12
void TWI_init_master(void);
13
void TWI_write_address(unsigned char);
14
void TWI_read_address(unsigned char);
15
void TWI_write_data(unsigned char);
16
void TWI_read_data(void);
17
void TWI_stop(void);
18
19
#define consthigh 0b00010000  // Betrieb mit 1 Lux Auflösung
20
#define constlow 0b00100011    // Betreib mit 4 Lux Auflösung
21
#define adr_wr 0b01000110    // Adresse mit r/w-Bit auf schreiben
22
#define adr_rd 0b01000111    // Adresse mit r/w-Bit auf lesen
23
24
unsigned recv_data;      
25
26
int main(void)
27
{
28
  DDRA |= 0b11111100;      //Pins 2-7 als Ausgänge //Jumper DB4-DB7 (der Rest raus)), RS auf PA2, E auf PA3, R/W auf GND,  
29
  char text[3];
30
  char high;
31
  char low;
32
  unsigned int ergebnis=0;
33
34
  lcd_linit();
35
  lcd_clear();
36
  TWI_init_master();  
37
38
  while(1)
39
  {  
40
    TWI_start();          // Startbedingung senden
41
    TWI_write_address(adr_wr);    // Adresse und Datenrichtung=schreiben senden
42
43
    TWI_write_data(constlow);    // Messung auslösen
44
    TWI_stop();            // Stoppbedingung senden
45
    
46
    _delay_ms(24);          // Messung abwarten
47
    
48
    TWI_start();
49
    TWI_read_address(adr_rd);    // Adresse und Datenrichtung=lesen senden
50
    TWI_read_data();        // Daten empfangen
51
    high=recv_data;
52
    TWI_read_data();        // Daten empfangen  
53
    TWI_stop();
54
    low=recv_data;  
55
56
    lcd_clear();
57
    
58
    itoa(low,text,10);
59
    lcd_command(line1);
60
    lcd_out(text);
61
    
62
    itoa(high,text,10);
63
    lcd_command(line2);
64
    lcd_out(text);
65
    
66
    _delay_ms(100);
67
  }
68
}
69
70
void TWI_init_master(void) // Function to initialize master
71
{
72
  TWBR=0xFF; // Bit rate
73
  TWSR=(0<<TWPS1)|(0<<TWPS0); // Setting prescalar bits
74
  // SCL freq= F_CPU/(16+2(TWBR).4^TWPS)
75
}
76
77
void TWI_start(void)
78
{
79
  // Clear TWI interrupt flag, Put start condition on SDA, Enable TWI
80
  TWCR= (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
81
  while(!(TWCR & (1<<TWINT))); // Wait till start condition is transmitted
82
  while((TWSR & 0xF8)!= 0x08); // Check for the acknowledgement
83
}
84
85
void TWI_repeated_start(void)
86
{
87
  // Clear TWI interrupt flag, Put start condition on SDA, Enable TWI
88
  TWCR= (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
89
  while(!(TWCR & (1<<TWINT))); // wait till restart condition is transmitted
90
  while((TWSR & 0xF8)!= 0x10); // Check for the acknoledgement
91
}
92
93
void TWI_write_address(unsigned char data)
94
{
95
  TWDR=data; // Address and write instruction
96
  TWCR=(1<<TWINT)|(1<<TWEN);    // Clear TWI interrupt flag,Enable TWI
97
  while (!(TWCR & (1<<TWINT))); // Wait till complete TWDR byte transmitted
98
  while((TWSR & 0xF8)!= 0x18);  // Check for the acknoledgement
99
}
100
101
void TWI_read_address(unsigned char data)
102
{
103
  TWDR=data; // Address and read instruction
104
  TWCR=(1<<TWINT)|(1<<TWEN);    // Clear TWI interrupt flag,Enable TWI
105
  while (!(TWCR & (1<<TWINT))); // Wait till complete TWDR byte received
106
  while((TWSR & 0xF8)!= 0x40);  // Check for the acknoledgement
107
}
108
109
void TWI_write_data(unsigned char data)
110
{
111
  TWDR=data; // put data in TWDR
112
  TWCR=(1<<TWINT)|(1<<TWEN);    // Clear TWI interrupt flag,Enable TWI
113
  while (!(TWCR & (1<<TWINT))); // Wait till complete TWDR byte transmitted
114
  while((TWSR & 0xF8) != 0x28); // Check for the acknoledgement
115
}
116
117
void TWI_read_data(void)
118
{
119
  TWCR=(1<<TWINT)|(1<<TWEN);    // Clear TWI interrupt flag,Enable TWI
120
  while (!(TWCR & (1<<TWINT))); // Wait till complete TWDR byte transmitted
121
  while((TWSR & 0xF8) != 0x58); // Check for the acknoledgement
122
  recv_data=TWDR;
123
124
}
125
126
void TWI_stop(void)
127
{
128
  // Clear TWI interrupt flag, Put stop condition on SDA, Enable TWI
129
  TWCR= (1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
130
  while(!(TWCR & (1<<TWSTO)));  // Wait till stop condition is transmitted
131
}

von Manuel H. (manu77)


Lesenswert?

Matthias S. schrieb:
> Ich vermute, dass ich einen Fehler mit der I²C-Schnittstelle im Bereich
> ack/nack habe.

Ich kann dir da nur einen Logik-Analyser empfehlen, ohne mich jetzt in 
deinen Code eingelesen zu haben. Das geht am schnellsten.

von Patrick C. (pcrom)


Lesenswert?

Zum fehlersuchen im I2C routinen ist es oft eine gute idee um zu testen 
mit ein PCF8574 chip, einfach 8 bits in/out.

Dabei ist es auch eine gute idee an zu fangen mit ein scan auf alle 
moeglichte i2c adresse (0x10-0xEE step 2) zum gucken ob da ein chip 
antwortet. Damit weisz man ob die gute adresse gewahlt ist und ob die 
ACK/NACK abhandlung gut funktioniert.

Oft passiert es das die 7/8 bit adressierung nicht gut eingestellt wird.

Patrick

von Matthias S. (matze11)


Lesenswert?

Hallo,

@Manuel: ich glaube nen Logik-Analysator lohnt sich bei mir nicht. :-)
@Patrick: ich habe leider kein anderes I²C-Bauteil zur Hand...

ich hoffe, dass jemand einfach das schon einmal mit einem 16-Bit-Wert 
gemacht hat. An sich ist es doch eine Standardanwendung, ich kenne mich 
bloß nicht mit der I²C und den entsprechenden Registern aus.

Hier noch einmal der Übertragungsrahmen:

Daten:         ST - Adr.+r/w - ACK - h. Byte - ACK - l. Byte - nACK - SP
Master/Slave:  (M)    (M)      (S)      (S)    (M)     (S)      (M) 
(M)

Vielleicht fällt jemanden ja noch etwas ein.

Gruß,
Matze

von Karl H. (kbuchegg)


Lesenswert?

Matthias S. schrieb:

> Ich vermute, dass ich einen Fehler mit der I²C-Schnittstelle im Bereich
> ack/nack habe.


?
Du unterscheidest ja gar nicht zwischen ACK und NAK.

Nachdem du das erste Byte gekriegt hast, bestätigst du mit einem NAK. 
Was für das andere Device bedeutet "Jetzt kommt nichts mehr, bzw. ich 
brauch nichts mehr liefern, jetzt kommt nur noch der Stop"

Sieh dir mal die Funktionen von Peter Fleury an. Da gibt es nicht ohne 
Grund 2 Funktionen
1
/*************************************************************************
2
 Read one byte from the I2C device, request more data from device 
3
 
4
 Return:  byte read from I2C device
5
*************************************************************************/
6
unsigned char i2c_readAck(void)
7
{
8
  TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA);
9
  while(!(TWCR & (1<<TWINT)));    
10
11
    return TWDR;
12
13
}/* i2c_readAck */
14
15
16
/*************************************************************************
17
 Read one byte from the I2C device, read is followed by a stop condition 
18
 
19
 Return:  byte read from I2C device
20
*************************************************************************/
21
unsigned char i2c_readNak(void)
22
{
23
  TWCR = (1<<TWINT) | (1<<TWEN);
24
  while(!(TWCR & (1<<TWINT)));
25
  
26
    return TWDR;
27
28
}/* i2c_readNak */

Nur für das letzte von dir erwartete Byte benutzt du die Funktion 
i2c_readNak. Alle anderen dazwischen liegenden Reads werden mit 
i2c_readAck abgewickelt.

von Manuel H. (manu77)


Lesenswert?


von Matthias S. (matze11)


Lesenswert?

Hallo Karl Heinz,

danke für den wertvollen Tipp!
Ich habe jetzt den Inhalt der beiden Funktionen
von Perter Fleury in meine Funktionen gepackt und
entsprechend angepasst.

Kaum zu glauben aber das Ganze funktioniert einwandfrei. ;-)

Habe vielen Dank für deine Hilfe!

Lieben Gruß,
Matze

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.