Forum: Mikrocontroller und Digitale Elektronik Anfängerfrage ATmega8 I/O Verständnisproblem


von Patrick B. (patricck)


Lesenswert?

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!

von Karl H. (kbuchegg)


Lesenswert?

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.

von Patrick B. (patricck)


Lesenswert?

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?

von Justus S. (jussa)


Lesenswert?

Karl heinz Buchegger schrieb:
> Der zweite Abschnitt ist komplett identisch mit dem ersten.

die letzte zeile ist anders...

von Ich (Gast)


Lesenswert?

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?

von spess53 (Gast)


Lesenswert?

Hi

>Die Stackpointer-Initialisierung brauchst du überhaupt nicht.

Über diese Brücke würde ich nicht gehen.

MfG Spess

von Lutz (Gast)


Lesenswert?

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.

von Lutz (Gast)


Lesenswert?

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 ...

von Karl H. (kbuchegg)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

Justus Skorps schrieb:
> Karl heinz Buchegger schrieb:
>> Der zweite Abschnitt ist komplett identisch mit dem ersten.
>
> die letzte zeile ist anders...

tatsächlich.
Hab ich nicht gesehen.

von Karl H. (kbuchegg)


Lesenswert?

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>

von Patrick B. (patricck)


Lesenswert?

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 =)

von Karl H. (kbuchegg)


Lesenswert?

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 :-)

von Patrick B. (patricck)


Lesenswert?

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 ;)

von Karl H. (kbuchegg)


Lesenswert?

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

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.