Guten Morgen,
Bin seid heute neu hier und hoffe ich hab den richtigen Forumsteil
erwischt =).
Ich beschäftige mich erst ein paar tage mit Microcontrollern und habe
mir zu Lernzwecken das "my AVR Lehrbuch" mit dazugehörigen blauen koffer
gekauft.
Vorkenntnisse hab ich in assembler leider nicht, programmiere sonst nur
SPS und das meistens im kop.
Ich beschäftige mich nun seid 2 abenden mit dem selben Programmteil. Es
geht darum mit 2 tastern 2 leds zum leuchten zu bringen.
(taster 1-> led1 , taster 2 ->led2).
Wäre super nett wenn sich das mal einer von euch anschauen könnte, das
ich Verständnisprobleme mit der zuweisung der pinbelegungen hab.
main: ldi r16,hi8(RAMEND)
out SPH,r16
ldi r16,lo8(RAMEND)
out SPL,r16
cbi DDRB , 0
sbi PORTB , 0
sbi DDRD ,1
ldi r18,hi8(RAMEND)
out SPH,r18
ldi r18,lo8(RAMEND)
out SPL,r18
cbi DDRB ,0
sbi PORTB ,0
sbi DDRB , 1
;-----------------------------------------------------------------------
-------
mainloop: wdr
ldi r16,0x01 ; 1 okay
in r17 ,PINB
sbrs r17 , 0
ldi r16 , 0x03; 3?? wieso muss(!) hier eine 3 stehen?
out PORTB , r16
ldi r16 ,0x01
ldi r18,0b00000100
in r19 ,PINB
sbrs r19 , 0
ldi r18,0b00001100
out PORTB , r18
ldi r18 ,0b00000100
rjmp mainloop
;-----------------------------------------------------------------------
-----
Meine Frage, was ist eigentlich r16? kann ich das als interne Adresse
betrachten oder ist es eine feste zuweisung? Wie kann man weitere
logische ausgange festlegen und benutzen?
Hab schon diverse Veränderungen an den zuweisungen (zb 0b00000100)
vorgenommen, aber keine hat mich leider zum erfolg geführt.
Es wäre sehr nett wenn mir da vielleicht jemand kurz helfen würde!
Danke schonmal im vorraus!
Patrick Berninghaus schrieb:> main: ldi r16,hi8(RAMEND)> out SPH,r16> ldi r16,lo8(RAMEND)> out SPL,r16> cbi DDRB , 0> sbi PORTB , 0> sbi DDRD ,1> ldi r18,hi8(RAMEND)> out SPH,r18> ldi r18,lo8(RAMEND)> out SPL,r18> cbi DDRB ,0> sbi PORTB ,0> sbi DDRB , 1
Warum doppelt?
Der zweite Abschnitt ist komplett identisch mit dem ersten.
> ;----------------------------------------------------------------------- -------> mainloop: wdr> ldi r16,0x01 ; 1 okay> in r17 ,PINB> sbrs r17 , 0> ldi r16 , 0x03; 3?? wieso muss(!) hier eine 3 stehen?
Weil das Bitmuster für 0x03 dieses hier ist
0000 0011
und höchst wahrscheinlich hängt an deinem Port an den beiden Pins
irgendwas dran (zb 2 LED) die auf diese 1 Bits reagieren.
> Meine Frage, was ist eigentlich r16?
Ein sog. 'Register'
> kann ich das als interne Adresse> betrachten oder ist es eine feste zuweisung?
Das kannst du dir wie eine Speicherzelle vorstellen, die in die CPU
eingebaut ist und die von diversen Befehlen benutzt werden kann.
So ähnlich, wie auch die billigen Taschenrechner immer so eine 'M' Taste
haben. Man kann dann auf dieses M-Register etwas addieren, subtrahieren
und wieder ausgeben lassen.
Nur dass dein AVR
mehr von diesen Registern hat
viel mehr Operationen damit machen kann.
Also ein Register ist einfach nur eine Speicherzelle im innersten Kern
deines µC, der zum Speichern von Werten benutzt werden kann und wo es
Operationen gibt, die mit diesen Registern etwas tun können. Auch wenn
es nicht ganz korrekt ist, kann man aber doch sagen: Ein Wert muss aus
dem normalen Speicher zunächst einmal in ein Register geladen werden,
damit man ihn manipulieren kann.
> Es wäre sehr nett wenn mir da vielleicht jemand kurz helfen würde!
Hast du dir das
AVR-Tutorial
schon angesehen?
Das sollte eigentlich eine ganz gute Ergänzung zu deinen Lehrunterlagen
sein.
erstmal danke für die schnelle Antwort!
Ja das Tutorial hab ich mir angesehn werd da aber leider nicht wirklich
schlau draus, da so gut wie alles für mich da neu ist :P der Vorsprung
den ich mir durch die SPS programmierung versprochen hab ist doch
seeeehr minimal.
Aber das mit dem register hab ich jetzt glaub ich verstanden.
Aber ich muss doch trotzdem wenn ich von Port B den Pin 2 als eingang
belegen will sagen, dass er einen pull down haben muss und das er ein
eingang ist... warum ist das dann oben doppelt? wie wäre es denn
richtig?
Jetzt fängst du am besten nochmal ganz von vorn an.
Die Stackpointer-Initialisierung brauchst du überhaupt nicht.
Den Watchdog genauso wenig.
Du hast 32 Register (kleine Speicher für 8bit).
ldi r16, 0b00000011 ; Die Zahl 3 ins Register r16
out DDRB, r16 ; Hier schaltest du von Port B die untersten beiden Bits
auf Ausgang
out PORTB, r16 ; Jetzt schaltest du die zwei Ausgänge ein.
Verstanden?
Etwas Assembler zu lernen ist sicherlich der beste Weg für die Zukunft,
da man internes Verständnis erlangt und später beim debuggen sich mal
das zugehörige Assemblerlisting anschauen kann (was manchmal wirklich
sehr hilfreich ist). Auf weitere Sicht solltest Du aber unbedingt mit C
als Hochsprache "anbändeln", das ist bei etwas größeren Programmen der
reine Segen! Ich habe damals hiermit angefangen
http://www.avr-asm-tutorial.net/avr_de/index.html , war super und
entsprach voll meiner damaligen Vorstellung => Kontrolle über jeden
einzelnen Takt, was passiert wie wo etc.. Als dann aber irgendwann das
Thema "Rechnen mit Assembler" drankam, habe ich mit Assembler aufgehört.
Aber das Basiswissen ist Gold wert!
Mit C geht es später dann hier los:
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial
Ist natürlich meine ganz persönliche Meinung.
spess53 schrieb:>>Die Stackpointer-Initialisierung brauchst du überhaupt nicht.>> Über diese Brücke würde ich nicht gehen.>> MfG Spess
Bei seinem Programm braucht er den Stack wohl nicht. Wichtig ist aber zu
wissen, was es alles mit dem Stack so auf sich hat und was mann alles
berücksichtigen muß. Und das man ihn beim überübernächsten Programm dann
auch zwingend benötigen wird. Als guten Programmierstil kann man es
daher aber schon ansehen, ihn immer zu initialisieren. Und sei es,
weil ein Programm irgendwann erweitert wird ...
spess53 schrieb:> Hi>>>Die Stackpointer-Initialisierung brauchst du überhaupt nicht.>> Über diese Brücke würde ich nicht gehen.
Für den Anfang schon.
Das sind Initialisierungen die er zur Zeit nicht braucht, die ihn aber
verwirren und von dem was er tun will ablenken.
Patrick Berninghaus schrieb:> schlau draus, da so gut wie alles für mich da neu ist :P der Vorsprung> den ich mir durch die SPS programmierung versprochen hab ist doch> seeeehr minimal.
Das vergisst du am besten alles gleich wieder.
> Aber das mit dem register hab ich jetzt glaub ich verstanden.> Aber ich muss doch trotzdem wenn ich von Port B den Pin 2 als eingang> belegen will sagen, dass er einen pull down haben muss
Nur dann wenn du einen brauchst.
Brauchst du einen?
> und das er ein> eingang ist...
ja klar
> warum ist das dann oben doppelt?
vergleich doch mal die beiden Sequenzen. Bis auf die letzte anweisung
ist alles identisch. Man könnte sagen, dein Programm hat hier gestottert
OK.
> Ich beschäftige mich nun seid 2 abenden mit dem selben Programmteil.> Es geht darum mit 2 tastern 2 leds zum leuchten zu bringen.> (taster 1-> led1 , taster 2 ->led2).
Gut.
Dann klären wir doch erst mal die Rahmenbedingungen.
Deine LED hängen wo?
Geraten: Am Port B
Dort sind die an welchen Anschlüssen?
An den Anschlüssen für Bit 0 und Bit 1
gut.
Deine Taster hängen wo?
Geraten: Am Port D
Dort sind sie an welchen Anschlüssen?
An den Anschlüssen für Bit 0 und Bit 1
Gut.
Nachdem diese Dinge grundsätzlich geklärt sind: Das bedeutet jetzt was
für deine physikalischen Ports:
Am Port B müssen die Anschlüsse 0 und 1 auf Ausgang geschaltet werden
Am Port D müssen die Auschlüsse 0 und 1 auf Eingang geschaltet werden.
Das wiederrum bedeutet:
Im DDR Register für Port B müssen die Bits 0 und 1 auf 1 gesetzt werden
IM DDR Register für Port D müssen die Bits 0 und 1 auf 0 gesetzt werden
Weiters. WEnn deine Tasten so wie im Tutorial unterer Abschnitt
angeschlossen sind, dann schalten sie nach Masse und haben keinen
eigenen PullUp-Widerstand. Wir brauchen also den internen Pullup
Widerstand an beiden Pins.
Pullup Widerstand an einem Eingang bedeutet, dass du im Port Register
die beiden Bits auf 1 stellen musst. Dann sind die Pullup eingeschaltet.
Um das System also erstmal in seinen Ausgangs-Konfigurationszustand zu
bringen, genügt es also
1
LDI R16, 0b00000011
2
OUT DDRB, R16
3
4
LDI R16, 0b00000000
5
OUT DDRD, R16
6
LDI R16, 0b00000011
7
OUT PORTD, R16
zu machen. Das konfiguriert den Port B und den Port D so wie das oben
festgestellt wurde. Das ich da jetzt R16 genommen habe, hat keine
spezielle Bedeutung. Ich hätte auch jedes andere Register nehmen können.
Der springende Punkt ist: man kann nicht direkt mit einem OUT Befehl an
das DDRDB oder an das PORTD etwas zuweisen. Man muss immer den Umweg
über eines der R-Register gehen.
Übrigens ist das nicht die einzige Möglichkeit um das DDRB bzw DDRD und
PORTD Register ín den gewünschten Zustand zu bringen. Wie so oft gibt es
viele Möglichkeiten. Beim gezeigten ist es zb so, dass ich durch das OUT
alle 8 Bit des DDRD Registers beeinflusse. Das möchte ich in diesem Fall
sogar haben, denn ich will gerne alles kontrollieren. Nicht das mir ein
nicht beachtetes Bit da irgendwelchen Unfug treibt.
Aber so ...
1
SBI DDRB, 0
2
SBI DDRB, 1
3
4
CBI DDRD, 0
5
CBI DDRD, 1
6
SBI PORTD, 0
7
SBI PORTD, 1
... wärs natürlich auch gegangen. Wichtig ist ja nur, dass die beiden
Bits 0 und 1 im DDR bzw. PORT Register danach so stehen wie das weiter
oben ausgeknobelt wurde.
Und das tun sie ja in beiden Fällen
<Fortsetzung folgt>
puh erstmal danke für die geduld!
Ich hatte zwar ursprünglich alles auf port B, aber da ich ein steck
board habe ist es egal. Ich habe die Eingangs / Ausgangszuweisung nun
einmal übernommen und versuche die Ausgänge anzusteuern.
Fortsetzung folgt =)
Nachdem jetzt geklärt ist, mit welchen Anweisungen zunächst mal die
Umgebung des Programms eingestellt wird, gehts zur Logik
Dazu ist es vorteilhaft, sich erst mal in eigenen Worten einen etwas
detaqilierteren Überblick über die Aufgabe zu verschaffen. Was war das
noch mal?
> Es geht darum mit 2 tastern 2 leds zum leuchten zu bringen.> (taster 1-> led1 , taster 2 ->led2).
Etwas ausführlicher gesagt:
Das Programm muss in einer Schleife ständig seine Arbeit verrichten.
Praktisch alle Programme auf einem µC arbeiten so: Innerhalb einer
Schleife stexckt die komplette Logik.
Die Logik sieht jetzt wie aus?
Na ganz einfach
1
mache
2
3
wenn Taste0 gedrückt ist
4
dann Led 0 einschalten
5
andernfalls
6
Led 0 ausschalten
7
8
9
wenn Taste1 gedrückt ist
10
dann Led 1 einschalten
11
andernfalls
12
Led 1 ausschalten
13
14
für immer
das ist der grundsätzliche Plan, den wir umsetzen wollen
dazu überlegen wir uns gleich mal ein paar Dinge:
Wie fragt man ab, ob eine Taste gedrückt ist. Im Tutorial (und
hoffentlich auch bei dir) ist es so, das das entsprechende Bit im
zugehörigen PIN Register (in diesem Fall PIND, weil die Tasten ja am
Port D hängen) auf 0 geht, wenn die Taste gedrückt wird. Dafür gibt es
aber verschiedene Befehle.
SBIC ist so einer. In Langform bedeutet er _S_kip _I_f _B_it _I_s
_C_leared
Also: Überspringe den nächsten Befehl, wenn das angegebene Bit im
Register gelöscht (also 0) ist.
D.h. der Plan wird etwas verfeinert. Nur für 1 Taste und 1 Led
1
Hauptschleife:
2
3
Skip if Bit 0 in PIND is cleared
4
gehe zu: Led 0 einschalten
5
6
Led 0 ausschalten
7
gehe zu: weiter
8
9
Led 0 einschalten
10
11
weiter:
12
13
....
14
15
gehe zu Hauptschleife
Mach dir klar, dass das immer noch dieselbe Logik ist wie vorhin, nur
diesmal schon etwas näher an dem, was wir mit unseren Befehlen
realisieren können. Dadruch das der Skip nur 1 Befehl überspringt, wenn
die Bedingung zutrifft, ist es notwendig da mit Sprungbefehlen die
Abarbeitungsreihenfolge zu verändern.
Noch näher am tatsächlichen Programm
1
HAUPT:
2
3
SBIC PIND, 0 ; Skip if Bit 0 in PIND is cleared
4
RJMP LED0_ON ; gehe zu: Led 0 einschalten
5
SBI PORTB, 0 ; Led 0 ausschalten
6
RJMP WEITER0 ; gehe zu: weiter
7
LED0_ON: CBI PORTB, 0 ; Led 0 einschalten
8
WEITER0:
9
10
...
Das ist genau dasselbe. Aber diesmal in den Anweisungen geschrieben, die
auch der Assembler versteht. Um die LED ein oder aus zu schalten, setzte
oder lösche ich am Port B das entsprechende Bit mittels SBI bzw. CBI so
wie ich das brauche. Der entscheidende Punkt ist aber immer noch das
SBIC. Mit ihm wird entweder der Sprung zu LED0_ON übersprungen oder er
wird nicht übersprungen. Je nachdem gehts einmal mit dem SBI und das
andere mal mit dem CBI weiter. Aber egal wie es weitergeht, beim Label
WEITER0 treffen sich die Aszuführuungspfade wieder. Dort beginnt dann
auch die Behandlung der anderen Led und das anderen Tasters
1
HAUPT:
2
3
SBIC PIND, 0 ; Skip if Bit 0 in PIND is cleared
4
RJMP LED0_ON ; gehe zu: Led 0 einschalten
5
SBI PORTB, 0 ; Led 0 ausschalten
6
RJMP WEITER0 ; gehe zu: weiter
7
LED0_ON: CBI PORTB, 0 ; Led 0 einschalten
8
WEITER0:
9
10
SBIC PIND, 1 ; Skip if Bit 1 in PIND is cleared
11
RJMP LED1_ON ; gehe zu: Led 1 einschalten
12
SBI PORTB, 1 ; Led 1 ausschalten
13
RJMP WEITER1 ; gehe zu: weiter
14
LED1_ON: CBI PORTB, 1 ; Led 1 einschalten
15
WEITER1:
16
17
RJMP HAUPT
und es wird dann ganz zum Abschluss wieder nach HAUPT, also an den
Anfang der Bearbeitung gesprungen, um die Schleife zu schliessen, die
dafür sorgt, dass die Abfrage der Tasten viele tausend mal in der
Sekunde gemacht wird.
Das ganze Programm ist dann
1
.include "m8def.inc"
2
3
LDI R16, 0b00000011 ; Am Port B, 0 und 1 auf Ausgang schalten
4
OUT DDRB, R16
5
6
LDI R16, 0b00000000 ; Am Port D, 0 und 1 auf Eingang
7
OUT DDRD, R16
8
LDI R16, 0b00000011 ; und die Pullups an den beiden Pins
9
OUT PORTD, R16 ; aktivieren
10
11
HAUPT:
12
13
SBIC PIND, 0 ; Skip if Bit 0 in PIND is cleared
14
RJMP LED0_ON ; gehe zu: Led 0 einschalten
15
SBI PORTB, 0 ; Led 0 ausschalten
16
RJMP WEITER0 ; gehe zu: weiter
17
LED0_ON: CBI PORTB, 0 ; Led 0 einschalten
18
WEITER0:
19
20
SBIC PIND, 1 ; Skip if Bit 1 in PIND is cleared
21
RJMP LED1_ON ; gehe zu: Led 1 einschalten
22
SBI PORTB, 1 ; Led 1 ausschalten
23
RJMP WEITER1 ; gehe zu: weiter
24
LED1_ON: CBI PORTB, 1 ; Led 1 einschalten
25
WEITER1:
26
27
RJMP HAUPT
(Und jetzt kann ich nur noch hoffen, dass es auch wirklich funktioniert.
Ich habs nämlich nicht getestet :-)
Super =) an Port D hat es zwar nicht geklappt aber an Port C machen
meine LEDs nun was sie sollen (Jubelgewitter^^).
Ich werde mich nun mal weiter durch meine Lektüre kauen und aufs beste
hoffen =)
Vielen dank für die Hilfe! Gestern war ich wirklich kurz davor die lust
zu verliehren ;)
Patrick Berninghaus schrieb:> Super =) an Port D hat es zwar nicht geklappt
PORTD D0 und D1 sind die Anschlüsse für die RS232.
Wenn da was dran hängt (MAX232 ?) dann ist klar, das das nicht gehen
kann.
Die anderen Pins müssten aber funktionieren.
> aber an Port C machen> meine LEDs nun was sie sollen (Jubelgewitter^^).
Super