Forum: Mikrocontroller und Digitale Elektronik Funktion zum Senden auf I2C nur einmal aufrufbar


von Matze (Gast)


Lesenswert?

Hallo,

Ich möchte einen I2C DS3231 RTC Auslesen.
Dazu habe ich eine Funktion SendByte geschrieben, welche beim 1. Aufruf 
auch korrekt das übergebene Byte Sendet, auch Ack wird erkannt.

Da ich den DS3231 aber zuerst "Auswählen" muss, (0xD1) wird dadurch der 
1. Aufruf der Funktion schon verbraucht.
Nun muss ich die Funktion noch ein 2. Mal aufrufen, um mitzuteilen 
welche Addresse ich lesen möchte.

Hier geht auf einmal vieles Schief, was tatsächlich gesendet wird hat 
nichts mit dem zu tun was der Funktion übergeben wird.
Wenn z.b. 0xff gesendet werden soll, so sendet sie 0x88.
Auch das Timing wird nicht mehr eingehalten, so ändern ich SDA und SCL 
zum Ende eines Bits gleichzeitig.
Ich sehe nicht wie dass in der Funktion möglich sein sollte.

Beim 1. Aufruf ändert sich SDA "Schön" innerhalb des SCL-High.

Wie kann das sein?
1
I2C_Start();
2
if(I2C_SendByte(0xD1))
3
{
4
  I2C_SendByte(0xff);
5
  I2C_ReadByte();
6
}
7
I2C_Stop();
8
9
int I2C_SendByte(unsigned short int Byte)
10
{
11
  int Vergl=0x80;
12
  PORTE.DIRSET=0x03;  //I2C Auf Ausgang
13
  _delay_us(20);
14
  for(int i=0;i<=7;i++,Vergl=Vergl/2)
15
  {
16
    PORTE.OUTCLR=0x02;  //SCL=LOW --> Daten änderbar
17
    _delay_us(20);
18
    if(Byte&Vergl)  // ->1 Senden
19
    {
20
      PORTE.OUTSET=0x01;  //SDA=High
21
    }
22
    else      // ->0 Senden
23
    {
24
      PORTE.OUTCLR=0x01;  //SDA=Low
25
    }
26
    _delay_us(20);
27
    PORTE.OUTSET=0x02;  //SCL=High --> Daten Gültig
28
    _delay_us(20);
29
  }
30
  //9. Bit für Ack
31
  PORTE.OUTCLR=0x02;  //SCL=LOW --> Daten änderbar
32
  _delay_us(2);
33
  PORTE.DIRCLR=0x01;  //SDA auf Eingang
34
  _delay_us(20);
35
  PORTE.OUTSET=0x02;  //SCL=High --> Slave gibt Ack
36
  _delay_us(20);
37
  //Nun sollte Ack Anliegen
38
  if(PORTE.IN&0x01)  //Ack Negativ
39
  {
40
    _delay_us(20);
41
    PORTE.OUTCLR=0x02;  //SCL=LOW --> Daten änderbar
42
    return(0x00);
43
  }
44
  else        //Ack Positiv
45
  {
46
    _delay_us(20);
47
    PORTE.OUTCLR=0x02;  //SCL=LOW --> Daten änderbar
48
    return(0x01);
49
  }
50
}

von spess53 (Gast)


Lesenswert?

Hi

>for(int i=0;i<=7;i++,Vergl=Vergl/2)

Das werden nur 7 Datenbits.

MfG Spess

von Karl H. (kbuchegg)


Lesenswert?

spess53 schrieb:
> Hi
>
>>for(int i=0;i<=7;i++,Vergl=Vergl/2)
>
> Das werden nur 7 Datenbits.

es werden schon 8.

Dieser Unsinn mit dem kleiner-gleich stirbt wohl nie aus.

von Matze (Gast)


Angehängte Dateien:

Lesenswert?

Hier ein Bild, um das Problem zu Veranschaulichen.

von (prx) A. K. (prx)


Lesenswert?

Die I2C Sequenz ist nicht zulässig. Entweder schreiben (0xD0), oder 
lesen (0xD1), nicht aber frei nach Laune beides.

Wenn der I2C Slave mit der 8-Bit Adresse 0xD0 eine Registernummer oder 
ein Kommandobyte verlangt, dann liest man ein Register üblicherweise so:
   start condition
   Adresse 0xD0 schreiben
   Registernummer schreiben
   repeated start condition
   Adresse 0xD1 schreiben
   Registerinhalt lesen
   stop condition

Bei manchen Slaves geht es auch so:
   start condition
   Adresse 0xD0 schreiben
   Registernummer schreiben
   stop condition

   start condition
   Adresse 0xD1 schreiben
   Registerinhalt lesen
   stop condition

: Bearbeitet durch User
von Matze (Gast)


Angehängte Dateien:

Lesenswert?

A. K. schrieb:
> Die I2C Sequenz ist nicht zulässig. Entweder schreiben (0xD0), oder
> lesen (0xD1), nicht aber frei nach Laune beides.

Ich versuche eigentlich nicht beides, oder wie ist das gemeint?

A. K. schrieb:
> Wenn der I2C Slave eine Registernummer verlangt, dann geht das
> üblicherweise so:
>    start condition
>    Adresse 0xD0 schreiben
>    Registernummer schreiben

Start Condition, ist drin.
Addresse D0 --> Bauteil Auswählen, zusammen mit info ob Lesen oder 
Schreiben.
Registernummer schreiben --> Hier Hackts

Anbei ein Auszug aus dem Datenblatt zu DS3231.

von Matze (Gast)


Lesenswert?

A. K. schrieb:
> I2C Slave mit der 8-Bit Adresse

Er hat eine 7 Bit Addresse, was soweit ich weiss auch normal ist.
Das 8. Bit entscheidet über lesen/Schreiben.

von spess53 (Gast)


Lesenswert?

Hi

>Dieser Unsinn mit dem kleiner-gleich stirbt wohl nie aus.

Stimmt, das <= hatte ich übersehen.

@Matze (Gast)

>  PORTE.DIRSET=0x03;  //I2C Auf Ausgang
>   ...
>      PORTE.OUTSET=0x01;  //SDA=High
>    }
>    else      // ->0 Senden
>    {
>      PORTE.OUTCLR=0x01;  //SDA=Low

Das ist aber nicht im Sinne des Erfinders von I2C. Die I2C-Pins sind 
Open Collektor. Da gibt es nur nur aktiv L. Das H wir durch die 
Pull-Up-Widerstände erzeugt. Praktisch mach man das, indem man den Port 
auf L setzt und nur zwischen Ein- und Ausgang umschaltet.

MfG Spess

von Sebastian W. (wangnick)


Lesenswert?

Matze schrieb:
> Er hat eine 7 Bit Addresse, was soweit ich weiss auch normal ist.
> Das 8. Bit entscheidet über lesen/Schreiben.

Ja eben. Bei D1 ist das Lesebit gesetzt, aber danach schreibst Du 
FF.

LG, Sebastian

von (prx) A. K. (prx)


Lesenswert?

Matze schrieb:
>> I2C Slave mit der 8-Bit Adresse
>
> Er hat eine 7 Bit Addresse, was soweit ich weiss auch normal ist.
> Das 8. Bit entscheidet über lesen/Schreiben.

Ich unterscheide so zwischen der 7-Bit Adresse 0x68 und der 8-Bit 
Adresse 0xD0. Manche I2C APIs verlangt es nach der 7-Bit Variante (die 
shiften dann intern), bei anderen schreibt man es so wie hier.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Matze schrieb:
> Anbei ein Auszug aus dem Datenblatt zu DS3231.

Fig 3 beschreibt die Sequenz zum Schreiben eines Registers vom DS3231.

Wenn du hingegen lesen willst, dann halte dich an Fig 5 (mein erstes 
Beispiel) oder Fig 4 (mein zweites).

von Matze (Gast)


Lesenswert?

spess53 schrieb:
> Das ist aber nicht im Sinne des Erfinders von I2C. Die I2C-Pins sind
> Open Collektor. Da gibt es nur nur aktiv L. Das H wir durch die
> Pull-Up-Widerstände erzeugt. Praktisch mach man das, indem man den Port
> auf L setzt und nur zwischen Ein- und Ausgang umschaltet.

Hab ich grade geändert
1
    PORTE.OUTCLR=0x03;  //Beide auf LOW
2
    I2C_Start();
3
    if(I2C_SendByte(0xD0))
4
    {
5
      I2C_SendByte(0x00);
6
      I2C_SendByte(0x02);
7
      I2C_SendByte(0x02);
8
      I2C_SendByte(0x02);
9
      I2C_SendByte(0x02);
10
    }
11
    I2C_Stop();
12
13
int I2C_SendByte(unsigned short int Byte)
14
{
15
  int Vergl=0x80;
16
  _delay_us(20);
17
  for(int i=0;i<8;i++,Vergl=Vergl/2)
18
  {
19
    PORTE.DIRSET=0x02;  //SCL=Ausgang->LOW --> Daten änderbar
20
    _delay_us(20);
21
    if(Byte&Vergl)  // ->1 Senden
22
    {
23
      PORTE.DIRCLR=0x01;  //SDA=Eingang->High
24
    }
25
    else      // ->0 Senden
26
    {
27
      PORTE.DIRSET=0x01;  //SDA=Ausgang->Low
28
    }
29
    _delay_us(20);
30
    PORTE.DIRCLR=0x02;  //SCL=Eingang->High --> Daten Gültig
31
    _delay_us(20);
32
  }
33
  //9. Bit für Ack
34
  PORTE.DIRSET=0x02;  //SCL=Ausgang->LOW --> Daten änderbar
35
  _delay_us(10);
36
  PORTE.DIRCLR=0x01;  //SDA=Eingang->High
37
  _delay_us(20);
38
  PORTE.DIRCLR=0x02;  //SCL=Eingang->High --> Slave gibt Ack
39
  _delay_us(20);
40
  //Nun sollte Ack Anliegen
41
  if(PORTE.IN&0x01)  //Ack Negativ
42
  {
43
    _delay_us(20);
44
    PORTE.DIRSET=0x02;  //SCL=Ausgang->LOW --> Daten änderbar
45
    return(0x00);
46
  }
47
  else        //Ack Positiv
48
  {
49
    _delay_us(20);
50
    PORTE.DIRSET=0x02;  //SCL=Ausgang->LOW --> Daten änderbar
51
    return(0x01);
52
  }
53
}
54
55
int I2C_Start() //SDA->GND, SCL=HIGH
56
{
57
  PORTE.DIRCLR=0x02;  //SCL=Eingang->High
58
  _delay_us(20);
59
  PORTE.DIRSET=0x01;  //SDA=Ausgang->LOW
60
  _delay_us(20);
61
}
62
63
int I2C_Stop()  //SDA->High, SCL=HIGH
64
{
65
  PORTE.DIRSET=0x01;  //SDA=Ausgang->LOW
66
  _delay_us(20);
67
  PORTE.DIRCLR=0x02;  //SCL=Eingang->High
68
  _delay_us(20);
69
  PORTE.DIRCLR=0x01;  //SDA=Eingang->High
70
  _delay_us(20);
71
}

Sebastian Wangnick schrieb:
> Ja eben. Bei D1 ist das Lesebit gesetzt, aber danach schreibst Du
> FF.

A. K. schrieb:
> Fig 3 beschreibt die Sequenz zum Schreiben eines Registers vom DS3231.
>
> Wenn du hingegen lesen willst, dann halte dich an Fig 5 (mein erstes
> Beispiel) oder Fig 4 (mein zweites).

Stimmt, das ist mir nicht aufgefallen, dachte ich müsste in jedem fall 
eine Addresse angeben ab der ich Lesen/Schreiben möchte.
Gemäß des Lesens denke ich nun, dass er mit einem Zeiger seine Register 
einfach durchliest.

So muss man wohl Figure 5 realisieren um mit sicherheit zu wissen wo man 
liest.

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.