Ingo Laabs schrieb:
> und den kram schnalle ich irgendwie nicht
Wo liegt das Problem.
Du fängst damit an, deine Funktionen in eine eigene *.c zu verschieben
eeprom.c
1 | int EEPROM_SCHREIBEN (void)
|
2 | {
|
3 | BasisAdresse = 0xA0;
|
4 | BlockAdresse = EEpromAdresse/256;
|
5 | BasisAdresse1 = BasisAdresse + BlockAdresse * 2;
|
6 | Speicherzelle = EEpromAdresse%256; // modulo
|
7 |
|
8 | TWSR = (0<<TWPS0) | (0<<TWPS1); // Prescaler 1
|
9 | TWBR = 0x34; // berechnung ergab 52
|
10 |
|
11 |
|
12 | TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTA); // start senden
|
13 | while (!(TWCR & (1<<TWINT))); // warten bis fertig
|
14 | if ((TWSR & 0xF8) != START)
|
15 | ERROR();
|
16 |
|
17 |
|
18 | TWDR = BasisAdresse1 ; // Controll BYTE fur 24c08 OxA1 = lesen !!
|
19 | TWCR = (1<<TWINT) | (1<<TWEN);
|
20 | while (!(TWCR & (1<<TWINT))); // warten bis fertig
|
21 | if ((TWSR & 0xF8) != TWI_MTX_ADR_ACK) // hats geklappt ?
|
22 | ERROR();
|
23 |
|
24 |
|
25 | TWDR = Speicherzelle; // Daten(ADRERSSE) an das EEPROM senden
|
26 | TWCR = (1<<TWINT) | (1<<TWEN);
|
27 | while (!(TWCR & (1<<TWINT))); // warten bis fertig
|
28 | if ((TWSR & 0xF8) != TWI_MTX_DATA_ACK) // hats geklappt ?
|
29 | ERROR();
|
30 |
|
31 |
|
32 | TWDR = EEpromDaten; // Daten(DATEN) an das EEPROM senden
|
33 | TWCR = (1<<TWINT) | (1<<TWEN);
|
34 | while (!(TWCR & (1<<TWINT))); // warten bis fertig
|
35 | if ((TWSR & 0xF8) != TWI_MTX_DATA_ACK) // hats geklappt ?
|
36 | ERROR();
|
37 |
|
38 | TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO); // und STOP senden
|
39 | while(TWCR & (1<<TWSTO)); // warten bis fertig
|
40 |
|
41 | return 0;
|
42 | }
|
Soweit so gut.
Jetzt brauchst du erst mal ein Header File dafür.
In das Header File kommt alles rein, was ein Verwender der Funktionen
wissen muss. Was könnte das sein? Nun zunächst mal muss er wissen welche
Funktionen es gibt, welche Argumente sie nehmen und welche Returnwerte
sie liefern.
Also fängst du an
eeprom.h
1 | int EEPROM_SCHREIBEN (void);
|
Du du ein guter C-Programmierer bist, legst du auch gleich noch einen
Include-Guard in das Header File
eeprom.h
1 | #ifndef EEPROM_H_INCLUDED
|
2 | #define EEPROM_H_INCLUDED
|
3 |
|
4 | int EEPROM_SCHREIBEN (void);
|
5 |
|
6 | #endif
|
Soweit so gut.
Zurück zum EEPROM.C
Wenn dieses File compiliert wird, für sich alleine, welche includes
benötigt es.
Dazu müsst du wissen, was denn alles in der Funktion verwendet wird.
Kurz über den Code drüber geschaut ergibt: io.h wird auf jeden Fall
drinnen sein. twi.h ebenfalls. Und das eigene Include File eeprom.h zu
inkludieren ist auch immer eine gute Idee. Auf die Art kann der Compiler
prüfen, ob der Prototyp im Header File auch mit der Funktionsdefinition
übereinstimmt.
Auch werden alle #define die für das EEPROM_SCHREIBEN notwendig sind aus
der Hauptdatei nach eeprom.c verschoben.
Ziel ist es, dass eeprom.c für sich alleine compiliert werden kann. Dazu
muss aber alles was da drinnen verwendet wird, auch irgendwo herkommen.
Also:
1 | #include <avr/io.h>
|
2 | #include <avr/twi.h>
|
3 | #include "eeprom.h"
|
4 |
|
5 | // General TWI Master staus codes
|
6 | #define START 0x08
|
7 |
|
8 | // TWI Master Transmitter staus codes
|
9 | #define TWI_MTX_ADR_ACK 0x18 // SLA+W has been tramsmitted and ACK received
|
10 | #define TWI_MTX_ADR_NACK 0x20 // SLA+W has been tramsmitted and NACK received
|
11 | #define TWI_MTX_DATA_ACK 0x28 // Data byte has been tramsmitted and ACK received
|
12 | #define TWI_MTX_DATA_NACK 0x30 // Data byte has been tramsmitted and NACK received
|
13 |
|
14 | int EEPROM_SCHREIBEN (void)
|
15 | {
|
16 | BasisAdresse = 0xA0;
|
17 | BlockAdresse = EEpromAdresse/256;
|
18 | BasisAdresse1 = BasisAdresse + BlockAdresse * 2;
|
19 | Speicherzelle = EEpromAdresse%256; // modulo
|
20 |
|
21 | TWSR = (0<<TWPS0) | (0<<TWPS1); // Prescaler 1
|
22 | TWBR = 0x34; // berechnung ergab 52
|
23 |
|
24 |
|
25 | TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTA); // start senden
|
26 | while (!(TWCR & (1<<TWINT))); // warten bis fertig
|
27 | if ((TWSR & 0xF8) != START)
|
28 | ERROR();
|
29 |
|
30 |
|
31 | TWDR = BasisAdresse1 ; // Controll BYTE fur 24c08 OxA1 = lesen !!
|
32 | TWCR = (1<<TWINT) | (1<<TWEN);
|
33 | while (!(TWCR & (1<<TWINT))); // warten bis fertig
|
34 | if ((TWSR & 0xF8) != TWI_MTX_ADR_ACK) // hats geklappt ?
|
35 | ERROR();
|
36 |
|
37 |
|
38 | TWDR = Speicherzelle; // Daten(ADRERSSE) an das EEPROM senden
|
39 | TWCR = (1<<TWINT) | (1<<TWEN);
|
40 | while (!(TWCR & (1<<TWINT))); // warten bis fertig
|
41 | if ((TWSR & 0xF8) != TWI_MTX_DATA_ACK) // hats geklappt ?
|
42 | ERROR();
|
43 |
|
44 |
|
45 | TWDR = EEpromDaten; // Daten(DATEN) an das EEPROM senden
|
46 | TWCR = (1<<TWINT) | (1<<TWEN);
|
47 | while (!(TWCR & (1<<TWINT))); // warten bis fertig
|
48 | if ((TWSR & 0xF8) != TWI_MTX_DATA_ACK) // hats geklappt ?
|
49 | ERROR();
|
50 |
|
51 | TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO); // und STOP senden
|
52 | while(TWCR & (1<<TWSTO)); // warten bis fertig
|
53 |
|
54 | return 0;
|
55 | }
|
Wenn du eeprom.c jetzt compilierst kommst du drauf:
Da sind ein Haufen Variablen undefiniert.
Jetzt rächt es sich, dass du sinnloserweise einen Haufen Variablen
global gemacht hast, anstatt das gleich vernünftig anzugehen.
int EEpromDaten ;
int EEpromAdresse ;
int BasisAdresse1;
int BlockAdresse;
int Speicherzelle;
int BasisAdresse;
Was ist damit?
Einige der Variablen ( BasisAdresse1, BlockAdresse, Speicherzelle und
Basisadresse) werden nur innerhalb der Funktion benötigt. Also macht man
die auch funktionslokal. Andere deiner Variablen EEpromDaten und
EEpromAdresse sind hingegen dafür zuständig, dass die Funktion mit
Werten versorgt wird. Die werden zu Funktionsargumenten.
1 | #include <avr/io.h>
|
2 | #include <avr/twi.h>
|
3 | #include "eeprom.h"
|
4 |
|
5 | // General TWI Master staus codes
|
6 | #define START 0x08
|
7 |
|
8 | // TWI Master Transmitter staus codes
|
9 | #define TWI_MTX_ADR_ACK 0x18 // SLA+W has been tramsmitted and ACK received
|
10 | #define TWI_MTX_ADR_NACK 0x20 // SLA+W has been tramsmitted and NACK received
|
11 | #define TWI_MTX_DATA_ACK 0x28 // Data byte has been tramsmitted and ACK received
|
12 | #define TWI_MTX_DATA_NACK 0x30 // Data byte has been tramsmitted and NACK received
|
13 |
|
14 | int EEPROM_SCHREIBEN ( int EEpromAdresse, int EEpromDaten )
|
15 | {
|
16 | int BasisAdresse1;
|
17 | int BlockAdresse;
|
18 | int Speicherzelle;
|
19 | int BasisAdresse;
|
20 |
|
21 | BasisAdresse = 0xA0;
|
22 | BlockAdresse = EEpromAdresse/256;
|
23 | BasisAdresse1 = BasisAdresse + BlockAdresse * 2;
|
24 | Speicherzelle = EEpromAdresse%256; // modulo
|
25 |
|
26 | TWSR = (0<<TWPS0) | (0<<TWPS1); // Prescaler 1
|
27 | TWBR = 0x34; // berechnung ergab 52
|
28 |
|
29 |
|
30 | TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTA); // start senden
|
31 | while (!(TWCR & (1<<TWINT))); // warten bis fertig
|
32 | if ((TWSR & 0xF8) != START)
|
33 | ERROR();
|
34 |
|
35 |
|
36 | TWDR = BasisAdresse1 ; // Controll BYTE fur 24c08 OxA1 = lesen !!
|
37 | TWCR = (1<<TWINT) | (1<<TWEN);
|
38 | while (!(TWCR & (1<<TWINT))); // warten bis fertig
|
39 | if ((TWSR & 0xF8) != TWI_MTX_ADR_ACK) // hats geklappt ?
|
40 | ERROR();
|
41 |
|
42 |
|
43 | TWDR = Speicherzelle; // Daten(ADRERSSE) an das EEPROM senden
|
44 | TWCR = (1<<TWINT) | (1<<TWEN);
|
45 | while (!(TWCR & (1<<TWINT))); // warten bis fertig
|
46 | if ((TWSR & 0xF8) != TWI_MTX_DATA_ACK) // hats geklappt ?
|
47 | ERROR();
|
48 |
|
49 |
|
50 | TWDR = EEpromDaten; // Daten(DATEN) an das EEPROM senden
|
51 | TWCR = (1<<TWINT) | (1<<TWEN);
|
52 | while (!(TWCR & (1<<TWINT))); // warten bis fertig
|
53 | if ((TWSR & 0xF8) != TWI_MTX_DATA_ACK) // hats geklappt ?
|
54 | ERROR();
|
55 |
|
56 | TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO); // und STOP senden
|
57 | while(TWCR & (1<<TWSTO)); // warten bis fertig
|
58 |
|
59 | return 0;
|
60 | }
|
Da sich die Funktionssignatur geändert hat, muss diese natürlich auch im
Header File angepasst werden
eeprom.h
1 | #ifndef EEPROM_H_INCLUDED
|
2 | #define EEPROM_H_INCLUDED
|
3 |
|
4 | int EEPROM_SCHREIBEN ( int EEpromAdresse, int EEpromDaten );
|
5 |
|
6 | #endif
|
Erneutes compilieren von eeprom.c ergibt, dass eine Funktion ERROR
aufgerufen wird. Das geht natürlich gar nicht. Wenn EEPROM_SCHREIBEN
einen Fehler feststellt, dann soll sie das melden aber auf keinen Fall
selbst eine Aktion unternehmen. Was im Fehlerfall geschehen soll obliegt
der Verantwortung des Aufrufers der Funktion.
Du hast noch den Funktionsreturnwert. Und den benutzet du um einen
Fehlercode zurückzugeben. Fürs erste reicht es völlig aus, wenn im
Fehlerfall 0 (also logisch FALSE laut C-Konvention) bzw. 1 (also TRUE
laut C-Konvention) zurückgegeben wird. FALSE steht dabei für Fehler,
TRUE für Aktion ausgeführt. Damit kann dann der Aufrufer von
EEPROM_SCHREIBEN auf einen Fehler so reagieren, wie es ihm richtig
erscheint.
1 | #include <avr/io.h>
|
2 | #include <avr/twi.h>
|
3 | #include "eeprom.h"
|
4 |
|
5 | // General TWI Master staus codes
|
6 | #define START 0x08
|
7 |
|
8 | // TWI Master Transmitter staus codes
|
9 | #define TWI_MTX_ADR_ACK 0x18 // SLA+W has been tramsmitted and ACK received
|
10 | #define TWI_MTX_ADR_NACK 0x20 // SLA+W has been tramsmitted and NACK received
|
11 | #define TWI_MTX_DATA_ACK 0x28 // Data byte has been tramsmitted and ACK received
|
12 | #define TWI_MTX_DATA_NACK 0x30 // Data byte has been tramsmitted and NACK received
|
13 |
|
14 | int EEPROM_SCHREIBEN ( int EEpromAdresse, int EEpromDaten )
|
15 | {
|
16 | int BasisAdresse1;
|
17 | int BlockAdresse;
|
18 | int Speicherzelle;
|
19 | int BasisAdresse;
|
20 |
|
21 | BasisAdresse = 0xA0;
|
22 | BlockAdresse = EEpromAdresse/256;
|
23 | BasisAdresse1 = BasisAdresse + BlockAdresse * 2;
|
24 | Speicherzelle = EEpromAdresse%256; // modulo
|
25 |
|
26 | TWSR = (0<<TWPS0) | (0<<TWPS1); // Prescaler 1
|
27 | TWBR = 0x34; // berechnung ergab 52
|
28 |
|
29 |
|
30 | TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTA); // start senden
|
31 | while (!(TWCR & (1<<TWINT))); // warten bis fertig
|
32 | if ((TWSR & 0xF8) != START)
|
33 | return 0;
|
34 |
|
35 |
|
36 | TWDR = BasisAdresse1 ; // Controll BYTE fur 24c08 OxA1 = lesen !!
|
37 | TWCR = (1<<TWINT) | (1<<TWEN);
|
38 | while (!(TWCR & (1<<TWINT))); // warten bis fertig
|
39 | if ((TWSR & 0xF8) != TWI_MTX_ADR_ACK) // hats geklappt ?
|
40 | return 0;
|
41 |
|
42 |
|
43 | TWDR = Speicherzelle; // Daten(ADRERSSE) an das EEPROM senden
|
44 | TWCR = (1<<TWINT) | (1<<TWEN);
|
45 | while (!(TWCR & (1<<TWINT))); // warten bis fertig
|
46 | if ((TWSR & 0xF8) != TWI_MTX_DATA_ACK) // hats geklappt ?
|
47 | return 0;
|
48 |
|
49 |
|
50 | TWDR = EEpromDaten; // Daten(DATEN) an das EEPROM senden
|
51 | TWCR = (1<<TWINT) | (1<<TWEN);
|
52 | while (!(TWCR & (1<<TWINT))); // warten bis fertig
|
53 | if ((TWSR & 0xF8) != TWI_MTX_DATA_ACK) // hats geklappt ?
|
54 | return 0;
|
55 |
|
56 | TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO); // und STOP senden
|
57 | while(TWCR & (1<<TWSTO)); // warten bis fertig
|
58 |
|
59 | return 1;
|
60 | }
|
Gut. Wenn ich jetzt nichts mehr übersehen habe, müsste eeprom.c
eigentlich compilieren.
Wie verwendet man nun das Ganze?
zb so
1 | // Mega 32
|
2 | // CPU 12 MHz EEPROM 24c08 100 KHz
|
3 | #include <stdlib.h>
|
4 | #include <avr/io.h>
|
5 | #include "eeprom.h" // damit wird hier Bescheid gegeben, was das
|
6 | // "EEPROM Modul" so alles kann.
|
7 |
|
8 | #define F_CPU 12000000UL
|
9 |
|
10 |
|
11 | int ERROR (void)
|
12 | {
|
13 | DDRA = 0xFF;
|
14 | PORTA = 0x01;
|
15 | while(1)
|
16 | {
|
17 | // Nüscht machen !
|
18 | }
|
19 | return 0;
|
20 | }
|
21 |
|
22 | int main (void)
|
23 | {
|
24 | int Addr;
|
25 |
|
26 | for (Addr=0; Addr <= 1023; Addr++)
|
27 | {
|
28 | if( !EEPROM_SCHREIBEN( Addr, 0 ) )
|
29 | ERROR();
|
30 | }
|
31 |
|
32 | while(1)
|
33 | {
|
34 | }
|
35 |
|
36 | return 0;
|
37 | }
|
In dein Projekt fügst du noch eeprom.c und eeprom.h mit ein.
Beim nächsten Build-Prozess werden eeprom.c compiliert, deien Haupt *.c
wird compiliert und die Ergebnisse beider Übersetzungsvorgänge (eeprom.o
und dein *.o) werden vom Linker zum Gesamtprogram zusammengefügt.
****************************************************
Im Grunde ist der Vorgang sehr simpel.
Du musst dich einfach nur von dem Prinzip leiten lassen:
Wenn eeprom.c für sich alleine compiliert wird, was braucht es alles
dafür?
Und so baust du dann das *.c File dafür auf. Dazu noch ein Header File,
welches vom Gedankengang getragen wird: Wenn jemand mein *.c verwenden
will, was muss er alles wissen?
In deinem Fall ist die Sache einfach. Jemand der deine EEPROM
Funktionalität benutzen will, muss nur wissen, dass es eine Funktion
EEPROM_SCHREIBEN gibt. Mehr nicht. Er muss nicht wissen, dass du intern
eine Variable BlockAdresse hast. Er muss nicht wissen, dass du eine
Variable Speicherzelle hast, etc. All das ist in der Funktion
EEPROM_SCHREIBEN verborgen und geht ausserhalb der Funktion niemanden
etwas an.
PS: Bist du dir sicher, dass ein Datentyp von int für EEpromDaten
sinnvoll ist?