Forum: Mikrocontroller und Digitale Elektronik TWI / I2C von 0 auf 255 gibt kein ACK


von Dirk (Gast)


Lesenswert?

Hallo,

Ich habe einen AT-Mega, an den ich ein PCF8574 (Portexpander) 
angeschlossen habe.

Im Moment bin ich dabei nur Daten zum PCF zu senden.

Das funktioniert jetzt alles ganz gut, es gibt nur einen sehr mysteriös 
Problem..

Ich sende eine Start Condition (TWSTA, TWEN, TWINT), dann die Adresse. 
Danach frage ich das Statusregister ab (TWSR) ob darin 18h steht 
(TW_MT_SLA_ACK).
Dann sende ich die Daten und frage wieder das Statusregister ab und 
überprüfe ob 28h (TW_MT_DATA_ACK) darin steht.

Dies ist bis auf exakt EINE Ausnahme der Fall.

Wenn ich zuerst als Data eine 0 zum PCF sende und danach eine 255, ist 
im TWSR keine 28h sondern eine 0!
Der PCF übernimmt aber beide Datenwerte ordnungsgemäß.

Hat dies Phänomen schon mal jmd. gehabt oder hat jmd. ein paar Ideen 
woran das liegen könnte?

(Die SCL Frequenz beträgt ziemlich genau 50kHz (gemessen))

von Axel R. (Gast)


Lesenswert?

0x00 Bus error due to an illegal
START or STOP condition
No TWDR action 0 1 1 X Only the internal hardware is affected, no STOP 
condition
is sent on the bus. In all cases, the bus is released
and TWSTO is cleared.

Illegal Start oder Stop Condition?

Poste mal den Code...

von Dirk (Gast)


Lesenswert?

Das ist die Funktion die für das Seden zuständig ist:


int twi_Send(uint8_t data, uint8_t address){
  char string[4];
/*  DDRC &= !((1<<DD0)|(1<<DD1));        //SCA und SCL: lesen..
  PORTC= (1<<DD0)|(1<<DD1);          //..und Pullups aktivieren*/

  TWCR= ((1<<TWINT)|(1<<TWSTA)|(1<<TWEN));  //TWI aktiveren und 
Start-Condition auslösen
  while(!(TWCR & (1<<TWINT)));        //Warten auf Start-Condition
  if((TWSR & 0xF8) !=TW_START) uart_PutS("FEHLER: keine 
StartCond");//Wenn keine Start-Condition gesendet wurde, abbrechen

  TWDR=address & (0xFE);            //Adresse mit Schreibbit (XXXXXXX0) 
in Datenregister laden..
  TWCR= ((1<<TWINT)|(1<<TWEN));        //..und senden
  while(!(TWCR & (1<<TWINT)));        //warten auf ACK oder NACK
  if((TWSR & 0xF8) != TW_MT_SLA_ACK) uart_PutS("FEHLER: Slave reagiert 
nich");//Wenn kein Slave reagiert, abbrechen

  TWDR=data;                  //Byte in Datenregister laden..
  TWCR= ((1<<TWINT)|(1<<TWEN));        //..und senden
  while(!(TWCR & (1<<TWINT)));        //warten auf ACK oder NACK
  if((TWSR & 0xF8) != TW_MT_DATA_ACK) uart_PutS("FEHLER: Slave 
akzeptiert nicht");  //Wenn nicht vom Slave akzeptiert, abbrechen

  TWCR= ((1<<TWINT)|(1<<TWSTO)|(1<<TWEN));  //Stop-Condition auslösen
  uart_PutS("STOP");
  return 0;
}

p.s.: kann man das nicht irgendwie auch als code formatieren lassen?

von Axel R. (Gast)


Lesenswert?

Ich als "C-Neuling": probier ich mal
Du sendest erst eine 0xFF als erstes Datenbyte und dann eine 0x00 als 
zweites Datenbyte.
ZWISCHEN diesen beiden sendest Du STOP. KLar, dass dann 0x00 im TWSR 
steht.

Code formatieren geht mit
<Eckige KLammer auf> grosses C <eckige KLammer zu>

Hier der code

<Eckige KLammer auf> Schrägstrich grosses C <eckige KLammer zu>
1
int twi_Send(uint8_t data1,unint8_t data2, uint8_t address){
2
  char string[4];
3
/*  DDRC &= !((1<<DD0)|(1<<DD1));        //SCA und SCL: lesen..
4
  PORTC= (1<<DD0)|(1<<DD1);          //..und Pullups aktivieren*/
5
6
  TWCR= ((1<<TWINT)|(1<<TWSTA)|(1<<TWEN));  //TWI aktiveren und
7
Start-Condition auslösen
8
  while(!(TWCR & (1<<TWINT)));        //Warten auf Start-Condition
9
  if((TWSR & 0xF8) !=TW_START) uart_PutS("FEHLER: keine
10
StartCond");//Wenn keine Start-Condition gesendet wurde, abbrechen
11
12
  TWDR=address & (0xFE);            //Adresse mit Schreibbit (XXXXXXX0)
13
in Datenregister laden..
14
  TWCR= ((1<<TWINT)|(1<<TWEN));        //..und senden
15
  while(!(TWCR & (1<<TWINT)));        //warten auf ACK oder NACK
16
  if((TWSR & 0xF8) != TW_MT_SLA_ACK) uart_PutS("FEHLER: Slave reagiert
17
nich");//Wenn kein Slave reagiert, abbrechen
data in data1 umbenannt: erstes Byte
1
  TWDR=data1;                  //Byte in Datenregister laden..
2
  TWCR= ((1<<TWINT)|(1<<TWEN));        //..und senden
3
  while(!(TWCR & (1<<TWINT)));        //warten auf ACK oder NACK
4
  if((TWSR & 0xF8) != TW_MT_DATA_ACK) uart_PutS("FEHLER: Slave
5
akzeptiert nicht");  //Wenn nicht vom Slave akzeptiert, abbrechen
data2 hinzugefügt: zweites Byte
1
  TWDR=data2;                  //Byte in Datenregister laden..
2
  TWCR= ((1<<TWINT)|(1<<TWEN));        //..und senden
3
  while(!(TWCR & (1<<TWINT)));        //warten auf ACK oder NACK
4
  if((TWSR & 0xF8) != TW_MT_DATA_ACK) uart_PutS("FEHLER: Slave
5
akzeptiert nicht");  //Wenn nicht vom Slave akzeptiert, abbrechen
6
7
8
9
  TWCR= ((1<<TWINT)|(1<<TWSTO)|(1<<TWEN));  //Stop-Condition auslösen
10
  uart_PutS("STOP");
11
  return 0;
12
}

man kann data auch als Zeiger übergeben und vorab als Array anlegen.
Als Parameter könntest Du dann noch leaenge mit übergeben und in einer 
Schleife deine Daten rausschicken, bis laenge erreicht wurde. und ERST 
DANN das STOP schicken.
1
while(dataindex < laenge) {
2
  TWDR=data[dataindex++);                  //Byte in Datenregister laden..
3
  TWCR= ((1<<TWINT)|(1<<TWEN));        //..und senden
4
  while(!(TWCR & (1<<TWINT)));        //warten auf ACK oder NACK
5
  if((TWSR & 0xF8) != TW_MT_DATA_ACK) uart_PutS("FEHLER: Slave
6
akzeptiert nicht");  //Wenn nicht vom Slave akzeptiert, abbrechen
7
}

laenge = 2
data[0]= 255
data[1]= 0
twi_Send(uint8_t laenge,unint8_t* data, uint8_t address)

bin ich jetzt aber nicht sicher, wie das mit den Zeigern war ;-))

Daher, weil es ja immer zwei Bytes sind, würde ich einfach zweimal (wie 
oben gezeigt) die Daten rausschicken, bevor STOP rausgeht.

Ich denke aber, das andere, die richtig Ahnung von C und Zeigern haben, 
da eher was zu sagen können, nicht wahr? ;-))

Viele Grüße
AxelR.

von Dirk (Gast)


Lesenswert?

Ersteinmal einen riesengroßen Dank für deine Mühe Axel!

Aber ich glaube ich habe mich schlecht ausgedrückt.
Ich will ja gar nicht direkt aufeinander folgend Bytes schicken sondern 
nur eines zur Zeit. Dann mach ich etwas anderes vllt, sogar mit einem 
anderen I2C Device "sprechen", und dann will ich erst ein weiteres 
Datenbyte senden.

Wenn ich z.B. eine 1 sende wird die 1 am PCF angezeigt. dann mache ich 
etwas anders, dann sende ich an den PCF eine 255. Im TWSR steht 28h=ACK.
Ich kann auch jede andere Zahl nehmen, die rüberschicken und danach 255 
rüberschicken - Im TWSR steht 28h.

NUR wenn ich zuerst eine 0 zum PCF sende (wird mit ACK bestätigt und 
kommt auch an) und danach eine 255 rüberschicke hab ich keine 28h im 
TWSR. Die 255 wird am PCF aber übernommen.

Sende ich zuerst eine andere Zahl (s.o.) aber schon.

(ich rufe einfach immer Obige Fkt. auf, d.h. es wird IMMER startcon, 
address, data, stopcon übertragen)

von Dirk (Gast)


Lesenswert?

Korrektur von:
Sende ich zuerst eine andere Zahl (s.o.) aber schon.

nach:
Sende ich zuerst eine andere Zahl (s.o.) steht aber die 28h im TWSR.

von Axel R. (Gast)


Lesenswert?

ICh habe das Datenblatt vom PCF nur überflogen, aber ich glaube man MUSS 
immer zwei Byte senden.

Nee, musst Du nicht - hatte irgentwie einen 16Bit Portexpander 
angeklickt, naja...
http://www.mikrocontroller.net/mc-project/Pages/Projekte/ICs/Port%20Expander/Portexpander%20Beispiel.zip
evtl. findest Du in dem Zip File was.

Ich kann mir das Verhalten dann auch nicht erklären :-(

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.