Forum: Mikrocontroller und Digitale Elektronik Problem mit Assembler (ATmega8 / AVR-GCC)


von Benedikt W. (whiteshampoo)


Lesenswert?

Hallo, ich versuche mich gerade etwas in Assembler einzuarbeiten.
Als ersten Schritt habe ich versucht ein kleines Programm zu schreiben, 
für einen ATmega8 mit 2 Tastern und 2 LEDs angeschlossen. Ziel ist es 
einfach nur den ATmega8 dazu zu bringen pro Taster eine LED 
einzuschalten.
Das sind meine ersten Gehversuche, ich hoffe ich habe alles richtig 
verstanden bisher.

1
#include "avr/io.h"
2
3
#define click R16
4
5
init:
6
  SER  click
7
  OUT  DDRC,  click  ; DDRC mit 0xFF beschreiben: Ausgänge
8
  CLR  click
9
  OUT  DDRB,  click  ; DDRB mit 0x00 beschrieben: Eingänge  
10
  OUT  PORTC,  click  ; LEDs ausschalten 
11
  OUT  PORTB,  click  ; und PullUps deaktivieren
12
13
  
14
loop:
15
  IN  click,  PINB    ; die Taster auslesen und
16
  OUT  PORTD,  click  ; und auf die LEDs spiegeln
17
  RJMP  loop        ; und nochmal...


Das ganze funktioniert allerdings leider gar nicht. Ich habe mir dann 
mal mit objdump das ganze nochmal disassembliert angesehen:

1
00000000 <init>:
2
   0:  0f ef         ldi  r16, 0xFF  ; 255
3
   2:  04 bf         out  0x34, r16  ; 52
4
   4:  00 27         eor  r16, r16
5
   6:  07 bf         out  0x37, r16  ; 55
6
   8:  05 bf         out  0x35, r16  ; 53
7
   a:  08 bf         out  0x38, r16  ; 56
8
9
0000000c <loop>:
10
   c:  06 b7         in  r16, 0x36  ; 54
11
   e:  02 bf         out  0x32, r16  ; 50
12
  10:  00 c0         rjmp  .+0        ; 0x12 <loop+0x6>


Was micht jetzt sehr wundert ist das der rjmp nach 0x12 geht, und nicht 
nach 0xC.

Wäre sehr froh wenn mir eben jemand aufzeigen könnte, wo mein Fehler 
ist.

Die Taster hängen mit 10k Ohm als PullDown-Widerstand und 
Entprellkondensator an PB0 und PB1. Die LEDs and PC0 und PC1.

von Peter R. (pnu)


Lesenswert?

Schau mal genau an, welche Ports und ihre Register DDR, port, und pin da 
beteiligt sind: B,C,D, vor Allem C und D musst Du Dir genau ansehen.

von Benedikt W. (whiteshampoo)


Lesenswert?

ok, den Fehler in der Vorletzten Zeile (D/C) habe ich gefunden, 
allerdings funktioniert immernoch nichts.

von no Moby (Gast)


Lesenswert?

1
.include "m8def.inc"; darf nicht fehlen
2
3
4
#define click R16
5
6
init:
7
  SER  click
8
  OUT  DDRC,  click  ; DDRC mit 0xFF beschreiben: Ausgänge
9
  CLR  click
10
  OUT  DDRB,  click  ; DDRB mit 0x00 beschrieben: Eingänge  
11
  OUT  PORTC,  click  ; LEDs ausschalten 
12
  OUT  PORTB,  click  ; und PullUps deaktivieren
13
14
  
15
loop:
16
  IN  click,  PINB    ; die Taster auslesen und
17
  OUT  PORTC,  click  ; und auf die LEDs spiegeln
18
  RJMP  loop        ; und nochmal...
Macht im Simulator was es soll. Ev. mal deine Hardware posten.

Viel Erfolg!

von Peter R. (pnu)


Lesenswert?

Du schaltest die pullups weg, wie sollen da gegen Masse schaltende 
Taster funktionieren?



also Zeile out portb,click nach oben schieben, wo click noch ff enthält. 
und mit pullups an den Tastern arbeiten.

von Walter S. (avatar)


Lesenswert?

Peter R. schrieb:
> Du schaltest die pullups weg, wie sollen da gegen Masse schaltende
> Taster funktionieren?

Benedikt W. schrieb:
> Die Taster hängen mit 10k Ohm als PullDown-Widerstand

wo steht was von "gegen Masse schaltende Taster"?

von no Moby (Gast)


Lesenswert?

Pull downs? Muss das sein?
Maches doch richtig: Interne und/oder externe Pull Ups an den Eingängen, 
LED mit Rv an +Ub hängen.
Dann gehts auch.
Ist natürlich nicht verboten, Pull Downs zu benutzen. Aber bitte miss 
die Pegel an den Eingängen nach.

von Benedikt W. (whiteshampoo)


Lesenswert?

Wenn ich ein ähnliches Programm in C schreiben funktioniert die ganze 
Sache, also meine Schaltung ist richtig. Allerdings ist mir aufgefallen 
das ab einer bestimmten größe der Hex-Datei das überspielen nicht mehr 
funktioniert. Entweder ist der Programmer oder der ATmega hinüber. 
Außerdem macht er lustige sachen wenn ich ihn berühre. Ich melde mich 
nochmal wenn ich Sicher funktionierende Hardware habe...

von Jobst M. (jobstens-de)


Lesenswert?

Benedikt W. schrieb:
> Außerdem macht er lustige sachen wenn ich ihn berühre.

Vielleicht weil Du so schön offene Eingänge ohne definierten Pegel hast, 
aber abfragst? Mach die Pullups bei den unbenutzen Eingängen wenigstens 
an ...


Gruß

Jobst

von Stefan E. (sternst)


Lesenswert?

Benedikt W. schrieb:
> Was micht jetzt sehr wundert ist das der rjmp nach 0x12 geht, und nicht
> nach 0xC.

Das ist vermutlich ungelinkter Code.

von Jobst M. (jobstens-de)


Lesenswert?

Stefan E. schrieb:
> Das ist vermutlich ungelinkter Code.

An der Stelle muss nichts gelinkt werden. Der muss dort 3 
Speicherstellen zurück springen. (Der PC steht bei der Ausführung von 
RJMP schon auf der nächsten Stelle) Dort sollte immer -3 stehen!

BTW:

Benedikt W. schrieb:
> #include "avr/io.h"

Wozu soll das sein?
Mir fehlt ehr sowas wie

.include "m8def.inc"

DA steht drin, wo sich die Ports befinden.

Worin programmierst Du Deinen Code?

Edit: Musste gerade nochmal die Werte editieren, weil ich den 
Schwachsinn aus Deinem Debuger geglaubt habe.
Der gibt vorne nicht die Adresse, sondern die Bytes an. **kopfschüttel*


Gruß

Jobst

: Bearbeitet durch User
von oldmax (Gast)


Lesenswert?

Hi
Benedikt W. schrieb:
> loop:
>   IN  click,  PINB    ; die Taster auslesen und
>   OUT  PORTD,  click  ; und auf die LEDs spiegeln
>   RJMP  loop        ; und nochmal...

Nun, Assembler ist genauso funktionstüchtig wie C und vermutlich 
funktioniert deine kleine Übung, wenn du auch dem Port C die Taster 
zuweist. Und nicht Port D!
Gruß oldmax

von Ralf G. (ralg)


Lesenswert?

Eine fehlerträchtige Konstruktion ist noch
1
  SER  click
2
  OUT  DDRC,  click  ; DDRC mit 0xFF beschreiben: Ausgänge
3
  CLR  click
Es kommt hier nicht darauf an, in einem Register alle Bits zu setzen, 
sondern es soll danach ein Port konfiguriert werden. Okay, hier betrifft 
es zuuufällig alle Bits.
Übersichtlicher:
1
.equ config  0b11111111
2
3
  ldi  click, config
4
  OUT  DDRC,  click  ; 'kann weg': DDRC mit 0xFF beschreiben: Ausgänge
5
  ldi  click, ~config
Jetzt erklärt sich der Quellcode fast von selbst. Außerdem muss zum 
Ändern der Konfiguration nur eine Konstante geändert werden.

von Stefan E. (sternst)


Lesenswert?

Jobst M. schrieb:
> An der Stelle muss nichts gelinkt werden. Der muss dort 3
> Speicherstellen zurück springen. (Der PC steht bei der Ausführung von
> RJMP schon auf der nächsten Stelle) Dort sollte immer -3 stehen!

Dass es im konkreten Fall eigentlich nicht nötig wäre, ist irrelevant. 
Da der Linker durchaus auch Änderungen am Code vornehmen kann, ist 
selbst im Fall eines relativen Sprungs zu einem für den Assembler 
sichtbaren Label die Distanz dem Assembler nicht immer eindeutig 
bekannt. Warum sollte er also versuchen, eine Fallunterscheidung (kann 
Adresse/Distanz selber eintragen oder muss vom Linker gemacht werden) zu 
machen, wenn es der einfachere und sichere Weg ist, es immer dem Linker 
zu überlassen.

Jobst M. schrieb:
> Benedikt W. schrieb:
>> #include "avr/io.h"
>
> Wozu soll das sein?

Das ist korrekt so (abgesehen davon, dass <> statt "" angebracht wäre).

Jobst M. schrieb:
> Worin programmierst Du Deinen Code?

Steht doch im Titel.

von Alexander S. (alesi)


Lesenswert?

Jobst M. schrieb:
> BTW:
>
> Benedikt W. schrieb:
>> #include "avr/io.h"
>
> Wozu soll das sein?
> Mir fehlt ehr sowas wie
>
> .include "m8def.inc"
>
> DA steht drin, wo sich die Ports befinden.

avr/io.h ist eine allgemeine io header-Datei für gcc-avr oder avr-as.
Beim Aufruf von gcc-avr oder avr-as gibt man mit der Option
 -mmcu=atmega8
an, aus welcher header-Datei die controller-spezifischen Parameter
gelesen werden.

Oben in der Datei io.h wird beschrieben, wie das funktioniert:
...
    This header file includes the apropriate IO definitions for the
    device that has been specified by the <tt>-mmcu=</tt> compiler
    command-line switch.
...

von Benedikt W. (whiteshampoo)


Lesenswert?

Ich hab den Fehler gefunden. Wer lesen kann ist klar im Vorteil ;)

Falls es jemanden Interessiert:

AVR-GCC sieht vor bei Schlüsselwörtern wie PORTB oder DDRA nicht die 
Port-Adresse, sondern die Adresse im Speicher anzusprechen. Sprich das 
funktioniert mit IN/OUT nicht. Um das zum laufen zu bekommen muss man 
_SFR_IO_ADDR(Port) verwenden, welches die Portadresse ausspuckt.

Hier der funktionierende Code:
1
#include <avr/io.h>
2
3
#define click  R16
4
#define input  0b11111100
5
#define output  0b11111111
6
7
init:
8
  LDI  click,  output
9
  OUT  _SFR_IO_ADDR(DDRC),  click  ; Ausgänge
10
  LDI  click,  input
11
  OUT  _SFR_IO_ADDR(DDRB),  click  ; Eingänge  
12
  LDI  click,   0x00
13
  OUT  PORTC,  click      ; LEDs ausschalten 
14
  OUT  PORTB,  click      ; und PullUps deaktivieren
15
16
  
17
loop:
18
  IN  click,  _SFR_IO_ADDR(PINB)  ; die Taster auslesen und
19
  OUT  _SFR_IO_ADDR(PORTC),  click  ; und auf die LEDs spiegeln
20
  RJMP  loop        ; und nochmal...

Falls es noch jemanden interessiert wie ich das in die ihex-Form 
gebracht habe:

Ich hab mir da ein mini-Shellscript geschrieben.
1
#!/bin/sh
2
3
avr-gcc -mmcu=$2 -c $1 -o tmp.elf -O0 -Wall
4
avr-objcopy -R .eeprom -R .fuse -R .lock -R .signature -O ihex tmp.elf flash.hex
5
rm tmp.elf

Anwendungsbeispiel:
1
script.sh sourcecode.sx atmega8
Danach hat man (wenn alles geklappt hat) eine flash.hex die man dem 
avrdude füttern kann.

-----

Alternativ könnte man Das programm auch ohne _SFR_IO_ADDR schreiben, was 
dann so aussehen würde:

1
#include <avr/io.h>
2
3
#define click  R16
4
#define input  0b11111100
5
#define output  0b11111111
6
7
8
main:
9
  LDI  click,  output
10
  STS  DDRC,  click  ; Ausgänge
11
  LDI  click,  input
12
  STS  DDRB,  click  ; Eingänge  
13
  LDI  click,   0x00
14
  STS  PORTC,  click  ; LEDs ausschalten 
15
  STS  PORTB,  click  ; und PullUps deaktivieren
16
17
  
18
loop:
19
  LDS  click,  PINB  ; die Taster auslesen und
20
  STS  PORTC,  click  ; und auf die LEDs spiegeln
21
  RJMP  loop    ; und nochmal...

von Alexander S. (alesi)


Lesenswert?

Benedikt W. schrieb:
> AVR-GCC sieht vor bei Schlüsselwörtern wie PORTB oder DDRA nicht die
> Port-Adresse, sondern die Adresse im Speicher anzusprechen.

Dazu noch eine Bemerkung: Wie Du richtig schreibst, ist es wichtig
das hier der avr-gcc bzw. indirekt der (gnu) avr-as verwendet wird.
Mit dem Atmel Assembler oder auch avra kann man die Befehle
"in" und "out" in der "loop" verwenden.

Z.B. (nicht von mir)
1
; Atmel Assembler oder avra!
2
; Port C: Ausgabe PC7 .. PC0 acht LEDs über 150R an 5 V
3
; Port B: Eingabe PB7 .. PB0 acht Taster gedrückt an Masse
4
; Beschaltung LEDs und Taster z.B. wie Atmel STK500
5
6
        .INCLUDE  "m8def.inc"   ; Deklarationen für Mega8
7
        .DEF    click = r16     ; Arbeitsregister
8
9
init:   ldi     click,$ff       ; Bitmuster 1111 1111
10
        out     DDRC,click      ; Port C ist Ausgang
11
                                ; Port B ist Eingang (default)
12
13
loop:   in      click,PINB      ; Eingabe Pin B  
14
        out     PORTC,click     ; Ausgabe Port C
15
        rjmp    loop
16
        .EXIT

Edit: Code formatiert.
Edit: Beschaltung beschrieben.

: Bearbeitet durch User
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.