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
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 ); |
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.
Ä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 | }
|
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.