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 Ruecksprung
Ich 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 Ruecksprung
Jetzt 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 Ruecksprung
Vielleicht 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 Ruecksprung
Beim ersten Durchlauf wird also pause mit gesetztem r5 aufgerufen, danach ist r5 jeweils 0.