Wie Assembler Code in eine ISR (C, AS7, GCC, ATmega)

Gast #4984068
Lesenswert?

Hallo Leute,

ich bin blutiger Anfänger also alles bitte ganz langsam.

Ich programmiere gerade in C eine ADC-Sample-Routine und habe von 
jemanden der das gleiche Projekt in BASCOM programmiert hat seine Timer0 
Comapare ISR in Assembler bekommen. Diese muss ich genauso d.h. in 
Assembler in meinen Code implementieren.
Ich habe das ganze wie unten zu sehen eingegeben aber die Syntax ist 
falsch und ich bekomme ständig Compilerfehler. Ich muss den Teil mit den 
Variablen irgendwie anders schreiben aber hab kein schimmer wie? Könnte 
mir jemand hierbei behilflich sein?

Habe global dafür 3 variablen:
1
volatile uint16_t sample_counter = 0;
2
volatile uint16_t audio_samples_buffer[1024];
3
volatile uint16_t Audio_sample_adress = 0;

Und hier meine ISR:
1
ISR(TIMER0_COMPA_vect)
2
{  
3
asm volatile(
4

5
      "push R27"  "\n\t"                              //X-Register High-Byte
6
      "push R26"  "\n\t"                              //X-Register Low-Byte
7
      "push R21"  "\n\t"
8
      "push R20"  "\n\t"
9
      "in   R20,SREG"  "\n\t"
10
      "push R20"  "\n\t"
11
      "in   R20,RAMPZ"  "\n\t"            //Kann auskommentiert werden
12
      "push R20"  "\n\t"                //Kann auskommentiert werden
13

14

15
      "LDS R20,ADCL"  "\n\t"                          //Load R20,R21 with ADCL,ADCH
16
      "LDS R21,ADCH"  "\n\t"
17

18
      "LDS R26,{Audio_sample_adress}"  "\n\t"         //Load X-Register pair with momentary audio_sample_adress
19
      "LDS R27,{Audio_sample_adress}+1"  "\n\t"
20
      "ST X+,R20"  "\n\t"                             //Store Low- and High-Byte of ADC-Sample to Audio-Sample-Array with Adress in X-Pointer; Post-Increment X-Counter
21
      "ST X+,R21"  "\n\t"
22
      "STS {Audio_sample_adress},R26"  "\n\t"         //Store incremented X-Register back to Audio_sample_adress
23
      "STS {Audio_sample_adress}+1,R27"  "\n\t"
24

25
      //*** Decrement Sample-Counter ***
26
      "LDS R26,{sample_counter}"  "\n\t"              //Load Sample-Counter to R26,R27
27
      "LDS R27,{sample_counter}+1"  "\n\t"
28
      "SBIW R26,1"  "\n\t"                            //Subtract 1
29

30
      "STS {sample_counter},R26"  "\n\t"              //Store back decremented Sample-Counter
31
      "LDS {sample_counter}+1,R27"  "\n\t"
32

33
      "BRNE jump_out"  "\n\t"                         //Jump to end of ISR if result<>0
34

35
      //*** Reset ADC-Interrupt Enable bit in ADCSRA as a flag for "ADC-Ready" ***
36
      "LDS R26, ADCSRA"  "\n\t"                       //Load R26 with ADCSRA
37
      "ANDI R26, &B11110111"  "\n\t"          //Reset bit 3 = ADIE by AND mith mask
38
      "STS ADCSRA,R26"  "\n\t"               //Store R26 back to ADCSRA
39

40

41
      //Jump_out:
42
      "POP R20"  "\n\t"                //Kann auskommentiert werden
43
      "OUT RAMPZ , R20"  "\n\t"            //Kann auskommentiert werden
44
      "POP R20"  "\n\t"
45
      "OUT SREG , R20"  "\n\t"
46
      "POP R20"  "\n\t"
47
      "POP R21"  "\n\t"
48
      "POP R26"  "\n\t"
49
      "POP R27"  "\n\t"
50

51
      "RETI"  "\n\t"                  //Return from ISR
52
);
53
}

Danke
Gast #4984144
Lesenswert?

Sorry, ich habe diesen Code wie gesagt bekommen, weil er in die ISR 
muss. Ob jetzt direkt oder über externe Datei ist mir egal...nur ich 
dachte nicht das dieser Schritt mich so lange aufhält und ich an meinem 
eigentlichen Projekt jetzt erstmal nicht weiter machen kann.
Gast #4984153
Lesenswert?

Ziegenpeter schrieb:
> "STS {sample_counter},R26"  "\n\t"              //Store back decremented
> Sample-Counter
> "LDS {sample_counter}+1,R27"  "\n\t"
> Außerdem müsste hier das LDS ein STS sein.

Das hat jetzt auch noch einen Error weniger gebracht.

Was bedeutet denn "garbage at end of line"?
1
ISR(TIMER0_COMPA_vect)
2
{  
3
asm volatile(
4

5
      "push R27"  "\n\t"                              //X-Register High-Byte
6
      "push R26"  "\n\t"                              //X-Register Low-Byte
7
      "push R21"  "\n\t"
8
      "push R20"  "\n\t"
9
      "in   R20,SREG"  "\n\t"
10
      "push R20"  "\n\t"
11
      "in   R20,RAMPZ"  "\n\t"            //Kann auskommentiert werden
12
      "push R20"  "\n\t"                //Kann auskommentiert werden
13

14

15
      "LDS R20,ADCL"  "\n\t"                          //Load R20,R21 with ADCL,ADCH
16
      "LDS R21,ADCH"  "\n\t"
17

18
      "LDS R26,Audio_sample_adress \n\t"         //Load X-Register pair with momentary audio_sample_adress
19
      "LDS R27,Audio_sample_adress +1 \n\t"
20
      "ST X+,R20"  "\n\t"                             //Store Low- and High-Byte of ADC-Sample to Audio-Sample-Array with Adress in X-Pointer; Post-Increment X-Counter
21
      "ST X+,R21"  "\n\t"
22
      "STS Audio_sample_adress,R26  \n\t"         //Store incremented X-Register back to Audio_sample_adress
23
      "STS Audio_sample_adress+1,R27  \n\t"
24

25
      //*** Decrement Sample-Counter ***
26
      "LDS R26,sample_counter} \n\t"              //Load Sample-Counter to R26,R27
27
      "LDS R27,sample_counter}+1  \n\t"
28
      "SBIW R26,1"  "\n\t"                            //Subtract 1
29

30
      "STS sample_counter,R26  \n\t"              //Store back decremented Sample-Counter
31
      "STS sample_counter+1,R27  \n\t"        //LDS in STS geändert laut forum
32

33
      "BRNE jump_out"  "\n\t"                         //Jump to end of ISR if result<>0
34

35
      //*** Reset ADC-Interrupt Enable bit in ADCSRA as a flag for "ADC-Ready" ***
36
      "LDS R26, ADCSRA"  "\n\t"                       //Load R26 with ADCSRA
37
      "ANDI R26, &B11110111"  "\n\t"          //Reset bit 3 = ADIE by AND mith mask
38
      "STS ADCSRA,R26"  "\n\t"               //Store R26 back to ADCSRA
39

40

41
      //Jump_out:
42
      "POP R20"  "\n\t"                //Kann auskommentiert werden
43
      "OUT RAMPZ , R20"  "\n\t"            //Kann auskommentiert werden
44
      "POP R20"  "\n\t"
45
      "OUT SREG , R20"  "\n\t"
46
      "POP R20"  "\n\t"
47
      "POP R21"  "\n\t"
48
      "POP R26"  "\n\t"
49
      "POP R27"  "\n\t"
50

51
      "RETI"  "\n\t"                  //Return from ISR
52
);
53
}
Moderator Persönliche Seite #4984167
Lesenswert?

Wofür denn das umständliche Assembler-Gewurschtel?

Das hier:
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3

4
uint16_t *Audio_sample_adress;
5
uint16_t sample_counter;
6

7
ISR(ADC_vect)
8
{
9
    *Audio_sample_adress = ADC;
10
    if (--sample_counter == 0)
11
        ADCSRA &= ~_BV(ADIE);  // no more interrupts
12
}

ergibt compiliert (für einen ATmega128 - ich sehe RAMPZ bei dir):
1
.global __vector_21
2
        .type   __vector_21, @function
3
__vector_21:
4
        push r1
5
        push r0
6
        in r0,__SREG__
7
        push r0
8
        clr __zero_reg__
9
        in r0,__RAMPZ__
10
        push r0
11
        push r24
12
        push r25
13
        push r30
14
        push r31
15
/* prologue: Signal */
16
/* frame size = 0 */
17
/* stack size = 8 */
18
.L__stack_usage = 8
19
        in r24,0x4
20
        in r25,0x4+1
21
        lds r30,Audio_sample_adress
22
        lds r31,Audio_sample_adress+1
23
        std Z+1,r25
24
        st Z,r24
25
        lds r24,sample_counter
26
        lds r25,sample_counter+1
27
        sbiw r24,1
28
        sts sample_counter+1,r25
29
        sts sample_counter,r24
30
        or r24,r25
31
        brne .L1
32
        in r24,0x6
33
        andi r24,lo8(8)
34
        out 0x6,r24
35
.L1:
36
/* epilogue start */
37
        pop r31
38
        pop r30
39
        pop r25
40
        pop r24
41
        pop r0
42
        out __RAMPZ__,r0
43
        pop r0
44
        out __SREG__,r0
45
        pop r0
46
        pop r1
47
        reti
48
        .size   __vector_21, .-__vector_21
49
        .comm   sample_counter,2,1
50
        .comm   Audio_sample_adress,2,1
51
        .ident  "GCC: (GNU) 5.4.0"

Ich seh' da jetzt nicht so große Unterschiede zu deinem handgefeilten
Assemblercode, dass sich der Zirkus irgendwie lohnen würde.
Gast #4984187
Lesenswert?

Jörg W. schrieb:
> Wofür denn das umständliche Assembler-Gewurschtel?
>
> Das hier:
> #include <avr/io.h>
> #include <avr/interrupt.h>
>
> uint16_t *Audio_sample_adress;
> uint16_t sample_counter;
>
> ISR(ADC_vect)
> {
>     *Audio_sample_adress = ADC;
>     if (--sample_counter == 0)
>         ADCSRA &= _BV(ADIE);  // no more interrupts
> }

Das heisst, ich würde mir den ganzen Assembler Code mit deiner ISR 
sparen?

Das Ganze ist zeitkritisch und alle ISR müssen in Assembler programmiert 
werden (Aussage von meinem Auftraggeber des Techniker Projektes)...

Das Prorogramm soll mit Timer0 im CTC Modus mit einer Samplerate von 
10kHz werte ins Array schreiben. Mit diesen Werten soll später eine 
Sinuswelle gezeichnet, TrueRMS berechnet werden und zum krönenden 
Abschluss eine FFT realisiert werden.
Moderator Persönliche Seite #4984200
Lesenswert?

AC schrieb:
> Das heisst, ich würde mir den ganzen Assembler Code mit deiner ISR
> sparen?

Ja.

> Das Ganze ist zeitkritisch und alle ISR müssen in Assembler programmiert
> werden

Naja, wirklich klein ist das Original nun auch nicht gerade.

Zugegeben, der Compiler generiert drei oder vier Befehle mehr, aber
man müsste dann schon die Erbsen zählen gehen um zu sehen, ob die das
Kraut fett machen dass es sich lohnt, irgendwelchen Assemblercode
dafür handzufeilen.

> (Aussage von meinem Auftraggeber des Techniker Projektes)

Wann hat der das letzte Mal einen aktuellen Compiler und dessen
generierten Code gesehen?

Also wenn es vorgeschrieben ist, weil es zu einer studentischen Übung
gehört, dann musst du die ISR natürlich in Assemblercode hacken.  Dann
nimm aber besser gleich eine separate Assemblerdatei.  Wenn's nur
wegen der Performance ist, würde ich erstmal analysieren, ob nicht das
Compilat genauso hinreichend ist.  Dass der äquivalente C-Code besser
lesbar ist, wird wohl kaum einer bestreiten wollen. :-)

Ich zähle jetzt irgendwie reichlich 60 Takte für das Compilat, das
wären bei angenommenen 16 MHz CPU-Takt etwa 4 µs, sagen wir 5 µs (der
Interrupt hat ja auch noch einen Overhead).  Bei 10 kHz Aufruffrequenz
braucht diese ISR folglich 50 ms pro Sekunde oder 5 % der CPU-Zeit.
Moderator Persönliche Seite #4984218
Lesenswert?

AC schrieb:

Btw:

> Habe global dafür 3 variablen:volatile uint16_t sample_counter = 0;
> volatile uint16_t audio_samples_buffer[1024];
> volatile uint16_t Audio_sample_adress = 0;

"volatile" würde ich die allerdings nicht machen, wenn die ISR in
C geschrieben wird, sonst zwingst du den Compiler zu deutlich
schlechterem Code.

Audio_sample_adress hat übrigens den falschen Typ; das sollte ein
Zeiger sein, kein Integer (so habe ich es in meinem Beispiel auch).

Ich vermute, dass "sample_counter" vom herumliegenden Programm zur
Synchronisation benutzt wird, oder?  Dann kann *) man diese Variable
natürlich "volatile" machen, dann muss in der ISR jedoch mit einer
Schattenkopie davon gearbeitet werden:
1
ISR(ADC_vect)
2
{
3
    *Audio_sample_adress = ADC;
4
    uint16_t sc = sample_counter;
5
    if (--sc == 0)
6
        ADCSRA &= ~_BV(ADIE);  // no more interrupts
7
    sample_counter = sc;
8
}

Das vermeidet die Pessimierung des Variablenzugriffs, die sonst
eintreten würde.  Denk aber dran, dass du außerhalb der ISR auf
diese Variable über die atomic-Makros (<util/atomic.h>) zugreifen
musst, ansonsten bekommst du subtile Fehler in dein Programm, wenn
der Interrupt genau in der Mitte des 16-bit-Zugriffs reinhaut.

*) Man kann auch den Speicherzugriff nur außerhalb erzwingen, aber
das geht jetzt ein bisschen zu weit.
Gast #4984228
Lesenswert?

Jörg W. schrieb:
> Also wenn es vorgeschrieben ist, weil es zu einer studentischen Übung
> gehört, dann musst du die ISR natürlich in Assemblercode hacken.

Nein, das ist es eben nicht...er ist nur der Meinung das dieser asm code 
zeit unkritisch ist und ich mit C Probleme haben werde.

Der Atmega läuft mit 20MHz. Könntest du mir deinen Code noch etwas 
erklären?

Ich muss die Werte ja in ein Array[1024] abspeichern, das müsste ich 
noch mit einer for Schleife erledigen?
Moderator Persönliche Seite #4984238
Lesenswert?

AC schrieb:

> Der Atmega läuft mit 20MHz.

Dann wird die Zeitbilanz noch 20 % besser als meine Rechnung.

> Könntest du mir deinen Code noch etwas
> erklären?

Naja, was ist denn daran so viel zu erklären?

Es ist eigentlich schlicht das Äquivalent deines Assemblercodes,
nicht weniger, aber auch nicht mehr.

> Ich muss die Werte ja in ein Array[1024] abspeichern, das müsste ich
> noch mit einer for Schleife erledigen?

Hat deine Assemblerversion doch auch nicht. ;-)

Eine for-Schleife kannst du nur innerhalb einer Funktion benutzen,
du musst hier jedoch das Weiterzählen über die einzelnen ISR-Aufrufe
hinweg machen.

Das Initialisieren von Audio_sample_adress und sample_counter muss
natürlich vor dem ersten Interrupt passieren; das ist daher weder
in meiner C-ISR noch in deinem geposteten Assemblercode enthalten.

> er ist nur der Meinung das dieser asm code zeit unkritisch ist und ich
> mit C Probleme haben werde.

Dann solltest du natürlich in der Arbeit bei der Diskussion des
Programms darauf eingehen, wie groß der vom Compiler erzeugte Code
tatsächlich ist und wie lange er braucht (also im Prinzip das, was ich
oben überschlagen habe, nur exakt nachgerechnet).
Gast #4984247
Lesenswert?

Ziegenpeter schrieb:
> Falls dir die c Version von Jörg Wunsch nicht reicht,
> das Garbage ist warscheinlich das hier: &B11110111
> Ist wahrscheinlich BASCOM Schreibweise, in c/asm müsste das 0b11110111
> sein.

Jep, das wurde in BASCOM geschrieben. Wenn ich Notation in 0b11110111 
ändere spuckt mir der compiler tastächlich noch mehr Fehler aus:

undefined reference to SREG
undefined reference to RAMPZ
undefined reference to ADCL
undefined reference to ADCH
undefined reference to jump out
undefined reference to ADCSRA und noch ein paar mehr, insg 12.


Der Timer0 läuft im CTC im 10kHz. bzw. löst die ISR richtig aus. Jetzt 
muss ich irgendwie die Werte abspeichern...

Was mach ich denn jetzt am besten?
Moderator Persönliche Seite #4984254
Lesenswert?

AC schrieb:
> Der Timer0 läuft im CTC im 10kHz. bzw. löst die ISR richtig aus.

Meine ISR ist übrigens für den ADC, nicht für den Timer.
Schließlich willst du ja ADC-Werte lesen, die solltest du dann lesen,
wenn der ADC auch fertig mit der Wandlung ist.

Deine gepostete ISR ist da irgendwie inkonsistent: sie gehört zum
Timer, aber wenn alle Wandlungen fertig sind, setzt sie (was ich
wieder korrekt finde) das ADIE im ADCSRA zurück.  Dennoch würde jedoch
der Timer fleißig weiter Interrupts triggern, folglich munter weiter
Audio_sample_adress erhöhen und an die entsprechende Stelle schreiben …

Wenn du den Timer als Trigger für den ADC benutzen willst, dann solltest
du dir im ADC das "auto trigger"-Feature ansehen und dort den Timer
als Triggerquelle nehmen, aber die Ergebnisse in der ADC-ISR lesen.
Gast #4984267
Lesenswert?

Jep, der ADC wird noch mit auto-trigger und timer compare match und die 
Variablen vor jedem sample zyklus initialisiert...ich denke, dass ich 
das hinkriegen würde aber die ISR an sich macht mir kopfzerbrechen, 
weswegen ich das mit dem inline assembler zuerst testen wollte.
Persönliche Seite #4984298
Lesenswert?

Mach's in Assembler. avr-gcc -x assembler-with-cpp ...
1
#define __SFR_OFFSET 0
2
#include <avr/io.h>
3

4
.text
5

6
.type   TIMER0_COMPA_vect, @function
7
.global TIMER0_COMPA_vect
8

9
TIMER0_COMPA_vect:
10

11
    push R27
12
    push R26
13
    push R21
14
    push R20
15
    in   R20, SREG
16
    push R20
17
    in   R20, RAMPZ
18
    push R20
19

20
    LDS R20, ADCL
21
    LDS R21, ADCH
22

23
    LDS R26, Audio_sample_adress
24
    LDS R27, Audio_sample_adress+1
25
    ST  X+, R20
26
    ST  X+, R21
27
    STS Audio_sample_adress, R26
28
    STS Audio_sample_adress+1, R27
29

30
    LDS R26, sample_counter
31
    LDS R27, sample_counter+1
32
    SBIW R26, 1
33
    STS sample_counter, R26
34
    STS sample_counter+1, R27
35

36
    BRNE jump_out
37

38
    LDS  R26, ADCSRA
39
    ANDI R26, 0b11110111
40
    STS  ADCSRA, R26
41

42
jump_out:
43
    POP R20
44
    OUT RAMPZ, R20
45
    POP R20
46
    OUT SREG, R20
47
    POP R20
48
    POP R21
49
    POP R26
50
    POP R27
51

52
    RETI
53

54
.size TIMER0_COMPA_vect, . - TIMER0_COMPA_vect


Kommentare sind Hausaufgabe.
Gast #4984831
Lesenswert?

Moin zusammen,

sorry das ich mich gestern nicht mehr gemeldet habe, war unterwegs und 
musste/muss mich noch in "Zeiger" einlesen und die ADC Register 
einstellen/verstehen.

Werde das ganze jetzt erstmal mit dem C Code versuchen:

Jörg W. schrieb:
> ISR(ADC_vect)
> {
>     *Audio_sample_adress = ADC;
>     uint16_t sc = sample_counter;
>     if (--sc == 0)
>         ADCSRA &= ~_BV(ADIE);  // no more interrupts
>     sample_counter = sc;
> }


hauptsache ich bekomme das erstmal irgendwie zum laufen zu bringen, das 
mir irgendwelche Werte ausgegeben werden.

Johann L. schrieb:
> Mach's in Assembler. avr-gcc -x assembler-with-cpp ...
> #define __SFR_OFFSET 0
> #include <avr/io.h>
>
> .text
>
> .type   TIMER0_COMPA_vect, @function
> .global TIMER0_COMPA_vect
>
> TIMER0_COMPA_vect:
>
>     push R27
>     push R26
>     push R21
>     push R20
>     in   R20, SREG
>     push R20
>     in   R20, RAMPZ
>     push R20
>
>     LDS R20, ADCL
>     LDS R21, ADCH
>
>     LDS R26, Audio_sample_adress
>     LDS R27, Audio_sample_adress+1
>     ST  X+, R20
>     ST  X+, R21
>     STS Audio_sample_adress, R26
>     STS Audio_sample_adress+1, R27
>
>     LDS R26, sample_counter
>     LDS R27, sample_counter+1
>     SBIW R26, 1
>     STS sample_counter, R26
>     STS sample_counter+1, R27
>
>     BRNE jump_out
>
>     LDS  R26, ADCSRA
>     ANDI R26, 0b11110111
>     STS  ADCSRA, R26
>
> jump_out:
>     POP R20
>     OUT RAMPZ, R20
>     POP R20
>     OUT SREG, R20
>     POP R20
>     POP R21
>     POP R26
>     POP R27
>
>     RETI
>
> .size TIMER0_COMPA_vect, . - TIMER0_COMPA_vect

Das habe ich auch mit einer externen .S Datei versucht, was aber auch 
nicht funktioniert und bei mir noch mehr Fragezeichen aufwirft.
Gast #4984841
Lesenswert?

AC schrieb:
>       "BRNE jump_out"  "\n\t"      //Jump to end of ISR if result<>0
> ...
>        "LDS {sample_counter}+1,R27"  "\n\t"
>
>       //Jump_out:
>       "POP R20"  "\n\t"                //Kann auskommentiert werden

> und ich bekomme ständig Compilerfehler.

Vielleicht vermisst der Assembler das Label für das Sprungziel?
Persönliche Seite #4985075
Lesenswert?

AC schrieb:
> Johann L. schrieb:
>> Mach's in Assembler. avr-gcc -x assembler-with-cpp ...
>> #define __SFR_OFFSET 0
>> #include <avr/io.h>
>> ...
>
> Das habe ich auch mit einer externen .S Datei versucht, was aber auch
> nicht funktioniert

Ich habe lediglich deinen Code nach GNU-Assembler übertragen.  Lässt 
sich Problemlos für einen ATmega168 assemblieren.

Die Bugs, die aufs Original-Posting zurückgehen, musst schon selbst 
beheben...

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren