1.4 Ports Einlesen und Ausgeben
Schalter an PB7..PB0 einlesen und auf LEDs ausgeben
main:
ldr r4,=GPIOB // Schalter
ldr r5,=GPIOC // LEDs
schleife:
ldrb r6,[r4,IDR] // Lade alle Schalter
strb r6,[r5,ODR] // Gib auf LEDs aus
b schleife
Die Werte der Schalter PB7..PB0 sollen auf den LED7..LED0 ausgegeben werden.
Im Debug-Modus fällt auf, dass LED3 leuchtet also ist PB3 beim Debuggen 1.
Knight-Rider-Lauflicht mit Shift-Befehlen
K.I.T.T. Scanner: Ein Lauflicht, dass LED0 bis LED7 und dann wieder zurück bis LED0 leuchten lässt, also wirkt wie ein Scanner, soll erstellt werden. Links der PAP dazu.
Die Leuchtdauer ist in einer Konstanten vorgegeben:.equ DELAYZEIT // Leuchtdauer der LED in ms
Entwickeln Sie den Assember-Code dazu und testen Sie ihn. Tipps:
warte(DELAYZEIT) mit diesem Code übersetzen:mov r0,#DELAYZEIT
bl HAL_Delay // benutzt R2 und R3
Lösung
.equ DELAYZEIT,200 // Leuchtdauer in ms
main:
ldr r4,=GPIOB // Schalter
ldr r5,=GPIOC // LEDs
mov r6,#1. // Muster in R6
do:
strb r6,[r5,ODR] // Ausgeben LEDs
mov r0,#DELAYZEIT
bl HAL_Delay // benutzt R2 und R3
lsl r6,r6,#1 // 1 nach links schieben
cmp r6,#128 // schon LED7 ?
blt do // Sprung, wenn kleiner
do2:
strb r6,[r5,ODR] // Ausgeben LEDs
mov r0,#DELAYZEIT
bl HAL_Delay
lsr r6,r6,#1. // 1 nach rechts schieben
cmp r6,#1. // schon LED0 ?
bhi do2 // Sprung, wenn groesser
b do
Lauflicht mit ausschaltbaren LEDs
Einzelne LED sollen nun mit den Schaltern PB7..PB0 ausgeschaltet werden, d.h. das Ausgeben wird bei PBi=0 übersprungen.
Dazu werden am Anfang jedes Durchlaufs die Schalter in r7 eingelesen und vor der Ausgabe mit bitweisem Und verglichen ob die LED leuchten soll. Dazu dieser Code:tst r6,r7 // bitweises und setzt Z-Flag
beq dunkel // falls Schalter PBi=0 Ausgabe überspringen
Implementieren und testen Sie die Programmerweiterung.
Lösung
.equ DELAYZEIT,200 // Geschwindigkeit einstellen
main:
ldr r4,=GPIOB // Schalter
ldr r5,=GPIOC // LEDs
mov r6,#1 // Muster in R6
loop:
ldrb r7,[r4,IDR] // Lade Schalter PB7..PB0
do:
tst r6,r7 // r6&r7 soll LED leuchten?
beq dunkel // nein dann weiter
strb r6,[r5,ODR] // Ausgeben LEDs
mov r0,#DELAYZEIT
bl HAL_Delay // benutzt R2 und R3
dunkel:
lsl r6,r6,#1 // 1 nach links schieben
cmp r6,#128 // schon LED7 ?
blt do // Sprung, wenn kleiner
do2:
tst r6,r7 // r6&r7 soll LED leuchten?
beq dunkel2 // nein dann weiter
strb r6,[r5,ODR]. // Ausgeben LEDs
mov r0,#DELAYZEIT
bl HAL_Delay
dunkel2:
lsr r6,r6,#1 // 1 nach rechts schieben
cmp r6,#1 // schon LED0 ?
bhi do2 // Sprung, wenn groesser
b loop
Bonus: Poti steuert über ADC die Geschwindigkeit.
In der Formelsammlung nachlesen wie die Analoge Eingabe in der Hardwareeinstellung aktiviert wird
main:
ldr r4,=GPIOB // Schalter
ldr r5,=GPIOC // LEDs
ldr r0,=hadc // AD-Wandler starten
bl HAL_ADC_Start
loop:
mov r6,#1 // Muster in R6
ldrb r7,[r4,IDR] // Lade Schalter
do:
tst r6,r7 // r6&r7 soll LED leuchten?
beq dunkel // nein dann weiter
strb r6,[r5,ODR] // Ausgeben LEDs
ldr r0,=hadc // Wert von ad-Wandler holen
bl HAL_ADC_GetValue // r0 <- 12 Bit Wert 0..4095
lsr r0,r0,#3 // /8
bl HAL_Delay // benutzt R2 und R3
dunkel:
lsl r6,r6,#1 // 1 nach links schieben
cmp r6,#128 // schon LED7 ?
blt do // Sprung, wenn kleiner
do2:
tst r6,r7 // r6&r7 soll LED leuchten?
beq dunkel2 // nein dann weiter
strb r6,[r5,ODR] // Ausgeben LEDs
ldr r0,=hadc // Wert von ad-Wandler holen
bl HAL_ADC_GetValue // r0 <- 12 Bit Wert 0..4095
lsr r0,r0,#3 // /8
bl HAL_Delay
dunkel2:
lsr r6,r6,#1 // 1 nach rechts schieben
cmp r6,#1 // schon LED0 ?
bhi do2 // Sprung, wenn groesser
b loop
.end
Lauflichtdurchgang mit Taster an PA1 starten
Das Lauflicht soll ruhen und ein Durchgang (hin und her) erst starten wenn Taster an PA1 gedrückt wird.
Nach der Marke loop: wird auf PA1=1 gewartet und dann ein Durchlauf ausgelöst. Verwenden Sie ein weiteres Register und den tst-Befehl dazu. Erweitern und testen Sie das Programm.
Poti-AD-Wandler steuert LED-Band
Erstellen Sie ein Programm, bei dem das Poti mit dem AD-Wandler kontinuierlich ausgelesen wird und den Wert auf den LEDs ausgibt. Beachten Sie: Der AD-Wandler hat 12-Bit Auflösung, es gibt jedoch nur 8 LED. Lösen Sie diese Teilaufgaben:
- Ein Hauptprogramm initialisiert die HW und ruft kontinuierlich die Ausgabe UPs auf.
- Ein UP leuchtpunkt verwendet Werte 0..7 in R6 keine bzw. eine der LED entsprechend leuchten.
- Ein UP leuchtband verwendet Werte 0..7 in R6 für eine LED-Band-Anzeige.
- Der Anzeige-Modus ist umschaltbar mit Schalter an PB0.
Lösung
main:
ldr r4,=GPIOB // Schalter
ldr r5,=GPIOC // LEDs
ldr r0,=hadc. // AD-Wandler starten
bl HAL_ADC_Start
loop:
ldr r0,=hadc // Wert von ad-Wandler holen
bl HAL_ADC_GetValue // r0 <- 12 Bit Wert 0..4095
lsr r6,r0,#9 // /512
ldrb r0,[r4,IDR] // Schalter einlesen
tst r0,#1 // PB0 geschlossen?
bne band // ja, dann leuchtband
bl leuchtpunkt // sonst leuchtpunkt
b loop
band:
bl leuchtband
b loop
leuchtpunkt:
mov r0,#1 // eine 1
lsl r0,r0,r6 // r6 Stellen nach links verschieben
strb r0,[r5,ODR] // auf LED ausgeben
bx lr // return
leuchtband:
mov r0,#1
bandbau:
cmp r6,0
beq ausgabe
lsl r0,r0,#1
add r0,r0,#1
sub r6,r6,#1
b bandbau
ausgabe:
strb r0,[r5,ODR] // auf LED ausgeben
bx lr // return
.end
Leuchtband mit Tasten PA1 und PA6 steuern
Ein Leuchtband soll mit Tasten PA1 (up) und PA6 (down) gesteuert werden. Durch Betätigen von PA1 wird das Leuchtband grösser und bei PA6 kleiner.
Lösung
main:
ldr r4,=GPIOA // Taster
ldr r5,=GPIOC // LEDs
mov r6,#0b1111 // Leuchtband
b ausgabe
loop:
ldrb r0,[r4,IDR] // Taster einlesen
tst r0,#2 // PA1 betaetigt?
beq weiter
cmp r6,#0xff // alle LED schon an
bge ausgabe // nicht weiter
lsl r6,r6,#1 // Up
add r6,r6,#1
b ausgabe
weiter:
tst r0,#0b1000000 // PA6 betaetigt?
beq loop
lsr r6,r6,#1 // Down
b ausgabe
ausgabe:
strb r6,[r5,ODR]
mov r0,#10 // 10 ms Prellen abwarten
bl HAL_Delay
wartegedrueckt:
ldrb r0,[r4,IDR] // Taster einlesen
tst r0,#0b1000010 // PA1 oder PA6 betaetigt?
bne wartegedrueckt
mov r0,#10. // 10 ms Prellen abwarten
bl HAL_Delay
b loop
.end
Werteausgabe auf 7-Segmentanzeige
Der Taster an PA1 soll den Wert von r7 für einen Zähler von 0..9 erhöhen. Der Wert von r7 soll auf einer 7-Segmentanzeige an PORTC ausgegeben werden. Die Kathoden der LED-Segmente a..g sind auf einem gemeinsamen Anschluss zusammengeschaltet. Um die Anzeige leuchten zu lassen muss für die jeweilige Stelle ein Transistor Q4 (Einer-Stelle gesteuert durch PC11) bzw. Q3 (Zehner-Stelle gesteuert durch PC12) die gemeinsame Kathode auf GND schalten. PC0 – a bis PC6 – g sind für die Segmente zuständig. PC7 schaltet den Dezimalpunkt dp ein.
¡Die Pins PC11 und PC12 müssen in der IOC als Ausgänge geschaltet sein!
Füllen Sie die Tabelle mit den Ausgabe-Werten für die 7 Segmentanzeige aus.
Die Daten von Umcodierungs-Tabellen, z.B. BCD nach 7 Segment werden im Speicher typischerweise hinter dem Programm hinterlegt. Die Codes brauchen ein Byte, die Adresse der Tabelle seg7 in r6 + der Ziffernwert in r7 ergibt hier die Adresse des Ausgabecodes geladen in r0.
Im GPIOC.ODR muss noch Bit11 gesetzt werden, damit die Anzeigenstelle leuchtet.
ldr r6,=seg7 // r6:=Adresse von Tabelle seg7
ldr r0,[r6,r7] // r0:=seg7[r7]
strb r0,[r5,ODR] // r0 auf die 7seg-Anz.
mov r0,0x800 // Bit11 in
str r0,[r5,BSR] // GPIOC.ODR setzen
....
seg7: //Codetabelle der Siebensegmentanzeige
.byte 0x3f,.. // Codewerte
BCD-Wert | Bin | Hex |
---|---|---|
0 | 0b00111111 | 0x3f |
1 | ||
2 | ||
3 | ||
4 | ||
5 | ||
6 | ||
7 | ||
8 | ||
9 |
Lösungsvorschlag
// 7 Segmentanzeige PC11 Einer, PC12 Zehner in IOC als Ausgang einschalten
main:
ldr r4,=GPIOA // r4 verweist auf GPIOA
ldr r5,=GPIOC. // r5 verweist auf GPIOC
ldr r6,=seg7 // r6 verweist auf die Tabelle seg7
mov r7,#0 // Einer
loop: // main-loop
ldr r0,[r4,IDR] // GPIOA.IDR einlesen
tst r0,#0b10 // PA1 testen
bne gedrueckt // nicht 0 dann gedrueckt
ldr r0,[r6,r7] // r0:=seg7[r7]
strb r0,[r5,ODR] // r0 auf die Siebensegmentanzeige ausgeben
mov r0,#0x800 // Bit11
str r0,[r5,BSR] // Bit11 von GPIOC.ODR setzen
b loop // von vorne
gedrueckt: // wenn PA10-Taste gedrueckt
mov r0,#10 // 10ms entprellen
bl HAL_Delay
add r7,#1 // um 1 hochzaehlen
nochgedrueckt: // warten solange Taste-PA10 noch gedrueckt
ldr r0,[r4,IDR] // GPIOA.IDR einlesen
tst r0,#0b10 // PA1 testen
bne nochgedrueckt//bei noch gedrueckt erneut Taste abfragen
mov r0,#10 // 10ms entprellen
bl HAL_Delay
b loop //von vorne
seg7: //Codetabelle der Siebensegmentanzeige
.byte 0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f
.end
Bonus: Zweistellige Ausgabe, Zeitmultiplexverfahren
Erweitern Sie das Programm für die Ausgabe zweier Stellen durch Zeitmultiplexen. Der Trick viele Stellen mit wenigen Leitungen ausgeben zu können beruht auf der Trägheit unserer Augen: Wenn die Stellen schnell genug kurz nacheinander aufleuchten, sieht es aus als ob sie ständig leuchten. 5ms die Einer, dann 5ms die Zehner leuchten lassen benötigt 10ms für die ganze Anzeige also 100 mal pro Sekunde, 100Hz, es flackert nicht mehr. Leitungen werden gespart: Statt 2*8 für 7 Segmente und Dezimalpunkt nur noch 8+2 für die Segmente und die jeweilige Stelle.
Erweitern Sie das Programm, hier eine mögliche „Roadmap“:
- Ein weiteres Register R8 zählt die Zehner.
- In der main-loop wird nach erfolgloser Tastenabfrage PA1 das Einerregister ausgegeben, 5ms ein- und dann ausschalten.
- Dann in gleicher Weise das Zehnerregister ausgeben.
- Wenn Tastendruck bei PA1 die Einer erhöhen und still halten bis PA1 los gelassen wird.
- Falls die Einer den Wert 10 haben, die Zehner um 1 erhöhen und die Einer auf 0 setzen.
- Wieder nach main-loop springen.
Lösungsvorschlag
// 7 Segmentanzeige PC11 Einer, PC12 Zehner in IOC als Ausgang einschalten
main:
ldr r4,=GPIOA // r4 verweist auf GPIOA
ldr r5,=GPIOC // r5 verweist auf GPIOC
ldr r6,=seg7 // r6 verweist auf die Tabelle seg7
mov r7,#0 // Einer
mov r8,#0 // Zehner
loop: // main-loop
ldr r0,[r4,IDR] // GPIOA.IDR einlesen
tst r0,#0b10 // PA1 testen
bne gedrueckt // nicht 0 dann gedrueckt
// Einer ausgeben
ldr r0,[r6,r7] // r0:=seg7[r7]
strb r0,[r5,ODR] // r0 auf die Siebensegmentanzeige ausgeben
mov r0,Bit11 // Einer einschalten
str r0,[r5,BSR] // Bit11 von GPIOC.ODR
mov r0,#5
bl HAL_Delay
mov r0,Bit11 // Einer ausschalten
str r0,[r5,BRR] // Bit11 von GPIOC.ODR
// Zehner ausgeben
ldr r0,[r6,r8] // r0=seg7[r8]
strb r0,[r5,ODR] // r0 auf die Siebensegmentanzeige ausgeben
mov r0,Bit12 // Zehner einschalten
str r0,[r5,BSR] // Bit12 von GPIOC.ODR
mov r0,#5
bl HAL_Delay
mov r0,Bit12 // Zehner ausschalten
str r0,[r5,BRR] // Bit12 von GPIOC.ODR
b loop // von vorne
gedrueckt: // wenn PA10-Taste gedrueckt
mov r0,#10 // 10ms entprellen
bl HAL_Delay
add r7,#1 // um 1 hochzaehlen
nochgedrueckt: // warten solange Taste-PA10 noch gedrueckt
ldr r0,[r4,IDR] // GPIOA.IDR einlesen
tst r0,#0b10 // PA1 testen
bne nochgedrueckt//bei noch gedrueckt erneut Taste abfragen
mov r0,#10
bl HAL_Delay
cmp r7,#10 // r7==10?
bne loop // nein, dann von vorne
mov r7,#0 // ja, dann r7 wieder bei 0 beginnen
add r8,#1
b loop // von vorne
seg7: //Codetabelle der Siebensegmentanzeige
.byte 0b00111111, 0b00000110, 0b01011011, 0b01001111, 0b01100110, 0b01101101, 0b01111101, 0b00000111, 0b01111111, 0b01101111
//.byte 0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f
.end
Werteausgabe auf LCD-Anzeige
main:
bl startLCD
bl LCD_i2c_clear
mov R0,0
bl LCD_i2c_cursorpos
mov R0,#1234
bl LCD_i2c_dezaus
mov R0,0x40
bl LCD_i2c_cursorpos
ldr R0,=Hallo
bl LCD_i2c_textaus
schleife:
b schleife
Hallo:
.asciz "Hallo Welt"
.end
-Texte als Array abspeichern und ausgeben
-AD-Werte ausgeben
.global _start
_start:
ldr R1,=0xff200000
ldr R2,=0xff200040
schleife:
ldr R0,[R2]
tst R0,#0x1
bne nichtGedruecktPC13
ldr R0,=1
str R0,[R1]
b schleife
nichtGedruecktPC13:
mov R0,#0
str R0,[R1]
b schleife
b _start
.end