Forum: Mikrocontroller und Digitale Elektronik STM32 I2C mit EEPROM - funktioniert nur zuverlässig mit Bus-Sniffer


von Walter T. (nicolas)



Lesenswert?

Hallo zusammen,
ich nutze einen STM32F103 mit einem EEPROM 24C02 am I2C-Bus, um den 
Flash etwas zu schonen. Der I2C läuft mit 400kHz auf 5V mit Pull-Ups von 
momentan 1kOhm.

Ich habe vier Testroutinen geschrieben:
void eeprom_test_read(void)
void eeprom_test_write(void)
void eeprom_test_read_block(void)
void eeprom_test_write_block(void),
die das EEPROM jeweils komplett auslesen b.z.w. beschreiben, einmal 
byteweise, einmal pageweise b.z.w. alles hintereinander.

Das Beschreiben des EEPROMs klappt immer, sei es Block- oder byteweise. 
Das Lesen schlägt oft fehl, es sei denn, der I2C-Sniffer von Peter 
Dannegger (Beitrag "I2C (TWI) Sniffer mit AVR") ist 
angeschlossen, wobei es egal ist, ob direkt oder über Potenzialtrennung 
mittels ADUM 1250.

Die Pull-up-Widerstände habe ich zwischen 4k7 und 1k0 variiert- das 
Oszillogramm wird zwar schöner, aber die Funktion wird nur über das 
Vorhanden- und Nichtvorhandensein der Bus-Sniffers entschieden.

Der Fehler äußert sich immer mit dem Status 0x64, also z.B. in 
eeprom_read_byte wird in Zeile 69 die Routine I2C_read_nack mit einem 
Timeout abgebrochen (I2C_NORMALTIMEOUT, hier 300 Millisekunden).

Der Fehler tritt auch nur bei 400kHz (eingestelltem) I2C-Takt auf, bei 
100kHz läuft alles einwandfrei, wobei der EEPROM auch für 400kHz 
spezifiziert ist.

Die I2C-Routinen sind größtenteils von 
http://eliaselectronics.com/stm32f4-tutorials/stm32f4-i2c-master-tutorial/ 
abgekupfert, wobei nur ein wenig Fehlerhandling hinzugekommen ist und 
das Warten auf das Busyflag vor dem Senden der Startbedingung 
ausgelagert wurde.

"printf_P" und "PSTR" sind nur definiert, um Kompatibilität mit einem 
AVR zu behalten und sind definiert als:
1
#define PSTR(a) (a)
2
#define printf_P printf
. Die Funktion key_getstroke() wartet auf einen Tastendruck, um mir eine 
Chance zu geben, das Display abzulesen.

Warum kann das EEPROM nur ausgelesen werden, wenn der I2C-Sniffer 
angeschlossen ist?
Und warum liefert ein mit 400kHz initialisierter I2C einen realen Takt 
von 420kHz und ist das für mein Problem relevant?

Viele Grüße
W.T.

von porter (Gast)


Lesenswert?

> Und warum liefert ein mit 400kHz initialisierter I2C einen realen Takt
> von 420kHz und ist das für mein Problem relevant?

Wahrscheinlich stimmt dein Prescaler nicht.
Da I2C synchron ist sollte das eigentliche Timing nicht so relevant 
sein.
Was ich mir aber vorstellen könnte ist, dass das EEPROM nur auf 400kHz 
spezifiziert ist. Leider schreibst du nicht was für eins du verwendest.
Schon mal den Takt etwas runter genommen?

von Walter T. (nicolas)


Lesenswert?

Jetzt geht alles.

Der Unterschied 420kHz zu 400kHz war wohl tatsächlich relevant. Ursache 
des Problems war eine kleine Lötperle, die einen Pin des 
Quarzoszillators gegen seinen Nachbarn kurzgeschlossen hat, so daß der 
STM32F103 klammheimlich auf seinen internen Oszillator umgeschaltet hat.

Danke fürs Lesen
W.T.

von Walter T. (nicolas)


Lesenswert?

Jetzt komme ich nach ein paar Wochen wieder dazu, mit dem I2C 
weiterzumachen. Es ist doch noch nicht so alles Gold, was glänzt.

Aus irgendeinem Grund muß ich die Initialisierung (init_I2C1()) immer 
zweimal durchführen (nach einem normalen Reset, kein Power cycle nötig); 
nach dem ersten Mal wird noch nicht einmal die Start-Bedingung auf den 
Bus gesendet; nach dem zweiten Mal geht sofort alles glatt.

Mit zweimal aufrufen meine ich auch unmittelbar doppelt, kein Warten:
1
uint8_t byte;
2
3
init_I2C1(); // initialize I2C peripheral
4
init_I2C1();
5
6
status =  eeprom_read_byte(0,&byte);
funktioniert unmittelbar, die "Variante"
1
uint8_t byte;
2
3
init_I2C1(); // initialize I2C peripheral
4
5
status =  eeprom_read_byte(0,&byte);
funktioniert immer erst beim zweiten Versuch; beim ersten Versuch wird 
nichts auf den Bus geschickt.

Die Initialisierungsroutine entspricht eigentlich fast genau dem 
obengenannten Tutorial. Zur Übersicht habe ich sie noch einmal 
angehängt.
1
#define STM32F10x
2
void init_I2C1(void){
3
4
  GPIO_InitTypeDef GPIO_InitStruct;
5
  I2C_InitTypeDef I2C_InitStruct;
6
7
  #ifdef STM32F10x
8
    // enable APB1 peripheral clock for I2C1, SCL and SDA pins
9
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);
10
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB |  RCC_APB2Periph_AFIO,ENABLE);
11
12
    // Setup SCL und SDA pins
13
    GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_6 | GPIO_Pin_7;
14
    GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AF_OD;
15
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
16
    GPIO_Init(GPIOB, &GPIO_InitStruct);
17
18
    GPIO_PinRemapConfig(GPIO_Remap_I2C1,DISABLE);
19
20
  #elif defined(STM32F4xx)
21
    // enable APB1 peripheral clock for I2C1, SCL and SDA pins
22
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
23
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
24
25
    // Setup SCL und SDA pins
26
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
27
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
28
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
29
    GPIO_InitStruct.GPIO_OType = GPIO_OType_OD;
30
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
31
32
    GPIO_Init(GPIOB, &GPIO_InitStruct);         // init GPIOB
33
34
    // Connect I2C1 pins to AF
35
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_I2C1); // SCL
36
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_I2C1); // SDA
37
  #else
38
    #error "No MCU defined"
39
  #endif
40
41
  I2C_DeInit(I2C1);
42
43
  // configure I2C1
44
  I2C_InitStruct.I2C_ClockSpeed = 400000;     // Hz
45
  I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;
46
  I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2;
47
  I2C_InitStruct.I2C_OwnAddress1 = 0x00;
48
  I2C_InitStruct.I2C_Ack = I2C_Ack_Disable;
49
  I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
50
  I2C_Init(I2C1, &I2C_InitStruct);
51
52
  I2C_Cmd(I2C1, ENABLE);
53
  i2c_status = i2c_ok;
54
}

Fällt jemandem der Fehler ins Auge?

Viele Grüße
W.T.

von ♪Geist (Gast)


Lesenswert?

Probiere mal die Error-Flags zu löschen und Deinit auszuführen bevor du 
die Pins initialisierst.
1
#define STM32F10x
2
void init_I2C1(void){
3
4
  GPIO_InitTypeDef GPIO_InitStruct;
5
  I2C_InitTypeDef I2C_InitStruct;
6
 
7
8
  I2C_DeInit(I2C1);
9
10
  #ifdef STM32F10x
11
    // enable APB1 peripheral clock for I2C1, SCL and SDA pins
12
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);
13
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB |  RCC_APB2Periph_AFIO,ENABLE);
14
15
    // Setup SCL und SDA pins
16
    GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_6 | GPIO_Pin_7;
17
    GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AF_OD;
18
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
19
    GPIO_Init(GPIOB, &GPIO_InitStruct);
20
21
    GPIO_PinRemapConfig(GPIO_Remap_I2C1,DISABLE);
22
23
  #elif defined(STM32F4xx)
24
    // enable APB1 peripheral clock for I2C1, SCL and SDA pins
25
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
26
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
27
28
    // Setup SCL und SDA pins
29
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
30
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
31
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
32
    GPIO_InitStruct.GPIO_OType = GPIO_OType_OD;
33
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
34
35
    GPIO_Init(GPIOB, &GPIO_InitStruct);         // init GPIOB
36
37
    // Connect I2C1 pins to AF
38
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_I2C1); // SCL
39
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_I2C1); // SDA
40
  #else
41
    #error "No MCU defined"
42
  #endif
43
44
45
  // configure I2C1
46
  I2C_InitStruct.I2C_ClockSpeed = 400000;     // Hz
47
  I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;
48
  I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2;
49
  I2C_InitStruct.I2C_OwnAddress1 = 0x00;
50
  I2C_InitStruct.I2C_Ack = I2C_Ack_Disable;
51
  I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
52
  I2C_Init(I2C1, &I2C_InitStruct);
53
54
  I2C_Cmd(I2C1, ENABLE);
55
  i2c_status = i2c_ok;
56
57
  I2C_ClearFlag(I2C_Module, I2C_FLAG_AF);
58
  I2C_ClearFlag(I2C_Module, I2C_FLAG_ARLO);
59
  I2C_ClearFlag(I2C_Module, I2C_FLAG_BERR);
60
61
}

von Walter T. (nicolas)


Lesenswert?

Danke, so geht es!

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.