Mini2440
Das Mini2440 ist ein ARM-Board mit s3c2440 System on Chip von Samsung.
Das Board bietet:
- 3,5", 7" Touchscreen-TFT oder VGA "Karte"
- ARM920T, 400Mhz standard Taktfrequenz, bis zu 533Mhz
- 64MB-1GB NAND Flash (User App/OS), 2MB NOR Flash (BIOS)
- 64MB RAM
- Audio (Stereo Out, 2xMono In)
- SD-Slot
- USB Host, USB Device
- 10/100MBit LAN (DM9000)
- 3 Serial Ports
- SPI, PWM, I2C, ...
- ...
Software
Als das passende Entwicklungswerkzeug ist ein Linux-Rechner mit passendem Cross-Compiler sowie ein paar Tools vorgesehen. Was wirklich benötigt wird:
- Serial Terminal (picocom, minicom, cutecom, gtkterm, komport, ...)
- Toolchain
- s3c_boot_usb oder mini2440_usb_upload (aktueller; mit Status-Anzeige etc.) zum Hochladen von Bootloader, Kernel etc. über USB
Empfehlenswert ausserdem noch:
- ncurses-dev-Dateien und dialog (Für Kernel-menuconfig)
- screen, midnight commander
- Editor mit root-Rechten (z.B. vim)
Vorbereitungen am/mit dem Board
Ganz wichtig ist es, eine gute Spannungsversorgung zu verwenden. Es kam anscheinend schon öfter vor, dass kleine Spannungseinbrüche das Board aus dem Gleichgewicht bringen. Ich verwende normalerweise das mitgelieferte Netzteil oder ein 5V/2A Schaltnetzteil, funktionieren beide einwandfrei. Gerade das grosse, 7", Display braucht ein starkes Netzteil.
Konfiguration | Stromverbrauch [mA] |
---|---|
mini2440 | 140 |
mini2440 + 3,5" TFT | 410 (T35 Display) |
mini2440 + 7" TFT | 1000 |
mini2440 + VGA Board | ??? |
Verbindung zum PC
Um vernünftig arbeiten zu können, braucht man auf jeden Fall diese Kabel:
- 1 USB A<->USB B-Kabel
- 1 9 poliges SUB-D Kabel mit zwei Buchsen
- eventuell einen USB-RS232 Adapter, falls man keinen Anschluss am Rechner hat
Einen Zugriff auf die Konsole hat man - nach Anpassung der IP - über Telnet sowie über die serielle Konsole.
Die serielle Konsole ist in der Standardkonfiguration aktiviert und sollte auch während des Entwickelns immer aktiv sein - so verhindert man ein ungewolltes Ausperren.
Netzwerk
Entweder klickt man sich quer durch die chinesische/japanische Software und stellt diese auf Englisch um, oder aber man klickt sich blind zu der Stelle, an der man IP Adresse, Gateway und Netzmaske angeben kann.
Etwa genauso einfach sieht es über die Konsole aus.
Die folgenden drei Zeilen, dürften das soweit bewerkstelligen, dass das Netzwerk-Device die IP-Adresse 192.168.0.55, die Netzmaske 255.255.255.0 zugeteilt bekommt. Die beiden anderen Anweisungen sagen dem System, welches Netzwerkgerät der Router ist. In diesem Fall 192.168.0.1.
Es ist darauf zu achten, was bisher vielleicht schon in der /etc/resolv.conf steht. Das lässt sich einfach mit cat /etc/resolv.conf ansehen. Wenn diese bereits den richtigen Inhalt hat, kann man die echo "nam...-Anweisung weglassen.
Wenn die resolv.conf nicht existiert oder einen falschen Wert enthält, überschreibt man diese, in dem man nur ein ">" nach das echo setzt. Stehen dort zwei ">" wird die nameserver-Info nur an die Datei angehängt und es kann zu Problemen kommen.
$ ifconfig eth0 192.168.0.55 netmask 255.255.255.0 $ route add default gw 192.168.0.1 $ echo "nameserver 192.168.0.1" > /etc/resolv.conf
Benutzeraccounts
Linux legt wie alle Unix-artigen Betriebssysteme sehr grossen Wert auf eine ordentliche Benutzer- und Rechteverwaltung.
Daher sollte man sich überlegen, ob es nicht vielleicht besser wäre, wenn man einen extra Benutzer zum Arbeiten/Basteln/Produktiv-Sein/Bier-Trinken/... anlegt.
Wenn nicht unbedingt nötig, sollte man auf das Arbeiten als root (oder auch Superuser, Admin, ...) verzichten. Dadurch dass man uneingeschränkt alle Rechte auf dem System besitzt kann es durch Programmierfehler oder auch Schadsoftware zu ernsthaften Schäden kommen (Datenverlust, eventuell Hardwareschäden).
Die Entscheidung ob man wirklich root sein muss, ist eigentlich relativ einfach. Sie wird normalerweise immer erstmal mit nein beantwortet.
Sollte sich im Lauf der Zeit doch herausstellen, dass man root sein muss, kann man diesen Weg immer noch einschlagen.
Für einige Aufgaben sind jedoch auch root-Rechte erforderlich.
Zum Beispiel, wenn man direkt mit outporb()/inportb()/outb()/inb()/... arbeiten will.
Für diese Fälle gibt es ein gutes Tool, es nennt sich sudo.
Man kann sudo z. B. sagen, dass Benutzer xy das Programm /usr/bin/blubb mit root-Rechten immer ausführen darf. Benutzer ab darf das aber nur, wenn er sein Passwort eingibt. Dann gibt es vielleicht noch Benutzer zz, der alles ohne Passwort als root ausführen darf.
Anlegen eines neuen Benutzeraccounts
Ich gehe davon aus, dass ein Image mit busybox auf dem Board installiert ist. Wenn das Image von FriendlyARM installiert ist, ist das der Fall.
Ein Auszug aus der Manpage von busybox:
adduser adduser [ OPTIONS ] user_name Adds a user to the system Options: -h DIR Assign home directory DIR -g GECOS Assign gecos field GECOS -s SHELL Assign login shell SHELL -G Add the user to existing group GROUP -S create a system user (ignored) -D Do not assign a password (logins still possible via ssh) -H Do not create the home directory
Heisst um einen Benutzer banana_joe mit dem Heimatverzeichnis /home/banana_joe anzulegen, muss man folgendes als root eingeben:
$ adduser -h /home/banana_joe -s /bin/sh banana_joe $ passwd banana_joe
adduser legt den Benutzer an, passwd vergibt ein Passwort.
Übertragen von Daten/Programmen in den Flash-Speicher
Das übertragen gestaltet sich auf ganz unterschiedliche Weise. Aus Gründen der Einfachheit werde ich hier nur die sinnvollsten/einfachsten/schnellsten Wege beschreiben. Andere Möglichkeiten wären noch lrzsz, USB und JTAG. Wer zuviel Zeit hat, kann sich ja auch ein eigenes Protokoll auf TCP/IP Ebene basteln oder den Kristallspeicher erfinden ;)
Wenn man eine Datei übertragen hat, muss man sich vergewissern, dass diese auch Ausführrechte besitzt, wenn sie ein Programm oder ein Ordner ist. Die Lese- und Schreibrechte müssen ebenfalls richtig gesetzt sein. Normalerweise stimmt dies alles so wie es ist, aber manchmal muss man ein wenig nachhelfen. Wenn sich ein Programm nicht ausführen lässt, oder man nicht in einen Ordner wechseln kann, fehlen die Rechte zum Ausführen. Geändert wird das so:
$ chmod +x <datei/ordner>
Da hier aber nicht die Unix-Rechte erklärt werden sollen, verweise ich hierauf
http
Für diese Methode muss natürlich das Netzwerk eingerichtet sein. Man braucht auf einem PC im Netz einen laufenden Werbserver, auf den man die zu übertragenden Dateien legt. Nun kann man diese in ein Verzeichnis seiner Wahl "herunterladen":
$ cd /tmp $ wget http://10.2.5.29/datei
ftp
Sobald auf dem Board ein FTP-Server läuft, kann man sich "ganz normal" mit einem FTP-Client (zum Beispiel gftp oder ncftp) verbinden und die Dateien hochladen. Ebenso kann man sich auch vom Board auf einen FTP-Server verbinden. Nennenswerte Programme sind z.B. ftp für die Linux-Konsole, welches im FriendlyARM Image bereits enthalten ist oder der ncurses-basierende Client ncftp. Allerdings sollte man sich für ncurses-Programme per Telnet oder SSH einloggen, da die serielle Konsole nur rudimentäre Terminal-Emulation unterstützt und die Bildschirmausgabe nicht gerade schön wirkt.
scp/ssh
Dazu muss zuerst einmal ein SSH Server auf dem Board laufen. openSSH ist dafür die erste Wahl. Mit dropbear gibt es einige Probleme, da man für SCP auch noch die SCP Binary von openSSH braucht.
Sobald der ssh server läuft, ist alles ganz einfach:
$ scp datei user@ziel:/pfad
Bei einem Board mit der IP 10.0.0.69 und dem User root wäre das die Befehlszeile:
$ scp datei root@10.0.0.69:/root
... und die Datei datei landet im Heimatverzeichnis von root
Um nicht jedes mal das Passwort eingeben zu müssen, lohnt es sich SSH so zu konfigurieren, dass es mit Key-Files läuft.
Toolchain
Toolchains gibt es im Netz und auf der CD/DVD zum Board mehrere fertig übersetzte - natürlich lässt sich die Toolchain auch selbst compilieren. Das Risiko beim selbst compilieren einer Toolchain liegt darin, dass man eventuell einen fehlerhaften Compiler produziert. Gründe hierfür sind z.B.:
- fehlende, wichtige Patches
- Verwenden von Entwicklerversionen mit eventuellen Bugs
- ein schon fehlerhafter Host-Compiler
- falsche Einstellungen (Prozessor-Architektur, Optimierung, ...)
Das sogenannte Bootstrappen eines Compilers erkennt solche Probleme in den meisten Fällen. Bootstrappen heißt, dass zuerst mit dem Host-Compiler der Quellcode des neuen übersetzt wird und mit diesem zweiten Compiler wird ein dritter Compiler übersetzt. Wenn dies klappt, kann man im Normalfall davon ausgehen, dass der dritte Compiler in Ordnung ist.
Jedoch ist dies bei einem Crosscompiler so nicht möglich, da der zweite Compiler ja keinen Code für den Host-Computer, sondern nur für das Target produzieren kann. Somit würde man sich einen Compiler für das Board bauen.
Wer auf Nummer sicher gehen will, nimmt am besten eine der bekannten ARM-Toolchains oder natürlich die von FriendlyARM.
old ABI / new EABI ?
ABI steht für Application Binary Interface, also eine Art "Standard" wie das compilierte Programm, die Binärdatei, auszusehen hat.
Die ABI ist schon etwas älter und wurde von der EABI (Embedded Application Binary Interface) abgelöst.
Da nahezu jede verfügbare Toolchain auf der EABI aufbaut, würde ich diese auch nutzen. Alte, für die ABI, compilierte Programme, lassen sich mit der Kernel-Unterstützung zum Ausführen von ABI Programmen trotzdem noch verwenden.
Durch das verwenden der EABI entstehen normalerweise keine unüberwindbaren Hürden, beim compilieren von älteren Quellcodes. Jedenfalls nicht durch den Unterschied ABI/EABI.
Kernel FPE / softfloat ?
Irgendwann stolpert jeder, der mit ARM-CPUs und Linux arbeitet über mehrere Fragen.
- Was ist softfloat/FPE?
- Was ist schneller?
- Kann man beides nutzen?
Was ist softfloat/FPE
Dadurch, dass eine ARM-CPU normalerweile keine Gleitkommaeinheit/FPU besitzt, müssen alle Berechnungen, welche Kommazahlen enthalten in Software gelöst werden. Bei einem PC-System übernimmt diese Aufgabe eben die FPU und ist somit um einiges schneller.
softfloat ist die Umsetzung durch den Compiler.
FPE bedeutet Floating Point Emulation und wird durch den Kernel bereit gestellt.
Was ist schneller
Beim FPE-Verfahren wird der Code, welcher die Gleitkomma-Anweisungen enthält auch so übersetzt. Da die CPU aber keine FPU besitzt und somit auch keine Maschinensprachen-Befehle dafür, wird eine Exception ausgelöst ("Fehler"), welche vom Kernel abgefangen wird.
Nun steht der Kernel vor der Aufgabe, die Gleitkomma-Operation zu lösen und dem Programm das Ergebnis mitzuteilen. Danach muss er den Code für die Exception noch zu Ende führen und zurückkehren.
Alles in allem eine sehr aufwendige Methode.
Da bei softfloat die Gleitkomma-Operationen vom Compiler soweit zerstückelt und wieder zusammengesetzt werden, dass gar keine Kommas mehr nötig sind, ist das in dem meisten Fällen flotter.
Das schnellste, ist natürlich eine Hardware-FPU, die es aber auf dem mini2440 nicht gibt.
Kann man beides nutzen?
Jein, wenn man im Kernel eine der beiden FPE-Methoden aktiviert hat und man führt ein softfloat-Programm aus, dann wird natürlich softfloat verwendet.
Wenn man allerdings ein FPE-Programm ausführen will und hat die Unterstützung nicht im Kernel, sieht es schlecht aus.
Also am besten eine FPE auswählen und mit einer softfloat-Toolchain compilieren. Das spart Zeit und Kopfzerbrechen über Invalid Opcode/Segfaults usw. ;)
Toolchain aufsetzen
Jenachdem, welche Toolchain man einsetzt, kann es sein, dass man den Compileraufruf anpassen muss. Wenn man eine Cross Toolchain installiert hat und nur gcc auf der Kommandozeile eintippt, wird man höchstwahrscheinlich beim lokalen gcc, keinem Cross-Compiler, landen. Überprüfen kann man das mit
$ which gcc
Die FriendlyARM Toolchain macht es uns da sehr leicht, sie verwendet aussagekräftige Namen für ihre Programme. Bei ihr wäre der gcc Aufruf der:
$ arm-linux-gcc oder $ arm-none-linux-gnueabi-gcc
Ich gehe im kompletten Artikel davon aus, dass man eine funktionierende Toolchain installiert hat, welche über arm-linux-... zu erreichen ist. Entweder passt man die gcc-Aufrufe von hier an, oder man kann sich auch symbolische Links anlegen. Zum Schluss sollte das dann ungefähr so aussehen, vom bin-Pfad der Toolchain aus!
$ ls -al /usr/local/arm/4.3.2/bin/ ... lrwxrwxrwx 1 root root 25 2010-02-25 07:15 arm-linux-ld -> arm-none-linux-gnueabi-ld ...
Ich selbst arbeite mit der FriendlyARM-Toolchain, werde mich hier also meistens auf diese beziehen, ausser anders vermerkt.
FriendlyARM
Hier als ARM-Linux-GCC zu finden.
Entpackt verbraucht diese Toolchain ca. 295MB. Mit ihr liessen sich bei mir bereits der Kernel, uboot, sowie zahlreiche Programme kompilieren.
Zuerst wird die Toolchain an die richtige Stelle entpackt, der Pfad mit angegeben und dann ist sie auch schon bereit.
Dieses Kommando sieht auf den ersten Blick etwas nach Dateisystem-Apokalypse aus, ist allerdings lage nicht so wild. Die Option -C / sagt, dass direkt ins Wurzelverzeichnis entpackt werden soll, daher benötigt man für diesen Schritt root-Rechte. Die FriendlyARM-Toolchain ist so aufgebaut, dass sie damit nach /usr/local/arm/4.3.2/ entpackt wird, unser Ziel :)
Die Version mit dem gcc 4.4.3 wird nach /opt/FreindlyArm/toolschain/4.4.3 entpackt. Die Pfade sind also entsprechend anzupassen.
$ tar xvfj arm-linux-gcc-4.3.2.tar.gz -C /
Wenn wir das nun so haben, müssen wir natürlich auch noch sagen, wo er den Compiler findet. Dazu öffnet man am besten seine "autorun-Datei" der Shell. In der Konsole ganz einfach mit vim/nano/etc zu erreichen.
$ vim /home/user/.bashrc
Dort tragen wir dann ganz ans Ende dies ein:
export PATH=$PATH:/usr/local/arm/4.3.2/bin
Kaum gespeichert, startet man eine neue shell (Konsole) und überprüft ob die Pfade stimmen. Wenn /usr/local/arm/... im Pfad enthalten ist, müsste alles stimmen.
$ echo $PATH
Tippt man nun nur arm- und drückt zweimal die Tabtaste, so müsste eine ellenlange Liste mit Programmen wie z. B. arm-linux-gcc oder arm-linux-ld erscheinen.
$ arm-linux-gcc -v
codesourcery
codesourcery arm-none-linux-gnueabi
buildroot
Von http://www.buildroot.net das letzte stable Release holen, mini-2440 wird unterstützt. Der Build dauert zwar einige Zeit, aber nacher kann man sich an X11 erfreuen.
PTXdist
PTXdist ist ähnlich wie BuildRoot und unterstützt das Mini2440 seit der Version 2011.05.0. PTXdist verwendet Barebox(UBoot-V2) als Bootloader. Die Projektseite zu dem Mini ist unter http://www.oselas.org/oselas/bsp/pengutronix/mini2440_bsp_en.html zu finden.
Wichtige Links:
- Quickstart Guide geschrieben für das Mini
- PTXdist Downloadverzeichnis
- OSELAS Toolchain Downloadverzeichnis
- Projektdateien für das Mini
- How to become a PTXdist Guru - Erklärung wie z.B. eigene Packete erstellt werden können
Cross Compiling
Sobald die Toolchain richtig aufgesetzt ist kann man anfangen, seine Programme zu übersetzten.
Hier folgt nur eine kurze Beschreibung, wer sowas genauer wissen will, sollte sich auf Seiten wie diesen umsehen:
- http://www.ailis.de/~k/archives/19-ARM-cross-compiling-howto.html
- http://linux.bytesex.org/cross-compiler.html
Ausserdem findet man zu vielen embedded-nutzbaren Programmen direkt ein Cross-Compiling Howto mit ein wenig google. Ob diese nun für ARM, AVR32, PPC, MIPS, ... sind, ist im Grunde egal, da man ja meistens nur die Architektur anpassen muss.
Es gibt mehrere Möglichkeiten. Die erste ist der direkte Aufruf vom Compiler oder mittels eines "festen" Makefiles. Die zweite, wahrscheinlich Häufigste, ist das ausführen eines configure-Scriptes bevor der make-Vorgang gestartet wird.
- Das einfache aufrufen des Compilers
Dazu ein einfaches Hello-World Programm:
#include <stdio.h>
int main() {
printf("hiho\n");
}
Compiliert wird das Programm ganz einfach so:
$ arm-linux-gcc <name.c> -o <name>
Danach wird die Datei <name> an das Board übertragen. Sobald <name> auf dem Board ist, kann es ausgeführt werden:
$ ./<name>
Eventuell fehlen die Rechte:
$ chmod +x <name>
- ./configure-Scripte
Hier sieht es schon ein klein wenig schwieriger aus, ist es aber eigentlich nicht. Normalerweise, aber nicht immer, sieht das compilieren eines Programms für das mini2440 so aus:
$ HOST=arm-linux ARCH=arm ./configure --build=arm-linux --your-options $ make
Man muss dem configure-Script lediglich angeben, für welche Architektur das Makefile konfiguriert werden soll und welchen Compiler er dafür nehmen soll.
Linux-Kernel
Den Kernel-Quellcode besorgt man sich am besten den Kernel-Fork von buserror.
Wenn man nicht auf dem ARM selber kompilieren möchte, benutzt man wie hier im Beispiel einen Cross-Compiler (Siehe oben). Weiterhin wird noch git benötigt, um den Kernel-Quellcode zu laden.
Folgende Einstellungen werden im Abschnitt verwendet:
Verzeichnis: /mnt/mini2440 CHOST/CROSS-COMPILE: arm-softfloat-linux-gnueabi
Starten wir damit, den Quellcode zu laden:
$ cd /mnt/mini2440 $ git clone git://repo.or.cz/linux-2.6/mini2440.git linux-2.6.32-rc8
Dann ab ins Kernel-Verzeichnis und den Kernel mit den Defaults konfigurieren:
$ cd linux-2.6.32-rc8 $ CROSS_COMPILE=arm-softfloat-linux-gnueabi- ARCH=arm make mini2440_defconfig
Wenn man noch etwas manuell ändern möchte:
$ CROSS_COMPILE=arm-softfloat-linux-gnueabi- ARCH=arm make menuconfig
Den Kernel anschließend kompilieren:
$ CROSS_COMPILE=arm-softfloat-linux-gnueabi- ARCH=arm make
Die Module bekommen wir wie folgt nach /mnt/mini2440/lib/modules installiert:
$ CROSS_COMPILE=arm-softfloat-linux-gnueabi- ARCH=arm INSTALL_MOD_PATH=/mnt/mini2440 make modules_install
Wenn wir mit u-boot arbeiten, dann benötigen wir das zImage in /mnt/mini2440/kernel-2.6.32-rc8/arch/arm/boot, um daraus mittels mkimage (Siehe uboot) ein uImage zu erstellen.
Kernel-Parameter
dem Kernel kann man zum Start noch Parameter für das Board mitgeben:
mini2440=[0..9][t][b]
[0..9] bestimmen die Display-Timings (varieren je nach verwendetem Kernel)
- 0 3,5" Display N35 + TS (alt);
- 1 7" Display + TS;
- 2 VGA-Board;
- 3 3,5" Display T35 + TS;
- 4 5,6" Display Innolux + TS;
[t] Touchscreen ist vorhanden;
[b] Backlight ist vorhanden;
root-fs
Um ein eigenes Root-Filesystem zu compilieren, gibt es mehrere Möglichkeiten.
Die einfachsten sind Cross-Umgebungen wie buildroot und openembedded.
Debian
Auch relativ einfach ist es, sich ein fertig compiliertes debian-arm zu installieren. Wenn man das Debian einmal auf seiner SD-Karte laufen hat, ist es kein Problem mehr, alles wie gewohnt mit apt zu installieren usw. usw.
Allerdings sind gerade die Scripte bei Debian noch nie dafür bekannt gewesen sonderlich schnell zu laufen und es ist sehr sehr träge beim booten.
Ich habe auf meinem mini2440 seit einiger Zeit Debian am Laufen, habe aber sämtliche Init-Scripte durch eigene ersetzt, welche um einiges flotter laufen. Ebenso verzichte ich auf sysvinit und damit aber auch auf sehr nützliche Tools/Funktionen, wie die runlevels und shutdown oder reboot. Diese Funktionen müssen auch von meinen eigenen Scripten übernommen werden. Dafür läuft es um einiges schneller.
Hier eine Anleitung für ein AT91-Linux Board mit Debian. Diese kann fast 1:1 übernommen werden.
Mit X11, Lighttpd, PHP, Python, mc, vim, mpd, alsa, gcc, ... (und auch apache als leidige Abhängigkeit von PHP) braucht mein Debian etwa 550MB auf der SD-Karte. Schon ein ordentlicher Brummer, aber egal, es bootet mit eigenen Scripten innerhalb von 15 Sekunden.
Gentoo
Ooooh wer daran denkt, hat einfach nur zuviel Zeit. Gentoo in allen Ehren, aber allein PHP zu compilieren dauert gut 4 Stunden... danach habe ich es aufgegeben.
Wer Gentoo installieren kann, wird sich auf dem Mini2440 auch nicht schwerer tun, als auf einem PC. Der Unterschied besteht nur darin, dass man die Stage auf SD Karte packen muss, am normalen Rechner natürlich, und dort dann schon die wichtigsten Einstellungen vornehmen muss.
Mit Portage und den ganzen Dev-Files ist Gentoo ein riesen Gebilde für so ein schwachbrüstiges Board. Eine 4GB SD-Karte ist das Minimum. Außer man bootet zuerst in den Flash und dann chrootet man auf ein Gentoo auf einer USB-Festplatte. uboot kann das, soweit ich weiß, nicht.
sys-fs
Sysfs ist ein virtuelles Dateisystem, welches seit dem Linux Kernel 2.6. zur Verfügung gestellt wurde. Der Kernel stellt dabei Konfigurationen, Informationen über Vorrichtungen und Treiber im sogenannten userspace zur Verfügung. Dies Bedeutet einfach, dass Informationen/Geräte an einer festgelegten Stelle im Dateisystem stehen und ausgelesen oder beschrieben werden können.
Über einige Dateien in diesem Dateisystem können GPIOs und z.B. auch LEDs angesteuert werden.
Framebuffer
Der Framebuffer ist von Haus aus im FriendlyARM Kernel aktiviert und ohne ihn lässt sich im Linux keine Ausgabe auf dem Display realisieren. Er ist unter /dev/fb0 zu erreichen.
In der Kernelkonfiguration muss die Framebuffer-Konsole und der Framebuffer-Treiber für das jeweilige Display gewählt werden.
Eine Textausgabe geht ganz einfach über das Terminal, welche auf ihn gemappt ist:
$ echo "hello world" > /dev/tty1
Man kann dem Framebuffer diverse Argumente mitgeben, die die Ausgabe auf dem Display beeinflussen. Diese werden über fbcon=... angeben. Man kann auch mehre Argumente angeben, das sähe zum Beispiel so aus:
root=/dev/mmcblk0p1 console=ttySAC0 fbcon=rotate:1 fbcon=scrollback:0
Alternativ können mehrere fbcon-Parameter auch durch Komma getrennt werden:
... fbcon=rotate:1,scrollback:0
Der Scrollback-Buffer ist auf diesem Display eigentlich nicht nötig, da man hier auf dem Mini2440 auf keiner Konsole mit Tastatur usw. arbeitet. Daher schaltet man ihn mit fbcon=scrollback:0 ab, das spart gegenüber dem Standardwert 32kB RAM.
Wer ihn doch benötigt, kann ihn natürlich trotzdem verwenden. Die Zahl nach dem Doppelpunkt steht für die Größe in Bytes. Ein k als Suffix steht für Kilobyte, fbcon=scrollback:1024k entspräche einem Scrollback-Buffer von 1MB.
Für die FB-Konsole gibt es einige weitere Möglichkeiten und viele Optionen lassen sich zur Laufzeit ändern. Hier habe ich nur das Wichtigste beschrieben. Wer darüber mehr erfahren will, sollte sich im Kernel-Quellcode die Datei Documentation/fb/fbcon.txt ansehen.
Font wählen
Die voreingestellte VGA8x16 Font ist etwas groß und es passt nicht allzuviel Text auf das Display. Die am besten lesbare, kleine Schrift ist die Mac6x11 Font. In menuconfig folgendes einstellen:
-> Device Drivers -> Graphics support -> Console display driver support -> Select compiled-in fonts <*> Framebuffer Console support [*] Map the console to the primary display device [*] Framebuffer Console Rotation [*] Select compiled-in fonts [ ] VGA 8x8 font [ ] VGA 8x16 font [*] Mac console 6x11 font (not supported by all drivers) [ ] console 7x14 font (not supported by all drivers) [ ] Pearl (old m68k) console 8x8 font [ ] Acorn console 8x8 font [ ] Mini 4x6 font [ ] Sparc console 8x16 font [ ] Sparc console 12x22 font (not supported by all drivers) [ ] console 10x18 font (not supported by all drivers)
Werden mehrere Fonts eincompiliert, können sie so ausgewählt werden:
(Kernel-Commandline) ... fbcon=font:xxxx
Mögliche Werte für xxxx sind:
- VGA8x8
- VGA8x16
- Mac6x11
- 7x14
- 10x18
- MINI4x6
- RomanLarge
- SUN8x16
- SUN12x22
- ProFont6x11
- Acorn8x8
- PEARL8x8
Bei machen Fonts steht in der Kernel-Menuconfig not supported by all drivers. Dies erklärt sich so:
Note, not all drivers can handle font with widths not divisible by 8, such as vga16fb. kernel-src/Documentation/fb/fbcon.txt
Manche Treiber können nicht mit Fontbreiten/-höhen umgehen, welche nicht durch 8 teilbar sind. Da aber z.B. die MAC6x11 Font auch nicht durch 8 teilbar ist und das Mini2440 das anzeigen kann, gehe ich davon aus, dass alle funktionieren.
Rotation
Um das Display zu "drehen" folgendes einstellen:
-> Device Drivers -> Graphics support -> Console display driver support -> Framebuffer Console support [*] Framebuffer Console Rotation
Also Kernel-Parameter im Bootloader dann einstellen:
fbcon=rotate:x
Für x sind Werte von 0 bis 3 gültig. Jeweils wird um 90° im Uhrzeigersinn weitergedreht.
Hinweis:
Die Rotation bezieht sich nur auf den Framebuffer. Ein anschließend gestartetes Qtopia (z.B.) bleibt von der gewählten Einstellung unberührt.
In eigenen Anwendungen, welche eine grafische Oberfläche bereitstellen, lässt sich die Rotation allerdings verwenden, indem man einfach die Datei /proc/cmdline ausliest und den dort gegebenen Wert verwendet, um seine Bildschirmausrichtung anzupassen.
Bootloader
Die beiden verfügbaren Bootloader sind vivi (ein "standard-s3c24xx-Linux-Bootloader") und u-boot, der Bootloader für embedded-Systeme schlechthin ;)
Gemeinsam haben sie eigentlich nur, dass sie a) funktionieren und b) einen Linux-Kernel laden können.
Der große Nachteil von vivi ist, dass es nur mit YAFFS2-Dateisystemen umgehen kann, die auf dem onboard-Flash liegen müssen.
Sobald der Kernel geladen ist, ist das kein Problem mehr, doch der Kernel muss eben im Flash liegen.
Um nun z. B. von USB-Festplatten/Sticks und SD-/MMC-Karten booten zu können, muss also u-boot installiert werden.
WARNUNG: An einem Bootloader herumzuspielen, kann sehr böse ausgehen, etwa mit dem unbootbar-mach-Vorgang, der durch zu wenig Nachdenken ausgelöst wird. Das mini2440 lässt sich zwar jederzeit per JTAG mit LPT-Adapter zurückholen, ist doch aber eigentlich unnötig ;)
uboot
Der Bootloader uboot wurde von "buserror" soweit an das mini2440 angepasst, dass er mehr oder weniger läuft. Für die verschiedenen Boards gelten also auch leicht verschiedene Anweisungen um uboot ins Wasser zu bekommen.
Das git und die Seite von "buserror" ist hier zu finden.
Boards:
- 64MB
- 128MB
- 256MB
- 1GB
Den u-boot-Fork von buserror laden wir am besten mit git. Weiterhin benötigen wir einen Cross-Compiler (Siehe oben). Die letzte mir bekannte Version des GCC mit dem sich das "U-Boot" kompilieren lässt ist 4.3.4.
Folgende Einstellungen werden im Abschnitt verwendet:
Verzeichnis: /mnt/mini2440 CHOST/CROSS-COMPILE: arm-softfloat-linux-gnueabi
Starten wir damit, den Quellcode zu laden:
$ cd /mnt/mini2440 $ git clone git://repo.or.cz/u-boot-openmoko/mini2440.git u-boot
Dann ab ins Source-Verzeichnis und mit den Defaults für das Mini2440 konfigurieren:
$ cd u-boot $ CROSS_COMPILE=arm-softfloat-linux-gnueabi- make mini2440_config
Wenn man noch etwas manuell ändern möchte, dann die Datei include/configs/mini2440.h bearbeiten, z. B. für das Mini2440-256MB:
#define USE_920T_MMU 1 //#define CONFIG_USE_IRQ 1 /* Needed for USB device! */
(CONFIG_USE_IRQ auskommentieren, damit das Mini2440 u-boot aus dem RAM ausgeführt werden kann. Leider kann man dann im "U-Boot" kein USB-Gerät mehr ansprechen, um ggfl. mal wieder aufzutauchen).
Zusätzlich kann man noch defaults für das Environment (bootargs, ip, ...) festlegen.
Anschließend das "U-Boot" kompilieren:
$ CROSS_COMPILE=arm-softfloat-linux-gnueabi- make
Im Verzeichnis tools findet man mkimage. Damit kann man aus einem zImage (Linux-Kernel) ein uImage machen. Dieses uImage lässt sich von u-boot aus laden (Nand, NFS, TFtp, MMC) und ausführen.
mkimage -A arm -O linux -T kernel -C none -a 0x30008000 -e 0x30008000 -d path-to/zImage path-to/uImage
Jetzt haben wir ein von u-boot ausführbaren Linux-Kernel.
Multimedia
Abspielen von Audio-Daten
Mit dem FriendlyARM-Image gibt es zwei Möglichkeiten Audio-Dateien abzuspielen.
Die Eine ist, Qtopia zu starten und dort einfach die Datei von SD-Karte, USB, ... auszuwählen und er spielt sie ab.
Die andere ist, die Datei auf der Konsole mit madplay dateiname.mp3 abzuspielen.
Hier die Manpage zu madplay.
MPD - der Music Player Daemon
Mit einem eigenen Image oder wenn man sich alles von Hand compilieren will ist der Music Player Daemon ein genialer Musik-Player. Er lässt sich durch sehr sehr viele verschiedene Clients steuern und braucht dabei nahezu kaum Rechenleistung.
Der eigentliche Player läuft vollkommen "versteckt" im Hintergrund. Um ihn zu steuern wird vom Client eine Verbindung aufgebaut und die entsprechenden Befehle gesendet.
Die Clients müssen noch nicht einmal auf dem Board laufen, können auch auf jedem anderen Rechner im Netzwerk oder Internet sein. Man muss einfach nur die IP und den Port angeben.
Mit gerade mal etwa 100 Zeilen Code lässt sich eine einfach zu nutzende Client-Lib in C programmieren um dann seine eigenen Clients zu schreiben. Damit lässt sich der MPD durch so ziemlich jedes Ereignis an einem Rechner steuern. hier habe ich einen Artikel erstellt, der das wichtige dafür beschreibt.
Aufnehmen von Audio-Daten
Abspielen von Video-Daten
Ich selbst habe noch keinen Player, bis auf den vom originalen Image mit Qtopia gesehen/compiliert, welcher Filme einigermaßen flüssig abspielt.
Mein Ansatz momentan ist, den MPlayer mit fbdev, directfb und sdl mit Framebuffer Ausgabe direkt auf den Framebuffer schreiben zu lassen. Allerdings habe ich damit bisher nur sehr sehr magere Ergebnisse gehabt.
Kamera-Interface
Hardware
Nahezu alles ist über die verschiedenen Header auf dem Board zu erreichen
Pinouts
Pin | Audio IN | CON1, CON2, CON3 | CON8 | J1 (ext. NAND/NOR-sw) |
---|---|---|---|---|
1 | IN | TX | VIN | NORBOOT |
2 | GND | RX | GND | OM0 |
3 | IN | 5V | GND | NANDBOOT |
4 | GND | VDD5V |
Pin | CON5 | CON4 GPIO | CAMERA | CON12 | JTAG |
---|---|---|---|---|---|
1 | 5V | 5V | SDA | EINT8/GPG0 | 3.3V |
2 | GND | 3.3V | SCL | EINT11/GPG3 | 3.3V |
3 | EINT17 | GND | EINT20 | EINT13/GPG5 | /TRST |
4 | EINT18 | /RESET | CAMRST | EINT14/GPG6 | /RESET |
5 | EINT3 | AIN0 | CAM_CLOCK | EINT15/GPG7 | TDI |
6 | EINT9 | AIN1 | CAM_HREF | EINT19/GPG11 | TDO |
7 | /GCS1 | AIN2 | CAM_VSYNC | 3.3V | TMS |
8 | /GCS2 | AIN3 | CAM_PCLK | GND | GND |
9 | /GCS3 | EINT0 | CAMDATA7 | TCK | |
10 | /GCS5 | EINT1 | CAMDATA6 | GND | |
11 | L/OE | EINT2 | CAMDATA5 | ||
12 | L/WE | EINT3 | CAMDATA4 | ||
13 | /WAIT | EINT4 | CAMDATA3 | ||
14 | /RESET | EINT5 | CAMDATA2 | ||
15 | /XDACK0 | EINT6 | CAMDATA1 | ||
16 | /XDREQ0 | EINT8 | CAMDATA0 | ||
17 | LADDR0 | EINT9 | 3.3V | ||
18 | LADDR1 | EINT11 | VDDCAM | ||
19 | LADDR2 | EINT13 | 1.8V | ||
20 | LADDR3 | EINT14 | GND | ||
21 | LADDR4 | EINT15 | |||
22 | LADDR5 | EINT17 | |||
23 | LADDR6 | EINT18 | |||
24 | LADDR24 | EINT19 | |||
25 | LDATA0 | MISO | |||
26 | LDATA1 | MOSI | |||
27 | LDATA2 | CLOCK | |||
28 | LDATA3 | SS | |||
29 | LDATA4 | SCL | |||
30 | LDATA5 | SDA | |||
31 | LDATA6 | GPB0 | |||
32 | LDATA7 | GPB1 | |||
33 | LDATA8 | CLKOUT0 | |||
34 | LDATA9 | CLKOUT1 | |||
35 | LDATA10 | ||||
36 | LDATA11 | ||||
37 | LDATA12 | ||||
38 | LDATA13 | ||||
39 | LDATA14 | ||||
40 | LDATA15 |
Steckverbinder
Digi-Key:
Connector | Hersteller | Artikelnummer | Teilenummer |
---|---|---|---|
CON1,2,3,8 | JST Sales America Inc | PHR-4 | 455-1164-ND |
Audio IN, J1 | JST Sales America Inc | PHR-3 | 455-1126-ND |
JST Sales America Inc | SPH-002T-P0.5L | 455-2148-1-ND | |
CON4 | Sullins Connector Solutions | SFH213-PPPN-D17-ID-BK-M181 | S9061-ND |
Sullins Connector Solutions | SBH21-NBPN-D17-ST-BK | S9023-ND |
onboard Hardware
Sobald man mal anfangen möchte, sich mit der Hardware auseinanderzusetzen, sollte man natürlich auch wissen wie, was und wo dran.
... an welchem EINT war ... Low oder High aktiv? ...
UART
Die S3C2440 besitzt insgesamt drei unabhängige UARTs.
Die Spannungspegel der Rx und Tx Pins sind etwas verwirrend. Auf dem Stecker liegen 5V an, doch das Datenblatt sagt nicht, ob die Rx und Tx Pins auch 5V-Tolerant sind. Daher sollte man das ausschliessen. Ein Spannungsteiler aus 3k3 und 2k2 an der Rx-Leitung vom Prozessor sollte das Problem beheben, solange der Empfänger an der Tx-Leitung 3.3V noch als H-Signal versteht.
UART | 64 byte FIFO | IrDA 1.0 | RTS/CTS Leitungen | Pegel |
---|---|---|---|---|
uart0 | * | * | * | 3,3V / RS232 |
uart1 | * | * | * | 3,3V |
uart2 | * | * | x | 3,3V |
Über die Ansteuerung auf Hardwareebene braucht man sich, solange man ein Betriebssystem verwendet, keine Gedanken zu machen. Unter Linux sind die drei UARTs über ttySAC0, ttySAC1 und ttySAC2 zu erreichen.
LEDs
Wenn der Kerneltreiber für die LEDs mit eincompiliert wurde, ist es ein leichtes, die LEDs mit einer kleinen Hand voll C-Zeilen anzusprechen.
Man öffnet die Gerätedatei und mittels ioctl() schreibt man die Werte in die Gerätedatei.
Der Aufruf von ioctl sieht so aus:
ioctl(descriptor, AN/AUS, LED_NUMMER);
Also muss man um zum Beispiel LED0 anzuschalten für AN/AUS 1 angeben und für LED_NUMMER die 0. Dafür habe ich im Abschnitt C-Code zwei Beispiel-Funktionen geschrieben. Die erste erwartet AN/AUS und LED_NUMMER als Argument, die Zweite erwartet eine Bitmaske, was mir persönlich doch besser gefällt ;)
C-Code
int setleds(int on, int led_no) {
int led_fd = open("/dev/leds0", 0);
if (led_fd < 0) { led_fd = open("/dev/leds", 0); }
if (led_fd < 0) { perror("open device leds"); return(-1); }
ioctl(led_fd, on, led_no);
close(led_fd);
return 0;
}
int setledmask(int mask) {
int led_fd = open("/dev/leds0", 0);
if (led_fd < 0) { led_fd = open("/dev/leds", 0); }
if (led_fd < 0) { perror("open device leds"); return(-1); }
if(mask & 0x01) ioctl(led_fd, 1, 0); else ioctl(led_fd, 0, 0);
if(mask & 0x02) ioctl(led_fd, 1, 1); else ioctl(led_fd, 0, 1);
if(mask & 0x04) ioctl(led_fd, 1, 2); else ioctl(led_fd, 0, 2);
if(mask & 0x08) ioctl(led_fd, 1, 3); else ioctl(led_fd, 0, 3);
close(led_fd);
return 0;
}
LED-sysfs
Bei aktiviertem sys-fs Kerneltreiber für LEDs, kann man diese im Verzeichnis '/sys/class/leds/' finden und ansteuern.
cd /sys/class/leds/ # wechselt in den Ordner ls -lsa # listet alle Vorhandenen LEDs auf echo 1 > led4/brightness # LED AN echo 0 > led4/brightness # LED AUS # oder echo 1 > /sys/class/leds/led4/brightness # LED AN echo 0 > /sys/class/leds/led4/brightness # LED AUS
I2C
i2ctools
Die i2ctools sind Linux-Userspace Programme, welche dafür gedacht sind, auf einfache Weise auf der Konsole mit I2C Geräten zu kommunizieren.
24xx EEPROMs
Port-Expander
PCF8574 / PCF8574a
Einen PCF8574 anzusteuern ist sehr einfach. Mit den i2ctools kann man damit erste Tests machen um zu sehen, ob er funktioniert.
Der Port-Expander wird mit den 3.3V versorgt, welche das Board liefert, SDA und SCL natürlich ebenfalls anschliessen. Ich habe alle drei Adress-Pins auf L gelegt und erhalte von i2cdetect so die Adresse 0x38.
SPI
PWM
onboard buzzer
ADC
Touchscreen
Wenn der Kernel-Treiber installiert ist, kann der Touchscreen wie z. B. jede Maus über das event-Interface angesprochen werden. Normalerweise liegt der Touchscreen auf /dev/input/event0. Diese Gerätedatei muss man auch der tslib angeben.
LCD Backlight
Wenn der Kernel-Treiber installiert ist, kann die Hintergrundbeleuchtung des Displays einfach über die entsprechende Geräte-Datei ein- bzw. ausgeschaltet werden.
einschalten:
# echo 1 > /dev/backlight
ausschalten:
# echo 0 > /dev/backlight
onboard Poti
Hierzu muss wieder der Kerneltreiber mit eincompiliert sein, damit man das Gerät /dev/adc hat. Im FriendlyARM-Kernel ist der Treiber fest mit drin.
Die Funktion getpoti() liest in einer Schleife mehrmals den Potiwert ein und bildet einen Mittelwert, der zurückgegeben wird. Die Anzahl der Werte, aus denen der Mittelwert gebildet wird, wird mit dem #define LOOPs angegeben.
C-Code
#define LOOPS 12 // 12 Werte fuer Mittelwert
unsigned int getpoti() {
int value, i, fd = open("/dev/adc", 0);
unsigned int add = 0, ret;
if (fd < 0) { printf("cant open adc device!\n"); return -1; }
for(i = 0; i < LOOPS; i++) {
char buffer[30];
int len = read(fd, buffer, sizeof buffer -1);
if (len > 0) {
buffer[len] = '\0';
value = -1;
sscanf(buffer, "%d", &value);
add += value;
} else {
printf("cant read from adc device!\n");
close(fd);
return -1;
}
}
close(fd);
ret = add / LOOPS;
return ret;
}
GPIO
Da der S3C2440 über sage und schreibe 130 GPIOs verfügt, mit diversen Funktionen, wie I2C, PWM, ..., das setzen der "Drive Strength" (maximaler Strom an machen Pins), ... ist das Ganze nicht mehr so einfach wie bei einem AVR. Der Bastler sollte hier schon genau wissen was er will vorallem das Datenblatt zu Rate ziehen. Die Funktionen und Register des Chips sind ab Seite 274 (Kapitel IO-Ports) zu finden. Die elektrischen Daten sind ab Seite 554 zu finden.
C-Code für die onboard-Buttons
Hierfür wird wird der Kerneltreiber benötigt. Im FriendlyARM-Kernel ist dieser fest mit drin.
Wenn die Buttons eingelesen werden, erhält man für jeden nicht gerdrücken Buttons 0x30 und für jeden gedrückten Button 0x31. Die Funktion liest die Buttons ein und vergleicht den Wert, sobald 0x31 kommt, ist sie beendet. Die Buttons sind von 0 bis 5 nummeriert.
int wait4button(int button) {
char btn[6] = {'0', '0', '0', '0', '0', '0'};
int fd = open("/dev/buttons", 0);
if (fd < 0) { printf("cant open buttons device"); return(-1); }
while(btn[button] == 0x30) { // if a button is released, we read 0x30, if pressed 0x31
if (read(fd, btn, sizeof btn) != sizeof btn) {
printf("cant read buttons!");
close(fd);
return(-1);
}
}
close(fd);
return 0;
}
GPIO-sysfs
Bei aktiviertem sys-fs Kerneltreiber für GPIOs, kann man diese im Verzeichnis '/sys/class/gpio/' finden und ansteuern. Eine gute Erklärung ist dazu hier zu finden.
Real Time Clock
Für die Real Time Clock, welche fest im Prozessor integriert ist, gibt es einen Kerneltreiber.
Lesen/Setzen lässt sich sich sehr einfach mit dem Tool hwclock.
Beispielprogramme
Hier habe ich ein paar kleine Beispielprogramme im Quellcode hochgeladen.
- Media:led_adc.c Herzschlag auf den LEDs, Geschwindigkeit mit Poti einstellen. Compilieren mit arm-linux-gcc led_adc.c -o led_adc.
Andere Betriebssysteme
uCos2
Windows CE
FreeRTOS
emdedian
Software mit Betriebssystem
- Grafische Oberfläche mit der SDL-Bibliothek erstellen:
Mini2440 SDL GUI Programmierung
Software ohne Betriebssystem
Entwicklungsumgebung
Beispiele
Wo kaufen?
Zu beziehen ist das Board entweder über Ebay, meist aus China, oder bei Watterott.
Eigene Displays an das Mini anschließen
- Displays mit LVDS Anschluss können z.B. mit diesem selbst gebauten Adapter an das Mini angeschlossen werden. http://www.mikrocontroller.net/topic/217774#new