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
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.
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
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
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:
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
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.
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.
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
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
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
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
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
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
:-)
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
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
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
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
voidfoo(intirgendwas)
2
{
3
machwas
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
voidfoo(intirgendwas)
2
{
3
fooEx(irgendwas,0);// 0 als Default für nochwas
4
}
5
6
7
voidfooEx(intirgendwas,intnochwas)
8
{
9
machwas
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.
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
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
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.
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:
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 ;-)
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
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
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