Forum: Compiler & IDEs Atmel ASM portieren


von Aike T. (biertrinker)


Lesenswert?

Hallo,

ich versuche momentan ein bischen Asembler zu lernen. Da ich einen Mac
habe und sonst auch mit Eclipse AVR Programme scheibe würde ich dafür
gerne den gcc-as nehmen.
Bei den ersten kleinen Testprogrammen hat das noch ganz gut funktionert.
Jetzt versuche ich das Software-PWM Beispiel hier für den GNU-Assembler
umzubauen, aber leider finde ich nur wenig Dokumentation dazu.
Daher habe ich 1. die Frage ob jemand eine Seite kennt die sich mit dem
GNU-Assember beschäftigt und 2. ob mir jemand mal dabei helfen kann das
Programm umzuarbeiten.

Original:

.include "m8def.inc"

.def temp  = r16

.def PWMCount = r17

.def ocr_1 = r18                      ; Helligkeitswert Led1: 0 .. 127
.def ocr_2 = r19                      ; Helligkeitswert Led2: 0 .. 127
.def ocr_3 = r20                      ; Helligkeitswert Led3: 0 .. 127
.def ocr_4 = r21                      ; Helligkeitswert Led4: 0 .. 127
.def ocr_5 = r22                      ; Helligkeitswert Led5: 0 .. 127
.def ocr_6 = r23                      ; Helligkeitswert Led6: 0 .. 127

.org 0x0000
        rjmp    main                  ; Reset Handler
.org OVF0addr
        rjmp    timer0_overflow       ; Timer Overflow Handler

main:
        ldi     temp, LOW(RAMEND)     ; Stackpointer initialisieren
        out     SPL, temp
        ldi     temp, HIGH(RAMEND)
        out     SPH, temp

        ldi     temp, 0xFF            ; Port B auf Ausgang
        out     DDRB, temp

        ldi     ocr_1, 0
        ldi     ocr_2, 1
        ldi     ocr_3, 10
        ldi     ocr_4, 20
        ldi     ocr_5, 80
        ldi     ocr_6, 127

        ldi     temp, 0b00000001      ; CS00 setzen: Teiler 1
        out     TCCR0, temp

        ldi     temp, 0b00000001      ; TOIE0: Interrupt bei Timer
Overflow
        out     TIMSK, temp

        sei

loop:   rjmp    loop

timer0_overflow:                      ; Timer 0 Overflow Handler
        inc     PWMCount              ; den PWM Zähler von 0 bis
        cpi     PWMCount, 128         ; 127 zählen lassen
        brne    WorkPWM
        clr     PWMCount

WorkPWM:
        ldi     temp, 0b11000000      ; 0 .. Led an, 1 .. Led aus

        cp      PWMCount, ocr_1       ; Ist der Grenzwert für Led 1
erreicht
        brlt    OneOn
        ori     temp, $01

OneOn:  cp      PWMCount, ocr_2       ; Ist der Grenzwert für Led 2
erreicht
        brlt    TwoOn
        ori     temp, $02

TwoOn:  cp      PWMCount, ocr_3       ; Ist der Grenzwert für Led 3
erreicht
        brlt    ThreeOn
        ori     temp, $04

ThreeOn:cp      PWMCount, ocr_4       ; Ist der Grenzwert für Led 4
erreicht
        brlt    FourOn
        ori     temp, $08

FourOn: cp      PWMCount, ocr_5       ; Ist der Grenzwert für Led 5
erreicht
        brlt    FiveOn
        ori     temp, $10

FiveOn: cp      PWMCount, ocr_6       ; Ist der Grenzwert für Led 6
erreicht
        brlt    SetBits
        ori     temp, $20

SetBits:                              ; Die neue Bitbelegung am Port
ausgeben
        out     PORTB, temp

        reti

Was ich bisher draus gemacht habe:
#include <avr/io.h>                /* das gibt den Controllertyp an */

#define temp r16

#define PWMCount r17

#define ocr_1 r18                      // Helligkeitswert Led1: 0 .. 127
#define ocr_2 r19                      // Helligkeitswert Led2: 0 .. 127
#define ocr_3 r20                      // Helligkeitswert Led3: 0 .. 127
#define ocr_4 r21                      // Helligkeitswert Led4: 0 .. 127
#define ocr_5 r22                      // Helligkeitswert Led5: 0 .. 127
#define ocr_6 r23                      // Helligkeitswert Led6: 0 .. 127

.org 0x0000
        rjmp    main                  // Reset Handler
.org OVF0addr
        rjmp    timer0_overflow       // Timer Overflow Handler

main:
        ldi     temp, LOW(RAMEND)     // Stackpointer initialisieren
        out     SPL, temp
        ldi     temp, HIGH(RAMEND)
        out     SPH, temp

        ldi     temp, 0xFF            // Port B auf Ausgang
        out     DDRB, temp

        ldi     ocr_1, 0
        ldi     ocr_2, 1
        ldi     ocr_3, 10
        ldi     ocr_4, 20
        ldi     ocr_5, 80
        ldi     ocr_6, 127

        ldi     temp, 0b00000001      // CS00 setzen: Teiler 1
        out     TCCR0, temp

        ldi     temp, 0b00000001      // TOIE0: Interrupt bei Timer
Overflow
        out     TIMSK, temp

        sei

loop:   rjmp    loop

timer0_overflow:                      // Timer 0 Overflow Handler
        inc     PWMCount              // den PWM Zähler von 0 bis
        cpi     PWMCount, 128         // 127 zählen lassen
        brne    WorkPWM
        clr     PWMCount

WorkPWM:
        ldi     temp, 0b11000000      // 0 .. Led an, 1 .. Led aus

        cp      PWMCount, ocr_1       // Ist der Grenzwert für Led 1
erreicht
        brlt    OneOn
        ori     temp, $01

OneOn:  cp      PWMCount, ocr_2       // Ist der Grenzwert für Led 2
erreicht
        brlt    TwoOn
        ori     temp, $02

TwoOn:  cp      PWMCount, ocr_3       // Ist der Grenzwert für Led 3
erreicht
        brlt    ThreeOn
        ori     temp, $04

ThreeOn:cp      PWMCount, ocr_4       // Ist der Grenzwert für Led 4
erreicht
        brlt    FourOn
        ori     temp, $08

FourOn: cp      PWMCount, ocr_5       // Ist der Grenzwert für Led 5
erreicht
        brlt    FiveOn
        ori     temp, $10

FiveOn: cp      PWMCount, ocr_6       // Ist der Grenzwert für Led 6
erreicht
        brlt    SetBits
        ori     temp, $20

SetBits:                              // Die neue Bitbelegung am Port
ausgeben
        out     PORTB, temp

        reti


kann mir da jemand helfen?

viele Grüße

Aike

von Peter D. (peda)


Lesenswert?

Schau Dir erstmal Dein Posting selber an und dann überlege, wie jemand 
da helfen können soll.

Niemand kann in Deinen Kopf sehen, also mußt Du schon sagen, wo Dich der 
Schuh drückt.

Niemand kann diesen Code so assemblieren, also Forenregel 5 beachten.


Der Assembler wird bestimmt eine Fehlermeldung ausgegeben haben, es ist 
unklug diese geheim zu halten oder im Wortlaut zu verändern.
Insbesondere die Zeilennummer in der Fehlermeldung sollte man mal näher 
untersuchen.


Peter

von Aike T. (biertrinker)


Lesenswert?

Nun ja, ich dache das es vielleicht in diesem Fall für einen etwas 
erfahreneren assembler Progrogrammierer ein leichtes ist die nötigen 
Änderungen zu erkennen.

Dann will ich die Probleme mal genauer aufdröseln.

Also, ich bekomme hier eine Warnung:

.org OVF0addr
        rjmp    timer0_overflow       ; Timer Overflow Handler

Warning: symbol "OVF0addr" undefined; zero assumed

warum ist OVR0addr nicht bekannt? Ok, sollte man wohl durch 0x07 
ersetzen können. Schön ist anders. Ausserdem habe ich irgendwo gelesen, 
das man .org nicht verwenden soll. Allerding ist mir an dieser Stelle 
nicht klar, wie ich Sprünge auf die Interupt-Handler anders da hin 
bekommen soll.

also weiter:

        ldi     temp, LOW(RAMEND)     // Stackpointer initialisieren
        out     SPL, temp
        ldi     temp, HIGH(RAMEND)
        out     SPH, temp

und die Fehlermeldungen:

../test.S:20: Error: garbage at end of line
../test.S:21: Error: number must be less than 64
../test.S:22: Error: garbage at end of line
../test.S:23: Error: number must be less than 64

ja, und dann noch die hier:

../test.S:57: Error: junk at end of line, first unrecognized character 
is `0'
../test.S:61: Error: junk at end of line, first unrecognized character 
is `0'
../test.S:65: Error: junk at end of line, first unrecognized character 
is `0'
../test.S:69: Error: junk at end of line, first unrecognized character 
is `0'
../test.S:73: Error: junk at end of line, first unrecognized character 
is `1'
../test.S:77: Error: junk at end of line, first unrecognized character 
is `2'

das sind jeweils die Zeilen mit dem $01 ...

viele Grüße

Aike

von holger (Gast)


Lesenswert?

Statt $01 nimm mal 0x01

von Johannes M. (johnny-m)


Lesenswert?

holger wrote:
> Statt $01 nimm mal 0x01
...Und statt LOW lo8 (und statt HIGH hi8), wenn ich mich recht 
entsinne...

(Alle Angaben ohne Gewähr)

Und dann schau in die Headerdatei vom ATMega8, wie da die Vektoradressen 
angegeben sind.

Es gibt hier im Forum übrigens auch für AVR-Assembler eine 
Formatierungsfunktion. Damit sieht der Code gleich viel besser und 
übersichtlicher aus.

von Skua C. (skua)


Lesenswert?

OVF0addr ist zwar in m8def.inc aber nicht in avr/io.h definiert.

org nicht verwenden ist unsinn.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

>      .......                // Stackpointer initialisieren
Das ist kein Assembler-Kommentar, sondern eben Garbage.
Kommentare im Assembler beginnen mit ';'
So etwa:
        .......                ; Stackpointer initialisieren

Ja, man sieht es genau: ein C-Programmierer  ;-)

von Aike T. (biertrinker)


Lesenswert?

@Lothar
  Ja, das stimmt, ist ein C-Kommentar. Das ist aber nicht der Fehler, 
der wird vom Preprozessor vor dem Assemblerlauf entfernt.

@allen anderen
  Ich setze gerade noch eure Tipps um!

von Aike T. (biertrinker)


Lesenswert?

So, ein Problem bleibt über:

       ldi     temp, lo8(RAMEND)
        out     SPL, temp
        ldi     temp, hi8(RAMEND)
        out     SPH, temp

führt zu:

../test.S:21: Error: number must be less than 64
../test.S:23: Error: number must be less than 64

was will der denn da?

viele Grüße

Aike

von holger (Gast)


Lesenswert?

Versuch mal das vor #include <avr/io.h>

#define _ASSEMBLER_ 1
#define __SFR_OFFSET 0

#include <avr/io.h>

von Stefan E. (sternst)


Lesenswert?

Aike Terjung wrote:
> So, ein Problem bleibt über:
>
>        ldi     temp, lo8(RAMEND)
>         out     SPL, temp
>         ldi     temp, hi8(RAMEND)
>         out     SPH, temp
>
> führt zu:
>
> ../test.S:21: Error: number must be less than 64
> ../test.S:23: Error: number must be less than 64
>
> was will der denn da?

out SPL, temp
->
out _SFR_IO_ADDR(SPL), temp

SPH entsprechend

von Aike T. (biertrinker)


Lesenswert?

@Stefan: Jep, das hilft, aber warum?

@holger: kann da keinen Unterschied bemerken, was soll das bewirken?

so, nun bleibt dieses Problem hier über:
1
17 .org 0x0000
2
18        rjmp    main                  // Reset Handler
3
19 .org 0x007
4
20         rjmp    timer0_overflow       // Timer Overflow Handler

führt zu:

../test.S:18: Error: odd address operand: 9

von Stefan E. (sternst)


Lesenswert?

Aike Terjung wrote:
> @Stefan: Jep, das hilft, aber warum?

Weil jedes IO-Register über 2 Adressen angesprochen werden kann, der 
IO-Adresse und der Memory-Mapped-Adresse. Die Register-Definitionen in 
den IO-Headern sind die Memory-Mapped-Adressen, weil das einfacher für C 
ist. Der Asm-Befehl out erwartet aber die IO-Adresse.

von Stefan E. (sternst)


Lesenswert?

Aike Terjung wrote:

> so, nun bleibt dieses Problem hier über:
>
>
1
17 .org 0x0000
2
> 18        rjmp    main                  // Reset Handler
3
> 19 .org 0x007
4
> 20         rjmp    timer0_overflow       // Timer Overflow Handler
5
>
>
> führt zu:
>
> ../test.S:18: Error: odd address operand: 9

Vermutlich arbeitet der gcc-as mit Byte-Adressen.

von Skua C. (skua)


Lesenswert?

Versuchs mal mit

.org 0x00E
       rjmp    timer0_overflow       // Timer Overflow Handler

Vieleicht zählt der ja nicht wortweise.

von Kernighan (Gast)


Lesenswert?

> ../test.S:18: Error: odd address operand: 9

sagte ja schon das 0x07 sicher nicht stimmen kann, weil 0x07 nicht even 
ist.

Cheers
Ritchie

von Aike T. (biertrinker)


Lesenswert?

Also, ich habe noch etwas weiter geforscht und gesucht. 1. Fehler ist 
wohl das die Adresse nicht 0x07 sondern 0x09 ist. Ganz so durchblicke 
ich das mit den Byte und Wort Adressen zwar noch nicht, aber für denn 
gcc müsste es dann ja 0x09*2 sein, also 0x12.
Damit assembliert das Programm schon mal durch, aber an den Ports 
passiert nix weiter.
Ich habe es inzwischen auch geschaft mittels Wine AVR Studio auf dem Mac 
zum laufen zu bringen. Jetzt kann ich damit zumindest den Simulator 
nutzen.
Vielleicht finde ich ja noch was dazu raus.

Ich pack trotzdem nochmal den Code dazu, vielleicht habe ich ja einfach 
noch einen Fehler den jemand entdecken kann.

vielen Dank

Aike
1
#include <avr/io.h>                /* das gibt den Controllertyp an */
2
3
#define temp r16
4
5
#define PWMCount r17
6
7
#define ocr_1 r18                      // Helligkeitswert Led1: 0 .. 127
8
#define ocr_2 r19                      // Helligkeitswert Led2: 0 .. 127
9
#define ocr_3 r20                      // Helligkeitswert Led3: 0 .. 127
10
#define ocr_4 r21                      // Helligkeitswert Led4: 0 .. 127
11
#define ocr_5 r22                      // Helligkeitswert Led5: 0 .. 127
12
#define ocr_6 r23                      // Helligkeitswert Led6: 0 .. 127
13
14
.org 0x12
15
        rjmp    timer0_overflow       // Timer Overflow Handler
16
17
.global main
18
main:
19
        ldi     temp, lo8(RAMEND) 
20
        out     _SFR_IO_ADDR(SPL), temp
21
        ldi     temp, hi8(RAMEND)
22
        out     _SFR_IO_ADDR(SPH), temp
23
24
        ldi     temp, 0xFF            // Port B auf Ausgang
25
        out     DDRB, temp
26
27
        ldi     ocr_1, 0
28
        ldi     ocr_2, 1
29
        ldi     ocr_3, 10
30
        ldi     ocr_4, 20
31
        ldi     ocr_5, 80
32
        ldi     ocr_6, 127
33
34
        ldi     temp, 0b00000001      // CS00 setzen: Teiler 1
35
        out     _SFR_IO_ADDR(TCCR0), temp
36
37
        ldi     temp, 0b00000001      // TOIE0: Interrupt bei Timer Overflow
38
        out     _SFR_IO_ADDR(TIMSK), temp
39
40
        sei
41
42
loop:   rjmp    loop
43
44
timer0_overflow:                      // Timer 0 Overflow Handler
45
        inc     PWMCount              // den PWM Zähler von 0 bis
46
        cpi     PWMCount, 128         // 127 zählen lassen
47
        brne    WorkPWM
48
        clr     PWMCount
49
50
WorkPWM:
51
        ldi     temp, 0b11000000      // 0 .. Led an, 1 .. Led aus
52
53
        cp      PWMCount, ocr_1       // Ist der Grenzwert für Led 1 erreicht
54
        brlt    OneOn
55
        ori     temp, 0x01
56
57
OneOn:  cp      PWMCount, ocr_2       // Ist der Grenzwert für Led 2 erreicht
58
        brlt    TwoOn
59
        ori     temp, 0x02
60
61
TwoOn:  cp      PWMCount, ocr_3       // Ist der Grenzwert für Led 3 erreicht
62
        brlt    ThreeOn
63
        ori     temp, 0x04
64
65
ThreeOn:cp      PWMCount, ocr_4       // Ist der Grenzwert für Led 4 erreicht
66
        brlt    FourOn
67
        ori     temp, 0x08
68
69
FourOn: cp      PWMCount, ocr_5       // Ist der Grenzwert für Led 5 erreicht
70
        brlt    FiveOn
71
        ori     temp, 0x10
72
73
FiveOn: cp      PWMCount, ocr_6       // Ist der Grenzwert für Led 6 erreicht
74
        brlt    SetBits
75
        ori     temp, 0x20
76
77
SetBits:                              // Die neue Bitbelegung am Port ausgeben
78
        out     PORTB, temp
79
80
        reti

von Stefan E. (sternst)


Lesenswert?

> out     DDRB, temp
> out     PORTB, temp

Wie gesagt, out benötigt die IO-Adresse.

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


Lesenswert?


von skua (Gast)


Lesenswert?

Ein

.org 0
       rjmp main

könnte helfen.

von Aike T. (biertrinker)


Lesenswert?

> Wie gesagt, out benötigt die IO-Adresse.

Ja, das stimmt, so weit hatte ich das gestern noch nicht durchdrungen, 
das hätte mir auffallen sollen.

Jörg Wunsch wrote:
> Siehe auch Beitrag "Re: avr-tutorial timer, interruptvektor ist nicht definiert"

der Beitrag hilft wirklich weiter. Jetzt habe ich glaube ich deutlich 
besser verstanden, wie das alles zusammen hängt.

Interessant ja vor allem auch für das Zusammenspiel zwischen C und ASM 
bestandteilen eines gemischten Programms.

viele Dank!

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.