Forum: Mikrocontroller und Digitale Elektronik I2C EEPROM Beschreiben mit SAM3X8: Pollingproblem


von Hanns-Jürgen M. (yogy)


Lesenswert?

Eigentlich klappt das Beschreiben des EEPROMS mit den daten, zumindest, 
solange ich nach dem Beschreiben einen Warteschlife einfüge.

Laut Datenblatt soll das Ende des Schreibzyklus auch per Poling 
festgestellt werden können, das ggf. Zeit spart. Aber das funzt bei mir 
irgendwie nicht. Da ist wohl ein blöder Fehler, den ich noch nicht 
finden konnte.

Das aktuelle Ende meiner Bescreibungsroutine sieht wie folgt aus:
1
  //wait for writecycle finished
2
3
  Delay1ms(20);
4
  /*
5
  while (millis_EEPROM < 24) {
6
    TWI0->TWI_MMR = 0;
7
    TWI0->TWI_MMR = TWI_MMR_MREAD | TWI_MMR_IADRSZ_2_BYTE | adr << 16; 
8
    TWI0->TWI_IADR = iadr;
9
    TWI0->TWI_CR = TWI_CR_START | TWI_CR_STOP; 
10
    Watchdog_Reset();
11
    
12
    if((TWI0->TWI_SR & TWI_SR_NACK) == 0) {
13
      millis_EEPROM = 100;
14
      j = 0;
15
    }
16
  }
17
  */
18
  return  j;

Mit der Notloesung "Delay1ms(20)", funktioniert es (Wartezeit 20 ms)

Die eigentlich, hier auskommentierte, vorgesehene Polling funktioniert 
nicht. Die darin befindliche Warteschleife wird mittels des ms-Zaehlers 
zwangsweise verlassen, damit sich die SW nicht aufhängen kann.

Wo bin ich wieder mal zu bloed gewesen?

von Peter D. (peda)


Lesenswert?

Zum Polling sendet man Start + Adresse und wertet aus, ob NACK 
geantwortet wird. NACK bedeutet, daß der EEPROM noch beschäftigt ist. 
Dann muß Stop gesendet werden und der nächste Pollingversuch kann 
erfolgen.
Hier mal ein Beispiel:
Beitrag "Re: I2C Eeprom AT24C512 Lib für Pagewrite in C"

von Hanns-Jürgen M. (yogy)


Lesenswert?

Ja, schon klar. Mit "manueller" I2C-Lösung oder auch mit einem AVR (dazu 
habe ich eine Vorlage) ist das auch okay, aber für den SAM3X8 nicht. Die 
dortige TWI-Maschine ist ziemlich komplex.

Da ich Datenblatt nun gelesen habe, daß zusammen mit NACK auch TXCOMP im 
Statusregister gesetzt werden, habe ich "meine" dennoch noch nicht 
funktionierende Polling-Routine wie folgt geändert:
1
millis_EEPROM=0;
2
  while (millis_EEPROM < 24) {
3
    TWI0->TWI_MMR = 0;
4
    TWI0->TWI_MMR = TWI_MMR_MREAD | TWI_MMR_IADRSZ_2_BYTE | adr << 16; 
5
    TWI0->TWI_IADR = iadr;
6
    TWI0->TWI_CR = TWI_CR_START | TWI_CR_STOP;  
7
    Watchdog_Reset();
8
    
9
    if(((TWI0->TWI_SR & TWI_SR_NACK) == 0) && ((TWI0->TWI_SR & TWI_SR_TXCOMP) == TWI_SR_TXCOMP)) {  //so muesste es dann gehen, oder?
10
      millis_EEPROM = 100;
11
      j = 0;
12
    }
13
  }

Die entspricht in etwa einem 1-byte Lesezyklus... Irgendwo mache ich da 
immer noch einen Fehler..

von TK (Gast)


Lesenswert?

Hallo,

ist Deine Anwendung so zeitkritisch, dass Du nicht 20ms nach einem 
PAGE-Write warten kannst?
In der Zeit, die Du Dich jetzt schon mit dem Polling beschäftigt hast, 
hättest Du locker Deine Delay-Routine anspringen können und gut ist.

Ist vielleicht nicht das, was Du hören möchtest - aber die schnellste
Art und Weise, Dein Problem zu beseitigen.

Gruß
TK

von Hanns-Jürgen M. (yogy)


Lesenswert?

TK schrieb:
> Hallo,
>
> ist Deine Anwendung so zeitkritisch, dass Du nicht 20ms nach einem
> PAGE-Write warten kannst?
> In der Zeit, die Du Dich jetzt schon mit dem Polling beschäftigt hast,
> hättest Du locker Deine Delay-Routine anspringen können und gut ist.
>
> Ist vielleicht nicht das, was Du hören möchtest - aber die schnellste
> Art und Weise, Dein Problem zu beseitigen.
>
> Gruß
> TK


So gesehen hast Du natürlich recht. Ich sehe die Lösung der 
Polling-Geschichte mehr sportlich, ich möchte halt, daß sie 
funktioniert. Quick & dirty ist eher nicht mein Fall.

CU Yogy

von Peter D. (peda)


Lesenswert?

Hanns-Jürgen M. schrieb:
> TWI0->TWI_CR = TWI_CR_START | TWI_CR_STOP;

Das sieht schon komisch aus, Start und Stop gleichzeitig.
Das Stop darf erst erfolgen, nachdem man NACK festgestellt hat.
Schau Dir mal meinen Ablauf an. Die SW-I2C Funktionen müßten sich doch 
1:1 auf das HW-I2C runterbrechen lassen.

von Peter D. (peda)


Lesenswert?

TK schrieb:
> ist Deine Anwendung so zeitkritisch, dass Du nicht 20ms nach einem
> PAGE-Write warten kannst?

Die typischen Schreibzeiten liegen weit darunter. Bei größeren Blöcken 
macht sich das deutlich bemerkbar.

von Hanns-Jürgen M. (yogy)


Lesenswert?

Peter D. schrieb:
> Hanns-Jürgen M. schrieb:
>> TWI0->TWI_CR = TWI_CR_START | TWI_CR_STOP;
>
> Das sieht schon komisch aus, Start und Stop gleichzeitig.
> Das Stop darf erst erfolgen, nachdem man NACK festgestellt hat.
> Schau Dir mal meinen Ablauf an. Die SW-I2C Funktionen müßten sich doch
> 1:1 auf das HW-I2C runterbrechen lassen.

Beim SAM3X8 (auf Arduino DUE) ist die TWI offenbar anders anzusteuern 
als beim AVR. Die Leseroutine fuer ein einziges Byte sieht dort wie 
folgt (meine Implementierung) aus
1
//*********************************************************************************************
2
//
3
//
4
//*********************************************************************************************
5
//  Lese ein byte aus dem Device <adr> an der internen Adresse <iadr> aus. iadr ist U16!  
6
//
7
U16 twi0_master_read_EE_byte(U8 adr,U16 iadr)
8
{
9
  U16 data = 0;
10
  U16 i = 0;
11
  TWI0->TWI_MMR = 0;
12
  TWI0->TWI_MMR = TWI_MMR_MREAD | TWI_MMR_IADRSZ_2_BYTE | adr << 16; //TWI0 Master Mode Register RW   (Bit ist 1 bei READ
13
  TWI0->TWI_IADR = iadr;
14
  
15
  millis_EEPROM=0;
16
  TWI0->TWI_CR = TWI_CR_START | TWI_CR_STOP;  // BEIDES muss hier gesetzt werden
17
   
18
     while((TWI0->TWI_SR & TWI_SR_RXRDY) != TWI_SR_RXRDY)
19
     {
20
       if (millis_EEPROM >= 2)
21
       return 0xffff;
22
     }
23
  data = TWI0->TWI_RHR;
24
  
25
  millis_EEPROM=0;
26
  while((TWI0->TWI_SR & TWI_SR_TXCOMP) != TWI_SR_TXCOMP)
27
     {
28
       if (millis_EEPROM >= 2)
29
       return 0xffff;
30
     }
31
     
32
     return  (U16)data;
33
}

Beim Lesen von mehrere Bytes ist die Stop-Anweisung beim letzten Byte zu 
setzen. z.B.:
1
//*********************************************************************************************
2
//
3
//
4
//*********************************************************************************************
5
//    lese einen fortlaufenden Block von IANZ-Zeichen ab Adresse IADR aus dem EEPROm aus und speichere die
6
//    Daten im Array TWI_ARRAY. Rueckgabe: CRC8 in dem LSByte, bei Rückgabe 0xffff -> Systemerror
7
//
8
U16 twi0_master_read_EE_block_into_twi_array(U8 adr,U16 iadr,U8 ianz)
9
{
10
  U16 i = 0;
11
  U16 j = 0;
12
  U8 crc_8 = 0;
13
  U8 wert;
14
  
15
  
16
  TWI0->TWI_MMR = 0;
17
  TWI0->TWI_MMR = TWI_MMR_MREAD | TWI_MMR_IADRSZ_2_BYTE | adr << 16;   //TWI0 Master Mode Register RW   (Bit ist 1 bei READ
18
  TWI0->TWI_IADR = iadr;
19
// 
20
  TWI0->TWI_CR = TWI_CR_START;  //
21
  while (j < ianz - 1) {
22
    Watchdog_Reset();
23
    millis_EEPROM=0;
24
    while((TWI0->TWI_SR & TWI_SR_RXRDY) != TWI_SR_RXRDY)
25
    {
26
      if (millis_EEPROM >= 14)
27
        return 0xffff;
28
    }
29
    wert = TWI0->TWI_RHR;
30
    twi0_array[j++] = wert;
31
    crc_8 = _crc8_update(crc_8, wert);
32
33
  }
34
35
  TWI0->TWI_CR = TWI_CR_STOP;  //
36
  
37
  millis_EEPROM=0;
38
  while((TWI0->TWI_SR & TWI_SR_RXRDY) != TWI_SR_RXRDY)
39
  {
40
      if (millis_EEPROM >= 2)
41
      return 0xffff;
42
  }
43
  wert = TWI0->TWI_RHR;
44
  twi0_array[j] = wert;
45
  crc_8 = _crc8_update(crc_8, wert);
46
  
47
  millis_EEPROM=0;
48
  while((TWI0->TWI_SR & TWI_SR_TXCOMP) != TWI_SR_TXCOMP)
49
  {
50
      if (millis_EEPROM >= 2)
51
      return 0xffff;
52
    }
53
  
54
  return (U16)crc_8;
55
}
56
//---------------------------------------------------------------------------------------------


Aber ich glaube, ich habe den Fehler entdeckt, ich werde das morgen mal 
checken.

Denn jetzt geniesse ich das Eifelwasser vom seligen Simon aus aus 
Bitburg...

Viele Grüße, Yogy

von Hanns-Jürgen M. (yogy)


Lesenswert?

Peter D. schrieb:
> TK schrieb:
>> ist Deine Anwendung so zeitkritisch, dass Du nicht 20ms nach einem
>> PAGE-Write warten kannst?
>
> Die typischen Schreibzeiten liegen weit darunter. Bei größeren Blöcken
> macht sich das deutlich bemerkbar.

Beim byteweisen Beschreiben von mehrenen k Bytes ist das natürlich 
richtig. Beim 24C256 kann ich aber in Blöcken a' 64 byte beschreiben und 
muß nur ein einziges Mal pro Block den Delay benutzen.

von Hanns-Jürgen M. (yogy)


Lesenswert?

So, ich glaube, wir können das Thema schließen. Ich habe zwar das 
"Polling Problem" nicht lösen können, aber meine Recherchen im web haben 
ergeben, daß die Atmels at91xx Bugs in der TWI haben, die insb das NACK 
Signal betreffen. Die (hier!) beschriebene Lösung (Workaround) erscheint 
mir aktuell zu aufwendig, um sie zu implementieren und zu testen. 
Vielleicht mache ich das "irgendwann".

der Link: https://www.mikrocontroller.net/articles/AT91-TWI

Schoenes WE, Yogy

von STM Apprentice (Gast)


Lesenswert?


von STM Apprentice (Gast)


Lesenswert?

STM Apprentice schrieb:
> Beitrag "Re: I2C auf STM32F103"

Ein paar Vorteile einer Soft-I2C-Implementierung:

- keine Geschwindigkeits-Verluste da der I2C Bus meist langsamer
arbeitet als der Controller

- Unahbhängigkeit von der SPI_harware bedeuted praktisch völlig
freie Wahl der Pins SCL/SDA am Controller.

- leicht zu debuggen da die I2C-Bus Slaves/Bausteine meist quasi-
statisch arbeiten und man jedes Bit einzeln anschauen kann (was
ja beim NACK/ACK sehr wichtig sein kann).

- "interruptible", aus dem gleichen Grund wie vorher (quasi-
statisch)

von Peter D. (peda)


Lesenswert?

Hanns-Jürgen M. schrieb:
> TWI0->TWI_CR = TWI_CR_START | TWI_CR_STOP;  // BEIDES muss hier
> gesetzt werden

Das kommt mir immer noch unlogisch vor.
Ich weiß doch erst nach dem NACK auf die I2C-Adresse, ob ich ein Stop 
senden muß oder ob der Söave adressiert wurde und ich weiter machen 
kann.
Ist das wirklich in dem Microchip Beispielcode so angegeben?

von Peter D. (peda)


Lesenswert?

Hanns-Jürgen M. schrieb:
> Beim 24C256 kann ich aber in Blöcken a' 64 byte beschreiben und
> muß nur ein einziges Mal pro Block den Delay benutzen.

Das sind dann beim Beschreiben der 32kB schon 10s Wartezeit gegenüber 
nur 2,5s, wenn der Chip schon nach 5ms wieder bereit ist.

von Hanns-Jürgen M. (yogy)


Lesenswert?

Peter D. schrieb:
> Hanns-Jürgen M. schrieb:
>> TWI0->TWI_CR = TWI_CR_START | TWI_CR_STOP;  // BEIDES muss hier
>> gesetzt werden
>
> Das kommt mir immer noch unlogisch vor.
> Ich weiß doch erst nach dem NACK auf die I2C-Adresse, ob ich ein Stop
> senden muß oder ob der Söave adressiert wurde und ich weiter machen
> kann.
> Ist das wirklich in dem Microchip Beispielcode so angegeben?

Nein, das ist kein Microchip Beispielcode gewesen. Ich weiß auch aktuell 
nicht mehr, wo ich ihn her habe. Ich glaube, das war grob in einer 
Applikationnote beschrieben, die ich wohl nicht abgespeichert hae. Das 
Auslesen von einem einigen Byte geht auf jeden Fall so, wie ich 
beschrieben habe. Das funktioniert ja auch.  Ich habe da gerade etwas 
bei GITHUB gefunden:

https://github.com/brandonbraun653/SAM3X8E-Libraries/blob/master/ASF_Support/TWI/twi.c

siehe ca Zeile 282.

BTW: Die dort auch angegeben Polling-Lösung funktioniert nicht, WTF auch 
immer. Ich bleibe erst einmal beim  Delay. (s.u.)

von Hanns-Jürgen M. (yogy)


Lesenswert?

Peter D. schrieb:
> Hanns-Jürgen M. schrieb:
>> Beim 24C256 kann ich aber in Blöcken a' 64 byte beschreiben und
>> muß nur ein einziges Mal pro Block den Delay benutzen.
>
> Das sind dann beim Beschreiben der 32kB schon 10s Wartezeit gegenüber
> nur 2,5s, wenn der Chip schon nach 5ms wieder bereit ist.

Nun, das EEPROM wird ja nicht ständig neu beschreiben. Es beinhaltet die 
Konfigurationsgrößen meines Systems. Und das sind aktuell 16 Tabellen a' 
64 byte, jeweils mit CRC sowie 182 Bytes an Einzelgroessen, deren Anzahl 
aber noch wachsen kann.

Alle diese Größen können natürlich geändert werden, aber das geschieht 
manuell über das User-Interface und ist daher usergemäß langsam.

Wenn ich das gesamte Systeme soweit ferig und funktionsfähig habe, werde 
ich mich ggf. an Optimierungen geben.

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.