DDR Wechsel I2C

OP #3327200
Lesenswert?

Hallo,

möchte ich Daten über I2C aus einem Speicherbauchstein auslesen muss ich

1. Die Adresse in den IC schreiben

und dann 2. die Daten empfangen.

Da das ja beides über einen PIN (SDA) laufen soll, muss ich zwischen 1 
und 2 folglich irgendwie von Output auf Input wechseln. Kann ich das so 
machen ohne irgendwelches Fehlverhalten zu erzeugen oder muss das anders 
gelöst werden?


Viele Grüße

Dennis
Gast #3327225
Lesenswert?

Warum sollte der Wechsel zwischen Aus- und Eingang Fehlverhalten 
erzeugen?
Was für ein Fehlverhalten stellst Du Dir vor?

Wie der Lesevorgang aus Deinem (für und unbekannten) Speicher auszusehen 
hat, steht normalerweise sehr ausführlich im zugehörigen Datenblatt. Je 
nach Controller (welcher wissen wir ebenfalls nicht) gibt es auch noch 
bergeweise Beispielcode oder Bibliotheksfunktionen, die wahrscheinlich 
genau das machen, was Du gerne hättest...
Gast #3327563
Lesenswert?

Deine Frage ist Vergleichbar mit folgender:

"Ich möchte mein Auto rückwärts einparken. Dazu brauche ich ja den 
Rückwärtsgang. Ist der jetzt vorne Links, oder hinten Rechts bei der 
H-Schaltung? Und muss ich sonst noch etwas beim Einparken beachten?"

Überleg mal was wir alles wissen sollten um dir Fragen überhaupt 
beantworten zu können.
(Firma: fritzler-avr.de) #3327936
Lesenswert?

Dennis A. schrieb:
> Dr.PillePalle schrieb:
>> Da hilft nur lesen
>> http://de.wikipedia.org/wiki/I2c
>
> Hätte wohl nicht gefragt wenn ich mich nicht schon eingelesen hätte. Für
> mich steht da (Wikipedia) keine Antwort auf meine Frage.

Naja ob du dich wirklich eingelesen lässt Zweifel.
Wegen dieser Aussage:

Dennis A. schrieb:
> Da das ja beides über einen PIN (SDA) laufen soll, muss ich zwischen 1
> und 2 folglich irgendwie von Output auf Input wechseln.

Wenn du dich eingelesen hättest wäre dir jetzt bekannt, dass I2C Pins 
open drain oder open collector sind...

Außerdem haste nocht nicht gesagt mit welchem Prozessor ausgelesen 
werden soll.
OP #3328778
Lesenswert?

Borsty Bürste schrieb:
> Ich denke der TO will wissen ob er beim Lesen / Schreiben dass
> DDR-Register seines Controllers von Output auf Input umstellen muss

Richtig

Borsty Bürste schrieb:
> Im Falle eines Atmel µC und falls du die Hardware TWI Schnittstelle
> verwendest musst du das nicht, da sich die Hardware selbst darum
> kümmert.

Es handelt sich um einen Atmel uC, allerdings einen Attiny 2313. Keine 
TWI Schnitstelle. Ich weiß dafür gibt es dann USI....

Es muss aber auch möglich sein das Ganze über normale I/O Pins zu 
realisieren. Dann wäre halt meine Frage die oben genannte. Muss ich Da 
zwischen Input undOutput wechseln?
#3328794
Lesenswert?

Dennis A. schrieb:
> Muss ich Da
> zwischen Input undOutput wechseln?

Im Prinzip ja. Es wird folgendermassen vorgegangen.

Bei I2C sind an den beiden Leitungen Pullup Widerstaende vorzusehen ad 
die Bausteine Opendrain/Opencollector Ausgaenge haben. Das heist der 
Highpegel wird durch den Widerstand erzeugt und der Lowpegel durch das 
durchschalten des Transistors. Fuer dich im Programm bedeutet das du 
must einen Opendrain Ausgang simulieren in deiner Software.

Wenn du eine '1' uebertragen willst stellst du deinen Pin auf Eingang 
(der Pullup sorgt fuer die '1'). Willst du einen '0' uebertragen 
schaltest du das Richtungsregister auf Ausgang und gibts dabei eine '0' 
raus.
(Firma: fritzler-avr.de) #3328807
Lesenswert?

Also anstatt auf PORTX zu schreiben, schreibste invertiert auf DDRX.

Ansonsten gibts schon genug Software I2C Librarys im Netz.
http://homepage.hispeed.ch/peterfleury/group__pfleury__ic2master.html
(je nach include ist die mit Hardwaremodul oder nur in Software)

Hätteste das im ersten Post bereits erwähnt, dasse Software I2C machen 
willst, dann hätteste dir so manchen Spruch sparen können!
OP #3328868
Lesenswert?

Helmut Lenzen schrieb:
> Bei I2C sind an den beiden Leitungen Pullup Widerstaende vorzusehen ad
> die Bausteine Opendrain/Opencollector Ausgaenge haben. Das heist der
> Highpegel wird durch den Widerstand erzeugt und der Lowpegel durch das
> durchschalten des Transistors. Fuer dich im Programm bedeutet das du
> must einen Opendrain Ausgang simulieren in deiner Software.
>
> Wenn du eine '1' uebertragen willst stellst du deinen Pin auf Eingang
> (der Pullup sorgt fuer die '1'). Willst du einen '0' uebertragen
> schaltest du das Richtungsregister auf Ausgang und gibts dabei eine '0'
> raus.

Das ist eine Antwort auf meine Frage. Danke!

Martin Wende schrieb:
> Hätteste das im ersten Post bereits erwähnt, dasse Software I2C machen
> willst, dann hätteste dir so manchen Spruch sparen können!

Hm ja, das liegt glaube ich daran, dass man irgendwie in seiner 
Gedankenwelt ist und es für selbstverständlich hält, dass andere dich 
ad-hoc verstehen. Dafür entschuldige ich mich.

Martin Wende schrieb:
> Also anstatt auf PORTX zu schreiben, schreibste invertiert auf DDRX.
Hab ich auch verstanden. Bleibt nur noch die Frage wie ich dann die 
Outputs des EEPROMS lese. Hier wurde ja nur von schreiben durch DDR 
gesprochen..
(Firma: matzetronics) #3328943
Lesenswert?

Dennis A. schrieb:
> Bleibt nur noch die Frage wie ich dann die
> Outputs des EEPROMS lese. Hier wurde ja nur von schreiben durch DDR
> gesprochen..

SCL bleibt ja ein Ausgang und wird weiterhin vom MC geclockt. 
Währenddessen liest du über das PINx Register die Daten von SDA, nachdem 
du wie oben beschrieben den SDA Pin auf Input gestellt hast.
OP #3334288
Lesenswert?

Hallo nochmal,

ich habe nun etwas experimentiert und wollte in einer ersten Funktion 
eine Schreiboperation initiieren. Dabei muss ich laut Datenblatt ( 
http://pdf1.alldatasheet.com/datasheet-pdf/view/246254/STMICROELECTRONICS/M24C01WBN3TG.html) 
erst einmal die Folge 1010 000 0 übertragen. Die ersten Bits 
identifizieren den Hersteller, die drei nachfolgenden die Nummer des 
Slaves (bei mir sind E0,E1 und E2 beide auf LOW) und die letzte 0 gibt 
an, dass es sich um einen Schreibvorgang handelt. Wie man in meinem Code 
sieht wird zwischen den Schritten geclockt.

Die Funktionen SDALOW setzt den SDA Pin als Ausgang und schreibt an den 
Port eine 0

Die Funktion SDAHIGH setzt den SDA Pin als Eingang und schreibt an den 
Port eine 1 (Pullup).

Das wurde alles bereits diskutiert. Wenn ich mich nun im 9. Clockzyklus 
befinde sollte ein ACK vom EEprom gesendet werden. Dabei gebe ich die 
Werte des SDA PORTS nach dem 8. und nach dem 9. Clockzyklus mit 
readSDA() per UART in der Konsole aus. Leider wird das Ack nicht 
empfangen da beide male der Wert 0xFF übertragen wird. (Wenn das Ack 
gesendet worden wäre, würde 0xFD stehen)

Wäre schön wenn mal einer drüber schauen könnte und hoffe auf 
konstruktive Tipps.

Hier die Funktionen:
1
void SDAHIGH ()
2
{
3
    DDRB &= ~(1);  //PB2 auf Eingang (HIGH übertragen)
4
    PORTB = 0xFF;
5
}
6

7
void SDALOW ()
8
{
9
    DDRB|= (1 << 2);  //PB2 auf Ausgang (LOW übertragen)
10
    digitalWrite(1,2,0);
11
}
12

13
void initIC2toWrite()
14
{
15
    SDALOW();
16
    SDAHIGH(); //Schreibe eine 1
17
    clock();
18
    SDALOW();  //Schreibe eine 0
19
    clock();
20
    SDAHIGH(); //Schreibe eine 1
21
    clock();
22
    SDALOW();  //Schreibe eine 0
23
    clock();
24
    SDALOW();  //Schreibe eine 0
25
    clock();
26
    SDALOW();  //Schreibe eine 0
27
    clock();
28
    SDALOW();  //Schreibe eine 0
29
    clock();
30
    SDALOW();  //Schreibe eine 0
31
    clock();
32
    //FERTIG nun warten auf ACK
33
    SDAHIGH();  //Schreibe eine 1 (Als Eingang schalten)
34
    readSDA();   //Lese Eingang
35
    clock();   //9. Clockzyklus
36
    readSDA();  //Lese Eingang (sollte ACK empfangen)
37
      _delay_ms(100);
38
}
39
int main(void)
40
{
41
    USART_Init();
42
    DDRD =(1<<2);       //PB1 = CLOCK (Ausgang)
43
    digitalWrite(2,2,1);     //Clock = High
44
    SDAHIGH();              //SDA=HIGH
45
    initIC2toWrite();
46
}
#3335012
Lesenswert?

Dennis A. schrieb:

> Wäre schön wenn mal einer drüber schauen könnte und hoffe auf
> konstruktive Tipps.


Das ist doch Schei...e

Mach dir doch als erstes gleich mal 2 Funktionen, die ein 0-Bit ausgeben 
bzw. ein 1 Bit ausgeben.

Sowas ist doch Blödsinn

>     SDAHIGH(); //Schreibe eine 1
>     clock();
>     SDALOW();  //Schreibe eine 0
>     clock();
>     SDAHIGH(); //Schreibe eine 1
>     clock();
>     SDALOW();  //Schreibe eine 0
>     clock();
>     SDALOW();  //Schreibe eine 0
>     clock();
>     SDALOW();  //Schreibe eine 0
>     clock();
>     SDALOW();  //Schreibe eine 0
>     clock();
>     SDALOW();  //Schreibe eine 0
>     clock();

Und als nächstes kommt dann eine 3.te Funktion, die 1 Byte mit allen 8 
Bits unter Zuhilfenahme der beiden Hilfsfunktionen für 0 und 1 ausgibt.

Und mit dieser 3.ten Funktion, die ein komplettes Byte ausgeben kann, 
arbeitest du dann in weiterer Folge in den höherliegenden Funktionen.

Hier siehst du doch vor lauter Lows und Highs und Clocks die Bäume vor 
lauter Wald nicht mehr.


Ausserdem:
> void initIC2toWrite()
> {
>     SDALOW();
>     SDAHIGH(); //Schreibe eine 1

Das ist doch keine gültige Startbedingung.

http://www.rn-wissen.de/index.php/I2C#Startbedingung

Am Besten du liest dir auch noch mal diesen Artikel komplett durch
OP #3335030
Lesenswert?

Karl Heinz Buchegger schrieb:
> Mach dir doch als erstes gleich mal 2 Funktionen, die ein 0-Bit ausgeben
> bzw. ein 1 Bit ausgeben.

Machen die beiden Funktionen doch SDAHIGH => 1 und SDALOW => 0

aber verstehe schon das Problem.

Noch dazu:

Karl Heinz Buchegger schrieb:
>> void initIC2toWrite()
>> {
>>     SDALOW();
>>     SDAHIGH(); //Schreibe eine 1
>
> Das ist doch keine gültige Startbedingung.

Naja ich ziehe SDA auf LOW während vorher SCL schon sowieso auf HIGH 
Steht. Bedingung erfüllt. Im nächsten Schritt geht es dann an die 
übertragung mit SDAHIGH() wird das 1. Bit gesetzt
#3335037
Lesenswert?

> void SDAHIGH ()
> {
>     DDRB &= ~(1);  //PB2 auf Eingang (HIGH übertragen)

Das ist doch nicht PB2!

Hier

> void SDALOW ()
> {
>    DDRB|= (1 << 2);  //PB2 auf Ausgang (LOW übertragen)

Hast du es fachlich korrekt geschrieben. Warum nicht bei SDAHIGH?

  DDRB &= ~( 1 << 2 );


> digitalWrite(1,2,0);
entweder du benutzt die digital_Dingsbums Funktionen oder du machst das 
mit den Portzugriffen. Aber nicht mischen. Da kommt nur Verwirrung raus.
1
Bit setzen
2
     register |= ( 1 << Bitnummer )
3

4
Bit löschen
5
     register &= ~( 1 << Bitnummer )
6

7
Bit auf 1 abfragen
8
     register & ( 1 << Bitnummer )
9

10
Bit auf 0 abfragen
11
     !( register & ( 1 << Bitnummer ) )
#3335042
Lesenswert?

Dennis A. schrieb:

> Karl Heinz Buchegger schrieb:
>>> void initIC2toWrite()
>>> {
>>>     SDALOW();
>>>     SDAHIGH(); //Schreibe eine 1
>>
>> Das ist doch keine gültige Startbedingung.
>
> Naja ich ziehe SDA auf LOW während vorher SCL schon sowieso auf HIGH
> Steht.

So?
Tut es das?

Warum macht das die Funktion nicht selbst, sondern verlässt sich darauf, 
dass das vom Aufrufer schon gemacht wurde?
#3335051
Lesenswert?

AUsserdem
1
int main(void)
2
{
3
    USART_Init();
4
    DDRD =(1<<2);       //PB1 = CLOCK (Ausgang)
5
    digitalWrite(2,2,1);     //Clock = High
6
    SDAHIGH();              //SDA=HIGH
7
    initIC2toWrite();
da geht zuerst der Clock auf High und erst dann kommt die SDA auf High. 
Das ist ist nicht das was gefordert war. Ja, ich weiss schon. Der 
Default ist Input und damit zieht der Pullup die Leitung auf high. 
Trotzdem. Das ist schon wieder implizites Zusatzwissen. Schreibs anders 
rum
1
  erst SDA auf High
2
  dann Clock auf High
3
  dann SDA auf Low
4
  dann Clock auf Low
und du hast eine direkte 1:1 Entsprechnung von der I2C Doku zu deinem 
Code. Und dann kann man das auch lesen und verstehen und muss nicht 200 
Zusatzbedingungen im Kopf haben.

?
1
  DDRD =(1<<2); //PB1 = CLOCK (Ausgang)
Das ist NICHT PB1!

Du hast da einen schönen Mischmasch.
Mach dir doch ein paar Define
1
#define SDA_PIN   PB2
2
#define SCLK_PIN  PB1
und mit denen schreibst du
1
  DDRB |= ( 1 << SDA_PIN );
dann kannst du dir auch einen Kommentar dazu sparen UND das Beste am 
ganzen: im Code steht selbst schon, welche Bedeutung dieser Pin hat. 
Hier wird ganz offensichtlich der SDA_PIN mittels DDR auf Ausgang 
geschaltet. Welcher konkreter Pin am Port B das dann auch immer war.
Keine magischen Konstanten im Code! In 3 Tagen weißt du nicht mehr ob 
mit
1
  PINB & ( 1 << 1 )
da jetzt die Clock Leitung oder die Datenleitung abgefragt wird. Mit
1
  PINB & ( 1 << SCLK_PIN )
weisst du es aber. Steht ja im Code, welche der beiden es sein soll.
(Firma: matzetronics) #3337941
Lesenswert?

Peter Dannegger schrieb:
>> SCL bleibt ja ein Ausgang und wird weiterhin vom MC geclockt.
>
> Halt!
>
> Das ist nur bei dummen Slaves erlaubt.
> Ist der Slave auch ein MC, muß er ja Interrupts verabeiten können und
> für diese Zeit zieht der Slave SCL auf low.
> Der Master darf also kein strong High auf SCL legen!

Ich habe bis jetzt noch kein I2C EEPROM gesehen, das sich durch die CLK 
Verlängerung mehr Zeit ausgebeten hätte, z.B. beim Schreibvorgang. Du 
kannst bei den meisten lediglich auf 'Busy' checken durch ein fehlendes 
ACK. Obwohl die CLK Verlängerung ein legitimes Verfahren wäre, wird es 
bei den EEPROMs nicht genutzt, vllt., um den MC nicht zu blocken.

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