Forum: Compiler & IDEs Wiederverwendung von Code


von Roland P. (pram)


Lesenswert?

Hallo, ich entwickle zur Zeit ein Projekt, bei dem mehrere Module über 
RS485 kommunizieren. Diese verwenden z.t. unterschiedliche AVR's bzw. 
unterschiedliche Belegung des RS-485 Transceivers und des 
angeschlossenen LCD

Ich habe alle zur Kommunikation relevanen Funktionen in eine rs485.c und 
rs485.h ausgelagert (genau so lcd.c/h), welche in allen Projekten 
genutzt werden. Jedes Projekt (AvrStudio) hat momentan ein eigenes Verz. 
in dem eine Kopie der jeweiligen c/h-Files liegt. Die .h Files werden 
geringfügig abgeändet, da da die Anschlussbelegungen "konfiguriert" 
sind.

Nun möchte ich aber in all meinen Projekten eine gemeinssme "rs485.c" 
und "lcd.c" verwenden, was aber glaub ich gar nicht so einfach ist, da 
diese je nach Projekt unterschiedliche .h Dateien einbinden muss.

Irgendwie steh ich jetzt da ein bisschen auf dem Schlauch (kann aber 
auch sein weil es schon so spät ist)

Gruß
Roland

von Klaus W. (mfgkw)


Lesenswert?

Man kann mit etwas guten Willen alles so bauen, daß die Pinzuordnung
nicht fest verdrahtet werden muß; gelegentlich kommt diesbezüglich
eine Diskussion hier auf, z.B. 
Beitrag "#define-Pinnamen beim Setzen von Pins"

Die üblichen lcd-routines.c/.h von hier setzen aber voraus, daß
alle Einstellungen in der .h gemacht werden.
Ich finde das ebenfalls unglücklich, andere mögen es :-)

Ich habe mir die lcd-routines so umgebaut, daß es eine
Initialisierungsfunktion gibt, der man alle Port- und Pinangaben
übergibt. Das geht für die Pins einfach als Zahl. Bei den Ports
(ggf. zzgl. PIN, PORT, DDR getrennt) kann man in C Zeiger
übergeben; ich mache es in C++ und kann daher Referenzen nehmen.
Nicht durchgehend akzeptiert, aber von mir auch so gemacht, ist
es möglich, nur z.B. PORT zu übergeben, und DDR und PIN
wegzulassen. Das kann in der init-Routine nämlich auch berechnet
werden, solange Atmel bei den AVRs die relative Zuordnung dieser
Register unterienander nicht ändert.
Konkret bei LCD kann ich dadurch ohne Quelltextänderungen
in den LCD-Routinen alle Anschlüsse frei einstellen und nach
Belieben auch mehrere LCDs gleichzeitig betreiben.

Bei der RS232 habe ich es so gemacht, daß im Quelltext mit
#ifdef auf die unterschiedlichen AVR-Typen eingegangen wird
(soweit ich es bisher brauchte, nicht alle).
Mehr als eine serielle brauchte ich noch nicht, und bei allen
konnte ich bisher einheitlich mit 256 Byte Puffer auskommen.
Dadurch brauche ich da auch nichts ändern, alles andere
(baud etc.) kann ich wieder von außen per init-Funktion
einstellen.

von Bernhard M. (boregard)


Lesenswert?

Hi,

die von mir bevorzugt Variante sieht so aus, daß die *.h Dateien (die zu 
den *.c gehören) nur die Interfaces exportieren; also LCD.h exportiert 
nur die Funktionen von LCD.c, die als Interface benutzt werden sollen.
Die Hardwaredefinition, bzw. die gesamte (statische) Konfiguration liegt 
ine einem "config.h", dieses muß von jeder *.c Datei (die 
hardwareabhängig ist) eingebunden werden.
Damit ist nur die config.h projektabhängig.

Gruß,
Bernhard

von Peter D. (peda)


Lesenswert?

Roland Praml schrieb:
> Nun möchte ich aber in all meinen Projekten eine gemeinssme "rs485.c"
> und "lcd.c" verwenden

Das habe ich auch mal ne Zeitlang gemacht, bis ich ein älteres Projekt 
wieder anfassen mußte und erstmal garnichts mehr ging.
Seitdem kriegt jedes Projekt immer eine Kopie der Lib in den 
Projektordner.
Damit kann man wieder beliebig an den Libs verbessern, ohne daß es 
Auswirkungen auf frühere Projekte hat.

Libs sind ja selten so ausgereift, daß man sie ewig verwenden kann. Und 
manchmal muß man auch Features hinzufügen.

Für das LCD nehme ich meine 4Bit-Lib mit beliebigen Pins. Die Pins 
werden in der Hardwaredefinition des Projekts als Bitvariablen 
definiert.

Für die RS-232 nehme ich meine FIFO-Lib, die per Defines das Namenschaos 
der AVRs entwirrt.


Peter

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Roland Praml (pram) schrieb:

> Nun möchte ich aber in all meinen Projekten eine gemeinssme "rs485.c"
> und "lcd.c" verwenden, was aber glaub ich gar nicht so einfach ist, da
> diese je nach Projekt unterschiedliche .h Dateien einbinden muss.

Ich habe da auch länger hin- und herüberlegt, habe dann aber irgendwo im 
Internet eine Lösung gefunden, die mir sehr gefallen hat und die ich 
seitdem ohne Probleme verwende:

Ich habe ein Verzeichnis "lib" angelegt, wo ich wiederverwendbaren Code 
ablege, nämlich z.B. eine

lib/rs485.c  - enthält den Code
lib/rs485.h  - enthält die extern-Deklaration der public-Funktionen

Eine Anwendung (nennen wir sie mal "app", die nun diesen Code verwenden 
soll, benutzt dann folgende Dateien:

app/app-main.c  - main-Routine
app/app-rs485.c - applikationsabhängige Verwendung

In app/app-rs485.c steht sehr wenig, nämlich nur folgendes:
1
#define RS485_XXXX_FOO    wasweissich    // Hardware-abhängige 
2
#define RS485_XXXX_BAR    undnochwas     // Einstellungen, Ports usw.
3
...
4
#include "../lib/rs485.c"

Es wird also hier die Hardware beschrieben und dann auf den C-Source im 
lib-Verzeichnis angewandt.

Und in app/app-main.c u.a.
1
#include "../lib/rs485.h"
2
...
3
   rs485_init ();                          // Aufruf von rs485_init
4
...
5
   while ((ch = rs485_getchar()) != '\n')  // Zeichen lesen
6
   {
7
       ...
8
   }
9
...

Ich weiss, man includiert eigentlich keine C-Sources, sondern höchstens 
H-Dateien, aber hier, wo at-compile-time (und nicht at-runtime, wie es 
bei Anwendung von C-Libs üblich ist) der Source bereits "konfiguriert" 
werden muss, finde ich das absolut legitim.

Gruß,

Frank

von P. S. (Gast)


Lesenswert?

Frank M. schrieb:

> Ich habe ein Verzeichnis "lib" angelegt, wo ich wiederverwendbaren Code
> ablege, nämlich z.B. eine

Ich wuerde dir empfehlen, das zu versionieren, und wenn es nur ueber 
eine Nummer am Verzeichnisnamen ist. Dann weisst du auch in ein paar 
Jahren noch, welche Version deines Codes mit welcher Version der Lib 
lief.

von Mehmet K. (mkmk)


Lesenswert?

Obwohl Peter jetzt wieder einen roten Kopf kriegen wird :)  ich bin seit 
einiger Zeit auf Libs umgestiegen und bin sehr zufrieden mit dieser 
Lösung. Okay, anfaenglich hatte ich ein furchbares Durcheinander. Aber 
jetzt, wo alles seinen Platz gefunden hat, würde ich nie mehr den 
Schritt zurück machen.

Es gibt bei dieser Lösung 2 Nachteile:
1. Direkte Hardware Zugriffe auf Ports kann man nicht in der Lib 
vornehmen und muss auf Funktionen in der jeweiligen Applikation 
zurueckgreifen.
2. Man muss für jede MCU eine eigene Lib erstellen (geht aber bei mir 
mit einer script automatisch)

Der Vorteil ist aber, dass ich nun meine bei jeder Applikation immer und 
immer wieder verwendeten Routinen zentral pflegen kann und sie bei 
Bedarf nur noch linken muss.
Okay, sind zwar noch nicht so viele Libs (Dataflash, Filesystem, GLCD, 
RTC und SD-Karte), aber sie erleichtern mir das Leben ungemein.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Peter Stegemann (pst) schrieb:

> Ich wuerde dir empfehlen, das zu versionieren, und wenn es nur ueber
> eine Nummer am Verzeichnisnamen ist. Dann weisst du auch in ein paar
> Jahren noch, welche Version deines Codes mit welcher Version der Lib
> lief.

Danke für den Tipp, ich benutze bereits CVS :-)

Aber ernsthaft: ich pflege den Source in den Libs so, dass er 
abwärtskompatibel bleibt. Kommt eine neue Einstellung hinzu, dann sorge 
ich dafür, dass der alte Source damit auch läuft.

Nehmen wir mal an, die RS485-Baudrate war früher fix auf 9600Bd 
eingestellt und ich habe nun lib/rs485.c so erweitert, dass man die 
Baudrate nun per

#define RS485_BAUDRATE    19200L

umstellen kann, dann schreibe ich in lib/rs485.c:

#ifndef RS485_BAUDRATE              // baudrate set?
#define RS485_BAUDRATE    9600L     // no, use default: 9600Bd
#endif

Dann kann der alte Source weiterhin mit der neuen lib/rs485.c übersetzt 
werden.

Andere Möglichkeit ist auch folgendes:

#ifndef RS485_BAUDRATE
#error Missing RS485_BAUDRATE
#endif

Dann wird man darauf hingewiesen, dass man den Source auf 
Anwendungsseite anpassen muss, weil sich was geändert hat.

Meine Meinung ist:

Wenn man prinzipiell darauf bedacht ist, möglichst allgemein 
verwendbaren Code (wo sämtliche Konstanten mittels Preprocessor-Define 
definiert und angewandt werden) zu schreiben, stellt sich dieses 
Problem, dass Du da ansprichst, eigentlich sehr selten. Das einzige, was 
dann noch Schwierigkeiten bereiten kann, sind Interface-Änderungen der 
Public-Functions. Aber das bekommt man auch größtenteils mittels #define 
hin. Die alte Public-Function wird dann einfach als #define-Makro 
definiert, welche die neue Public-Function mit geändertem Interface 
aufruft. So bleibt alles abwärtskompatibel.

Gruß,

Frank

von Roland P. (pram)


Lesenswert?

Also, ihr scheint zumindest mein Problem verstanden zu haben :-)

das mit der Init-Funktion würde ich mir ja für einfache 
Initialisierungen noch eingehen lassen (z.B. uart_init(9600) ), aber 
wenn dann schon mal eine kompliziertere LCD-Konfig da ist (RS /E /RW 
/Data ...) dann denke ich mal, erzeugt das ziemlich viel Overhead 
(Ramverbrauc, Laufzeit) beim Ansprechen der dynamisch konfiguriereten 
Ports, da der compiler hier wohl nur noch wenig optimieren kann.

Alternativ wäre noch eine Konfiguration im Makefile. (was ich vermutl. 
auch machen werde)

@Peter Danegger: Du hast natürlich gewissermaßen recht, mir geht es 
immer so, wenn ich die GCC-Version aktualisiere ;-)
Ich hab aber momentan 3 Projekte, welche gewissermaßen zusammengehören 
(die Module kommunizieren ja miteinander) und hier ist es extrem 
wünschenswert, wenn ich z.B. was im rs485-Protokoll ändere, dass das 
auch die anderen Projekte in dem Zusammenhang mitbekommen. (Momentan 
mache ich das eben über copy&paste)

Gruß
Roland

von Peter D. (peda)


Lesenswert?

Mehmet Kendi schrieb:
> Es gibt bei dieser Lösung 2 Nachteile:
> 1. Direkte Hardware Zugriffe auf Ports kann man nicht in der Lib
> vornehmen und muss auf Funktionen in der jeweiligen Applikation
> zurueckgreifen.

D.h. man muß also jede Lib in 2 Teile aufteilen, den hardwareabhängigen, 
der dann doch wieder in das Projekt kopiert werden muß und den 
hardwareunabhängigen.
Da bleib ich doch lieber bei der ungeteilten, die ich als Kopie einfüge.


> 2. Man muss für jede MCU eine eigene Lib erstellen (geht aber bei mir
> mit einer script automatisch)

Da bin ich nicht so der Crack. Die Lib enthält also z.B. 20 Objekte für 
20 AVR-Typen.
Und man kann dann in der compilierten Lib mit dem MCU-Typ das passende 
Object auswählen lassen?


> Der Vorteil ist aber, dass ich nun meine bei jeder Applikation immer und
> immer wieder verwendeten Routinen zentral pflegen kann und sie bei
> Bedarf nur noch linken muss.

Nun, zentral pflege ich die Libs doch auch.
Aber eben als Source, so daß beim Compilieren automatisch die passende 
Pinzuweisung, Taktzuweisung usw. des Projekts erfolgt.


Peter

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Roland Praml (pram) schrieb:

> das mit der Init-Funktion würde ich mir ja für einfache
> Initialisierungen noch eingehen lassen (z.B. uart_init(9600) ), aber
> wenn dann schon mal eine kompliziertere LCD-Konfig da ist (RS /E /RW
> /Data ...) dann denke ich mal, erzeugt das ziemlich viel Overhead
> (Ramverbrauc, Laufzeit) beim Ansprechen der dynamisch konfiguriereten
> Ports, da der compiler hier wohl nur noch wenig optimieren kann.

Das wäre bei einer Konfiguration at-runtime tatsächlich so, ja.

Aber bei einer Konfiguration at-compiletime gibt es dieses Problem 
nicht.

Auszug aus meiner app/app-lcd.c:
1
#define LCD_DATA_PORT          PORTA   // data port
2
#define LCD_DATA_DDR           DDRA    // ddr of data port
3
#define LCD_DATA_PIN           PINA    // pin of data port
4
// #define LCD_DATA_MASK       0xF0    // upper nibble of port is used for 4 bit data transfer
5
#define LCD_DATA_MASK          0x0F    // lower nibble of port is used for 4 bit data transfer
6
7
#define LCD_ROWS               4       // number of lines
8
#define LCD_COLS               20      // number of columns
9
10
#define LCD_HAS_BACKLIGHT      1       // 0 or 1, 0: has no backlight, 1: has backlight
11
#define LCD_BACKLIGHT_PORT     PORTA   // port of backlight
12
#define LCD_BACKLIGHT_DDR      DDRA    // ddr of backlight
13
#define LCD_BACKLIGHT          0       // bit of backlight, e.g. port A0
14
15
#define LCD_IDLE_CLEAR_DISPLAY 1       // 1: clear display, if idle, 0: don't clear (can be a runtime condition!)
16
#define LCD_IDLE_CLEAR_TIMEOUT 45      // switch off display & backlight after 45 seconds idle-time
17
18
#define LCD_RS_PORT            PORTA   // port of RS signal
19
#define LCD_RS_DDR             DDRA    // ddr of RS signal
20
#define LCD_RS                 1       // bit of RS signal
21
22
#define LCD_RW_PORT            PORTA   // port of RW signal
23
#define LCD_RW_DDR             DDRA    // ddr of RW signal
24
#define LCD_RW                 2       // bit of RW signal
25
26
#define LCD_EN_PORT            PORTA   // port of Enable signal
27
#define LCD_EN_DDR             DDRA    // ddr of Enable signal
28
#define LCD_EN                 3       // bit of Enable signal
29
30
#include "lib/lcd.c"

Erst wird die Hardware beschrieben und dann der allgemein verwendbare 
Source eingefügt - fertig. Gibt es zum Beispiel kein Backlight am LCD in 
der Applikation, wird einfach LCD_HAS_BACKLIGHT auf 0 gesetzt. Und schon 
werden die Backlight-Funktionen per "#ifdef LCD_HAS_BACKLIGHT" in 
lib/lcd.c komplett weggeblendet und gar nicht erst mitübersetzt.

Ich glaube, das ist die flexibelste Methode, kompakten (und schnellen) 
Code zu erzeugen.

Gruß,

Frank

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Frank M. (ukw) schrieb:

> Und schon werden die Backlight-Funktionen per "#ifdef LCD_HAS_BACKLIGHT" in
> lib/lcd.c komplett weggeblendet [...]

Per "#if LCD_HAS_BACKLIGHT == 1" meinte ich natürlich.

Gruß,

Frank

von P. S. (Gast)


Lesenswert?

Frank M. schrieb:

> Aber ernsthaft: ich pflege den Source in den Libs so, dass er
> abwärtskompatibel bleibt. Kommt eine neue Einstellung hinzu, dann sorge
> ich dafür, dass der alte Source damit auch läuft.

Du gehst bei jeder Aenderung alle alten Projekte durch und testest die? 
Respekt. Das habe ich nach dem 5ten Projekt aufgegeben.

> Dann kann der alte Source weiterhin mit der neuen lib/rs485.c übersetzt
> werden.

Das erklaert schoen, warum Projekte irgendwann in Altlasten ersticken 
:-)

von Oliver (Gast)


Lesenswert?

Frank M. schrieb:

> Aber ernsthaft: ich pflege den Source in den Libs so, dass er
> abwärtskompatibel bleibt. Kommt eine neue Einstellung hinzu, dann sorge
> ich dafür, dass der alte Source damit auch läuft.

Ganz blöde Frage:

Wie muß ich mir den Teil mit dem "sorgen" vorstellen?

Oliver

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Peter Stegemann (pst) schrieb:

> Du gehst bei jeder Aenderung alle alten Projekte durch und testest die?

Nein, nur die noch relevanten, d.h. die aktuell noch zu pflegenden. Ich 
schicke die dann durch den Compiler. Testen am "lebenden Stück" tue ich 
sie dann erst, wenn wirklich eine "Verbesserung" auch für das jeweilige 
Projekt zum Tragen kommt. Bisher ist das immer gutgegangen :-)

> Das erklaert schoen, warum Projekte irgendwann in Altlasten ersticken :-)

Ich verstehe das mal als Ironie, denn tatsächlich kenne ich persönlich 
keine Altlasten. Ich programmiere in C für UNIX seit 1984 (habe damals 
mit der Programmierung von UNIX-Device-Treibern für 68030er (Motorola) 
unter SYSV angefangen) und habe daher für die Programmierung von 
Hardware genügend Erfahrung, um diese auf die Programmierung von 
Microcontrollern umzusetzen.

Später gings dann mit Linux weiter - da aber eben mehr auf 
Anwendungsebene - und gründete unter anderem die Open-Source-Projekte 
fli4l und eisfair.

Die (uralten) Konzepte der libc unter UNIX/LINUX helfen einem wirklich 
weiter bei der Programmierung von möglichst Altlasten-freiem Code. Dort 
wird Hardware meist mittels den Standard-Funktionen open, read, write, 
ioctl und close angesprochen. Genauso einfach (und einfacher) kann man 
auch das Interface zu C-Modulen für Hardware an AVR-µCs halten.

Altlasten entstehen nur, wenn man nicht von vornherein plant, wie man 
die Sache anzugehen hat und deshalb dauernd irgendwas dran ändern muss. 
Sources wandern bei mir erst in das lib-Verzeichnis, wenn sie sich 
bewährt haben - unter verschiedenen Umgebungen (sprich Projekten).

Gruß,

Frank

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Oliver (Gast) schrieb:

> Ganz blöde Frage:
> Wie muß ich mir den Teil mit dem "sorgen" vorstellen?

Ich jage sie durch den Compiler, siehe mein Posting obendrüber.

Gruß,

Frank

von Karl H. (kbuchegg)


Lesenswert?

Oliver schrieb:

> Wie muß ich mir den Teil mit dem "sorgen" vorstellen?

Ganz auscschliessen kann man Probleme natürlich nie. Aber man kann 
zumindest versuchen Vorsorge zu treffen.
Kommt ein neues Feature/Variable dazu, dann kann man schon beim 
Schreiben darauf achten, dass alter Code nach wie vor weiterläuft. Zb 
dadurch, dass neue Features nicht unbedingt aktiv initialisiert werden 
sondern mit Defaults funktionieren, die so gewählt sind, dass alles wie 
gehabt weiter funktioniert, wenn keine explizite Initialisierung dieses 
Features erfolgt.

Hat man eine Funktione
1
void foo( int irgendwas )
2
{
3
  mach was
4
}

und man benötigt einen neuen, zusätzlichen Parameter dazu, dann kann man 
zb in den sauren Apfel beissen und eine 2-te Funktion mit dem 
zusätzlichen Paramater schreiben, die Funktionalität von foo dorthin 
verlagern und in foo einen Aufruf mit einem Default-Parameter einbauen
1
void foo( int irgendwas )
2
{
3
  fooEx( irgendwas, 0 );  // 0 als Default für nochwas
4
}
5
6
7
void fooEx( int irgendwas, int nochwas )
8
{
9
  mach was
10
}

kann man foo dann auch noch als inline Funktion ins Header-File 
verschieben, hat man noch nicht einmal einen Runtime-Penalty dafür zu 
zahlen.

Also: Möglichkeiten gibt es schon. Aber es gibt natürlich auch 
Änderungen bei denen alles nichts hilft. Man muss den alten Code 
durchgehen und an die neuen Gegebenheiten anpassen. Ist so etwas 
absehbar, dann ist man immer gut beraten, dafür zu sorgen dass alter 
Code auf jeden Fall einen Syntax Error produzieren wird und einem der 
Compiler so hilft, die kritischen Stellen zu finden.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Karl heinz Buchegger schrieb:

> Ganz auscschliessen kann man Probleme natürlich nie. Aber man kann
> zumindest versuchen Vorsorge zu treffen.

Jepp.

> Kommt ein neues Feature/Variable dazu, dann kann man schon beim
> Schreiben darauf achten, dass alter Code nach wie vor weiterläuft. Zb
> dadurch, dass neue Features nicht unbedingt aktiv initialisiert werden
> sondern mit Defaults funktionieren, die so gewählt sind, dass alles wie
> gehabt weiter funktioniert, wenn keine explizite Initialisierung dieses
> Features erfolgt.

Genau das war mein Beispiel mit:

| #ifndef RS485_BAUDRATE              // baudrate set?
| #define RS485_BAUDRATE    9600L     // no, use default: 9600Bd
| #endif

> Hat man eine Funktione [...]
> und man benötigt einen neuen, zusätzlichen Parameter dazu, dann kann man
> zb in den sauren Apfel beissen und eine 2-te Funktion mit dem
> zusätzlichen Paramater schreiben, die Funktionalität von foo dorthin
> verlagern und in foo einen Aufruf mit einem Default-Parameter einbauen

Das meine ich mit:

| Das einzige, was
| dann noch Schwierigkeiten bereiten kann, sind Interface-Änderungen der
| Public-Functions. Aber das bekommt man auch größtenteils mittels #define
| hin. Die alte Public-Function wird dann einfach als #define-Makro
| definiert, welche die neue Public-Function mit geändertem Interface
| aufruft. So bleibt alles abwärtskompatibel.

Und ein Makro, was meine neue Funktion abwärtskompatibel bedient, sehe 
ich nicht unbedingt als "Altlast" an.

> void fooEx( int irgendwas, int nochwas )
> [...]
> kann man foo dann auch noch als inline Funktion ins Header-File
> verschieben, hat man noch nicht einmal einen Runtime-Penalty dafür zu
> zahlen.

Das entspricht bei einer inline-Funktion ja (fast) dem Makro.

> Also: Möglichkeiten gibt es schon. Aber es gibt natürlich auch
> Änderungen bei denen alles nichts hilft. Man muss den alten Code
> durchgehen und an die neuen Gegebenheiten anpassen. Ist so etwas
> absehbar, dann ist man immer gut beraten, dafür zu sorgen dass alter
> Code auf jeden Fall einen Syntax Error produzieren wird und einem der
> Compiler so hilft, die kritischen Stellen zu finden.

Eben genau das beschrieb ich oben mit der Alternative:

| #ifndef RS485_BAUDRATE
| #error Missing RS485_BAUDRATE
| #endif

Ich glaube, wir sind da beide auf gleicher Wellenlänge :-)

Gruß,

Frank

von Mehmet K. (mkmk)


Lesenswert?

Peter Dannegger schrieb:
> D.h. man muß also jede Lib in 2 Teile aufteilen, den hardwareabhängigen,
> der dann doch wieder in das Projekt kopiert werden muß und den
> hardwareunabhängigen.
Da habe ich mich vermutlich schlecht ausgedrückt. Ich kopiere nichts. 
Ich habe nur in der RTC-Lib, die z.Bsp. ein CS-Signal bedienen muss, 
eine rtc_toggle_cs() Funktion, die aber in der eigentlichen Applikation 
vorhanden ist. Schliesslich kann ja die Lib nicht wissen, an welchen 
Pins die RTC angeschlossen ist.

> Da bin ich nicht so der Crack. Die Lib enthält also z.B. 20 Objekte für
> 20 AVR-Typen.
Korrekt. Ich arbeite mit einer begrenzten Anzahl von AVR-Typen: ich 
glaube es sind 5 Stück. Und für die RTC-Lib habe ich somit 5 verschiede 
Libs.
- librtc_ds1302_atmega32.a
- librtc_ds1302_atmega324p.a
- etc.
(Die aber wie schon erwaeht mit einem script automatisch erstellt 
werden)

> Und man kann dann in der compilierten Lib mit dem MCU-Typ das passende
> Object auswählen lassen?
Diese Frage habe ich nicht verstanden.

MfG

von Karl H. (kbuchegg)


Lesenswert?

Mehmet Kendi schrieb:
> Peter Dannegger schrieb:
>> D.h. man muß also jede Lib in 2 Teile aufteilen, den hardwareabhängigen,
>> der dann doch wieder in das Projekt kopiert werden muß und den
>> hardwareunabhängigen.
> Da habe ich mich vermutlich schlecht ausgedrückt. Ich kopiere nichts.
> Ich habe nur in der RTC-Lib, die z.Bsp. ein CS-Signal bedienen muss,
> eine rtc_toggle_cs() Funktion, die aber in der eigentlichen Applikation
> vorhanden ist. Schliesslich kann ja die Lib nicht wissen, an welchen
> Pins die RTC angeschlossen ist.

Also hast du doch ein paar Funktionen, die du bei jeder Applikation neu 
schreiben musst. OK, die sind primitiv zu schreiben, aber immerhin.

>> Da bin ich nicht so der Crack. Die Lib enthält also z.B. 20 Objekte für
>> 20 AVR-Typen.
> Korrekt. Ich arbeite mit einer begrenzten Anzahl von AVR-Typen: ich
> glaube es sind 5 Stück. Und für die RTC-Lib habe ich somit 5 verschiede
> Libs.
> - librtc_ds1302_atmega32.a
> - librtc_ds1302_atmega324p.a
> - etc.
> (Die aber wie schon erwaeht mit einem script automatisch erstellt
> werden)

Das heisst, du hast für jeden µC Typ eine eigene Lib.

>> Und man kann dann in der compilierten Lib mit dem MCU-Typ das passende
>> Object auswählen lassen?
> Diese Frage habe ich nicht verstanden.

Die hat sich, denke ich mal, damit auch erledigt. Du sorgst 
wahrscheinlich im Makefile dafür, dass gegen die korrekte Lib für diesen 
Prozessor gelinkt wird.

von Klaus W. (mfgkw)


Lesenswert?

Nur der Vollständigkeit halber: Die Frank M.-Variante geht auch
ohne die kleinen Zwischen-Quelltexte, indem man Module, die
er mit #include "....c" einbindet, direkt kompiliert und beim Aufruf
dabei die #defines als Compileroptionen angibt:
1
 ggc ... -D'RS485_BAUDRATE=9600L' ...

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Mehmet Kendi (mkmk) schrieb:

> Ich habe nur in der RTC-Lib, die z.Bsp. ein CS-Signal bedienen muss,
> eine rtc_toggle_cs() Funktion, die aber in der eigentlichen Applikation
> vorhanden ist. Schliesslich kann ja die Lib nicht wissen, an welchen
> Pins die RTC angeschlossen ist.

Das ist zwar auch eine Methode, die ich vielleicht in Projekten 
einsetzen würde, wo ich mit Megabytes an RAM und entsprechender CPU nur 
so um mich schleudern kann (z.B. irgendein GUI-Programm unter Windows, 
das bei $KUNDE Eindruck schinden soll), aber bei der Programmierung von 
µCs könnte ich mich damit nicht wirklich anfreunden.

Gründe:

1. Jeder I/O kostet bei Dir einen Funktionsaufruf (kostet Zeit und 
Speicher)
2. Die Aufruf-Hierarchie app->lib->app finde ich nicht gerade 
glücklich.[1]
3. Der Code für eine HW ist nicht gekapselt, d.h. er ist nicht als 
Black-Box anwendbar.[2]

> Korrekt. Ich arbeite mit einer begrenzten Anzahl von AVR-Typen: ich
> glaube es sind 5 Stück. Und für die RTC-Lib habe ich somit 5 verschiede
> Libs.
> - librtc_ds1302_atmega32.a
> - librtc_ds1302_atmega324p.a
> - etc.

Wenn das Compilieren einer Lib eine halbe Stunde oder mehr dauern würde, 
dann könnte ich mir sowas auch vorstellen[3]. Tatsächlich sind es aber 
heutzutage bei µCs nur wenige Sekunden, bis der Code durch den Compiler 
ist. Die Mühe würde ich mir sparen.

Gruß,

Frank

[1] Wenn schon "Rückwärtsaufrufe" von der Lib zur App, dann über 
Funktionspointer, die an die Lib runtergegeben werden (Stichwort: 
"Callback-Function").

[2] Ich gebe ja zu: Bei meiner Methode stehen die HW-Beschreibungen auch 
in app/app-irgendwas.c, gehören source-mäßig also zur Applikation. Aber 
eben nur die Beschreibung der HW. Die eigentliche "Action" findet in der 
Lib statt, was mir persönlich besser gefällt.

[3] Vor 20 Jahren hat das Comilieren einer Lib unter UNIX wirklich noch 
eine halbe Stunde und mehr gebraucht, da ging das gar nicht anders, als 
mit "echten" libs (*.a) zu arbeiten ;-)

von Peter D. (peda)


Lesenswert?

Mehmet Kendi schrieb:
> - librtc_ds1302_atmega32.a
> - librtc_ds1302_atmega324p.a

D.h. Du hast für jeden AVR verschiedene Libs und mußt sie auch in jedem 
Projekt manuell auswählen.
Und wenn Du das mal verpennst, wird die falsche Lib gelinkt und es 
kracht.


>> Und man kann dann in der compilierten Lib mit dem MCU-Typ das passende
>> Object auswählen lassen?
> Diese Frage habe ich nicht verstanden.

Das meint, ob die richtige Lib automatisch ausgewählt wird, oder man da 
Fehler machen kann.

Bei einer Source als Lib wird dagegen immer das richtige Include (io.h) 
genommen. Die Anpassung an den MCU-Typ erfolgt also vollautomatisch 
(eine Fehlerquelle weniger).


Peter

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Klaus Wachtler schrieb:

> Nur der Vollständigkeit halber: Die Frank M.-Variante geht auch
> ohne die kleinen Zwischen-Quelltexte, indem man Module, die
> er mit #include "....c" einbindet, direkt kompiliert und beim Aufruf
> dabei die #defines als Compileroptionen angibt:
>
> ggc ... -D'RS485_BAUDRATE=9600L' ...

Da hast Du natürlich recht. Kann unter Umständen aber zu einer 
unüberschaubaren Menge an Compileroptionen führen:

Wenn ich RS485, RS232, RFM12, LCD, LEDs, Relais und auch noch ein paar 
I2C-Devices an einen µC gestöpselt habe, dann kommen da schon glatt 
mehrere Dutzend Optionen zusammen. Da finde ich die Aufführung der 
möglichen Optionen in den Zwischen-Quelltexten doch schon 
übersichtlicher.

Gruß,

Frank

von Mehmet K. (mkmk)


Lesenswert?

Peter Dannegger schrieb:
> Und wenn Du das mal verpennst, wird die falsche Lib gelinkt und es
> kracht.
> ....
> Das meint, ob die richtige Lib automatisch ausgewählt wird, oder man da
> Fehler machen kann.

Nein, die richtige Lib wird nicht automatisch gelinkt. Aber die Anzahl 
der Projekte pro Jahr sind bei mir begrenzt; und da ich mich mit Alkohol 
im Blut weder hinter das Steuer, noch vor den Monitor setze, bin ich 
über den von dir skizzierten Lapsus noch nicht gestolpert.

MfG

von Klaus W. (mfgkw)


Lesenswert?

Wir halten fest: Peter Dannegger programmiert besoffen...

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Klaus Wachtler schrieb:

> Wir halten fest: Peter Dannegger programmiert besoffen...

YMMD!

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.