Forum: Mikrocontroller und Digitale Elektronik RPi 4 Cross-Compiler Probleme


von Mirco G. (mirco432)


Lesenswert?

Guten Tag!

Ich habe Probleme mit meiner RPi4 Cross-Compiler.

Ich fange gerade erst mit dem RPi an und habe mich entsprechend ein 
bisschen eingelesen.

=> Embedded Linux mit Raspberry Pi und Co. v. Ralf Jesse.

Das Buch ist schon ein eigentlich f. RPi B+. Also ein bisschen älter. 
Trotzdem ist es glaube ich eine gute Einführung.

In dem Buch werden zwei Ansätze verfolgt. Einmal mit crosstool -ng und 
einmal mit Code::Blocks.

Ich habe mich für Code::Blocks entschieden weil im Buch hauptsächlich 
damit gearbeitet wird.

Wenn ich mit Code Blocks mein Programm "builden" möchte bekomme ich den 
Fehler:

ld returned 1 exit status

Build-Log:

-------------- Build: Debug in Test2 (compiler: GNU GCC Compiler for 
ARM)---------------

arm-linux-gnueabihf-gcc  -o bin/Debug/Test2 obj/Debug/main.o 
/usr/local/lib/libwiringPi.so
/usr/local/lib/libwiringPi.so: file not recognized: File format not 
recognized
collect2: error: ld returned 1 exit status
Process terminated with status 1 (0 minute(s), 0 second(s))
1 error(s), 0 warning(s) (0 minute(s), 0 second(s))

Ich hab es aber nochmal geprüft. Ich hab es genau so eingestellt wie im 
Buch.

Unter LinkerSettings => Link libraries => /usr/local/lib/libwiringPi.so

Was mich beim Installieren der Toolchain aber irgendwie gewundert hat 
ist das die Dateien oft mit BCM 2708 bezeichnet sind. Das ist ja 
eigentlich nicht der Prozessor vom RPi4. Der ist ja von einem älteren 
Modell.

Die aktuelle Anleitung verweist aber auf die gleiche alte Toolchain.

https://www.raspberrypi.org/documentation/linux/kernel/building.md

Stehe ein bisschen auf dem Schlauch :D.

Vielen Dank!

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

> /usr/local/lib/libwiringPi.so: file not recognized: File format not
recognized

Vom Pfad her sieht das aus wie eine Host Library, für Cross-Compiling 
brauchst du aber ein Lib die zum Target passt.

von Heiko L. (zer0)


Lesenswert?

Mirco G. schrieb:
> /usr/local/lib/libwiringPi.so

Keine Ahnung, wie du das eingerichtet hast, aber "/usr/local" nimmt man 
idR nicht für Dateien, die sich auf eine andere Platform beziehen.

Mach mal ein
1
readelf -h /usr/local/lib/libwiringPi.so
und schau, ob das echt ein ARM-binary ist.

von Programmierer (Gast)


Lesenswert?

Mirco G. schrieb:
> /usr/local/lib/libwiringPi.so

Wo kommt die hier, wie hast du die installiert?

von AVR (Gast)


Lesenswert?

Kompiliere doch einfach auf dem Pi.

von Klaus (Gast)


Lesenswert?

AVR schrieb im Beitrag #6212145:
> Kompiliere doch einfach auf dem Pi.

Richtig! Insbesondere der 4 hat dafür massig Power.

MfG Klaus

von Programmierer (Gast)


Lesenswert?

Was für ein OS hast du auf dem Pi und auf dem PC? Auf Debian-basierten 
PC-OSen (z.B. Ubuntu, Mint) kann man zwar relativ leicht und sauber über 
den Paket-Manager den Cross-Compiler und Libraries installieren, aber 
wenn das nicht exakt die Versionen vom Pi sind kann es Probleme geben 
(und die Versionen müssen leider exakt die selben wie die für den Host 
sein). Yocto Linux hat da ein System für um exakt die Versionen des 
Targets zu nutzen, aber das kommt hier wohl eher nicht in Frage.

von Mirco G. (mirco432)


Lesenswert?

Heiko L. schrieb:
> Keine Ahnung, wie du das eingerichtet hast, aber "/usr/local" nimmt man
> idR nicht für Dateien, die sich auf eine andere Platform beziehen.

Da ich die wiringpi Library nicht so wie im Buch oder auf 
http://wiringpi.com/download-and-install/ erläutert installieren konnte 
habe ich folgende Dateien von git runtergeladen:

https://github.com/WiringPi/WiringPi

Für die Programme sollte ich laut Buch einen Ordner "toolchain" unter 
/opt anlegen und die Dateien von Code Blocks und der Library hier 
ablegen.

Dann hab ich die entsprechende Build Datei aus den wiringpi Dateien von 
git ausgeführt. Diese hat die Dateien dann wie im Buch beschrieben unter 
usr/local/lib und usr/local/include abgelegt.

Heiko L. schrieb:
> Mach mal einreadelf -h /usr/local/lib/libwiringPi.so
> und schau, ob das echt ein ARM-binary ist.
1
ELF Header:
2
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
3
  Class:                             ELF64
4
  Data:                              2's complement, little endian
5
  Version:                           1 (current)
6
  OS/ABI:                            UNIX - System V
7
  ABI Version:                       0
8
  Type:                              DYN (Shared object file)
9
  Machine:                           Advanced Micro Devices X86-64
10
  Version:                           0x1
11
  Entry point address:               0x4450
12
  Start of program headers:          64 (bytes into file)
13
  Start of section headers:          89152 (bytes into file)
14
  Flags:                             0x0
15
  Size of this header:               64 (bytes)
16
  Size of program headers:           56 (bytes)
17
  Number of program headers:         7
18
  Size of section headers:           64 (bytes)
19
  Number of section headers:         28
20
  Section header string table index: 27

AVR schrieb im Beitrag #6212145:
> Kompiliere doch einfach auf dem Pi.

Ich finde es entspannter wenn ich auf meinem normalen PC arbeiten kann.

Programmierer schrieb:
> Was für ein OS hast du auf dem Pi und auf dem PC?

PC = Linux Mint 19.2 Tina
Pi = Raspbian Linux 10

Programmierer schrieb:
> Yocto Linux hat da ein System für um exakt die Versionen des
> Targets zu nutzen, aber das kommt hier wohl eher nicht in Frage.

Ich wollte halt erstmal ganz einfache Programme für den Pi schreiben um 
mit dem ganzen mal ein bisschen warm zu werden. Bin auch noch nicht 
lange mit Linux unterwegs.

Was haltet ihr denn von crosstool -ng? Das sieht irgendwie ziemlich 
kompliziert aus finde ich.

Ich lese mich da auch gerne selber ein. Der RPi ist doch so populär da 
müsste es doch was gescheites geben oder? Zu meiner Verwunderung konnte 
ich halt irgendwie nichts finden.

Vielen Dank!

Mirco

von Heiko L. (zer0)


Lesenswert?

Mirco G. schrieb:
> Machine:                           Advanced Micro Devices X86-64

Also ist das definitiv keine ARM-Lib. Vermutlich soll man das eigentlich 
auf dem Pi selbst kompilieren und installieren.

Um ein Programm, dass die lib verwendet, cross zu kompilieren (crunchy) 
brauchst du Header und ein Binary zum linken. Die Header scheinst du ja 
rumfliegen zu haben. Was das Binary angeht ist es wohl am einfachsten, 
die libwiringPi.so von einem Pi runterzukopieren und dagegen zu linken.

von Mirco G. (mirco432)


Lesenswert?

Heiko L. schrieb:
> Um ein Programm, dass die lib verwendet, cross zu kompilieren (crunchy)
> brauchst du Header und ein Binary zum linken.

Mich hat das mit den Libraries ein bisschen verwirrt. Ich war mir unklar 
warum ich überhaupt zwei verschiedene habe einmal eine .so und einmal 
eine .h. Ich habe bisher nur mit normalen Controllern und den 
entsprechenden Hersteller IDEs gearbeitet.

Nach ein wenig rumsuchen bin ich zu folgendem Ergebnis gekommen:

Ich hab mir die wiringP.h mal angeschaut und dabei ist mir aufgefallen 
das die Funktionen hauptsächlich nur als extern definiert wurden => es 
muss ein anderes File geben welches diese definiert.

Deswegen habe ich mir die Funktionen der libwiringPi.so mal mit objdump 
-T ausgeben lassen. Dabei kommt dann folgendes raus.
1
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 puts
2
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 setsockopt
3
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 pthread_setschedparam
4
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 fcntl
5
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.17  clock_gettime
6
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 write

Die als extern definierten Funktionen der .h kommen also aus der .so.

Da Du es als Binary bezeichnet hast nehme ich mal an das die .so schon 
compilierte Teilfunktionen enthält.

Vorcompiliert wird vermutlich damit das compilieren auf einem RPi nicht 
so lange dauert? Ansonsten sehe ich darin nämlich keinen Sinn :D.

Warum muss ich jetzt aber nochmal gesondert linken? Eigentlich linked 
mir meine IDE in meinem Fall Code:Blocks doch nachher alles was ich 
brauche zusammen oder?


Heiko L. schrieb:
> Was das Binary angeht ist es wohl am einfachsten,
> die libwiringPi.so von einem Pi runterzukopieren und dagegen zu linken.

Wo soll ich die dann ablegen wenn usr/local/  nicht in ordnung ist?

Möchte darauf vielleicht noch jemand eingehen? =>

Mirco G. schrieb:
> Was mich beim Installieren der Toolchain aber irgendwie gewundert hat
> ist das die Dateien oft mit BCM 2708 bezeichnet sind. Das ist ja
> eigentlich nicht der Prozessor vom RPi4. Der ist ja von einem älteren
> Modell.
>
> Die aktuelle Anleitung verweist aber auf die gleiche alte Toolchain.
>
> https://www.raspberrypi.org/documentation/linux/kernel/building.md

Vielen Dank!

von Heiko L. (zer0)


Lesenswert?

Mirco G. schrieb:
> Wo soll ich die dann ablegen wenn usr/local/  nicht in ordnung ist?

Das ist im Prinzip egal.
Google: sysroot


Mirco G. schrieb:
> Warum muss ich jetzt aber nochmal gesondert linken?

Das ist eine exzellente Frage.
Touche.

Dafür gibt es, glaube ich, eigentlich keine zwingende technische 
Notwendigkeit, das ist nur so eine Prüfung, dass nachher im Programm 
alles definiert ist und zur Generierung von Dependency-Einträgen 
verwendet wird.

Zu deinen restlichen allgemeinen Fragen, würde ich dich bitten, das 
weitere Internet zu konsultieren, was .so und .h Dateien sind usw. Da 
gibt es so viele Erklärungen...

von S. R. (svenska)


Lesenswert?

Mirco G. schrieb:
> Ich war mir unklar warum ich überhaupt zwei verschiedene
> habe einmal eine .so und einmal eine .h.

In der .h ist die Schnittstelle zur Bibliothek definiert (d.h. welche 
Funktionen gibt es, wie werden sie aufgerufen). Die benutzt der 
Compiler, um den passenden Aufrufcode zu generieren.

Die .so ist ein shared object und enthält den fertigen Binärcode, der 
aber noch nicht vollständig gelinkt ist.

Mirco G. schrieb:
> Da Du es als Binary bezeichnet hast nehme ich mal an das
> die .so schon compilierte Teilfunktionen enthält.

Das ist alles schon vollständig fertig compiliert (zumindest die 
Funktionen, die da drin sind). Da sind nur zusätzliche Informationen 
drin, die man braucht, um die Datei in den Speicher eines anderen 
Programmes laden zu können.

Mirco G. schrieb:
> Vorcompiliert wird vermutlich damit das compilieren auf einem RPi nicht
> so lange dauert? Ansonsten sehe ich darin nämlich keinen Sinn :D.

Nein, das ist aber ein nützlicher Effekt. Eine shared library (im 
Gegensatz zu einer static library) bleibt im Dateisystem immer 
getrennt vom eigentlichen Programm. Wenn du das Programm startest, lädt 
der dynamische Linker (ld.so) alle benötigten .so-Dateien in den 
Speicher und passt alle Funktionsaufrufe des Programms so an, dass die 
auf die richtigen Adressen zeigen.

Das heißt, dass (a) mehrere Programme die gleiche Datei nutzen können, 
was Plattenplatz spart; (b) mehrere Programme die gleiche Datei im RAM 
nutzen können (zumindest den readonly-Anteil, also v.a. Code), das spart 
RAM; (c) du die Bibliothek getrennt vom Programm aktualisieren kannst.

Der dritte Punkt ist interessant, denn damit kannst du einerseits für 
jeden Raspberry Pi eine spezielle Bibliothek anbieten, ohne dein 
Programm ändern zu müssen - andererseits brauchst du, wenn die 
Bibliothek einen Fehler weniger hat, dein Programm nicht neu zu linken.

Unter Windows gibt es das gleiche Konzept und heißt dort DLL.

Mirco G. schrieb:
> Warum muss ich jetzt aber nochmal gesondert linken?

In deinem Programm steht drin, welche .so-Dateien nötig sind, welche 
Funktionen daraus benutzt werden, und wo die Datei zu finden ist. Diese 
Informationen benutzt ld.so, um das Programm zu starten.

(Stark vereinfacht, das ganze.)

von Mirco G. (mirco432)


Lesenswert?

Ich habe jetzt die wiringPi.so und wiringPi.h von meinem RPi genommen.

Jetzt habe ich beim compilieren keine Fehler mehr.

Ich hab an Pin 17 eine LED angeschlossen die ich auf dem Pi auch mit dem 
Terminal ansteuern kann.

Auf meinen Compilierten Code reagiert die aber nicht.

Das hier ist mein Code
1
#include <wiringPi.h>
2
#include <stdio.h>
3
4
int main(void)
5
{
6
   wiringPiSetup();
7
    pinMode(17,OUTPUT);
8
9
   while(1)
10
   {
11
   digitalWrite(17,HIGH);
12
    delay(500);
13
    digitalWrite(17,LOW);
14
    delay(500);
15
  
16
    printf("hallo\n");
17
   }
18
19
   return 0;
20
21
}

Das Printf gibt er mir auf dem Pi auch richtig im Terminal aus.

Ich hab aber ehrlich gesagt keine Ahnung wie ich jetzt rausfinden kann 
woran es liegt.

Kann mir da jemand helfen?

DANKE!

von Mirco G. (mirco432)


Lesenswert?

Hat sich erledigt.

Ich musste die wiringPi lib nochmal updaten auf 2.52.

Nachtrag:

Was mich aber verwundert ist warum jetzt der auf meinem PC compilierte 
Code auf dem Pi funktioniert obwohl ich die wiringPi.so und wiringPi.h 
von der 2.5 Version zum Compilieren verwendet habe :D.

An meinem Compilierten File hat sich ja nichts geändert :D.

: Bearbeitet durch User
von Christian H. (netzwanze) Benutzerseite


Lesenswert?

Mirco G. schrieb:
> Ich finde es entspannter wenn ich auf meinem normalen PC arbeiten kann.

ssh/putty und wenn es grafisch sein soll, noch einen X-Server dazu.

von Christian H. (netzwanze) Benutzerseite


Lesenswert?

Mirco G. schrieb:
> Was mich aber verwundert ist warum jetzt der auf meinem PC compilierte
> Code auf dem Pi funktioniert obwohl ich die wiringPi.so und wiringPi.h
> von der 2.5 Version zum Compilieren verwendet habe :D.

In der wiringPi.h sind nur die Prototypen enthalten. Die ändern sich bei 
unterschiedlichen Bibliotheksversionen nicht groß. Ebenfalls ändern sich 
die Funktionen einer Bibliothek nur selten. Oft sind es Bugfixes, 
Erweiterungen der vorhandenen oder komplett neue Funktionen.
Daher ist es Deinem Programm fast egal, welche Bibliothek verwendet 
wird.

von Andreas M. (amesser)


Lesenswert?

Du musst noch vielen lernen. Mach Dich doch erstmal über das generelle 
Prinzip eines dynamischen Linkers, insbesondere unter Linux, vertraut. 
Es gibt  da nämlich ein paar Konventionen und Festlegungen damit man 
eben gerade die Libraries gegen neuere Versionen tauschen kann ohne das 
vorhandene Programm neu kompilieren zu müssen.

Warum du allerdings dein Programm unbedingt per Cross-Compiler 
übersetzen willst ist mir schleierhaft. Ein vollständige, zu dem 
Raspi-System passende Cross-Compile Umgebungen aufzusetzen ist nicht 
trivial, eine ungünstig oder unabsichtlich falsch gesetzte 
Compileroption, eventuell einfach durch irrtümliche Nutzung von 
pkgconfig und Du Kompilierst dein Programm gegen Header von deinem PC 
System aber linkst gegen die Raspi Libs.

Warum arbeitest Du nicht direkt auf dem Raspi? Der hat Power genug 
dafür. Man kann auch vom PC aus remote per SSH auf dem Raspi arbeiten. 
Ganz intuitiv wird das mit Visual Studio Code und den Remote 
Erweiterungen. Da arbeitet man in der Gui am PC aber alle Aktionen 
werden transparent per SSH auf dem Zielsystem ausgeführt. Ist wirklich 
eine tolle Sache.

von Mirco G. (mirco432)


Lesenswert?

Andreas M. schrieb:
> Du musst noch vielen lernen.

Da gebe ich dir recht :).

Andreas M. schrieb:
> Mach Dich doch erstmal über das generelle
> Prinzip eines dynamischen Linkers, insbesondere unter Linux, vertraut.

Ich lese mich ja parallel ein. Von einfachem lesen wird man aber oft 
nicht schlau. Deswegen arbeite ich nebenbei immer schon praktisch. Auch 
wenn dann oft fragen aufkommen.

Andreas M. schrieb:
> Warum du allerdings dein Programm unbedingt per Cross-Compiler
> übersetzen willst ist mir schleierhaft.

Ich habe das nur gemacht weil das in meinem Buch so vorgeschlagen war. 
Ich hab vor einem Jahr schon ein ganz kleines bisschen über ssh 
gearbeitet. Deswegen wollte ich es mal ausprobieren. Hab auch ein wenig 
was über Cross Compiler gelernt den Rest mache ich jetzt auch direkt auf 
dem Pi.

Andreas M. schrieb:
> Ganz intuitiv wird das mit Visual Studio Code und den Remote
> Erweiterungen. Da arbeitet man in der Gui am PC aber alle Aktionen
> werden transparent per SSH auf dem Zielsystem ausgeführt. Ist wirklich
> eine tolle Sache.

Schaue ich mir mal an. Danke!

von Mirco G. (mirco432)


Lesenswert?

Ich würde ganz gerne zumindest die GPIO Bedienung verstehen. Bei einem 
uC werden die GPIOs ja über die entsprechenden Register angesteuert. Das 
wird beim RPi ja vermutlich nicht anders sein.

https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2711/rpi_DATA_2711_1p0.pdf

Die Registerbeschreibung kann ich mit der wiringPi.h aber nicht 
nachverfolgen. Die Funktionen müssten also in der wiringPi.so stehen. 
Davon kann ich mir ja aber nur die entsprechenden Funktionsnamen 
ausgeben lassen.

Ich habe hier ein paar Beispiele für die Ansteuerung gefunden:

https://elinux.org/RPi_GPIO_Code_Samples

Das erste Beispiel für den direkten Registezugriff ist für mich 
unverständlich, obwohl ich es bei "norameln" uC schon mehrfach gemacht 
habe.

Bei dem Beispiel "Zugriff über sysfs" wird das sysfs Interface irgendwie 
ein bisschen mit C missbraucht. Das ist ja vermutlich auch nicht Sinn 
und Zweck oder?

Ich bin mir auch unsicher ob und wo der Kernel beim Ausführen meines 
compilierten binarys (von oben) ins Spiel kommt. Wird mithilfe von 
wiringPi direkt an die Register geschrieben oder werden irgendwelche 
Gerätedatein dadurch verändert. Diese Wissenslücke kann ich gerade 
irgendwie nicht schließen.

Ich hab auch schon ordentlich gegoogelt. Ich konnte keine Quelle finden 
die darauf so detailliert eingeht.

Kurz gesagt: Wie ist der grobe Ablauf bei der GPIO Bedienung von meinem 
Sourcecode zur Registerbeschriebung im Controller. Wo kommt da mein 
Betriebssystem und die entsprechende Gerätedatei ins Spiel.

VIELEN DANK!

Mirco

von Andreas M. (amesser)


Lesenswert?

Mirco G. schrieb:
> Ich würde ganz gerne zumindest die GPIO Bedienung verstehen. Bei einem
> uC werden die GPIOs ja über die entsprechenden Register angesteuert. Das
> wird beim RPi ja vermutlich nicht anders sein.

Ja ist das gleich Prinzip, mit dem Unterschied das du einen Linux-Kernel 
hast und der SoC auch noch eine MMU.

Der Linux-Kernel erlaubt einer Anwendung normalerweise nicht, einfach 
irgendwo im  Addressraum zu lesen und zu schreiben. Dazu kommt, dass 
Dein Programm unter Linux in einem virtuellen Addressraum lebt. D.h. Für 
jedes Programm was im Linux läuft führt der Linux-Kernel eine Tabelle 
die eine Speicher-Addresse im Programm (Virtuelle Addresse) auf eine 
Physikalische Addresse abbildet. (Natürlich nicht für jede Speicherzelle 
einzeln aber in Blöcken/Gruppen) Das ist auch der Grund, warum bei zwei 
Programmen unter Linux (und auch unter Windows) man unter der selben 
Speicheraddresse meist unterschiedliche Daten findet, denn für jedes 
Program wird diese gleiche virtuelle Addresse auf einen anderen 
physikalischen Speicherbereich abgebildet. Man kann das natürlich auch 
umgekehrt benutzen um zwei Programmen unter (eventuell 
unterschiedlichen) virtuellen Addressen Zugriff auf den gleichen 
Physikalischen Speicher zu ermöglich. (Genau das passiert unter Linux 
wenn zwei Programme die gleiche Library laden, die liegt dann effektiv 
nur einmal im Speicher) Diese Umrechnung von Virtueller nach 
Physikalischer Addresse macht die MMU. (Und noch ein bischen mehr)

> Die Registerbeschreibung kann ich mit der wiringPi.h aber nicht
> nachverfolgen. Die Funktionen müssten also in der wiringPi.so stehen.

Ja, normalerweise könntest du in den Quellcode schauen, das scheint aber 
gerade down zu sein

https://git.drogon.net/

> Ich habe hier ein paar Beispiele für die Ansteuerung gefunden:
>
> https://elinux.org/RPi_GPIO_Code_Samples
>
> Das erste Beispiel für den direkten Registezugriff ist für mich
> unverständlich, obwohl ich es bei "norameln" uC schon mehrfach gemacht
> habe.

Naja, wie oben gesagt, erlaubt der Linux-Kernel nicht jedem x-Beliebigen 
Programm irgendwo in den Speicher zu schreiben. Dazu kommt, das für Dein 
Program die MMU so eingestellt werden muss, dass der Physikalische 
Speicherbereich der GPIO Peripherie in den virtuellen Addressraum deines 
Programmes eingeblendet wird.

Dass passiert in dem o.g. Beispiel durch die Kombination von 
open("/dev/mem"), mmap() (und close) "/dev/mem" ist ein sogenanntes 
device-file. (eine schnittstelle zum kernel.) Der mmap() Aufruf wird an 
den Kernel-Treiber der an "/dev/mem" angebunden ist weitergeleitet und 
dieser macht dann was. In diesem Fall hier sagt der mmap-Aufruf "Blende 
bitte BLOCK_SIZE Bytes ab der physikalischen Speicheraddresse GPIO_BASE 
in meinem virtuellen Speicher ein". Der Rückgabewert der Funktion ist 
die Speicheraddresse im Programm an der dann auf den Physikalischen 
Speicher von GPIO_BASE bis GPIO_BASE+BLOCK_SIZE zugegriffen werden kann 
und damit direkt auf den GPIO Peripherie Block.

Achtung: Zugriffe auf /dev/mem funktionieren normalerweise nur, wenn das 
Programm als "root" läuft. Ich weis jetzt nicht ob es da bei RPi 
Ausnahmen gibt.

> Bei dem Beispiel "Zugriff über sysfs" wird das sysfs Interface irgendwie
> ein bisschen mit C missbraucht. Das ist ja vermutlich auch nicht Sinn
> und Zweck oder?

Das sysfs ist einfach eine weitere Schnittstelle zu den Kerneltreibern. 
Der Vorteil ist, das diese sehr einfach benutzt werden kann. Man öffnet 
einfach die Datei, schreibt einen "Befehl" rein und schließt sie wieder. 
(Oder liest daraus informationen) Der entsprechende Treiber im Kernel 
erhält dann den Befehl und führt ihn aus oder erzeugt die gewünschten 
Daten.

Durch diesen einfachen Mechanismus kann man das auch aus Skripten heraus
gut benutzen.


> Ich bin mir auch unsicher ob und wo der Kernel beim Ausführen meines
> compilierten binarys (von oben) ins Spiel kommt. Wird mithilfe von
> wiringPi direkt an die Register geschrieben oder werden irgendwelche

Da müsste man sich den Quellcode von wiringPi anschauen.

> Gerätedatein dadurch verändert. Diese Wissenslücke kann ich gerade
> irgendwie nicht schließen.
>
> Ich hab auch schon ordentlich gegoogelt. Ich konnte keine Quelle finden
> die darauf so detailliert eingeht.

Ja, das wollen die meisten heute auch gar nicht mehr wissen; "gibt ja ne 
fertige Library". Libraries sind nicht schlecht, ich bin aber auch dafür 
das man die Grundlagen verstehen sollte. Hilft beim Fehlersuchen.

Hier ist übrigens der Quellcode des Kernel Treiber für die "/dev/mem":

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/char/mem.c?h=v5.6

Wenn du in deinem Program "mmap" für "/dev/mem" aufrufst, dann kommst Du 
dort in der Funktion "mmap_mem" an.

mmap() kann man auch mit normalen Dateien benutzen. Dann kann man 
einfach über Zeiger auf den Dateiinhalt zugreifen. Die Datei wird dafür 
zunächst gar nicht geladen, sondern die MMU so eingestellt, das beim 
ersten Zugriff auf den Virtuellen Speicher der Kernel ein Signal bekommt 
das der betreffende Bereich angefordert wurde und mit Daten gefüllt 
werden müsste. Erst dann werden die Daten von der Festplatte in den Ram 
geladen. Mit dem gleichen Mechanismus implementiert der Kernel auch den 
Auslagerungsspeicher, d.h. die Möglichkeit ein laufendes Programm auf 
der Festplatte zu parken.

von STK500-Besitzer (Gast)


Lesenswert?

Mirco G. schrieb:
> Das erste Beispiel für den direkten Registezugriff ist für mich
> unverständlich, obwohl ich es bei "norameln" uC schon mehrfach gemacht
> habe.

Was verstehst du daran nicht?
Pointer-Operationen in C sind dir geläufig?


Mirco G. schrieb:
> Ich konnte keine Quelle finden
> die darauf so detailliert eingeht.
Die erste Quelle sollte das Datenblatt des Bausteins sein, den du 
ansprechen willst.

von S. R. (svenska)


Lesenswert?

Mirco G. schrieb:
> Ich würde ganz gerne zumindest die GPIO Bedienung verstehen.
> Bei einem uC werden die GPIOs ja über die entsprechenden
> Register angesteuert. Das wird beim RPi ja vermutlich nicht
> anders sein.

Das stimmt. Aber auf dem Raspberry Pi läuft ein richtiges 
Betriebssystem, mit Speicherschutz, allgemeiner Treiberunterstützung und 
ordentlichen Programmierschnittstellen.

Man kann direkt auf den Registern rumschreiben (siehe /dev/mem). Aber 
es ist viel einfacher, fertige Treiber zu benutzen. Das gilt 
insbesondere dann, wenn die Dinge komplizierter werden, z.B. für 
Ethernet- oder CAN-Hardware.

> Die Registerbeschreibung kann ich mit der wiringPi.h
> aber nicht nachverfolgen.

In einer Headerdatei steht nur die API. Das ist der Teil, den du 
brauchst, um die Bibliothek benutzen zu können, nicht wie sie 
funktioniert. Das ist nur die Bedienungsanleitung.

> Die Funktionen müssten also in der wiringPi.so stehen.

Ja, aber schon fertig kompiliert als Binärcode. Wenn du wissen willst, 
wie das im Detail funktioniert, dann musst du in den Code der Bibliothek 
schauen.

Den gibt's z.B. auf Github: https://github.com/WiringPi/WiringPi (und 
die Originalbibliothek wird nicht mehr weiterentwickelt, weil gewisse 
Menschen Arschlöcher sind).

> Das erste Beispiel für den direkten Registezugriff ist
> für mich unverständlich, obwohl ich es bei "norameln" uC
> schon mehrfach gemacht habe.

Wenn du keine MMU hast, dann ist die Adresse 0x12345678 irgendwo im 
physischen Adressraum und wenn da zufällig dein Register ist, dann 
kannst du darauf zugreifen.

Wenn du eine MMU hast, dann ist die Adresse 0x12345678 irgendwo im 
Adressraum deiner Anwendung, und da ist garantiert kein Register, wenn 
du die MMU nicht vorher angewiesen hast, das dorthin zu mappen.

Deswegen brauchst du den /dev/mem und mmap()-Tanz.

> Bei dem Beispiel "Zugriff über sysfs" wird das sysfs Interface
> irgendwie ein bisschen mit C missbraucht. Das ist ja vermutlich
> auch nicht Sinn und Zweck oder?

Sysfs ist nunmal eine Kernel-API für das GPIO-Subsystem. Wenn die GPIOs 
dem Kernel bekannt sind, dann kannst du über sysfs darauf zugreifen. Das 
soll schon so, auch wenn sysfs keine angenehme C-Schnittstelle ist.

Dafür funktioniert sie auch mit Shellscripts, Perl, Rust sowie jedem 
anderen Kram, der Dateien lesen und schreiben kann...

> Ich bin mir auch unsicher ob und wo der Kernel beim Ausführen meines
> compilierten binarys (von oben) ins Spiel kommt.

Wenn du WiringPi benutzt, dann hängen die Details von der Implementation 
von WiringPi ab. Du kannst auch eine andere Bibliothek mit der gleichen 
API schreiben (d.h. die würde dann die gleiche wiringpi.h benutzen), die 
das ganz anders macht.

> Kurz gesagt: Wie ist der grobe Ablauf bei der GPIO Bedienung von meinem
> Sourcecode zur Registerbeschriebung im Controller. Wo kommt da mein
> Betriebssystem und die entsprechende Gerätedatei ins Spiel.

Sinnvollerweise würde ich mich von der Hardware lösen. Direkte 
Registerzugriffe macht man unter Linux einfach nicht, das lässt man 
Linux machen. Und das heißt, dass du dich in "GPIOs unter Linux" 
einarbeitest.

Hat den Vorteil, dass es auf jedem beliebigen Gerät gleich funktioniert. 
Zumindest von "mein Programm will was tun" und "der Hardwaretreiber tut 
jetzt was". Und wenn du dann noch weitersuchen willst, dann schaust du 
in die Linux-Treiber - die meisten Kerneltreiber sind halbwegs lesbar.

Inzwischen kann Linux auch "besser als sysfs", siehe z.B. hier: 
https://embeddedbits.org/new-linux-kernel-gpio-user-space-interface/

Mirco G. schrieb:
> Ich konnte keine Quelle finden die darauf so detailliert eingeht.

Weil ein Raspberry Pi im Prinzip nichts anderes ist als ein normaler 
Linux-Computer auch. Die Erklärungen findet man an anderer Stelle - man 
muss nicht speziell "Linux auf Raspberry Pi" erklären, weil es nicht 
speziell ist.

von S. R. (svenska)


Lesenswert?

Mirco G. schrieb:
> Wird mithilfe von wiringPi direkt an die Register geschrieben
> oder werden irgendwelche Gerätedatein dadurch verändert.

Ich habe gerade ein bisschen in WiringPi/wiringPi.c gesurft.

Die Bibliothek liest die Chipversion aus /proc/cpuinfo aus, nutzt 
/dev/mem (oder /dev/gpiomem, wenn keine Zugriffsrechte) für direkten 
Registerzugriff und sysfs für Interrupts.

Außerdem implementiert WiringPi ziemlich viele Chiptreiber selbst, 
obwohl der Kernel selbst wahrscheinlich viele Treiber schon enthält. Ist 
halt einfacher.

Beitrag #6216227 wurde vom Autor gelöscht.
von Mirco G. (mirco432)


Lesenswert?

Vielen Dank Andreas und Svenska :). Super das Ihr euch die Zeit genommen 
habt das zu erläutern! Ist nicht selbstverständlich :).

S. R. schrieb:
> Sinnvollerweise würde ich mich von der Hardware lösen. Direkte
> Registerzugriffe macht man unter Linux einfach nicht, das lässt man
> Linux machen. Und das heißt, dass du dich in "GPIOs unter Linux"
> einarbeitest.

Das möchte ich auch. Ich finde es aber immer ganz gut zumindest einfache 
Aufgaben wie die GPIO Bedienung zu verstehen.

Durch eure Erläuterungen hab ich vieles besser verstanden. Jetzt kann 
ich mit ruhigem Gewissen entfernter von der Hardware arbeiten, weil ich 
zumindest das Grundprinzip ein wenig durchblickt habe:).

Vielen vielen Dank!

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.