sudo diskutil unmount /dev/disk2s1Dann mit folgendem Befehl das Diskimage gespeichert:
sudo dd if=./2012-12-16-wheezy-raspian of=/dev/disk2 bs=1mVorsicht dass wirklich die richtige Disk angegeben wird!
Meine selbst geschriebenen Utilities werden gleich wie unter Linux installiert.
Also z.B. das undump2.tar.gz wird so installiert:
mkdir ~/bin ;falls nicht schon gemacht, dann Terminal neu starten tar zxvf undump2.tar.gz cd undump2/ make clean make make install
Zum Installieren wieder wie üblich die folgenden Schritte machen:
tar zxvf armdisassembler.tar.gz cd armdisassembler/ make clean make make installDann das Programm z.B. zum Überprüfen von blinken2.s anwenden:
cd ~/tutorial/ undump blinken2.s kernel.img da kernel.img >test.sJetzt sollten Sie test.s mit blinken2.s vergleichen.
0 CS (Control-Status) 4 Zähler (untere 32 Bits) 8 Zähler (obere 32 Bits) C Compare 0 10 Compare 1 14 Compare 2 18 Compare 3Da der Zähler bei 1MHz über eine Stunde braucht um die unteren 32 Bits durchzuzählen, reicht es völlig aus dass die Vergleichsregister auf 32 Bit beschränkt sind.
Jetzt können wir unser Pause-Unterprogramm etwas eleganter programmieren:
Timeradr: 0x20003000 .word 0x20003000 @ Timer-Basis-Adresse als konstanter Zahlenwert pause: @ Aufrufparameter: r0: soviele Microsekunden warten 0xE92D4130 push {r4,r5,r8,lr} @ Register retten 0xE51F8010 ldr r8, Timeradr @ Timerbasisadresse laden 0xE5984004 ldr r4, [r8,#4] @ Zaehler des Timers einlesen, nur untere 32-Bit 0xE0844000 add r4, r0 @ Endzeitpunkt untere 32 Bits berechnen 0xE588400C str r4, [r8,#12] @ speichern im Vergleichsregister "Compare 0" 0xE3A05001 mov r5, #1 @ Zum das Bit rueckzusetzen wirklich eine 1 schreiben! 0xE5885000 str r5, [r8,#0] @ Bit M0 im CS ruecksetzen L3: 0xE5985000 ldr r5, [r8,#0] @ neuer Status abfragen 0xE2155001 tst r5, #1 @ ist M0 gesetzt? 0x0AFFFFFC beq L3 @ nein-> warten bis gesetzt 0xE8BD8130 pop {r4,r5,r8,pc} @ gerettete Register zurueckholen, und RuecksprungIch habe mal einen kleinen Fehler eingebaut. Mit obigem Disassembler sollten Sie den leicht finden.
Wenn Sie sich über den Befehl "ldr r8, Timeradr" wundern, das ist lediglich eine abgekürzte Schreibweise für "ldr r8, [pc,#Timeradr]".
Ein weiterer Befehl, den wir bisher noch nicht verwendet haben, ist noch "tst".
Dieser Befehl gehört zur Mov-Gruppe und wird hier verwendet um den Zustand eines einzelnen Bits
zu testen. Wenn das Bit 0 ist wird das Zero-Flag gesetzt, sonst nicht. Genau genommen ist es "tsts", aber da
der einzige Sinn dieses Befehls ist, die Flags zu setzen, lässt man das 's' weg und setzt das S-Flag trotzdem.
Gleiches gilt auch für die Befehle teq cmp cmn. Auch hier setzt man das S-Flag immer.
Genau dieses Beispielprogramm wollen wir jetzt unter Verwendung aller Vergleichsregister entwerfen.
Dazu sollten Sie (falls nicht schon gemacht) mindestens 4 LEDs am GPIO anschliessen.
Zum Beispiel gemäss diesem Schema: ../index.html#gpio
Wir machen also einen ersten Entwurf unseres Programms:
@ blinken4.s lasse 4 LEDs unabhaengig voneinander blinken 0xE3A0D902 mov sp, #0x8000 @ Stack Pointer setzen bl gpio_initialisieren ldr r0, zeit1 ldr r1, zeit2 ldr r2, zeit3 ldr r3, zeit4 mov r6, 0xF @ 4 unterste Bits im r6 sollen den Status der LEDs representieren L1: bl leds_gemaess_r6_schalten bl pause b L1 zeit1: .word 500000 @ 0.5 Sekunden, also 1 Hz Blinkfrequenz zeit2: .word 600000 @ 0.6 Sec, also etwas langsamer blinken, etwa 0.833 Hz zeit3: .word 700000 @ 0.7 Sec, etwa 0.714 Hz zeit4: .word 800000 @ 0.8 Sec, genau 0.625 Hz pause: @ Verschieden lange Zeiten warten 0xE92D403F push {r0-r5,lr} @ Register retten ldr r8, Timeradr @ Timerbasisadresse laden L3: 0xE5985000 ldr r5, [r8,#0] @ neuer Status abfragen ands r5, #0xF @ ist mindestens eins der Bits M0,M1,M2,M3 gesetzt? 0x0AFFFFFC beq L3 @ nein-> warten bis gesetzt 0xE5984004 ldr r4, [r8,#4] @ Zaehler des Timers einlesen, nur untere 32-Bit 0xE0800004 add r0, r4 @ 1. Endzeitpunkt untere 32 Bits berechnen 0xE588000C str r0, [r8,#0xC] @ speichern im Vergleichsregister "Compare 0" add r1, r4 @ 2. Endzeitpunkt berechnen str r1, [r8,#0x10] @ speichern im Vergleichsregister "Compare 1" add r2, r4 @ 3. Endzeitpunkt berechnen str r2, [r8,#0x14] @ speichern im Vergleichsregister "Compare 2" add r3, r4 @ 4. Endzeitpunkt berechnen str r3, [r8,#0x18] @ speichern im Vergleichsregister "Compare 3" 0xE3A0500F mov r5, #0xF @ Alle 4 Bits im CS rueckzusetzen 0xE5885000 str r5, [r8,#0] @ Bit M0-M3 alle ruecksetzen eor r6, r5 @ r6 aktualisieren 0xE8BD803F pop {r0-r5,pc} @ gerettete Register zurueckholen, und RuecksprungJetzt fehlen aber noch einige Teile. Ausser den noch fehlenden Unterprogrammen, müssen wir in "pause" noch jeweils nur die Endzeitpunkte neu setzen, welche auch abgelaufen sind. Ausserdem müssen die Endzeitpunkte vor Aufruf von "pause" noch ein erstes mal gesetzt werden.
Um wirklich nur die abgelaufenen Vergleichsregister neu zu setzen können wir sowas machen:
tst r5, #1 @ Bit M0 gesetzt? addne r0, r4 @ ja: neue Zeit berechnen strne r0, [r8,#0xC] @ und im Compare 0 speichernEntsprechendes machen wir dann auch für die andern Bits, also "tst r5, #2" fuer M1, usw.
Um das Unterprogramm leds_gemaess_r6_schalten zu schreiben wäre ein Unterprogramm hilfreich,
wo wir die GPIO-Nummer direkt als Zahl angeben können, und ein Register das sagt ob der
Ausgang ein- oder aus-geschaltet werden soll.
Hier ein Vorschlag für dieses Unterprogramm:
gpio_ein_aus: @ Parameter: r0=Nummer, r1.bit0=ein/aus (0=aus, 1=ein), r7=GPIO-Basisadresse 0xE92D4004 push {r2,lr} @ Register retten 0xE3A02001 mov r2, #1 0xE1A02012 lsl r2, r0 @ r2 = (1<<r0) 0xE3111001 tst r1, #1 @ ist erstes Bit in r1 gleich Null? 0x05872028 streq r2, [r7,#40] @ ja: Spannung aus 0x1587201C strne r2, [r7,#28] @ nein: Spannung ein 0xE8BD8004 pop {r2,pc} @ gerettete Register zurueckholen, und RuecksprungVielleicht sollten wir die Initialisierung des GPIO auch gleich als Unterprogramm schreiben. Wir werden das wohl noch öfter brauchen.
gpio_initialisieren: @ Rueckgabeparameter: r7=GPIO-Basisadresse 0xE92D4001 push {r0,lr} @ Register retten 0xE3A07202 mov r7, #0x20000000 0xE2877602 add r7, #0x200000 @ GPIO-Adresse im r7 @ alle an LEDs angeschlossenen GPIO-Pins als Ausgang setzen: 0xE3A00302 mov r0, #(1<<9*3) @ GPIO09 blaue LED 0xE5870000 str r0, [r7,#0] 0xE3A00701 mov r0, #(1<<6*3) @ GPIO16 gruen OK-LED 0xE2800602 add r0, #(1<<7*3) @ GPIO17 rote LED 0xE2800001 add r0, #1 @ GPIO10 gruene LED 0xE5870004 str r0, [r7,#4] 0xE3A00602 mov r0, #(1<<7*3) @ GPIO27 gelbe LED 0xE2800D01 add r0, #(1<<2*3) @ GPIO22 orange LED 0xE5870008 str r0, [r7,#8] 0xE8BD8001 pop {r0,pc} @ gerettete Register zurueckholen, und Ruecksprung
Um die Timer-Vergleichsregister zum ersten mal setzen, hatte ich wie oben angedeutet mal ein weiteres Unterprogramm "timer_setzen" entworfen. Aber da dieses wieder fast das gleiche macht wie "pause" habe ich eine elegantere Lösung gefunden:
mov r5, #0xF @ r5 setzen fuer ersten Durchlauf von pause L1: bl leds_gemaess_r6_schalten bl pause b L1 pause: @ Verschieden lange Zeiten warten 0xE92D411F push {r0-r4,r8,lr} @ Register retten ldr r8, Timeradr @ Timerbasisadresse laden tst r5, #0xF @ ist r5 schon gesetzt? bne L3 @ ja-> Warteschlaufe ueberspringen L2: 0xE5985000 ldr r5, [r8,#0] @ neuer Status abfragen ands r5, #0xF @ ist mindestens eins der Bits M0,M1,M2,M3 gesetzt? 0x0AFFFFFC beq L2 @ nein-> warten bis gesetzt L3: eor r6, r5 @ r6 aktualisieren mov r5, #0 @ r5 loeschen fuer naechsten Durchlauf 0xE8BD811F pop {r0-r4,r8,pc} @ gerettete Register zurueckholen, und RuecksprungBeim ersten Durchlauf wird also pause mit gesetztem r5 aufgerufen, danach ist r5 jeweils 0.