Forum: Mikrocontroller und Digitale Elektronik Suche E2PROM Manager für STM32F10x


von Christian J. (Gast)


Lesenswert?

Hallo,

kann ja sein, dass den schon jemand hat. Basierend auf den StdperiphLibs 
für STM32F10x oder andere M3 oder M4, das passe ich mir schon an.

Funktion:

Optimiertes Managen eines I2C EEPROMs (64kb oder 128Kb) für beliebig 
lange Datensätze auf beliebige Addressen. Code soll erkennen wann 
schnelles Page Write möglich und wann Einzelbyte. Fehlerabfragen der 
Events sollten alle da sein. Ebenso Abfragen des ACK für optimierte 
Schreibzeit.

write_string(int* adress,int *data, *int len)
read_string(int* target, int* source, *int len)

Mehr braucht es nicht.

Klingt auf den ersten Biss ganz easy, ist es aber nicht ganz zudem ich 
noch vor habe auf diese Routinen eine Art Filesystem aufzusetzen, um 
auch gestückelte Datensätze zu handhaben und eine Art einfache FAT dabei 
mit Header, Verkettung der Blocks usw.

Falls jemand sowas hat bitte winken!

Gruss,
Christian

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

Anbei fürn AVR mit SW-I2C.

von Christian J. (Gast)


Lesenswert?

Hallo,

bin kurz drüber, da AVR leider in keinster Weise kompatibel zum STM32 
ist und alle LL-Routinen daher nicht funktionieren. Die Hardwareroutinen 
habe ich aber schon geschrieben, es fehlt nur noch der "Manager".

Wo ist denn da die Intelligenz drin?

Hier ?
1
do{
2
    if( !set_addr( eeaddr ))
3
      return 0;                                         // 0 = failed
4
    do
5
      si2c_w( *sram++ );
6
    while( --len && (++eeaddr & (PAGE_SIZE-1)));        // end or next page
7
    si2c_stop();
8
  }while( len );

von Peter D. (peda)


Lesenswert?

Christian J. schrieb:
> alle LL-Routinen daher nicht funktionieren

Die 4 Grundfunktionen (Start, Stop, Write, Read) sind doch schön 
gekapselt, sollten sich daher leicht auf jedes x-beliebiges HW-I2C 
anpassen lassen.

Christian J. schrieb:
> Hier ?

Ja.
Es wird automatisch in Pagewrites zerlegt.

von Christian J. (Gast)


Lesenswert?

Ähm Peter.... hust

meine I2C Routinchen für die HW des Cortex sehen aber so aus, sind schon 
ein wenig "mehr". Die Primary Cell von ARM für I2c ist quasi der 
Porsche. Da wird ne Statemachine on-the-fly mit Daten gefüttert und der 
Transfer rattert dann automatisch durch während die CPU schon woanders 
ist. Durch die Events werden INts ausgelöst oder eben per Polling 
nachgeschaut was Sache ist. Aber die Lösuing ist ja sehr einfach und 
wird eingearbeitet.
1
// ---------------------------------------------------------------
2
// Initialisiere das E2Prom
3
// ---------------------------------------------------------------
4
void e2p_Init_I2C()
5
{
6
    GPIO_InitTypeDef GPIO_InitStruct;       // Port Init Struct
7
    I2C_InitTypeDef I2C_InitStruct;         // I2C Init Struct
8
9
    // Peripherie Takt einschalten I2C3: PA8 (I2C SCL) und PC9 (I2C3_SDA)
10
    RCC_APB1PeriphClockCmd(I2C_RCC, ENABLE);                    // Enable APB1 peripheral clock für I2C3
11
    RCC_AHB1PeriphClockCmd(I2C_PERIPH_SCL_PORT, ENABLE);       // Enable clock für PortA SCL pin
12
    RCC_AHB1PeriphClockCmd(I2C_PERIPH_SDA_PORT, ENABLE);       // Enable clock für PortC SDA pin
13
14
    I2C_Cmd(I2C_CHANNEL, DISABLE);                              // I2C abschalten, damit Bus still bleibt
15
16
    // Port mit SCL einrichten
17
    GPIO_InitStruct.GPIO_Pin   = I2C_SCL_PIN;                // Pin A8 (I2C3_SCL)
18
    GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AF;              // Pins als Alternate Function einstellen
19
    GPIO_InitStruct.GPIO_Speed = GPIO_Fast_Speed;           // IO Speed, nicht Baudrate, 2 Mhz reicht
20
    GPIO_InitStruct.GPIO_OType = GPIO_OType_OD;             // Open Drain Output
21
    GPIO_InitStruct.GPIO_PuPd  = GPIO_PuPd_NOPULL;
22
    GPIO_Init(I2C_SCL_PORT, &GPIO_InitStruct);
23
24
    // Port mit SDA einrichten
25
    GPIO_InitStruct.GPIO_Pin   = I2C_SDA_PIN;
26
    GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AF;               // Pin C9 (SDA)
27
    GPIO_InitStruct.GPIO_Speed = GPIO_Fast_Speed;
28
    GPIO_InitStruct.GPIO_OType = GPIO_OType_OD;
29
    GPIO_InitStruct.GPIO_PuPd  = GPIO_PuPd_NOPULL;
30
    GPIO_Init(I2C_SDA_PORT, &GPIO_InitStruct);
31
32
    // Pins sind nun auf AF verbunden
33
    // USART3 kann nun Kontrolle über Pins übernehmen
34
    GPIO_PinAFConfig(I2C_SCL_PORT, I2C_SCL_SOURCE, I2C_GPIO_AF);
35
    GPIO_PinAFConfig(I2C_SDA_PORT, I2C_SDA_SOURCE, I2C_GPIO_AF);
36
37
    // Struktur Parameter fuer I2C setzen
38
    I2C_StructInit(&I2C_InitStruct);                                        // Rücksetzen auf Default
39
    I2C_InitStruct.I2C_ClockSpeed           = 100000;                       // 100 khz Busfrequenz
40
    I2C_InitStruct.I2C_Mode                 = I2C_Mode_I2C;                 // Normaler I2C Mode
41
    I2C_InitStruct.I2C_DutyCycle            = I2C_DutyCycle_2;              // 50:50 Duty Cycle
42
    I2C_InitStruct.I2C_OwnAddress1          = 0xEE;
43
    I2C_InitStruct.I2C_Ack                  = I2C_Ack_Enable;               // ACK senden
44
    I2C_InitStruct.I2C_AcknowledgedAddress  = I2C_AcknowledgedAddress_7bit; // 7 Bit Adressierung
45
46
    // I2C einschalten
47
    I2C_Cmd(I2C_CHANNEL, ENABLE);
48
49
    // I2C3 mit den obigen Parametern konfigurieren
50
    I2C_Init(I2C_CHANNEL, &I2C_InitStruct);
51
}
52
53
// ---------------------------------------------------------------
54
// Setzt alle Einstellungen zurück auf Default
55
// ---------------------------------------------------------------
56
void e2p_DeInit_I2C()
57
{
58
    // I2C Einheit zurücksetzen
59
    I2C_DeInit(I2C_CHANNEL);
60
    RCC_APB1PeriphClockCmd(I2C_RCC, DISABLE);    // Enable APB1 peripheral clock für I2C3
61
    I2C_initialized = false;
62
}
63
64
// ---------------------------------------------------------------
65
// Beschreibe das E2PROM einzelnen mit mehreren Bytes
66
// ---------------------------------------------------------------
67
uint8_t e2p_WriteMultiBytes(uint16_t addr, uint8_t *data, size_t nrBytes)
68
{
69
    if (!I2C_initialized) {
70
         e2p_Init_I2C(I2C_CHANNEL);
71
         I2C_initialized = true;
72
    }
73
74
    for (uint16_t i = 0; i< nrBytes; i++) {
75
        if (e2p_Write_Byte(addr++,*(data++)))
76
            return ERROR;
77
    }
78
79
    return SUCCESS;
80
}
81
82
// ---------------------------------------------------------------
83
// Beschreibe das E2PROM mit einem Byte
84
// ---------------------------------------------------------------
85
uint8_t e2p_Write_Byte(uint16_t Addr, const uint8_t Data)
86
{
87
    if (!I2C_initialized) {
88
        e2p_Init_I2C(I2C_CHANNEL);
89
        I2C_initialized = true;
90
    }
91
92
    // Transmission starten
93
    e2p_StartWriteAddress(Addr);
94
95
    // Sende Datum
96
    I2C_SendData(I2C_CHANNEL, Data);
97
    if (I2C_WaitForEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS)
98
        return ERROR;
99
100
    // Sende I2C3 STOP Condition
101
    I2C_GenerateSTOP(I2C_CHANNEL, ENABLE);
102
103
    // Warte bis fertig geschrieben
104
    if (e2p_WaitForWriteReady() != SUCCESS)
105
       return ERROR;
106
107
    return SUCCESS;
108
}
109
110
// ---------------------------------------------------------------
111
// Ein Byte aus E2PROM lesen
112
// ---------------------------------------------------------------
113
uint8_t e2p_Read_Byte(const uint16_t Addr)
114
{
115
    if (!I2C_initialized) {
116
        e2p_Init_I2C(I2C_CHANNEL);
117
        I2C_initialized = true;
118
    }
119
120
    // ACK ein
121
    I2C_AcknowledgeConfig(I2C_CHANNEL, ENABLE);
122
123
    // Transmission starten
124
    if (e2p_StartWriteAddress(Addr) != SUCCESS)
125
            return ERROR;
126
127
    // AF Flag evtl löschen
128
    I2C_CHANNEL->SR1 |= (uint16_t)0x0400;
129
130
    /* -------- E2PROM Lesemodus (MASTER Receiver Mode)------ */
131
132
   // Erzeuge START Bedingung
133
    I2C_GenerateSTART(I2C_CHANNEL, ENABLE);
134
    if (I2C_WaitForEvent( I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS)
135
        return 0xff;
136
137
    // Adressiere das E2PROM
138
    I2C_Send7bitAddress(I2C_CHANNEL, MEM_DEVICE_READ_ADDR, I2C_Direction_Receiver);
139
    if (I2C_WaitForEvent(I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) != SUCCESS)
140
        return 0xff;
141
142
    /* Wichtiger Hinweis, bzgl der Reihenfolge des Bit Setzens, Manual. STM32F407, S834
143
        Die Konfiguration muss VOR dem Buszugriff erfolgen, d.h. das Datenbyte wird
144
        erst ganz zum Schluss ausgelesen */
145
146
    // NACK einstellen E2PROM anzuzeigen, dass keine weiteren Daten angefordert sind
147
    I2C_AcknowledgeConfig(I2C_CHANNEL, DISABLE);
148
    if (I2C_WaitForEvent(I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS)
149
        return 0xff;
150
151
    // Stop Bedingung erzeugen NACH dem nächsten Lesezugriff
152
    I2C_GenerateSTOP(I2C_CHANNEL, ENABLE);
153
    if (I2C_WaitForEvent(I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS)
154
        return 0xff;
155
156
    return I2C_ReceiveData(I2C_CHANNEL);
157
}
158
159
/* /////////////////////////////////////////////////////////
160
        Low Level Routinen für die Bedienung des E2PROMs
161
* /////////////////////////////////////////////////////// */
162
163
164
// Hilfsroutine zum Auslesen der Events mit Timeout
165
static uint8_t I2C_WaitForEvent(uint32_t event)
166
{
167
   timeout = I2C_TIMEOUT_MAX;
168
   while(I2C_CheckEvent(I2C_CHANNEL, event) != SUCCESS) {
169
        if ((timeout--) == 0) return ERROR;
170
    }
171
    return SUCCESS;
172
}
173
174
// ---------------------------------------------------------------
175
// Starte die E2PROM Sequenz: Startbit, E2prom Adresse, Zieladresse...
176
// ---------------------------------------------------------------
177
static uint8_t e2p_StartWriteAddress(uint16_t Addr)
178
{
179
    uint8_t lower_addr, upper_addr;
180
181
    lower_addr = (uint8_t)((0x00FF) & Addr);
182
    Addr = Addr>>8;
183
    upper_addr = (uint8_t)((0x00FF) & Addr);
184
185
    /* -------- E2PROM Adresszeiger auf Zieladresse setzen ------ */
186
187
    // Erzeuge START Bedingung
188
    I2C_GenerateSTART(I2C_CHANNEL, ENABLE);
189
    if (I2C_WaitForEvent(I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS)
190
        return 0xff;
191
192
    // Adressiere das E2PROM
193
    I2C_Send7bitAddress(I2C_CHANNEL, MEM_DEVICE_WRITE_ADDR, I2C_Direction_Transmitter);
194
    if (I2C_WaitForEvent(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS)
195
        return 0xff;
196
197
    // Adresse als 2 x 8 Bit senden
198
    I2C_SendData(I2C_CHANNEL,upper_addr);
199
    if (I2C_WaitForEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS)
200
        return 0xff;
201
202
    I2C_SendData(I2C_CHANNEL, lower_addr);
203
    if (I2C_WaitForEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS)
204
        return 0xff;
205
206
    return SUCCESS;
207
}
208
209
// ---------------------------------------------------------------
210
// Warte bis Schreibzyklus beendet ist (ACK Polling)
211
// ---------------------------------------------------------------
212
static uint8_t e2p_WaitForWriteReady() {
213
214
    // Warte bis Byte weggeschrieben wurde (Slave sendet solange kein ACK)
215
    uint32_t timeout = I2C_TIMEOUT_MAX;
216
    do{
217
        if (--timeout == 0)
218
            return ERROR;
219
        // Erzeuge Schreibzugriff......
220
        I2C_GenerateSTART(I2C_CHANNEL, ENABLE);
221
        while(!I2C_CheckEvent(I2C_CHANNEL, I2C_EVENT_MASTER_MODE_SELECT));
222
        I2C_Send7bitAddress(I2C_CHANNEL, MEM_DEVICE_WRITE_ADDR, I2C_Direction_Transmitter);
223
    } while(I2C_WaitForEvent(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS);
224
225
    // Sequenz abschliessen
226
    I2C_GenerateSTOP(I2C_CHANNEL, ENABLE);
227
228
    return SUCCESS;
229
}

von Peter D. (peda)


Lesenswert?

Das ganze Hardware-Geraffel ist natürlich nen ordentlichen Zacken mehr 
Holz.
Da ist ja mein Code nur ein Bruchteil groß.

Der Pagetest ist ganz einfach. Es wir solange die innere Schleife nicht 
verlassen, wie die nächsten lower Pagebits != 0 sind.

Ich teste auch nicht auf jeden Fehler. Wenn die 16Bit-Adresse ACK 
empfängt, gehe ich davon aus, daß auch das Bytes schreiben gut geht. Der 
EEPROM sitzt bei mir direkt am MC, da sind Störeinkopplungen 
unwarscheinlich.

Und das SW-I2C kann sich auch nicht intern aufhängen. Daher habe ich das 
HW-I2C absichtlich nicht benutzt.
Vorteil des SW-I2C: weniger Code, weniger Trouble.
Vorteil des HW-I2C: weniger CPU-Load, wenn es als Interrupt oder DMA 
läuft.

: Bearbeitet durch User
von Christian J. (Gast)


Lesenswert?

Hi Peter,

ich danke Dir, damit ist meine Frage auch beantwortet. Und das ganze 
Geraffel ist kompiliert nur noch winzig klein, aber viel Schreibarbeit,
um kompatibel zu der API zu bleiben. Und es kann abstürzen, leider.
Bei AVR nutze ich schon lange einen SW-I2C-Master, besser als solche 
schlimmen Sachen wir das TWI zu benutzen.
Beim Cortex eher Spass an der Sache, wissen wie es geht, Spass am 
Denksport, da ich beruflich schon lange keine HW und SW mehr entwickle.

Danke Dir und Grüße,
Christian

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.