Forum: Mikrocontroller und Digitale Elektronik Plug and Play Interface Konzept für Module in C


von chris_ (Gast)


Lesenswert?

Hallo Zusammen,

kennt Ihr das Problem auch: Ihr habt z.B. einen Treiber, der aus 
mehreren C-Files besteht und möchtet diesen in verschiedenen Projekten 
verwenden. Jedes mal gibt es aber ein Problem, weil irgend welche 
benötigten Dateien fehlen und es dauert relativ lange, bis der Treiber 
eingepasst ist.

Ich überlege mir gerade ein Konzept mit Dummy-Funktionen, so dass man 
den Treiber erst einmal offline compilieren kann und eventuell 
Funktionen daraus testweise aufzurufen.

Welches Konzept habt Ihr?

von D. F. (Firma: EDF) (derdan)


Lesenswert?

c++

von chris_ (Gast)


Lesenswert?

Es gibt sehr viele Projekte, in denen Du nur C verwenden darfst.
Deshalb ist es von Vorteil einige eigene Libraries in C zu schreiben, 
damit diese universell verwendet werden können.

von chris_ (Gast)


Angehängte Dateien:

Lesenswert?

Hier ein Interface-Beipiel.

von ui (Gast)


Lesenswert?

Also ich komm bei deinem Problem noch nicht so ganz mit...

chris_ schrieb:
> Ihr habt z.B. einen Treiber, der aus
> mehreren C-Files besteht und möchtet diesen in verschiedenen Projekten
> verwenden. Jedes mal gibt es aber ein Problem, weil irgend welche
> benötigten Dateien fehlen und es dauert relativ lange, bis der Treiber
> eingepasst ist.

Auf der einen Seite sagst du das. Das hat erstmal nichts mit deiner 
Programmiersprache zu tun, wenn du vergisst Dateien einzubinden bzw. an 
die richtige Stelle zu kopieren. Gehts um das? Also quasi ein 
Modulmanagment?

chris_ schrieb:
> Ich überlege mir gerade ein Konzept mit Dummy-Funktionen, so dass man
> den Treiber erst einmal offline compilieren kann und eventuell
> Funktionen daraus testweise aufzurufen.

Das versteh ich aber ganz anders. Hier geht es eher um die 
Treiberintegration in deiner Programmiersprache bzw. um den eigentlichen 
Treiber, also wie du den testest?

Was genau ist denn jetzt dein Problem?

von Jack (Gast)


Lesenswert?

chris_ schrieb:
> kennt Ihr das Problem auch: Ihr habt z.B. einen Treiber, der aus
> mehreren C-Files besteht und möchtet diesen in verschiedenen Projekten
> verwenden. Jedes mal gibt es aber ein Problem, weil irgend welche
> benötigten Dateien fehlen

Dann ist der Code nicht vernünftig modular geschrieben. Das 
Strukturieren von Code-Dateien nennt sich Physical Design bzw. Physical 
Code Organization.

Im Prinzip geht es darum erst einmal zu verstehen was echte Module sind, 
statt die immer wieder anzutreffende Behauptung man würde schon modular 
entwickeln. Dann geht es darum die Module und eventuell sogar ihre 
internen Komponenten so zu strukturieren, dass es keine zyklischen 
Abhängigkeiten gibt (auch nicht direkt zwischen zwei Modulen). Weiter 
geht es um den Schnittstellen-Entwurf (API Design) und zum Schluss packt 
man das Ergebnis in eine Library und eine Header-Datei.

Wichtige Code-Techniken sind alle, mit denen man feste Kopplungen 
vermeidet. In C gehen zum Beispiel Callbacks, Inversion of Control 
(Dependency Injection), Plugins.

> Welches Konzept habt Ihr?

Sauberen Code schreiben, statt nachträglich zu flicken.

von Thomas W. (diddl)


Lesenswert?

Zwei Ansätze:



Du kannst C++ in C "nachbilden" indem du struct verwendest. Es ist ein 
Kompromiss, der Overhead ist nicht so groß wie in C++, aber da alles auf 
Zeiger basiert ist es natürlich etwas langsamer und größer.



Ansatz zwei sind Callback Routinen, also Zeiger auf Funktionen. Du 
initialisierst das richtige Modul und rufst immer dieselben Funktionen.

Zb. LCD Display. Entweder mit 4 IO oder 8, mit I2C und dawomöglich 
verschiedene I2C IC's. Die Applikation sieht immer nur die standard C 
Funktionen und je nach verwendeter Hardware Konfiguration werden 
unterschiedliche Module aufgerufen.

von NurC (Gast)


Lesenswert?

Das ist so eine seltsame Forderung. Ala man soll schnell fahren können 
darf aber auf keinem fall einen Motor verwenden. C++ bringt halt für das 
angesprochene Problem die Lösungen mit sich

von ui (Gast)


Lesenswert?

NurC schrieb:
> Das ist so eine seltsame Forderung. Ala man soll schnell fahren können
> darf aber auf keinem fall einen Motor verwenden. C++ bringt halt für das
> angesprochene Problem die Lösungen mit sich

Nein bringt es nicht. In C++ kann man genauso schlonzigen "modularen" 
Code schreiben wie in C. Das ist ein guter alter Trugschluss, dass OO 
bzw. C++ immer alles per Sprachdesign löst. Kann es ja gar nicht, hat ja 
alle Unzulänglichkeiten von C einfach übernommen.

Sauberen, modularen Code kann man in C genauso programmieren wie in C++, 
damit beschäftigen muss man sich in beiden Fällen.
Ein gutes Beispiel wie man modularen Code aufbauen kann sieht man bei 
Autosar. Da haben sich Leute hingesetzt, die von C ne Ahnung haben und 
haben aufgezeigt, wie man richtig modular in C programmiert.

von Random .. (thorstendb) Benutzerseite


Lesenswert?

für µC: schau mal nach CMSIS-Pack.

von Sepp Huber (Gast)


Lesenswert?

>kennt Ihr das Problem auch: Ihr habt z.B. einen Treiber, der aus
>mehreren C-Files besteht und möchtet diesen in verschiedenen Projekten
>verwenden. Jedes mal gibt es aber ein Problem, weil irgend welche
>benötigten Dateien fehlen und es dauert relativ lange, bis der Treiber
>eingepasst ist.

nein das Problem kenne ich nicht, du kannst ja eine Lib machen die du 
dann statisch mitlinkst oder was meinst du genau, ist der Treiber 
jedesmal anzupassen an die HW? Dann solltest du den Treiber aufteilen, 
in den HW unabhängigen Teil und in einen HW abhängigen Teil.

von chris_ (Gast)


Angehängte Dateien:

Lesenswert?

Hier mal ein kleines Beispiel für das Plug- and Play-Konzept.

Die Namensgebung ist an die AUTOSAR-Interface Namen angelehnt.
Es gibt

- required
- provided

Client-Server Interfaces.

Das System muss quasi die Required-Interfaces zur Verfügung stellen.

von D. F. (Firma: EDF) (derdan)


Lesenswert?

Das würde aber mal ganz grass das DRY Prinzip verletzen. Respekt.
und die unübliche Namesgebung der Header File.
Überleg mal jeder der einen bestimmten Aspekt seiner Datei in den 
Dateinamen hinterlegt würd das so machen.

Schau dir lieber mal Clean Code Regeln an.
Die kann man auch für C einhalten.

von chris_ (Gast)


Lesenswert?

>Das würde aber mal ganz grass das DRY Prinzip verletzen.

Was genau meinst Du damit?

von chris_ (Gast)


Lesenswert?

>Respekt.
>und die unübliche Namesgebung der Header File.

Da liegt vermutlich ein Missverständnis vor.
Die Paketbezeichung "Hallo" habe ich nur als Beispiel verwendet.
Du kannst auch "EDF" daraus machen. Vielleicht wäre die Abstraktion 
einfacher für Dich gewesen, ich hätte das Paket "BEISPIEL" genannt.

von Markus F. (mfro)


Lesenswert?

chris_ schrieb:
> z.B. einen Treiber,
...

Gerade für Treiber gibt's ein uraltes Konzept, das (mindestens) seit den 
ersten Unix-Systemen erfolgreich funktioniert. Alt muß nicht unbedingt 
schlecht sein.

ein Low-Level-Treiber besteht aus nur ganz wenigen Funktionen:

device_init()
device_open()
device_read()
device_write()
device_close()
device_ioctl()

Mehr braucht's nicht und wer so ein Treibermodul zum ersten Mal sieht, 
kann es sofort verwenden. Spezialfälle werden als Einstellungen oder 
Abfragen in ioctl() abgefackelt und das ist praktisch auch die einzige 
Funktion, die dokumentiert werden muß.

von D. F. (Firma: EDF) (derdan)


Lesenswert?

chris_ schrieb:
>>Das würde aber mal ganz grass das DRY Prinzip verletzen.
>
> Was genau meinst Du damit?

Erweitere dein Beispiel um einen fast gleichen 2. Treiber, dann wirst du 
sehen dass gerade in deiner *_required.h immer wieder dasselbe darin 
steht.
Je nach Sicht gibt es *A*_required.h und *B*_provided.h mit identischen 
Inhalt.
Zusammengefasst meine ich das das gleiche Interface einmal Quelle und 
einmal senke ist. Aber in unterschiedlichen Dateien völlig identisch 
geschrieben werden muss.

Als Beispiel: Wenn du die Aufrufparameter einer Funktion abänderst musst 
du 3 Dateien ändern. Prinzipiell ist bei 2 Dateien das DRY Prinzip schon 
verletzt. Das ist mit ein Grund weshalb modernere Sprachen wie C# 
zwischen Interface und Implementierung nicht mehr unterscheiden.

von chris_ (Gast)


Angehängte Dateien:

Lesenswert?

Hier noch mal eine neue Version.

Ich habe das Paket mal in "BEISPIELPAKET" umbenannt, damit es hier keine 
Missverständnisse mehr gibt.

Es gibt noch ein passendes Test-Paket dazu, dass sozusagen den Unit-Test 
gleich mitbringt.

Außerdem gibt es jetzt ein "PLATFORM"-Paket, dass auf die Low-Level 
Funktionen des Systems zugreift. Das BEISPIELPAKET selber ist völlig 
unabhängig.

Das Ganze wird über die RTE verbunden. Der Nachteil an der Verbindung 
über "Defines" ist aber, dass man z.B. mit Eclipse nicht mehr 
automatisch zu den anderen Funktionen springen kann. ( Der Nachteil 
haben die echten Implementierungen von AUTOSAR aber leider meistens auch 
).

Autor: D. F. (Firma: EDF) (derdan)
>Wenn du die Aufrufparameter einer Funktion abänderst musst
>du 3 Dateien ändern.

Meiner Erfahrung nach sind die Änderungen an den Interfaces die größten 
Probleme in Softwareprojekten. Deshalb müssen die von Anfang an sehr gut 
überlegt sein und sollten so wenig wie möglich geändert werden.

von Daniel A. (daniel-a)


Lesenswert?

Ich habe jenachdem unterschiedliche vorgehen.


Wenn ich ein Projekt habe, und ich will beliebig viele Module für eine 
Funktionalität haben, und brauche alles nur bei diesem Projekt, 
definiere ich ein Interface, und implementiere dieses mehrfach. Dann 
packe ich die object files (.o files) des Moduls in eine statische 
library (.a files), sofern es mehr als ein object file ist.

Beispiel Interface:
https://github.com/Daniel-Abrecht/DPA-UCS/blob/master/src/headers/DPA/UCS/driver/eth/driver.h

Beispiel Implementationen:
https://github.com/Daniel-Abrecht/DPA-UCS/blob/master/src/server/drivers/eth/dummy.c
https://github.com/Daniel-Abrecht/DPA-UCS/blob/master/src/server/drivers/eth/linux.c
https://github.com/Daniel-Abrecht/DPA-UCS/blob/master/src/server/drivers/eth/enc28j60.c


Wenn ich mehrere Module mit Abhängigkeiten habe, und ich nur eins 
brauch, mache ich sowas:
Interface:
https://github.com/Daniel-Abrecht/DPA-UCS/blob/master/src/headers/DPA/UCS/adelay.h
Implementation:
https://github.com/Daniel-Abrecht/DPA-UCS/blob/master/src/server/drivers/adelay/clock.c
https://github.com/Daniel-Abrecht/DPA-UCS/blob/master/src/server/drivers/adelay/timer.c
Verwendung woanders:
https://github.com/Daniel-Abrecht/DPA-UCS/blob/master/src/server/protocol/tcp/tcp.c

Ich habe also das Interface beim Programm definiert, welches die Module 
verwendet. Wenn ich aber plane, etwas in mehreren Projekten zu nutzen, 
dann baue ich eine statische Library, die keine Abhängigkeiten zum 
Projekt hat, und nutze das als Modul. Das Interface ist dann in einer 
Headerdatei beim Modul definiert.

In jeden fall sind bei mir .h und .c files in unterschiedlichen 
unterverzeichnissen.

: Bearbeitet durch User
von chris_ (Gast)


Lesenswert?

Autor: Markus F. (mfro)
>Gerade für Treiber gibt's ein uraltes Konzept, das (mindestens) seit den
>ersten Unix-Systemen erfolgreich funktioniert. Alt muß nicht unbedingt
>schlecht sein.

>ein Low-Level-Treiber besteht aus nur ganz wenigen Funktionen:

>device_init()
>device_open()
>device_read()
>device_write()
>device_close()
>device_ioctl()

Das sind aber alles Funktionen, die der Treiber zur Verfügung stellt.
In AUTOSAR würde man diese

provided client/server interfaces

nennen.

Einen Treiber in der Art zu schreiben ist kein Problem, weil sich die 
Interfaces des Treibers zum System nicht ändern ( bzw. die sind hier gar 
nicht sichtbar ).

Ein Problem tritt dann auf, wenn man ein Paket schreiben will was sich 
auf anderen Pakte bezieht bzw. Funktionen aus anderen Pakten braucht ( 
sogenannte provided interfaces ).

von W.S. (Gast)


Lesenswert?

chris_ schrieb:
> kennt Ihr das Problem auch: Ihr habt z.B. einen Treiber, der aus
> mehreren C-Files besteht und möchtet diesen in verschiedenen Projekten
> verwenden.

So herum wird das nie und nimmer etwas.

Ich mache das genau umgekehrt: Der Low-Level-Treiber kapselt die 
darunter liegende HW und stellt "nach oben" eine hardwareunabhängige 
Schnittstelle zur Verfügung. Damit kann ich alle höheren 
Programmschichten zumeist ohne Änderungen auf verschiedensten Systemen 
verwenden. Lediglich der unterste Treiber muß eben an die jeweilige HW 
angepaßt sein. Manchmal kann man den sogar ganz woanders 
wiederverwenden, siehe z.B. der SDIO-Treiber für die älteren LPC 
(Arm7Tdmi) und der SDIO-Treiber für die STM32F1xx.

Aber wozu man für einen einzigen Treiber mehrere C-Dateien benötigt, 
erschließt sich mir nicht. OK, es gibt scharenweise Programmierer, die 
aus jedem winzigen Furz eine separate Datei machen - und damit die nicht 
so winzig aussieht, wird noch die GPL in voller Länge eingefügt. Sehr 
leserlich, sowas.

W.S.

von HolzMin (Gast)


Lesenswert?

ich glaube auch das der Weg den der _chris da gehen sehr aus Holz ist...

von chris_ (Gast)


Lesenswert?

Autor: HolzMin (Gast)
>ich glaube auch das der Weg den der _chris da gehen sehr aus Holz ist...
Dann wäre AUTOSAR auf dem Holzweg. Aber da gibt es ja tatsächlich viele, 
die das glauben, nicht war?
( chris_ )

Autor: Thomas Winkler (Firma: privat) (diddl)
>Ansatz zwei sind Callback Routinen, also Zeiger auf Funktionen. Du
>initialisierst das richtige Modul und rufst immer dieselben Funktionen.

Callback-Routinen sind tatsächlich ein Ansatz, damit man keine 
Funktionen "nach unten" ( required interfaces braucht ). Da die Zeiger 
im RAM gehalten werden müssen, bedeutet das aber einen höheren 
Speicherverbrauch. Außerdem würde ich noch NULL-Pointer Checks einbauen, 
damit bei nicht gesetztem Call-Back kein Crash auftritt.

Man könnte also so was machen:
1
void setStdOut(const void* stdOut);

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.