Forum: Mikrocontroller und Digitale Elektronik AVR Tutorial : Tasten


von Axel H. (mf-futzi)


Lesenswert?

Hallo an alle,

erstmal nachträglich noch ein schönes Weihnachtsfest, auch wenn es 
mittlerweile schon fast vorbei ist ;-)

Nun zu meiner Frage: Ich habe mir gerade das AVR Tutorial-Tasten 
(Entprellung) vorgenommen und diesen Code in meinem Atmega 8 geflasht:


.include "m8def.inc"      ; Definitionsdatei


.def temp1  = r17
.def temp2  = r18

  ldi  temp1, $00    ; 0xFF ins Arbeitsregister   schreiben
  out   DDRB, temp1    ; Datenrichtungsregister auf Ausgang

  ldi  temp1, $00    ; $00 ins Arbeitsregister schreiben
  out  DDRD, temp1     ; r16 ins IO-Register PortB ausgeben

  ldi  temp1, $FF    ; Pullup Widerstäde aktivieren
  out  PORTD, temp1

  mov  r3, temp1    ; temp1=$FF, d.h. keine Taste gedrückt

loop:
  in  r4, PIND    ; den jetzigen Zustand der Taster holen
  mov  temp1, r4    ; und in temp1 sichern
  eor  r4, r3      ; mit dem vorhergehenden Zustand XOR
  mov  r3, temp1    ; den jetzigen Zustand für den nächsten
          ; Schleifendurchlauf als alten Zustand merken

  breq  loop      ; Das Ergebnis XOR auswerten:
          ; Wenn keine Taste gedrückt war -> neue Schleife

  and  temp1, r4    ; war das ein 1->0 Übergang, wurde der Taster
          ; also gedrückt (in key_now steht das Ergebnis
          ; vom XOR)
  brne  loop

  ldi  temp1, $FF

wait1:          ; Warten...
  dec  temp1
  brne  wait1


  in  temp1, PIND    ; ...und nachsehen, ob Taste immer noch gedrückt 
ist
  and  temp1, r4
  brne loop      ; wenn Taste nicht mehr gedrückt -> neue Scheife

  in  temp1, PORTB    ; wenn Taste immer noch gedrückt ->
  com  temp1      ; den Zustand der LED umdrehen
  out  PORTB, temp1

  rjmp  loop

Die Entprellung funktioniert zwar reibungslos, was mir auffällt ist 
aber, dass die angeschlossene LED relativ schwach brennt. Ich habe am 
Port 1,8V gegen Vcc gemessen. Das sollten aber doch 5V sein, oder? (wenn 
ich die LED direkt an GND anschliesse, dann brennt sie mit 'normaler' 
Helligkeit.

board: Experimentierboard nach Roland Walter
mC: Atmega8 interner Takt
Betr.-spannung: 5,0V (stab.)

Kann mir da jemand einen Tipp geben?

Vielen Dank

von Stefan B. (stefan) Benutzerseite


Lesenswert?

1/ Kannst du mal diese Zeile kontrollieren, die ist fischig:

>  in  temp1, PORTB    ; wenn Taste immer noch gedrückt ->

2/ Wie hast du die LED anegschlossen (einfaches Schaltbild reicht)?

von Oliver S. (eragon)


Lesenswert?

Das du nur 1.8V über der LEd hast find ich ganz okey. Die 
durchlassspannung dieser LED lioegt einfach bei 1.8 V. Wie bei Dioden 
0.7V.
Ich denke der Restliche Spannnungsabfall wird sich über den internen 
Widerständen (Transistoren) finden lassen.

Hoffe ich liege da nicht ganz falsch. Bin mir aber doch ziemlich sicher.

von Axel H. (mf-futzi)


Lesenswert?

Hallo stefb,

> 1/ Kannst du mal diese Zeile kontrollieren, die ist fischig:

  brne loop      ; wenn Taste nicht mehr gedrückt -> neue Scheife

  in  temp1, PORTB    ; wenn Taste immer noch gedrückt ->
  com  temp1      ; den Zustand der LED umdrehen
  out  PORTB, temp1

-Nachdem der Tastendruck erkannt wurde, wird in temp1 der aktuelle 
Zustand der    LED (PORTB) geschrieben (an oder aus)
-com invertiert dann die bits von temp1 (an->aus bzw aus->an)
-und anschl. invertierte Ausgabe (temp1->PORTB)

> 2/ Wie hast du die LED anegschlossen (einfaches Schaltbild reicht)?

Auf dem board sind 3 LEDs, die mit ihrer Kathode an GND hängen. An den 
Anoden ist jeweils ein Widerstand 1,2k angeschlossen. Der PortPin Pb7 
ist dann mit einer LED verbunden.

von Niels H. (monarch35)


Lesenswert?

Axel Hüser wrote:

> Die Entprellung funktioniert zwar reibungslos, was mir auffällt ist
> aber, dass die angeschlossene LED relativ schwach brennt. Ich habe am
> Port 1,8V gegen Vcc gemessen. Das sollten aber doch 5V sein, oder? (wenn
> ich die LED direkt an GND anschliesse, dann brennt sie mit 'normaler'
> Helligkeit.

Wenn das bedeutet, das du die LEDs testweise ohne Vorwiderstand 
betrieben hast, würde daß die schwache Lichtausbeute erklären: die LEDs 
sind defekt.

LEDs niemals ohne Vorwiderstand betreiben. Auch nicht testweise...

von Axel H. (mf-futzi)


Lesenswert?

Hallo an alle,

sorry hab' falsch geschriebenRichtig ist: (wenn
ich die LED direkt an Vcc anschliesse, dann brennt sie mit 'normaler'
Helligkeit). Ich habe die LED also mit Widerstand getestet - LEDs sind 
OK

> Das du nur 1.8V über der LEd hast find ich ganz okey.

Wenn ich aber diesen Code flashe (LED leuchtet solange Taste 
gedrückt-ohne Entprellung):


...
  ldi  r16, 0b00000000    ; alle LEDs aus
  out  PORTB, r16

loop:

  in  r16, PIND    ; Zustand der Tasten ins Register r16 laden
  com  r16      ; alle Bits umkehren
  out  PORTB, r16    ; r16 ins IO-Register PortB ausgeben

  rjmp loop      ; Endlosschleife

...dann leuchtet die LED hell. Hier wird die LED doch auch über den 
PORTB angesteuert!

von Niels H. (monarch35)


Lesenswert?

Dann ist alles klar. Deine LED leuchtet mit dem ersten Code nicht 
schwach, sondern sie blinkt! Und zwar mit einer sehr hohen Frequenz.

Vermutlich hast du einfach nur einen logischen Fehler im Code...

von Hannes L. (hannes)


Lesenswert?

Warum liest Du eigentlich die Tutorial-Seite nicht zu Ende und benutzt 
die clevere Lösung von PeDa? Gut, etwas schwer zu verstehen, aber 
unschlagbar ressourcenschonend.

...

von Axel H. (mf-futzi)


Lesenswert?

Hallo hannes,

> Warum liest Du eigentlich die Tutorial-Seite nicht zu Ende und benutzt
> die clevere Lösung von PeDa? Gut, etwas schwer zu verstehen, aber
> unschlagbar ressourcenschonend.

ich hab schon zu Ende gelesen. Ich will's mir auch anschliessend 
vornehmen. Aber da ich gerade erst mit Assembler angefangen habe, will 
ich erst mal etwas Syntax und Code der Reihe nach lernen, und nicht wie 
mit meinem LCD den 3. Schritt vor dem 2. machen. Also nur Code kopieren, 
aber nicht wissen warum's dann funktioniert, war nicht mein Ziel.

Aber trotzdem Danke für den Tip! Ich hab mir auch schon eine zusätzliche 
Platine mit 8 Tastern und 8 LEDs gebaut, um den Code von PeDa 
auszuprobieren.

von Hannes L. (hannes)


Lesenswert?

Von PeDa gibt es auch einen cleveren Code für nur einen Taster. Er nutzt 
nur 4 Bit eines Registers und ist hocheffizient. Er wird auch zyklisch 
aufgerufen (am besten im Timer-Int. oder vom Timer synchronisiert). Hier 
http://www.mikrocontroller.net/forum/read-1-140056.html#140946 wird 
darüber diskutiert.

...

von Axel H. (mf-futzi)


Lesenswert?

Hallo an alle,

ich hab den Fehler nun selbst gefunden :-)...nach langem Suchen und 
Vergleichen mit anderen Codes.

falsch:
  ldi  temp1, $00      ; 0xFF ins Arbeitsregister   schreiben
  out   DDRB, temp1    ; Datenrichtungsregister auf Ausgang

richtig:
  ldi  temp1, 0xFF     ; 0xFF ins Arbeitsregister   schreiben
  out   DDRB, temp1    ; Datenrichtungsregister auf Ausgang

Ich hab fälschlicherweise das DDRB als Eingang ($00) anstatt als Ausgang 
(0xFF) deklariert.  (aber Kommentierung war richtig)

Der Grund war, weil im Tutorial der Befehl steht:
.equ LED = 0
...
ldi temp1, 1<<LED

Da mir das '1<<LED' nicht klar war, was es exakt bedeutet, habe ich es 
durch $00 (.equ LED = 0) ersetzt.

Also jetzt klappt's.


Danke an alle!

von Niels H. (monarch35)


Lesenswert?

Axel Hüser wrote:

> falsch:
>   ldi  temp1, $00      ; 0xFF ins Arbeitsregister   schreiben
>   out   DDRB, temp1    ; Datenrichtungsregister auf Ausgang

Das ist das was ich mit "lesbarkeit des Quellcodes" meine. In C fallen 
solche Flüchtigkeitsfehler einfach schneller auf.

von Hannes L. (hannes)


Lesenswert?

Axel Hüser wrote:
> Hallo an alle,
>
> ich hab den Fehler nun selbst gefunden :-)...nach langem Suchen und
> Vergleichen mit anderen Codes.
>
> falsch:
>   ldi  temp1, $00      ; 0xFF ins Arbeitsregister   schreiben
>   out   DDRB, temp1    ; Datenrichtungsregister auf Ausgang

Ja, falsch...

>
> richtig:
>   ldi  temp1, 0xFF     ; 0xFF ins Arbeitsregister   schreiben
>   out   DDRB, temp1    ; Datenrichtungsregister auf Ausgang

Nööö, immernoch falsch...

>
> Ich hab fälschlicherweise das DDRB als Eingang ($00) anstatt als Ausgang
> (0xFF) deklariert.  (aber Kommentierung war richtig)

Du wolltest doch aber eigentlich nicht den ganzen Port als Ausgang 
schalten, sondern nur den LED-Pin, oder???

>
> Der Grund war, weil im Tutorial der Befehl steht:
> .equ LED = 0
> ...
> ldi temp1, 1<<LED

Das bedeutet, dass Du nur das Bit mit dem Namen "LED" (und dem Bitwert 
0) setzen (auf 1 setzen) willst und alle anderen Bits auf 0. Der 
Zahlenwert in temp1 wäre demnach 1 ($01, 01h, 0x01, 0b00000001). hätte 
"LED" den Bitwert 3, dann wäre der Wert in temp1 8.

>
> Da mir das '1<<LED' nicht klar war, was es exakt bedeutet, habe ich es
> durch $00 (.equ LED = 0) ersetzt.

Das war falsch. Stattdessen solltest Du Dich informieren, wie mit man 
beim Namen genannten Bits umgeht. Dieses Wissen brauchst Du nicht nur in 
Assembler, sondern auch in C.

>
> Also jetzt klappt's.

Nein, es klappt nicht, es sieht nur so aus. Du hast nämlich Ports als 
Ausgang konfiguriert, die Du gar nicht als Ausgang wolltest. Dass 
dadurch kein Schaden entsteht, ist reiner Zufall.

>
>
> Danke an alle!

Nix zu danken

...

von Axel H. (mf-futzi)


Lesenswert?

Hallo hannes,

du hast mir wirklich sehr geholfen! Ich habe meinen Code jetzt so 
abgeändert:

...
.equ LED  = 0      ; Das bit0 hat den Namen 'LED'

  ldi  temp1, 1<<LED    ; Das bit0 wird 1 gesetzt, alle anderen 0
          ; und ins Arbeitsregister temp1 geschrieben
  out   DDRB, temp1    ; bit0 auf Ausgang

  ldi  temp1, $00    ; $00 ins Arbeitsregister schreiben
  out  DDRD, temp1     ; r16 ins IO-Register PortB ausgeben
          ; PORTD ist Eingang
...

Mit dem Taster verändere ich jetzt nur noch die LED am PB0 und nicht 
mehr alle (PB1-PB7).
Hätte ich den Code aus dem Tutorial nur kopiert, hätte es zwar sofort 
funktioniert. Jetzt weis ich aber, was es mit '<<' schieben auf sich hat 
und was ich falsch gemacht habe.
Ist Kommentierung so OK?

hannes Danke nochmal.

von Philipp B. (philipp_burch)


Lesenswert?

Axel Hüser wrote:
> Ist Kommentierung so OK?

Nö. Zum Anfangen ist es noch ganz ok, alles etwas mehr zu kommentieren, 
aber insbesondere Kommentare wie "out  DDRD, temp1     ; r16 ins 
IO-Register PortB ausgeben" sind nicht ganz ungefährlich. Vielleicht 
kommst du mal auf die Idee, temp1 auf r17 zu legen, dann sagt der 
Kommentar etwas anderes als dein Programm. Zeilen wie
1
  ldi  temp1, 1<<LED
2
  out   DDRB, temp1
3
4
  ldi  temp1, $00
5
  out  DDRD, temp1
braucht man eigentlich überhaupt nicht zu kommentieren, das ist einfach 
selbsterklärend. Daher auch die benannten Bits, das vermeidet redundante 
Informationen.

von Hannes L. (hannes)


Lesenswert?

Die Namen für die Register, I/O-Register und Bits haben ja den Sinn, 
dass man sich nicht mehr darum kümmern muss, welches Register (Nummer) 
oder welches Bit (Nummer) man eigentlich meint. In den Kommentaren hat 
daher die Nummer des Registers oder Bits nichts zu suchen. Wenn bei den 
Deklarationen (.equ, .def) die Namen vergeben werden bzw. Portbits 
verteilt werden, dann spricht man sie konsequenterweise im gesamten 
Programm mit diesen Namen an. Das ermöglicht das Verändern der 
Pinzuweisungen in den Deklarationen, ohne im Programm auch nur eine 
Zeile ändern zu müssen.
Dabei kann es durchaus vorkommen, dass ein Port mehrere Namen bekommt, 
weil an seinen Bits unterschiedliche Dinge angeschlossen sind. Je 
nachdem, welches Teil man ansprechen will, wird der entsprechende Name 
benutzt. Liegt z.B. an PortD Bit0 (PD0) eine LED, so bekommt PortD 
zusätzlich den Namen LED_Port und PD0 den Namen LED_Bit. Will ich die 
LED einschalten, schreibe ich im Programm sbi LED_Port,LED_Bit. Will ich 
sie ausschalten, dann cbi statt sbi.

Du sagst, das Schieben mit << hast Du verstanden. Trotzdem solltest Du 
nochmal den Unterschied zwischen Bitmaske (0..255) und Bitnummer (0..7) 
ergründen, dazu solltest Du Dir die Beschreibung einiger Befehle 
ansehen, die mit Konstanten oder Bitnummern arbeiten. Ergründe mal den 
Unterschied der Bit-Auswahl zwischen ldi (andi, ori, sbr, cbr, ...) und 
sbrs (sbrc). Das Eine braucht die Bitmaske, das andere die Bitnummer. Um 
nun das Bit mit demselben Namen ansprechen zu können, wird es bei 
benötigter Bitnummer direkt angegeben, bei benötigter Bitmaske als 
Exponent zur Basis 2 benutzt, was dem Linksschieben einer Eins um n 
(Bitnummer) Stellen entspricht. Aus den Bitnummern 0, 1, 2, 3, 4, 5, 6 
und 7 werden dadurch die Bitmasken mit den Werten 1, 2, 4, 8, 16, 32, 64 
und 128.
Schau Dir auch mal den Unterschied zwischen den (ähnlich klingenden) 
Befehlen sbr/cbr und sbi/cbi an, das ist nämlich auch ein bekannter 
Stolperstein für Anfänger.

...

von Karl H. (kbuchegg)


Lesenswert?

Philipp Burch wrote:
> Axel Hüser wrote:
>> Ist Kommentierung so OK?
>
> Nö. Zum Anfangen ist es noch ganz ok, alles etwas mehr zu kommentieren,
> aber insbesondere Kommentare wie "out  DDRD, temp1     ; r16 ins
> IO-Register PortB ausgeben" sind nicht ganz ungefährlich. Vielleicht
> kommst du mal auf die Idee, temp1 auf r17 zu legen, dann sagt der
> Kommentar etwas anderes als dein Programm.

@Axel
Wie wahr, wie wahr
:-)   :-)
1
   out DDRD, temp1  ; r16 ins IO-Register PortB ausgeben

Fällt dir was auf?  DDRD - PortB

Wenn ich noch meinen Senf dazu geben darf:
Kommentiere nicht was im Source Code sowieso schon dortsteht.

Nur so als Beispiel:
Aus deinem korrigierten Programm
1
   ldi  temp1, 0xFF     ; 0xFF ins Arbeitsregister   schreiben
2
   out   DDRB, temp1    ; Datenrichtungsregister auf Ausgang

Was erzählt mir der Kommentar, was nicht auch schon im Source
Code steht? Gar nichts!
Im Kommentar steht, dass du 0xFF ins Arbeitsregister schreibst.
Dassselbe steht auch im Source Code:   ldi temp1, 0xFF
OK 'Datenrichtungsregister auf Ausgang' enthält ein Fitzelchen
Information, dass nicht so offensichtlich im Source Code steht.
Dazu muss man wissen, dass eine 1 einen Pin auf Ausgang schaltet.
Aber so wirklich weltbewegend Neues enthält der Kommentar nicht
wirklich.

Erzähl mir (dir, deinem Leser) in einem Kommentar nicht, was er
durch Lesen des Source Codes erfahren kann. Erzähl im lieber die
Dinge, die nicht im Source Code stehen. Sehr oft sind das die
Dinge, die sich auf die Frage: Warum? ergeben.
Also: Warum schaltest du den Port B auf Ausgang.
Antwort: Weil dort die Led hängen.
Das solltest du zb. kommentieren.

von Axel H. (mf-futzi)


Lesenswert?

Hallo Karlheinz,

> Kommentiere nicht was im Source Code sowieso schon dortsteht.

Werde ich beherzigen. Als Anfänger war es für mich aber hilfreich. Aber 
auch mehr Sorgfalt ist bei meiner Kommentierung noch nötig ;-)

hannes,

danke für die ausführlichen Hinweise und Erklärungen (und verwendete 
Zeit) mit Bitmaske und Bitnummer.

Axel

von Hannes L. (hannes)


Lesenswert?

> Kommentiere nicht was im Source Code sowieso schon dortsteht.

Es kommt auf die Situation an. Also darauf, für wen der Quelltext 
geschrieben worden ist. Ist er für einen Anfänger als Hilfe zur 
Selbsthilfe, dann finden sich auch in meinen Kommentaren 
Befehlsbeschreibungen oder Hinweise auf das Datenblatt (Seitenzahl in 
eckigen Klammern). Ist der Quelltext für Jemanden, der sich nicht mit 
ASM auseinandersetzen will und der das Programm nur benutzen will, dann 
enthalten meine Kommentare solche Erklärungen nicht.

...

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.