Forum: Compiler & IDEs avr-gcc + Assembler Problem


von Jasmin T. (Gast)


Lesenswert?

Hallo,
ich arbeite unter Linux und habe ein Problem mit dem kompilieren von 
Assembler mit dem avr-gcc.
Habe den Code bei einem Kollegen unter Windows im AVR Studio simuliert 
und  auf den Controller programmiert. Damit hat es ohne Probleme 
funktioniert. Ausgenommen natürlich das ändern von hi8 (RAMEND) auf 
high(RAMEND) und das gleiche für low.
Am Board liegt es nicht, dies habe ich bereits ausgiebig getestet, 
vieleicht findet jemand von euch einen Fehler.
Für mich sieht es so aus als würde einfach der Interrupt nicht ausgelöst 
werden.
Nach dem Start leuchtet die Led korrekterweise an Pin A0.

Danke im Vorraus
Jasmin


Folgender Code:
1
.NOLIST
2
.INCLUDE "m16def.inc"
3
.LIST
4
 
5
.org 0x000
6
         rjmp main            ; Reset Handler
7
.org INT0addr
8
         rjmp int0_handler    ; IRQ0 Handler
9
10
 
11
 
12
main:                         ; hier beginnt das Hauptprogramm
13
 
14
         ldi r16, lo8(RAMEND)
15
         out SPL, r16
16
         ldi r16, hi8(RAMEND)
17
         out SPH, r16
18
19
         ldi r16, (0<<DDD2) ;INT0(PORT D2) muss als input konfiguriert werden)
20
         out DDRD, r16 
21
     
22
     ldi r16, (1<<PD2) ;Pullup
23
         out PORTD, r16 
24
 
25
         ldi r16,(1<<PA0)+(1<<PA1)+(1<<PA2); Ausgänge
26
         out DDRA, r16
27
28
         ldi r16, (1<<ISC00)+(0<<ISC01) ;0b00000001  ; INT0 konfigurieren - Jede änderung löst einen interrupt aus
29
         out MCUCR, r16
30
 
31
         ldi r16, (1<<INT0) ; INT0 aktivieren
32
         out GICR, r16
33
34
         sei       ; Interrupts allgemein aktivieren 
35
    rcall loadfirst
36
37
loop:    rjmp loop             ; eine leere Endlosschleife
38
 
39
int0_handler:
40
        
41
    lsl r17    ; left shift
42
   cpi r17,0x8  ;wenn > led 3
43
   brge loadfirst  ;Springe zurück
44
   out PORTA, r17
45
46
reti
47
 
48
49
 
50
loadfirst:    
51
  
52
         ldi r17,(1<<PA0);0x01  ; este Lampe brennt
53
         out PORTA, r17         ; erleuchte dich
54
reti

von Falk (Gast)


Lesenswert?

@Jasmin T.

>ich arbeite unter Linux und habe ein Problem mit dem kompilieren von
>Assembler mit dem avr-gcc.

Ähhh, der AVR-gcc ist ein C-Compiler. Der "frisst" deinen Source Code 
der unten dranhängt nicht. Das ist reiner Assembler. AVR-Studio (auf 
Windows) ist die allgemeine Oberfläche, welche sowohl Assembler als auch 
C Projekte verwalten kann.

MfG
Falk

von Jasmin T. (Gast)


Lesenswert?

gcc bedeutet GNU Compiler Collection. Also nicht unbedingt nur c.

Wenn ich den Code unter Windows im AVRStudio kompiliere, funktioniert 
das Programm, aber ich muss es unter Linux übersetzen können und da 
macht die Interruptroutine eben Probleme (Ohne Interrupts läufts auch 
dort!) und ich weis nicht wieso.

von Falk (Gast)


Lesenswert?

@Jasmin T.

>gcc bedeutet GNU Compiler Collection. Also nicht unbedingt nur c.

Ach so, wieder was gelernt.

>macht die Interruptroutine eben Probleme (Ohne Interrupts läufts auch
>dort!) und ich weis nicht wieso.

Gibt es eine Fehlermeldung? Vielleicht ein Fehler in den Include Files?

MFG
Falk

von Jasmin T. (Gast)


Lesenswert?

Nein gibt es keine, dann wüsst ich ja wo suchen, das Programm 
kompiliert, wird korrekt übertragen und dann leuchtet das erste Led wies 
soll. Der Interrupt der zwar aktiviert ist wird allerdings nie 
aufgerufen.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Wie hast du denn den Assembler und Linker aufgerufen?

Wenn du dafür den GCC als Frontend nimmst, dann baut der den
normalen startup-Code ein, der auch die Interruptvektoren definiert.
Dann hälst du dich besser an das Protokoll, wie es z. B. in

http://www.nongnu.org/avr-libc/user-manual/group__asmdemo.html

demonstriert wird.

von Jasmin T. (Gast)


Lesenswert?

Ich habe den Assembler folgendermaßen aufgerufen:

avr-gcc -x assembler -mmcu=ATmega16 -o demo.aout demo.s
avr-objcopy -O ihex demo.aout demo.ihex

von Wolfram (Gast)


Lesenswert?

irgendeine deiner Aussagen stimmt nicht
AVR Studio benutzt einen eigenen Assembler "AVR-Assembler" , der hat 
einen anderen Syntax als der Assembler des gcc "gas"
wenn du in AVRStudio assemblierst hast du nicht den gas benutzt.
Bei assembler spricht man nicht von compilieren sondern assemblieren.

>ldi r16,(1<<PA0)+(1<<PA1)+(1<<PA2);
gewöhn dir sowas erst gar nicht an, das sind logische verknüpfungen 
nicht arithmetische Ausdrücke
deine LED ist doch in r17, wenn du die nicht setzt ist sie undefiniert 
für den Anfang oder steht irgendwo das die Register am Anfang 0 sind? 
Nehmen wir mal 0 an. Was passiert wenn du 0 shiftest? Meiner Meinung 
nach bleibt es 0.

von Jasmin T. (Gast)


Lesenswert?

>irgendeine deiner Aussagen stimmt nicht
>AVR Studio benutzt einen eigenen Assembler "AVR-Assembler" , der hat
>einen anderen Syntax als der Assembler des gcc "gas"
>wenn du in AVRStudio assemblierst hast du nicht den gas benutzt.
>Bei assembler spricht man nicht von compilieren sondern assemblieren.
Deswegen sag ich ja im 1. Post dass der Syntax etwas anders ist, es ist 
mir schon bewusst.
Hab mir überlegt vieleicht behandelt der Gnu-asm das SEI anders als der 
AvrStudio-asm.

>gewöhn dir sowas erst gar nicht an, das sind logische verknüpfungen
>nicht arithmetische Ausdrücke
Wie soll ich es dann machen? Etwa ldi r16,0b00000111
Ändert allerdings auch nix daran, dass es nicht geht g

>deine LED ist doch in r17, wenn du die nicht setzt ist sie undefiniert
>für den Anfang oder steht irgendwo das die Register am Anfang 0 sind?
>Nehmen wir mal 0 an. Was passiert wenn du 0 shiftest? Meiner Meinung
>nach bleibt es 0.
Naja r17 wird doch bei loadfirst initialisiert mit 0x01.
r16 benutz ich zum Initialisieren der einzelnen Ein/Ausgaberegister.

von Wolfram (Gast)


Lesenswert?

>Naja r17 wird doch bei loadfirst initialisiert mit 0x01.
>r16 benutz ich zum Initialisieren der einzelnen Ein/Ausgaberegister.
einen rcall mit reti beenden?
Bleibt da nicht das Flagregister auf dem Stack, so daß du mit der Zeit 
einen Stacküberlauf produzierts?
Aber dazu scheint es ja bis jetzt nicht zu kommen
entweder hältst du dich an Jörgs Vorschlag oder du rufst avr-as direkt 
auf.

von Michael U. (Gast)


Lesenswert?

Hallo,

beim AVR ist der Unterschied zwischen RET und RETI nur der, daß RETI die 
Interrupts wieder freigibt, die der Ausruf der ISR gesperrt hat.

Es hat hier also keine Nebenwirkungen, man sollte es sich aber garnicht 
erst angewöhnen. RCALL -> RET, ISR -> RETI.

Zu
>ldi r16,(1<<PA0)+(1<<PA1)+(1<<PA2)

Der Präprozessor macht eine Addition, das gent nur gut, solange die 
Werte nur Bitwerte sind und solange nicht das gleiche Bit betroffen ist.

Mal es Dir auf Papier unter der Annahme, daß die Werte nicht Portbits 
sind, sondern andere Vereinbarungen:

.equ X1 = 1
.equ Y1 = 2
.equ Z1 = 1

>ldi r16,(1<<X1)+(1<<Y1)+(1<<Z1)

Ich habe das früher auch meist so gemacht, stört in 99% der Fälle auch 
nicht, den Fehler beim letzten Prozent sucht man dafür dann eine 
Woche...

>ldi r16,(1<<PA0)|(1<<PA1)|(1<<PA2)
Der Präprozessor macht eine OR-Verknüpfung

>ldi r16,(1<<X1)|(1<<Y1)|(1<<Z1)

Schau Dir den Unterschied beim Ergebnis an. ;)

Gruß aus Berlin
Michael

von Falk (Gast)


Lesenswert?

@ Wolfram

>einen rcall mit reti beenden?

Nee, es ist ein branch (brge). Ist aber dennoch schlechter 
Programmierstil, die zwei Zeilen sollte man lieber in die 
Interruptroutine integrierern und dann am Ende nur ein RETI haben.

>Ich habe das früher auch meist so gemacht, stört in 99% der Fälle auch
>nicht, den Fehler beim letzten Prozent sucht man dafür dann eine
>Woche...

>>ldi r16,(1<<X1)|(1<<Y1)|(1<<Z1)

>Schau Dir den Unterschied beim Ergebnis an. ;)

Böse Falle! ;-)

MfG
Falk

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Jasmin T. wrote:

> Ich habe den Assembler folgendermaßen aufgerufen:
>
> avr-gcc -x assembler -mmcu=ATmega16 -o demo.aout demo.s

Aha, also über den Compilertreiber.  Damit linkst du implizit den
C-Startup-Code mit, da der Compiler nach dem Assemblieren den
Linker aufruft.  Dieser Code wiederum beinhaltet die Vektoren.

Bitte sieh dir das genannte Demo an sowie die Doku überhaupt, die
enthält auch noch ein reines Assemblerbeispiel.  Prinzipiell denke
ich, dass du bei der Variante mit dem Compiler bleiben solltest,
aber dann musst du dich an die Konventionen für Interruptvektornamen
etc. halten.

Außerdem nimmt man in diesem Kontext normalerweise nicht
1
.INCLUDE "m16def.inc"

sondern wie in C-Programmen üblich
1
#include <avr/io.h>

zusammen mit der entsprechenden -mmcu= Option des Compilers.  Dazu
benennst du bitte die Datei nicht mehr foo.s sondern foo.S (und
gibst sie auch mit dem großen S so auf der Kommandozeile des
Compilers an), damit sie durch den Präprozessor geschickt wird.
Das mit dem -x assembler entweder ganz weglassen (das merkt der
Compiler anhand des Suffixes), oder du musst dir das passende
aus dem Manual raussuchen (-x assembler-with-cpp oder so ähnlich --
ich benutze immer die impliziten Regeln an Hand der Suffixe, ich
kann's dir nicht genau sagen).

Der Billischassembler von Atmel ist wirklich nur 'ne Einsteigerversion
ohne jeglichen Komfort, entsprechend auch ohne Linker.  Daher muss
man dort alles (auch die Vektoren) in einer Datei drin haben.

von Falk (Gast)


Lesenswert?

@Jasmin T.

>Hab mir überlegt vieleicht behandelt der Gnu-asm das SEI anders als der
>AvrStudio-asm.

Das wäre äusserst komisch. Aber bei Linus ist ja alles möglich. 
duckundwech

>Wie soll ich es dann machen? Etwa ldi r16,0b00000111
>Ändert allerdings auch nix daran, dass es nicht geht *g*

Nein. Du sollst anstatt

ldi r16,(1<<PA0)+(1<<PA1)+(1<<PA2)    ; Addition

besser schreiben

ldi r16,(1<<PA0)|(1<<PA1)|(1<<PA2)    ; ODER Verknüpfung

>>deine LED ist doch in r17, wenn du die nicht setzt ist sie undefiniert
>>für den Anfang oder steht irgendwo das die Register am Anfang 0 sind?
>>Nehmen wir mal 0 an. Was passiert wenn du 0 shiftest? Meiner Meinung
>>nach bleibt es 0.
>Naja r17 wird doch bei loadfirst initialisiert mit 0x01.
>r16 benutz ich

FALSCH!

Wenn r17 beim Start zufällig Null ist, wird load first nie angesprungen.
Assembler != C !
In C sind alle Variablen initialisiert, und sei es implizit mit 0.
IN Assembler musst du das von Hand machen. Und gerade die Register und 
die Variablen im SRAM haben nach dem Reset UNDEFINIERTE Werte! Das kann 
böse ins Auge gehen. Nichtinitialisierte Variablen sind ein immer wieder 
gern gesehener Fehler, Das Debugging kann ewig dauern (weil der Fehler 
ggf. nur äusserst sporadisch auftritt).

MfG
Falk

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Falk wrote:

>>Hab mir überlegt vieleicht behandelt der Gnu-asm das SEI anders als
>>der AvrStudio-asm.

> Das wäre äusserst komisch. Aber bei Linus ist ja alles möglich.

Was hat der GNU-Assembler (noch dazu für das AVR-Target) mit Linus zu
tun?  Außer dass er diesen Assembler natürlich nutzt, aber dann hat er
genauso viel mit Falk zu tun...

> Nein. Du sollst anstatt

> ldi r16,(1<<PA0)+(1<<PA1)+(1<<PA2)    ; Addition

> besser schreiben

> ldi r16,(1<<PA0)|(1<<PA1)|(1<<PA2)    ; ODER Verknüpfung

Wobei der Konflikt nur auftreten kann, wenn jemand aus Versehen

ldi r16, (1 << PA0) | (1 << PA0);

schreibt, weil er sich beim zweiten Bit verschrieben hat, und dann
hat derjenige so oder so ein Problem.  Du bläst das hier gerade unnütz
auf (m. E.).  Solange es ausschließlich verschiedene Bits sind, isses
wurscht.

von Wolfram (Gast)


Lesenswert?

@Falk:
so dachte ich das am Anfang auch aber er macht

        sei       ; Interrupts allgemein aktivieren
    rcall loadfirst              <<<<<<
loop:    rjmp loop             ; eine leere Endlosschleife

im Hauptprogramm
was ich dann ziemlich autsch finde ist der Abschluß des rcall mit iret
Ok, das geht. Das Flags register wird tatsächlich nicht gesichert. Aber 
sowas zu machen wenn man noch 15,8 KB im FLASH frei hat,
also ich würde es nicht machen.

von Falk (Gast)


Lesenswert?

@Jörg Wunsch

>> Das wäre äusserst komisch. Aber bei Linus ist ja alles möglich.

>Was hat der GNU-Assembler (noch dazu für das AVR-Target) mit Linus zu
>tun?

Tippfehler, sollte auf X enden. ;-)

>Wobei der Konflikt nur auftreten kann, wenn jemand aus Versehen
>ldi r16, (1 << PA0) | (1 << PA0);
>schreibt, weil er sich beim zweiten Bit verschrieben hat, und *dann*
>hat derjenige so oder so ein Problem.

Ja, aber du weisst ja wie das ist mit den Pferden vor der Apotheke . . . 
;-)

@Wolfram

>so dachte ich das am Anfang auch aber er macht

>        sei       ; Interrupts allgemein aktivieren
>    rcall loadfirst              <<<<<<
>loop:    rjmp loop             ; eine leere Endlosschleife

>im Hauptprogramm

Ohh, jetzt seh ichs auch.

>was ich dann ziemlich autsch finde ist der Abschluß des rcall mit iret

Eben.

MfG
Falk

von Falk (Gast)


Lesenswert?

@Wolfram

>so dachte ich das am Anfang auch aber er macht

Ich glaube der ER ist eine SIE, wenn man im Internet den Nicknames 
trauen darf;-).
Oder meinst du DEN AVR? ;-)

MfG
Falk

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

> Tippfehler, sollte auf X enden. ;-)

Selbst dann sehe ich noch keinen Bezug.  Oder sind die GNU Binutils
mittlerweile von Linux aufgekauft worden?

von Falk (Gast)


Lesenswert?

@ Jörg Wunsch

>> Tippfehler, sollte auf X enden. ;-)

>Selbst dann sehe ich noch keinen Bezug.  Oder sind die GNU Binutils
>mittlerweile von Linux aufgekauft worden?

ES WAR EIN GAG!!!!
Linux und Linus und GCC sind ganz toll!

;-)

MfG
Falk


von Wolfram (Gast)


Lesenswert?

>Oder meinst du DEN AVR?
NATÜRLICH!!!  ;-)

(Sorry Jasmin T., ich sollte wohl öfters auf den Nickname schauen,
sonst ist 8-) nötig.)

von Jasmin T. (Gast)


Lesenswert?

Danke für die vielen Tipps, leider brachte mich bisher keiner auf das 
Problem bezogen weiter.

von Jasmin T. (Gast)


Lesenswert?

Danke an Jörg Wunsch der hat mich auf die richtige Spur gebracht, da es 
sich bei meiner
1
m16def.inc
 um eine modifizierte Version mit Wordadressen handelt muss ich nur alle 
Adressen * 2 rechnen und schon funktioniert das Zeug.
1
.org INT0addr*2
2
     rjmp int0_handler    ; IRQ0 Handler

Aber danke an alle, die mir auch was zum Thema schönen Code beigebracht 
haben. Ich geh jetzt mal jemanden lünchen.

@Wolfram
Ich verzeih dir nochmals, wegen des Namens. :P

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.