STm32Fxxx: I2C Hardware Experten gesucht

Gast #4951633
Lesenswert?

Hallo,

ich habe da vor 2 Jahren mal ein Thread gehabt, der aber nicht zu Ende 
geführt wurde. Bin seit 2 Tagen an der I2C Statemaschine eines STM32F103 
dran, um richtige und verständliche EEPROM Routinen zu schreiben. Habe 
einen Logic Analyser mit dran und kann mit das Ganze auch anschauen.

Falls hier jemand ist, der sich schon INTENSIV mit dem Thema befasst hat 
und vor allem selbst den Code geschrieben hat, der möge doch mal bitte 
hier schreien.

Bytes schreiben klappt soweit ganz gut, auch mit dem Polling. Bytes 
lesen liefert zwar richtige Ergebnisse aber mich stört das 255+NAK im 
Bild. Da geht zeit bei flöten, der Stop müsste eigentlich direkt 
dahinter kommen.

Code aus dem Netz gibt es zwar aber auch da sind Fehler drin, weil da 
eine "serielle Denke" am Werk war, zb bei

uint8_t i2c_receive_nack()

stand ursprünglich noch ein i2c_stop() dahinter, so wie man es von einem 
AVR her kennt. Fakt ist aber, dass wir es hier mit einer sehr komplexen 
Multi-Master fähigen Statemachine zu tun haben, die ein ganzes Netzwerk 
betreiben kann und die VORHER gefüttert wird und die dann ihr Programm 
auf dem Bus runterspult.

Mein Hauptanliegen ist, ob ich das richtig codiert habe und vor allem, 
ob ich das richtig verstanden habe?

Ich frage mich zb immer noch was das "Event 5" auslöst? Ich frage es 
zwar aber aber mein Code bleibt da hängen, wenn das EEPROM mit seinem 
internetn Zyklus befasst ist.

Gruss,
Christian
1
/* ---------------------------------------------------------------
2
   Schreibt ein Byte in eine einzelne Adresse
3
   Eingabe: Nr. des EEPROMs (0 und 1)
4
            16 Bit Adresse
5
            8 Bit Datenwort
6
   --------------------------------------------------------------*/
7
uint8_t EE_WriteByte(uint8_t EE, uint16_t adr, uint8_t data)
8
{
9
    uint8_t lower_addr, upper_addr;
10

11
    // EEPROM 1 oder 2?
12
    switch (EE) {
13
        case 0: EE = E1_WR_ADR;
14
                break;
15
        case 1: EE = E2_WR_ADR;
16
                break;
17
    }
18

19
    lower_addr = (uint8_t)((0x00FF) & adr);
20
    adr = adr>>8;
21
    upper_addr = (uint8_t)((0x00FF) & adr);
22

23
    // Wait until I2C1 is not busy anymore
24
    //while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));
25

26
    // ACK ein
27
    I2C_AcknowledgeConfig(I2C1, ENABLE);
28

29
    do {
30
        /* Erzeuge START Bedingung */
31
        I2C_GenerateSTART(I2C1, ENABLE);
32
        if (I2C_WaitForEvent(200, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS)
33
            return ERROR;
34
        // Adressiere das E2PROM
35
        I2C_Send7bitAddress(I2C1, EE, I2C_Direction_Transmitter);
36
     } while (I2C_WaitForEvent(200, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS);
37

38

39
    // Adresse als 2 x 8 Bit senden
40
    i2c_transmit(upper_addr);
41
    i2c_transmit(lower_addr);
42

43
    // Sende Datum
44
    i2c_transmit(data);
45

46
    // Sende I2C3 STOP Condition
47
    i2c_stop();
48

49
    return SUCCESS;
50

51
}
52

53
/* ---------------------------------------------------------------
54
   Ein Byte aus E2PROM lesen
55
   Eingabe: Adresse 16 Bit
56
   ------------------------------------------------------------- */
57
uint8_t EE_Read_Byte(uint8_t EE, uint16_t adr)
58
{
59
    uint8_t ee_read_adr, ee_write_adr;
60
    uint8_t lower_addr, upper_addr, data;
61

62
    lower_addr = (uint8_t)((0x00FF) & adr);
63
    adr = adr>>8;
64
    upper_addr = (uint8_t)((0x00FF) & adr);
65

66
    // EEPROM 1 oder 2?
67
    switch (EE) {
68
        case 0: ee_write_adr = E1_WR_ADR;
69
                ee_read_adr  = E1_RD_ADR;
70
                break;
71
        case 1: ee_write_adr = E2_WR_ADR;
72
                ee_read_adr  = E2_RD_ADR;
73
                break;
74
    }
75

76
    /* -------- E2PROM Lesemodus (MASTER Receiver Mode)------ */
77

78
    // Adressiere das E2PROM auf zu lesende Speicherstelle
79
    i2c_start();
80
    i2c_address_direction(ee_write_adr, I2C_Direction_Transmitter);
81
    i2c_transmit(upper_addr);
82
    i2c_transmit(lower_addr);
83

84
    // Umschalten in Lesemodus
85
    i2c_start();
86
    i2c_address_direction(ee_read_adr, I2C_Direction_Receiver);
87
    data = i2c_receive_nack();
88

89
    return (data);
90

91
}
1
/* ---------- Empfange ACK ------------*/
2
uint8_t i2c_receive_ack()
3
{
4
    // Enable ACK of received data
5
    I2C_AcknowledgeConfig(I2C1, ENABLE);
6
    // Wait for I2C EV7
7
    // It means that the data has been received in I2C data register
8
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED));
9
    // Read and return data byte from I2C data register
10
    return I2C_ReceiveData(I2C1);
11
}
12

13
/* ---------- Empfange NACK ------------*/
14
uint8_t i2c_receive_nack()
15
{
16
    // ACK bei empfangenen Daten abschalten
17
    I2C_AcknowledgeConfig(I2C1, DISABLE);
18
    // Warte fuer EV7 = Daten sind empfangen worden
19
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED));
20

21
    // Stop direkt danach erzeugen
22
    I2C_GenerateSTOP(I2C1, ENABLE);
23
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED));
24

25
    // Read and return data byte from I2C data register
26
    return I2C_ReceiveData(I2C1);
27
}
Angehängte Dateien:
Gast #4951655
Lesenswert?

Danke, den kenne ich schon.  Ist wohl aus den Beispielen.
Habe ihn aber beiseitige gelegt, weil da noch DMA als zusätzliche 
Erschwernis mit hinzu kommt.

geht mir mhr um so einfache Dinge warum eine einfache Abfrage der SM im 
i2c_start(), ob sie beschäftigt ist

while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));

schon einen Hänger verursacht an der Stelle, weil die Schleife nie 
verlassen wird.
Gast #5082905
Lesenswert?

Hallo,

ich schieb den nochmal hoch, da ich mich nach langer Pause mal wieder 
damit befassen wollte.

Wer kennt sich richtig gut aus mit der I2C State Machine des STM32F103 ? 
Wo welche Events abgefragt und ausgelöst werden müssen, wo Timeout und 
wo nicht usw. Ich habe funktionierenden Code laut Analyzer aber der 
hängt sich auch ab und an mal einfach auf, Bumms.... Event kommt nicht, 
alles steht still und das geht nicht.

Diese Statemachine ist nicht trivial, vor allem nicht wenn man auf 
maximalen Durchsatz gehen will, Pageing optimiert nutzt. Und 
Softwarelösungen kommen mir nicht ins Haus, wenn ich eine Top Hardware 
habe, die im DMA arbeiten kann.

Gruss,
Christian
Gast #5083037
Lesenswert?

Super! Danke Dir! Ich schau mir das mal in Ruhe genau an und gleiche es 
mit meinem Code ab.

Berücksichtigst Du auch die Zykluszeit fürs Schreiben oder nimmst du 
einfach einen festen Wert, zb 3-4ms. Ich schreibe quasi alles mit 
Pagewerite, sobald es mehr als 2 Bytes sind.

Weiterhin schafft man es, grad beim Debuggen so ein E2PROM auch intern 
abzuschiessen, so dass es gar nicht mehr reagiert. Man muss nur eine 
Sequenz mittendrin abbrechen, dann ist die interne Statemachine 
durcheinander. Dafür habe ich noch keine Lösung ausser ON/OFF der ganzen 
Schaltung.
Angehängte Dateien:
Gast #5083278
Lesenswert?

Klaus schrieb:

> Christian J. schrieb:
>> Weiterhin schafft man es, grad beim Debuggen so ein E2PROM auch intern
>> abzuschiessen, so dass es gar nicht mehr reagiert. Man muss nur eine
>> Sequenz mittendrin abbrechen, dann ist die interne Statemachine
>> durcheinander. Dafür habe ich noch keine Lösung ausser ON/OFF der ganzen
>> Schaltung.
>
> Beitrag "Re: I2C hängt sich auf"
>
> MfG Klaus

Ähm.... wie kann man sich das genau vorstellen, dieses "Wackeln"? Das 
Schaltwerk meiner Waschmaschine ist dafür nämlich auch anfällig. Da 
nützt kein normaler Start mehr was. Und bei einer Statemachine wie der 
Prime Cell des STM32 kannste die Leitungen eh nicht manuell ansteuern, 
musst sie vorher abkoppeln. Die versteht nur die regulären Abläufe.
Gast #5083453
Lesenswert?

@Felix:

Habe jetzt 2 Stunden versucht deinen Code zum Laufen zu kriegen. Ist 
noch ein kleiner Bug drin, nämlich dass

 // Generate I2C stop condition
    I2C_GenerateSTOP(I2C_KANAL, ENABLE);
    // Wait until I2C stop condition is finished
    while (I2C_GetFlagStatus(I2C_KANAL, I2C_FLAG_STOPF));

die stop Condition nicht auf Ausführung überprüft wird.

Auch hier ist ein Unterschied, nämlich die Verwendung von I2C-CheckEvent 
und I2C_FlagStatus.

// Start-Sequence
    I2C_GenerateSTART(I2Cx, ENABLE);

    timeout = I2C1_TIMEOUT;
    while(!I2C_GetFlagStatus(I2Cx, I2C_FLAG_SB)) {
        if(timeout != 0)
            timeout--;
        else
            return(I2C1_Timeout(I2Cx, -1));
    }

 I2C_GenerateSTART(I2Cx, ENABLE); löst nämlich ein Event aus.


Du verwendest kein Pagewrite, deine Byte Write Routine wird daher Fehler 
melden, wenn du zu schnell schreibst, denn dann antwortet das Eprom 
nicht solange der Schreibzyklus nicht beendet ist.

Da habe ich das eingebaut, der hämmert solange eine Start Cond. raus, 
bis das Ding endlich antwortet.

/* Warte bis I2C Interface frei */
    while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));

    /* ACK ein */
    I2C_AcknowledgeConfig(I2C_KANAL, ENABLE);

    do {
        /* Erzeuge START Bedingung ---- FIXXXME */
        I2C_GenerateSTART(I2C_KANAL, ENABLE);
        if (I2C_WaitForEvent(200, I2C_EVENT_MASTER_MODE_SELECT) != 
SUCCESS)
            return ERROR;
        // Adressiere das E2PROM
        I2C_Send7bitAddress(I2C_KANAL, EE, I2C_Direction_Transmitter);
     } while (I2C_WaitForEvent(200, 
I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS);

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