Forum: Mikrocontroller und Digitale Elektronik STM32F103: EEPROM Abstürze abfangen


von Christian J. (Gast)


Lesenswert?

Hallo,

leider kommt es doch immer wieder mal vor, dass meine E2's sich 
aufhängen und das Programm dann endlos in einer Abfrage eines Events 
hängen bleibt. Ich benutze die I2C Hardware des Chips, frage Fehler 
"meistens" ab aber nicht hinter jedem einzelnen Befehl, weil das 
Programm dadurch sehr unübersichtlich wird und es auch keine sinnvolle 
Reaktion darauf gibt, dann steht die Kiste eben still, merke ich an den 
Anzeigen ja. Da das bis zu 4-fach geschachtelt ist muss ein ERROR auch 
bis nach ganz oben an den Aufrufer durchgereicht werden, globale ERROR 
Variable nutze ich nicht.

Man muss halt jede Lib Funktion nochmal kapseln und davon gibt es 
reichlich. Prinzipiell kann jedes Event eben nicht kommen.
1
static uint8_t i2c_stop()
2
{
3
    unsigned int timeout = I2C_TIMEOUT;
4
5
    I2C_GenerateSTOP(I2C_KANAL, ENABLE);
6
    /* Warte bis Stop akzeptiert wurde */
7
    while (I2C_GetFlagStatus(I2C_KANAL, I2C_FLAG_STOPF))
8
    {
9
       if (!(--timeout))
10
        return ERROR;
11
    };
12
13
    return SUCCESS;
14
}


Ist allerdings nicht so wie bei einer Soft I2C, dass man mal eben an den 
Pins wackeln kann, die sind schon fest mit der Hardware verbunden und 
manuell gar nicht ansprechbar, das macht die Statemachine des Chips.

Hat da schon mal jemand was Kluges ausgefrickelt, ob solche Aufhänger zu 
resetten? Schalte ich i2C ab gegen die Pins in High-Z, hängen also auf 
+5V. Nützt auch nichts.

Gruss,
Christian

von Dr. Sommer (Gast)


Lesenswert?

Die I2C Peripherie hat eine Reset Funktion über ein bestimmtes Bit im 
Register. weiß ich grad nicht auswendig, musst du mal nach suchen. Im 
Zweifelsfall einfach über den RCC resetten. Ich verwende für meine 
asynchrone (per Interrupts statt Warte Schleife) I2C Ansteuerung 
nebenher einen Timer, um Timeouts zu erkennen und eben diese Reset 
Funktion zu nutzen. Die I2C Peripherie der STM32 ist da besonders 
zickig, wenn die einen "low" Störimpuls sehen gehen die dauerhaft auf 
"busy" o.ä.

von Christian J. (Gast)


Lesenswert?

Ist mir nicht entgangen:
1
  // Peripherie Takt einschalten I2C3: PB6 (I2C1 SCL) und PB7 (I2C1_SDA)
2
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
3
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
4
5
    I2C_DeInit(I2C_KANAL);
6
    I2C_Cmd(I2C_KANAL, DISABLE);            // I2C abschalten, damit Bus still bleibt

Mann muss ihn vor der Config abschalten, sonst hat man seltsame 
Glitches, beim LA zu sehen. Aber die Reset Funktion muss ich auch mal 
suchen, bisher noch nicht gewusst, dass es sowas überhaupt gibt.... die 
Idee mit dem Timer ist gut! Da braucht man ja nur eine zeit wo man eie 
Variable zurück setzt und fertig.

Hmmm..... meinst Du 22.6.1: SWRST: Sofwtare Reset Bit?

von Dr. Sommer (Gast)


Lesenswert?

Christian J. schrieb:
> Mann muss ihn vor der Config abschalten, sonst hat man seltsame
> Glitches
Hm, das Problem habe ich nicht. Ich schalte ihn einfach ein:
1
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
2
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN | RCC_APB1ENR_I2C1EN;
3
/* Hier Timing-Konfiguration... */
4
I2C1->CR1 = I2C_CR1_PE;

Christian J. schrieb:
> Hmmm..... meinst Du 22.6.1: SWRST: Sofwtare Reset Bit?
Ja genau.

Christian J. schrieb:
> Da braucht man ja nur eine zeit wo man eie
> Variable zurück setzt und fertig.
Ich starte/resette den Timer immer wenn eine Operation gestartet wird, 
und wenn der Timer-Interrupt vor dem I2C-Interrupt kommt war das ein 
Timeout, und ich fange ganz von vorne an (reinitialisiere auch den 
externen IC).

Die lieben E-Techniker bauen immer Platinen mit ganz viel I2C weil das 
ja so schön einfach ist, und als Programmierer darf man sich dann mit 
sowas rumschlagen ;-)

von Christian J. (Gast)


Lesenswert?

Dr. Sommer schrieb:
>> Hmmm..... meinst Du 22.6.1: SWRST: Sofwtare Reset Bit?
> Ja genau.

Und wie "bedient" man das?

von Dr. Sommer (Gast)


Lesenswert?

Christian J. schrieb:
> Und wie "bedient" man das?
Einfach einschalten?
1
I2C1->CR1 = I2C_CR1_SWRST;
2
// Zur Sicherheit Schreibvorgang abwarten
3
__DSB (); __NOP ();
4
I2C1->CR1 = 0;
5
// I2C neu intialisieren

von Christian J. (Gast)


Lesenswert?

Oder so? Bin nicht so der register Fan...

  /* Enable I2C2 reset state */
    RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C2, ENABLE);
    /* Release I2C2 from reset state */
    RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C2, DISABLE);

von Dr. Sommer (Gast)


Lesenswert?

Christian J. schrieb:
> Oder so? Bin nicht so der register Fan...
>
>   /* Enable I2C2 reset state */
>     RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C2, ENABLE);
>     /* Release I2C2 from reset state */
>     RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C2, DISABLE);
Würde ich nur so machen wenn das SWRST Bit nicht geht.

Mit direkten Registerzugriffen zu programmieren find ich einfacher, weil 
die schön ausführlich im Reference Manual dokumentiert sind, und man 
einfach nur machen muss was da steht. Bei den Library-Funktionen muss 
man erstmal in den Source-Code schauen was die überhaupt machen, das mit 
dem Reference Manual abgleichen und sich dann rückwärts überlegen wann 
man welche Funktion aufrufen muss damit die richtigen Bits gesetzt 
werden.

von Arduinoquäler (Gast)


Lesenswert?

Christian J. schrieb:
> Hat da schon mal jemand was Kluges ausgefrickelt, ob solche Aufhänger zu
> resetten?

Du solltest deine Hardware zurechtfrickeln damit dein I2C-Bus
störungsfrei funktioniert.

Herzeigen willst du deinen Aufbau und deine Schaltung ja sowieso
nicht denn dann kämen womöglich grössere Peinlichkeiten ans
Tageslicht.

Also belasssen wir es lieber bei dieser vernebelten Situation
und stochern so weiter. Wenn deine Annahmen über Elektronik
und Schaltungsphysik am I2C Bus so professionell sind wie beim
Thema NRF24 dann ist das auch besser so
(Beitrag "Re: Welche NRF24L01+ 2.4 Ghz Transmitter taugen?").

Und du frickelst besser weiter nur an der Software herum.

von Dr. Sommer (Gast)


Lesenswert?

Arduinoquäler schrieb:
> Du solltest deine Hardware zurechtfrickeln damit dein I2C-Bus
> störungsfrei funktioniert.
Das ist nicht zielführend. Oft ist es einfacher/billiger/leichter, 
Fehler in Software abzufangen als alles massiv gegen Störungen zu 
schirmen. Der I2C-Bus ist notorisch empfindlich (insbesondere bei den 
STM32) und eine Fehlerbehandlung ist ohnehin nötig, denn irgendwann wird 
doch ein Fehler auftreten. Leider hat I2C ja weder Checksum noch 
differentielle Übertragung, was da ganze besonders spaßig macht.

von Christian J. (Gast)


Lesenswert?

Muss ich nach dem Reset das Ganze wieder neu initialisieren?

von Dr. Sommer (Gast)


Lesenswert?

Christian J. schrieb:
> Muss ich nach dem Reset das Ganze wieder neu initialisieren?

Glaub schon... Ruf die Initialisierungsfunktion einfach erneut auf ;-) 
Statt eines externen EEPROMs kann man auch den internen Flash 
beschreiben, wenn man's schlau macht hält der auch genau so lange.

von Christian J. (Gast)


Lesenswert?

Dr. Sommer schrieb:
> Christian J. schrieb:
>> Muss ich nach dem Reset das Ganze wieder neu initialisieren?
>
> Glaub schon... Ruf die Initialisierungsfunktion einfach erneut auf ;-)
> Statt eines externen EEPROMs kann man auch den internen Flash
> beschreiben, wenn man's schlau macht hält der auch genau so lange.

Ja.... nur ist der eh schon fast randvoll..... und so einfach ist das 
auch nicht, geht ja nur blockweise.

So.... gucken ob es passt.
1
void EE_Reset_Hardware() {
2
3
      I2C1->CR1 = I2C_CR1_SWRST;
4
      __DSB ();
5
      __NOP ();
6
      I2C1->CR1 = 0;
7
8
      /* Enable I2C2 reset state */
9
      RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C2, ENABLE);
10
      /* Release I2C2 from reset state */
11
      RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C2, DISABLE);
12
13
      EE_Init();
14
15
}

von Chris J. (Gast)


Lesenswert?

Moin,

alle 2-3 Tage steht meine schöne Anzeigetafel an der Wald still, weil 
sich der I2C Bus aufhängt. Wo genau weiss ich nicht, hängt ja nicht am 
Debugger aber meist kommt ein sog. Event einfach nicht. 2x EE je 64kb, 
4,7kOhm an 5V, angesteuert durch OD Ausgang des F103.

Blind geschrieben mein Versuch alles was mit I2C zu tun hat, also der 
Master im F103 und das EEPROM zu resetten. Irgendwann schlägt der WDT ja 
zu nach 10s und dann wird immer und immer wieder versucht das EEPRPOM 
anzusprechen.

Hat sich schon mal jemand damit intensiver befasst EE's und anderes aus 
dem Schlaf zu klingeln?
1
uint8_t EE_Reset_Hardware()
2
{
3
4
  GPIO_InitTypeDef GPIO_InitStruct;       // Port Init Struct
5
6
  /* I2C abschalten */
7
  
8
  I2C_DeInit(I2C_KANAL);
9
    I2C_Cmd(I2C_KANAL, DISABLE);      
10
  
11
  /* Statemachine resetten */
12
  
13
    I2C1->CR1 |= I2C_CR1_SWRST;  // reset the peripheral
14
     __DSB ();
15
     __NOP ();
16
  I2C1->CR1 &= ~I2C_CR1_SWRST; // re-enable
17
      
18
  
19
    /* Enable I2C2 reset state */
20
    RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, ENABLE);
21
    /* Release I2C2 from reset state */
22
    RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, DISABLE);
23
  
24
        EE_Init();
25
  if (EE_TestDevice(0))
26
    return SUCCESS;
27
  
28
  /* Port mit SCL/SCA als normaler GPIO */
29
  I2C_DeInit(I2C_KANAL);
30
    I2C_Cmd(I2C_KANAL, DISABLE); 
31
  
32
  /* SCL = Output */  
33
    GPIO_InitStruct.GPIO_Pin   = I2C_SCL_PIN;        
34
    GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_Out_PP;         
35
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;   
36
    GPIO_Init(I2C_SCL_PORT, &GPIO_InitStruct);
37
  
38
  /* SDA = Input */
39
  GPIO_InitStruct.GPIO_Pin   = I2C_SDA_PIN;      
40
    GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_IN;        
41
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;  
42
    GPIO_Init(I2C_SCL_PORT, &GPIO_InitStruct);
43
    
44
  /* An SCL Pin wackeln und prüfen, ob SDA high get */
45
  
46
  _Bool pinstat;
47
  volatile uint32_t i;
48
  uint16_t k = 9;
49
  do {
50
    GPIO_ResetBits(HC595_CTRL,I2C_SCL_PIN);  
51
    for (i = 0; i < 60000; i++);
52
    GPIO_SetBits(I2C_SCL_PORT,I2C_SCL_PIN);
53
    for (i = 0; i < 60000; i++);
54
    pinstat = GPIO_ReadBit(I2C_SCL_PORT,I2C_SDA_PIN);
55
  } while (k-- && !pinstat)
56
  
57
  if (pinstat) {
58
    EE_Init();
59
    return SUCCESS;
60
  }
61
  
62
  /* Fehler kann nicht behoben werden */
63
  return ERROR;
64
}

von Frickelfritze (Gast)


Lesenswert?

Chris J. schrieb:
> Wo genau weiss ich nicht

Ich weiss es auch nicht, aber ich bin mir ziemlich sicher
dass dein (mieser) Hardware-Aufbau schuld ist.

Aber daran kann es ja nach deiner Auffassung sicherlich
nicht liegen. Es ist immer der Hersteller eines Chips oder
der fehlerhafte Compiler dran schuld.

von Hmm (Gast)


Lesenswert?

Arduinoquäler schrieb:
> Christian J. schrieb:
>> Hat da schon mal jemand was Kluges ausgefrickelt, ob solche Aufhänger zu
>> resetten?
>
> Du solltest deine Hardware zurechtfrickeln damit dein I2C-Bus
> störungsfrei funktioniert.

Dem schließe ich mich an.

Man sollte sich mal klarmachen, dass I2C KEINE Maßnahmen zur 
Fehlererkennung- und Korrektur hat. Das verlässt sich auf 0% Fehlerrate 
auf physikalischer Ebene.
Wenn also der Bus aus elektrischen Gründen Bitfehler hat, dann ist das 
Kind schon im Brunnen. Da ist der Fehler auf Hardwareebene zu finden und 
zu beheben.

Was ich aus der Praxis sagen kann:
Ich kann von den I2C-Blöcken von ST nichts negatives berichten. Mein 
Arbeitgeber hat mehrere hunderttausend Platinen mit STM32 und I2C im 
Feld, wo das ohne Probleme läuft.
Was klar sein muss ist, dass I2C ein störanfälliger Bus ist, und 
besondere Sorgfalt verlangt. Er ist aber zuverlässig, vorausgesetzt man 
hat das sauber ausgelegt und in Betrieb genommen.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Christian J. schrieb:
> Hat da schon mal jemand was Kluges ausgefrickelt, ob solche Aufhänger zu
> resetten?

Vielleicht solltest Du erstmal versuchen, die Ursache zu finden als an 
den Symptomen herumzudoktern.

Viele I2C-Beispiele, die man so im Netz findet, vergessen nämlich, nach 
einem Daten-Transfer (bzw. vor dem nächsten) I2C_FLAG_BUSY abzufragen. 
Das funktioniert dann solange gut, wie man nur Daten tröpfchenweise 
liest oder schreibt. Kommen zum Beispiel mehrere I2C-Transferblöcke 
direkt hintereinander, hängt sich ohne Berücksichtigung des Busy-Flags 
die I2C-Peripherie einfach weg und ist nicht mehr ansprechbar. Da nutzt 
auch kein 9-maliges Pinwackeln mehr, nur noch ein HW-Reset des STM32.

von Frickelfritze (Gast)


Lesenswert?

Frank M. schrieb:
> Vielleicht solltest Du erstmal versuchen, die Ursache zu finden als an
> den Symptomen herumzudoktern.

Nein! Für das I2C EEPROM baue ich mir eine Schaltung mit MOSFET
damit ich mir bei "Bedarf" das EEPROM aus- und neu einschalten
kann. Das ist wahre Ingenieuerskunst.

Von wegen schlechte Hardware, paahhh ....

von Dr. Sommer (Gast)


Lesenswert?

Frank M. schrieb:
> Kommen zum Beispiel mehrere I2C-Transferblöcke
> direkt hintereinander, hängt sich ohne Berücksichtigung des Busy-Flags
> die I2C-Peripherie einfach weg und ist nicht mehr ansprechbar
Es reicht schon eine eingekoppelte negative Flanke...

Frank M. schrieb:
> Da nutzt
> auch kein 9-maliges Pinwackeln mehr, nur noch ein HW-Reset des STM32.
Schon mal das Reset-Bit der I2C-Peripherie ausprobiert? Das hat bei mir 
immer geholfen.

Hmm schrieb:
> Ich kann von den I2C-Blöcken von ST nichts negatives berichten. Mein
> Arbeitgeber hat mehrere hunderttausend Platinen mit STM32 und I2C im
> Feld, wo das ohne Probleme läuft.
Welcher STM32 und synchrone oder asynchrone Ansteuerung? Bei den alten 
(F1, F4) ist die asynchrone Ansteuerung sehr kompliziert, da kann man 
leicht Fehler machen die zu Instabilitäten führen. Diese Komplexität ist 
mMn etwas sehr negatives an dieser Hardware.

Vermutlich hängt sich hier der EEPROM-IC auf. Die Peripherie vom STM32 
wird ja resettet. Hat der IC denn keinen Reset-Eingang?

Frickelfritze schrieb:
> Nein! Für das I2C EEPROM baue ich mir eine Schaltung mit MOSFET
> damit ich mir bei "Bedarf" das EEPROM aus- und neu einschalten
> kann. Das ist wahre Ingenieuerskunst.
Wenns nicht anders geht... Auch bei guter Hardware muss man immer davon 
ausgehen, dass Fehlübertragungen passieren. Die Software müsste dann 
dafür sorgen, dass diese abgefangen werden. Das ist bei I²C, wie wir 
sehen, aber schwierig (keine Checksum, Slave und Master hängen sich 
auf). Bei CAN z.B. wird da explizit drauf geachtet, und temporäre 
Störungen/Verschlucker können einen CAN kaum dauerhaft aus dem Takt 
bringen.

von Frickelfritze (Gast)


Lesenswert?

Dr. Sommer schrieb:
> Wenns nicht anders geht... Auch bei guter Hardware muss man immer davon
> ausgehen, dass Fehlübertragungen passieren.

Seltsam. 99% aller PCs haben keine Möglichkeit die Datenübertragung
zwischen Prozessor und Arbeitsspeicher zu validieren da ihnen
die Paritätsprüfung fehlt. Es gibt viele, viele weitere Beispiele
wo man sich auf die Übertragung einfach verlässt. Weil sie in der Tat
zuverlässig ist. Aber hier beim I2C redest du diese Notwendigkeit
herbei. Ich behaupte dass I2C - wenn es richtig gemacht ist - genau
so zuverlässig ist wie ein Speicherinterface (als Beispiel) beim PC.

Aber eigentlich klingt mir diese Meinung eher wie das Fähnchen im
Wind.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Dr. Sommer schrieb:
> Es reicht schon eine eingekoppelte negative Flanke...

Ist bei mir noch nicht aufgetreten.

> Schon mal das Reset-Bit der I2C-Peripherie ausprobiert? Das hat bei mir
> immer geholfen.

Nein, habe ich nicht, aber das werde ich mal testen. Ich bin damals mal 
lediglich in die Busy-Flag-Falle getippt, als ich die ersten Versuche 
mit STM32 und I2C machte. Da orientiert man sich erstmal an bereits 
vorhandenen Sourcen im Netz. Es hat mich nach einigen Recherchen doch 
arg gewundert, wieviele Beispiele im Netz dieses Flag einfach 
schlichtweg ignorieren.

von Dr. Sommer (Gast)


Lesenswert?

Frickelfritze schrieb:
> Ich behaupte dass I2C - wenn es richtig gemacht ist - genau
> so zuverlässig ist wie ein Speicherinterface (als Beispiel) beim PC.
Also m.W. ist das Speicherinterface differentiell, hat Push-Pull-Stufen 
und ist perfekt gelayoutet. Das ist schonmal ne ganz andere Nummer als 
die typischen verwaschenen Flanken von I²C. Und bei wichtigen 
Anwendungen verlässt man sich ja doch eben nicht drauf. Bei der 
Software-Entwicklung macht man es sich ganz schön leicht wenn man sagt 
"die Hardware passt schon" ;-)

Frank M. schrieb:
> Ist bei mir noch nicht aufgetreten.
Da hatte ich schon jede Menge Probleme mit... Wenn man eine 
störungsreiche Umgebung (z.B. Fahrzeug) hat kann man viel "Spaß" mit 
sowas haben.

von Chris J. (Gast)


Lesenswert?

Moin,

ein Lochrasteraufbau ist so eine Sache... ist schließlich ein 
Bastelprojekt. Kuperkabel, ca 3-4cm lang kreuz und quer. I2C ist wie SPI 
ein Platinenbus über kurze Strecken, wäre er länger wäre es zb ein LIN 
BUS, der deutlich robuster ist, jedoch physikalisch ähnlich wie I2C 
funktioniert und logisch wie  RS232.

Mit Störungen muss immer gerechnet werden. Ich vermute grob geschätzt, 
dass von den rund 260.000 Lesezugriffen in 3 Tagen, nämlich jede Sekunde 
einer irgendeiner faul ist. Das ist immer noch eine verschwindend kleine 
Fehlerrate.

Die Idee das EE mit einem Pin zu versorgen hatte ich auch schon, nur 
sind es 3.3V, reicht nicht. Und ein Mosfet gibt es nicht, der 3.3V 
schaltet, nur einer dieser Steine mit eingebauter Ladungspumpe.

Die Idee mit dem 9 Mal bimmeln stammt aus einer AppNote von Microchip, 
die die mal rausgegeben haben. Auf den Seite den I2C Konsortiums finden 
sich auch Hinweise, allerdings dieser nicht. Nur dass I2C Hardware mit 
einem reset versehen werden kann durch Strom-Wegnehmen.

von Chris J. (Gast)


Angehängte Dateien:

Lesenswert?

Frank M. schrieb:
> Nein, habe ich nicht, aber das werde ich mal testen. Ich bin damals mal
> lediglich in die Busy-Flag-Falle getippt, als ich die ersten Versuche
> mit STM32 und I2C machte.

Kannst Du das mit dem Busy mal beschreiben? Ich papp mal meinen Code 
dran... der ist eigentlich schon "totoptimiert".

von Strippe (Gast)


Lesenswert?

Hallo,

dieses Problem kommt auch mir bekannt vor. Busy Bit...oder sagen wir: 
Stress Bit. >:->

Vllt mein Beitrag, so ich mich noch erinnere...

Hier wurde ja schon einiges genannt. Timeout, halbwegs vernünftige 
Verdrahtung. Aber eines hattet Ihr noch nicht genannt. Man kann das 
EPROM neu starten. Dafür gibt's es eine Prozedur. Siehe AN1471 von ST, 
"What happens to the M24xxx I²C EEPROM if the I²C bus communication is 
stopped?".

Soweit ich mich weiter erinnere, habe ich den I2C Controller als letzte 
Option doch vom Netz genommen und direkt via GPIO dies durchgeführt.

Und zumindest bei den ST EEPROM steht auch im DB, dass die sich intern 
erst mal vom Netz hängen, wenn sie mit Schreiben noch nicht fertig sind. 
Das kriegt man dann aber über den Timeout hin. Man lässt ihn mehr als 
ein mal ablaufen und sendet nach jeden Timerablauf eine  START condition 
in Erwartung des ACK. Damit bügelt man schon einiges weg.

MfG

von Chris J. (Gast)


Lesenswert?

Strippe schrieb:
> Soweit ich mich weiter erinnere, habe ich den I2C Controller als letzte
> Option doch vom Netz genommen und direkt via GPIO dies durchgeführt.
>
> Und zumindest bei den ST EEPROM steht auch im DB, dass die sich intern
> erst mal vom Netz hängen, wenn sie mit Schreiben noch nicht fertig sind.
> Das kriegt man dann aber über den Timeout hin. Man lässt ihn mehr als
> ein mal ablaufen und sendet nach jeden Timerablauf eine  START condition
> in Erwartung des ACK. Damit bügelt man schon einiges weg.

Also irgendwie müsstest Du diesen Text mal in Deutsche übersetzen. Vom 
Netz? Steckdose? Und ein EE was schreibt ist tot, blind und merkt gar 
nichts. Daher ballert man ja Starts drauf, erst wenn es ACK'ed ist es 
wieder frei und kann weiter Daten schlucken.

1
 /* Warte bis I2C Interface frei */
2
    while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));
3
4
    /* ACK ein */
5
    I2C_AcknowledgeConfig(I2C_KANAL, ENABLE);
6
7
    /* Wenn das E2PROM noch in einem Schreibzyklus ist muss die Start
8
       Bedingung so oft wiederholt werden, bis es antwortet */
9
10
    do {
11
        /* Erzeuge START Bedingung */
12
        if (i2c_start() == ERROR)
13
            return ERROR;
14
        if (I2C_WaitForEvent(200, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS)
15
            return ERROR;
16
        // Adressiere das E2PROM
17
        I2C_Send7bitAddress(I2C_KANAL, EE, I2C_Direction_Transmitter);
18
     } while (I2C_WaitForEvent(200, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS);
19
20
    /* Adresse als 2 x 8 Bit senden */
21
    if (!i2c_transmit(adr>>8))
22
        return ERROR;
23
    if (!i2c_transmit(adr))
24
        return ERROR;
25
26
    /* Sende Datum */
27
    if (!i2c_transmit(data))
28
        return ERROR;
29
30
    /* Sende I2C3 STOP Condition */
31
    if (!i2c_stop())
32
        return ERROR;

von grundschüler (Gast)


Lesenswert?

Auch bei mir hat der Anschluss eines i2c-LCDs an stm32f103  aus mir bis 
heute unerfindlichen Gründen per hw-i2c nicht geklappt. Prima 
funktioniert hat es dann mit sw-I2c. Ist auch nicht viel langsamer. So 
kann man jedenfalls testen, ob die Hardware in Ordnung ist.

von Stefan F. (Gast)


Lesenswert?

> von den rund 260.000 Lesezugriffen in 3 Tagen, nämlich jede Sekunde
> einer irgendeiner faul ist.

3 Tagen haben ungefähr 260.000 Sekunden.

> Das ist immer noch eine verschwindend kleine Fehlerrate.

Nein, das ist ungefähr 100% Fehlerrate.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Chris J. schrieb:
> Kannst Du das mit dem Busy mal beschreiben? Ich papp mal meinen Code
> dran... der ist eigentlich schon "totoptimiert".

Der Test ist in Deinem Source zumindest in der Funktion 
EE_TestDevice(uint8_t eeno) drin:
1
    while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));

Die Abfrage passiert hier vor jedem I2C-IO. Das ist hier in Ordnung so. 
Dasselbe gilt auch für die Write-Funktionen EE_WriteDataSet() und 
EE_WriteByte(). Auch bei der Funktion EE_ReadByte() ist sie drin.

Aber in der Funktion EE_ReadDataSet() scheint diese Abfrage zu fehlen, 
jedenfalls sehe ich sie auch beim zweiten Hingucken nicht. Stattdessen 
findet man hier einen ominösen delay-Aufruf. Das kommt mir sehr spanisch 
vor. Delays sollte man immer hinterfragen.

An der Stelle solltest Du nachbessern, zumindest die obige Busy-Abfrage 
hinter den delay-Aufruf - also vor den i2c_init()-Aufruf setzen.

: Bearbeitet durch Moderator
von Chris J. (Gast)


Angehängte Dateien:

Lesenswert?

Frank M. schrieb:
> Aber in der Funktion EE_ReadDataSet() scheint diese Abfrage zu fehlen,
> jedenfalls sehe ich sie auch beim zweiten Hingucken nicht. Stattdessen
> findet man hier einen ominösen delay-Aufruf. Das kommt mir sehr spanisch
> vor. Delays sollte man immer hinterfragen.

Du meinst das hier? Das steht deshalb drin, weil der letzte 
Schreibzugriff nicht "delayed" wird, d.h. die CPU wartet nicht, bis er 
abgeschlossen ist, sondern rennt weiter. Und wenn dann direkt ein 
Lesezugriff kommt reagiert das EE nicht auf die Start Condition. Das 
kann auch ein Busy Bit nicht erkennen, für die CPU ist der Vorgang 
abgeschlossen. Schreibe ich aber fortlaufend findet die Prüfung bei 
jedem neuen Eintritt in die Routine statt.

Ich hab die Routinen mit einem Test umgeben, der mir alle denkbaren 
Möglichkeiten erzeugte, alles durcheinander zu benutzen und nachher zu 
vergleichen. Dabei fiel mir das so auf. Im LA wurde die typische 
Schreibzeit mit 3,2ms gemessen, da sind 4ms halt "grosszügig."

Das Ganze ist halt nur sowas wie auf dem Bild, typischer 
Lötrasteraufbau....

/* Nach vorherigem Schreibzugriff warten */
    if (f_lastwrite) {
        DelayMs(4);
        f_lastwrite = 0;
    }

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Chris J. schrieb:
> /* Nach vorherigem Schreibzugriff warten */
>     if (f_lastwrite) {
>         DelayMs(4);
>         f_lastwrite = 0;
>     }

Dann setze doch mal die Zeile
1
 while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));

genau darunter.

von Karl K. (leluno)


Lesenswert?

Christian J. schrieb:
> Ist allerdings nicht so wie bei einer Soft I2C,

Hast du das ganze mal mit Soft I2C probiert? Bei dem Projekt dürften ein 
paar ms mehr doch kein Problem sein.

von Chris J. (Gast)


Lesenswert?

Karl K. schrieb:
> Hast du das ganze mal mit Soft I2C probiert?

Ist ne Glaubensfrage: Bei einem Hobbyprojekt geht es ja grad darum die 
Hardware auszureizen, da ist der Weg das Ziel. Ich will ja wissen wie 
das alles da drin funktioniert :-)

von Chris J. (Gast)


Lesenswert?

Frank M. schrieb:
> Dann setze doch mal die Zeile while (I2C_GetFlagStatus(I2C1,
> I2C_FLAG_BUSY));
>
> genau darunter.

Frank, das ist nicht richtig. Es geht nicht darum, dass die I2C Prime 
Cell fertig ist, sondern das EEPROM. Es gibt keinen Indikator dafür, es 
reagiert einfach nicht. Sobald das STOP kommt geht es in den Write Mode 
und solange ist es taubstumm.

Alles ok :-)

von STM Apprentice (Gast)


Lesenswert?

Chris J. schrieb:
> Es gibt keinen Indikator dafür, es reagiert einfach nicht.

Doch, und genau das ist der Lösungsweg. Ich zitiere aus dem
Datenblatt des AT24C128 für den Schreibvorgang:

-----------------------------------------------------------
ACKNOWLEDGE POLLING: Once the internally-timed write cycle
has started and the EEPROM inputs are disabled, acknowledge
polling can be initiated. This involves sending a start
condition followed by the device address word. The
read/write select bit is representative of the operation
desired. Only if the internal write cycle has completed will
the EEPROM respond with a zero, allowing the read or write
sequence to continue.
-----------------------------------------------------------

Man wartet nach dem Schreiben eines Blocks solange bis das
EEPROM ein Acknowledge liefert, dann ist es fertig mit
seiner internen Maschinerie.

So "einfach" ist das.

von Chris J. (Gast)


Lesenswert?

Guckst Du hier :-)

Kann sein, dass ich da noch etwas "unschön" habe, da ich vor dem 
Schreiben abfrage, ob es fertig ist. Im LA sieht das alles schon ganz 
schnuckelig aus.
Das Problem trat ja nur auf, wenn Schreiben und Lesen im Wechsel 
erfolgten.

Das Blöde ist immer, dass man sich nicht traut etwas anzufassen was ja 
funktioniert. Selbst die Abstürze habe ich jetzt im Griff durch das 
"klingeln" und resetten der Hardware + Watchdog. Irgendwann fängt es 
sich wieder und es geht weiter. 4.7kOhm für 2 EEPROMs bei 100khz. 400khz 
gehen auch, jedenfalls im Debugger aber .... naja, lieber etwas weniger 
Gas, dafür auch weniger Unfälle.

Dieser kleine STM32F103 auf dem 2,50 Euro Board ist jedenfalls eine 
schweinegeile Maschine für "Physical Computing", für alles andere nehme 
ich lieber F429.
1
* Wenn das E2PROM noch in einem Schreibzyklus ist muss die Start
2
       Bedingung so oft wiederholt werden, bis es antwortet */
3
4
    do {
5
        /* Erzeuge START Bedingung */
6
        if (i2c_start() == ERROR)
7
            return ERROR;
8
        if (I2C_WaitForEvent(200, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS)
9
            return ERROR;
10
        // Adressiere das E2PROM
11
        I2C_Send7bitAddress(I2C_KANAL, EE, I2C_Direction_Transmitter);
12
     } while (I2C_WaitForEvent(200, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS);

von STM Apprentice (Gast)


Lesenswert?

Chris J. schrieb:
> * Wenn das E2PROM noch in einem Schreibzyklus ist muss die Start
>        Bedingung so oft wiederholt werden, bis es antwortet */

Nein, eine Start-Bedingung zu senden reicht nicht, siehe
mein Zitat.

Es muss die Adresse des EEPROMs gesendet werden und das
(irgendwann) darauf folgende ACK gilt als Bestätigung dass
das EEPROM wieder frei ist.

von Chris J. (Gast)


Angehängte Dateien:

Lesenswert?

STM Apprentice schrieb:
> Nein, eine Start-Bedingung zu senden reicht nicht, siehe
> mein Zitat.

Das steckt im

/ Adressiere das E2PROM
        I2C_Send7bitAddress(I2C_KANAL, EE, I2C_Direction_Transmitter);

mit drin. Das ist eine Statemachine, die Zieladresse ist bereits 
dauerhaft hinterlegt. Da ist eine etwas andere Denke nötig, da diese SM 
nur angestossen wird und dann von allein läuft, während die CPU weiter 
rennt. Nur an den Events, die alle interruptfähig sind kann sie 
erkennen, dass was passiert ist.

von STM Apprentice (Gast)


Lesenswert?

Chris J. schrieb:
> do {
>         /* Erzeuge START Bedingung */
>         if (i2c_start() == ERROR)
>             return ERROR;
>         if (I2C_WaitForEvent(200, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS)
>            return ERROR;

Du springst ja schon mit Error heraus bevor du die Adresse
sendest. Allerdings weisss ich nicht genau welche
Fehlerbedingungen bei dir dazu führen ....

von Chris J. (Gast)


Lesenswert?

STM Apprentice schrieb:
> Du springst ja schon mit Error heraus bevor du die Adresse
> sendest. Allerdings weisss ich nicht genau welche
> Fehlerbedingungen bei dir dazu führen

Das ist das EV5. Die SM gibt einen Start auf den Bus und guckt, ob 
dieser überhaupt frei ist, d.h. SDA = high. Ist er es nicht, kommt das 
EV5 nicht und man muss einen Timeout einleiten. Es ist schwierig zu 
unterscheiden welcher der beiden Teilnehmer die Events auslöst. Ich bin 
da auch noch nicht ganz durchgedrungen.

Du könntest aber recht haben.... ich muss das mal in Ruhe mit dem LA 
anschauen, jetzt gehts erstmal auf den Modellflugplatz bei dem Wetter. 
Ist eher was für Regentage.

Auf dem LA Diagramm war allerdings in einer testanwendung, die viele 
Bytes per Pagewrite wegschrieb eindeutig zu sehen, dass erst so 3-4 
Starts kommen im Abstand von 0,5ms und dann die Antwort nach ca 3,2ms. 
Das war also alles richtig, daher habe ich es so gelassen. 
Möglicherweise nochmal drüber nachdenken und nachmessen.
1
/* ----- Erzeuge Start Bedingung ------ */
2
static uint8_t i2c_start()
3
{
4
   int timeout = I2C_TIMEOUT;
5
6
   I2C_GenerateSTART(I2C_KANAL, ENABLE);
7
   /* Warte auf I2C EV5. Bus frei, Start akzeptiert */
8
   while (!I2C_CheckEvent(I2C_KANAL, I2C_EVENT_MASTER_MODE_SELECT))
9
   {
10
       if (!(--timeout))
11
        return ERROR;
12
   }
13
14
   return SUCCESS;
15
}

von Hans-Georg L. (h-g-l)


Lesenswert?

Frickelfritze schrieb:
> Chris J. schrieb:
>> Wo genau weiss ich nicht
>
> Ich weiss es auch nicht, aber ich bin mir ziemlich sicher
> dass dein (mieser) Hardware-Aufbau schuld ist.
>
> Aber daran kann es ja nach deiner Auffassung sicherlich
> nicht liegen. Es ist immer der Hersteller eines Chips oder
> der fehlerhafte Compiler dran schuld.

Aber es kommt vor ;-)

Some Cortex-M3 cores can cause data corruption when ldrd instructions 
with overlapping destination and base registers are used. This option 
avoids generating these instructions. This option is enabled by default 
when -mcpu=cortex-m3 is specified.

@Christian

wenn du den GCC benutzt setze mal -mcpu=cortex-m3

Das bewirkt machmal Wunder bei der HW SPI und dem busy Bit;-)

von Chris J. (Gast)


Angehängte Dateien:

Lesenswert?

Hans-Georg L. schrieb:
> wenn du den GCC benutzt setze mal -mcpu=cortex-m3
>
> Das bewirkt machmal Wunder bei der HW SPI und dem busy Bit;-

von Hans-Georg L. (h-g-l)


Lesenswert?

Dann probier mal für Spass den Fix direkt zu setzen ...

-mfix-cortex-m3-ldrd

von Chris J. (Gast)


Lesenswert?

-mfix-cortex-m3-ldrd
Some Cortex-M3 cores can cause data corruption when ldrd instructions 
with overlapping destination and base registers are used. This option 
avoids generating these instructions. This option is enabled by default 
when -mcpu=cortex-m3 is specified.

https://gcc.gnu.org/onlinedocs/gcc-4.7.4/gcc/ARM-Options.html

von Peter D. (peda)


Lesenswert?

Christian J. schrieb:
> Ich benutze die I2C Hardware des Chips, frage Fehler
> "meistens" ab aber nicht hinter jedem einzelnen Befehl

Das ist bei HW-I2C aber Pflicht.
Z.B. nach NACK auf Adresse/Daten wird Dir das HW-I2C was husten, wenn Du 
weitere Daten in das Senderegister schreibst.

Hier mal ein einfaches SW-I2C (AVR):

https://www.mikrocontroller.net/attachment/119775/xeeprom.c


Christian J. schrieb:
> Ist allerdings nicht so wie bei einer Soft I2C, dass man mal eben an den
> Pins wackeln kann

Nach I2C-Disable sind die Pins wieder normale IOs.

von Chris J. (Gast)


Lesenswert?

N'Abeeeend,

ich habe die Ursache für das Aufhängen des EE's gefunden, was ja 
sporadisch aber doch regelmässig auftrat.

Es lag an den vielen Ints, die bei mir rumfleuchen und von denen zb der 
des RF Moduls oder die Bespielung der HC595 Kette mit 6 Bausteinen recht 
lange dauern kann. Seitdem ist nun seit 3 Tagen Ruhe im Karton. 
Sicherheitshalber habe ich alle Kommandos gekapselt, auch wenn einige es 
vielleicht nicht brauchen.
1
static uint8_t i2c_receive_nack()
2
{
3
    unsigned int timeout = I2C_TIMEOUT;
4
5
    // ACK bei empfangenen Daten abschalten
6
    __disable_irq();
7
    I2C_AcknowledgeConfig(I2C_KANAL, DISABLE);
8
    // Warte fuer EV7 = Daten sind empfangen worden
9
    while (!I2C_CheckEvent(I2C_KANAL, I2C_EVENT_MASTER_BYTE_RECEIVED))
10
    {
11
       if (!(--timeout)) {
12
            __enable_irq();
13
            return ERROR;
14
       }
15
    }
16
17
    // Stop direkt danach erzeugen
18
    I2C_GenerateSTOP(I2C_KANAL, ENABLE);
19
    timeout = I2C_TIMEOUT;
20
    while (!I2C_CheckEvent(I2C_KANAL, I2C_EVENT_MASTER_BYTE_RECEIVED))
21
    {
22
       if (!(--timeout)) {
23
            __enable_irq();
24
            return ERROR;
25
       }
26
    }
27
28
    // Read and return data byte from I2C data register
29
    uint8_t byte = I2C_ReceiveData(I2C_KANAL);
30
    __enable_irq();
31
    return byte;
32
}

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.