Forum: Mikrocontroller und Digitale Elektronik Laufzeitproblem


von Detlef (Gast)


Lesenswert?

Hallo v.a. an alle Assembler-Götter.

Ich habe ein kleines Problem mit der Laufzeit - auf meinem ATMEGA32 mit 
16MHz liegt auf INT2 ein Clock-Signal. Das Clock-Signal kommt alle paar 
Sekunden und taktet dann 80 Bits, die gesampelt werden sollen.


Das Sampling des Datenpins erfolgt auf fallender Flanke des 
Clock-Signals.
Der Abstand zwischen den fallenden Flanken ist 3.3µs.
Insgesamt kommen 80 Takte (80 Bits) die gesampelt werden sollen.

In C sieht die IRQ so aus:
1
ISR(INT2_vect)
2
{
3
  r[r_cnt] =  PIN_R & (1 << DATA_R);
4
  r_cnt++;
5
}

Assembler mit -os:
1
ISR(INT2_vect)        
2
{        
3
      d8:  1f 92         push  r1  
4
      da:  0f 92         push  r0  
5
      dc:  0f b6         in  r0, 0x3f  ; 63
6
      de:  0f 92         push  r0  
7
      e0:  11 24         eor  r1, r1  
8
      e2:  8f 93         push  r24  
9
      e4:  9f 93         push  r25  
10
      e6:  ef 93         push  r30  
11
      e8:  ff 93         push  r31  
12
  r[r_cnt] =  PIN_R & (1 << DATA_R);      
13
      ea:  90 91 92 01   lds  r25, 0x0192  
14
      ee:  80 b3         in  r24, 0x10  ; 16
15
      f0:  e5 e4         ldi  r30, 0x45  ; 69
16
      f2:  f1 e0         ldi  r31, 0x01  ; 1
17
      f4:  e9 0f         add  r30, r25  
18
      f6:  f1 1d         adc  r31, r1  
19
      f8:  80 78         andi  r24, 0x80  ; 128
20
      fa:  80 83         st  Z, r24  
21
  r_cnt++;      
22
      fc:  9f 5f         subi  r25, 0xFF  ; 255
23
      fe:  90 93 92 01   sts  0x0192, r25  
24
}        
25
     102:  ff 91         pop  r31  
26
     104:  ef 91         pop  r30  
27
     106:  9f 91         pop  r25  
28
     108:  8f 91         pop  r24  
29
     10a:  0f 90         pop  r0  
30
     10c:  0f be         out  0x3f, r0  ; 63
31
     10e:  0f 90         pop  r0  
32
     110:  1f 90         pop  r1  
33
     112:  18 95         reti        
34
  }


Dauer von 48 Cycles bei 16MHz sind 3µs. Nun würde ich gerne wissen, ob 
es eine Implementierung in Assembler gibt, die ich mit meinem C-Projekt 
nutzen kann.

Wäre für Tipps oder Beispiele dankbar.

Gruß,
Detlef

von Peter II (Gast)


Lesenswert?

wirklich viel zu machen ist da nicht. selbst mit ASM sieht es nicht so 
aus ob man da noch etwas sparen kann.

Wenn man die aktuelle adresse  r[r_cnt] in Register sich merken könnte, 
dann spart man etwas. Aber diese möglichkeit besteht nur wenn man alles 
in ASM programmiert.

Dann würde das laden und speichern von r_cnt wegfallen sowie die 
addition.

Teste mal was bei C mit folgenden Code:

uint8_t* WritePos = r;

ISR(INT2_vect)
{
  *WritePos = PIN_R & (1 << DATA_R);
  WritePos++;
}

von MaWin (Gast)


Lesenswert?

> Dauer von 48 Cycles bei 16MHz sind 3µs. Nun würde ich gerne wissen, ob
> es eine Implementierung in Assembler gibt, die ich mit meinem C-Projekt
> nutzen kann.

Ein INTERRUPT um schnelle Signale zu bearbeiten ?
Ja bist du denn mit dem Klammerbeutel gepudert ?

Es werden alle Bits in einer Schleife eingesammelt
und erst nach 264us die Interrupt-Routine verlassen
(mna knn auhc ganz stumpf 256 mal im 1.1us Abstand
die beiden Bits CLD und DATA einsammeln und erst
hinterher auswerten, ob das Datenmust welches nach
dem Interrupt kam irgendwie passend ist oder nur ein
Störsignal war, dann klappt das sogar mit einem 2MHz
uC).

Wenn das deine sonstigen Prozesse stört, baust du in
die Interrupt-Routine noch Befehle ein, die innerhalb
der 3.3us Wartezeit diese -falls nötig- bearbeiten,
viele Befehle dürfen es nicht sein, falls es mehr sind
weisst du sowieso schon, daß dein uC dafür zu langsam
ist.

Wollte man wirklich Assembler programmieren, sorgt
man erst mal dafür, daß bestimmte Register im Hauptprogramm
nicht verwendet werden, so daß sie in der Interrupt-Routine
nicht gerettet werden müssen, und andere Werte vor dem ersten
Interrupt passend initialisiert sind. Dann besteht die nur noch
aus

*data=INPUT_PORT
data++;

Erst HINTERHER wird das passende bit aus dem PORT maskiert.

Übrigens hast du mit deiner Interrupt-Routine noch das Riesen
Problem, daß du nicht merkst, wenn nur 79 statt 80 Interrupts
kommen weil einer verloren gint. Nach dem ersten Interrupt
musst du nach 280us abbrechen und die Botschaft verwerfen,
noch exakter müsstest du sogar bei jedem Interrupt gucken,
ob der vorherige 3-3.6us her war, und wenn nicht, dieses als
Störung verwerfen.

Das alles geht in einem nicht per Interrupt sondern per Laufzeit
der Befehle angepassten Assemblerprogramm viel einfacher zu
beachten.

von Der (Gast)


Lesenswert?

Falls es die Pinbelegung auf deinem Board zulässt, könntest du auch SPI 
verwenden. µC als Slave, MOSI als Dateneingang und CLK als Takteingang. 
Nach jeweils 8 Bits bekommst du einen Interrupt und kannst in 8*3,5µs = 
28µs bequem das komplette Byte kopieren.

von Markus W. (Firma: guloshop.de) (m-w)


Lesenswert?

Peter II schrieb:
> wirklich viel zu machen ist da nicht. selbst mit ASM sieht es nicht so
> aus ob man da noch etwas sparen kann.

Naja, nach grober Schätzung würde ich sagen, dass man etwa 40 bis 50 % 
weniger Takte brauchen würde. C ist bei solchen Dingen immer etwas 
umständlich.

Wenn man das ganze Programm in Assembler schreiben würde, hätte man 
noch mehr Einfluss auf die Registerbelegung, dann könnte man 
wahrscheinlich 70 bis 80 % der Takte sparen.

> Wenn man die aktuelle adresse  r[r_cnt] in Register sich merken könnte,
> dann spart man etwas. Aber diese möglichkeit besteht nur wenn man alles
> in ASM programmiert.

Richtig!

Was ohne viel Aufwand auch ein bisschen Beschleunigung bringen würde, 
wäre natürlich, auf einen aktuellen Baustein umzusteigen: ATmega324A. 
Der schafft dann 20 MHz Takt und nicht nur 16 wie das alte Modell.

von Peter II (Gast)


Lesenswert?

Markus Weber schrieb:
> Naja, nach grober Schätzung würde ich sagen, dass man etwa 40 bis 50 %
> weniger Takte brauchen würde. C ist bei solchen Dingen immer etwas
> umständlich.

da bin ich ja gespannt, sagt doch mal was du alles einsparen willst (nur 
die umsetung von C nach ASM ohne globale änderungen).

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Eine Abkürzung geht zum Beispiel so:
1
  push r24
2
  in   r24, __SREG__
3
  push r24
4
  push r30
5
  push r31
6
7
  lds  r30, r_cnt
8
  inc  r30
9
  sts  r_cnt, r30
10
  clr  r31
11
  subi r30, lo8(-(r-1))
12
  sbci r31, hi8(-(r-1))
13
  in   r24, 0x16
14
  andi r24, 1 << 7
15
  st   Z, r24
16
17
  pop  r31
18
  pop  r30
19
  pop  r24
20
  out  __SREG__, r24
21
  pop  r24
22
  reti
 
Verwendet man einen Zeiger zum Speichern (hier addr), sieht es so aus:
 
1
  push r24
2
  in   r24, __SREG__
3
  push r24
4
  push r30
5
  push r31
6
7
  lds  r30, addr
8
  lds  r31, addr+1
9
  in   r24, 0x16
10
  andi r24, 1 << 7
11
  st   Z+,  r24
12
  sts  r30, addr
13
  sts  r31, addr+1
14
15
  pop  r31
16
  pop  r30
17
  pop  r24
18
  out  __SREG__, r24
19
  pop  r24
20
  reti
 
Hier ist nicht einzusehen, warum das Extrahieren des Bits in der ISR 
geschehen muss.  Das kann ebenso nach dem Samplen während der Auswertung 
geschehen.  Damit vereinfacht sich die ISR weiter, denn SREG wird nicht 
verändert:
 
1
  push r24
2
  push r30
3
  push r31
4
5
  lds  r30, addr
6
  lds  r31, addr+1
7
  in   r24, 0x16
8
  st   Z+,  r24
9
  sts  r30, addr
10
  sts  r31, addr+1
11
12
  pop  r31
13
  pop  r30
14
  pop  r24
15
  reti

von Peter II (Gast)


Lesenswert?

Johann L. schrieb:
> Eine Abkürzung geht zum Beispiel so:

das hast du glück gehabt mehr als 40% weniger.

Auf die Idee mit dem

subi r30, lo8(-(r-1))
sbci r31, hi8(-(r-1))

bin ich beim überfliegen nicht gekommen.

von MaWin (Gast)


Lesenswert?

>  push r24
>  push r30
>  push r31
>
>  lds  r30, addr
>  lds  r31, addr+1
>  in   r24, 0x16
>  st   Z+,  r24
>  sts  r30, addr
>  sts  r31, addr+1

>  pop  r31
>  pop  r30
>  pop  r24
>  reti

Das entspricht ja meinem Vorschlag, Pointer und Bits hinterher 
maskieren.

Wenn man die Register 24, 30, 31 nun noch reserviert

-ffixed-24 -ffixed-30 -ffixed-31

entfällt das push und pop (vorausgesetzt man hat ALLEN C Code damit 
kompiliert, auch die verwendeten library Funktionen) und initialisisren 
der Register r30 und r31 auf addr (das hat dann schon das Rahmenprogramm 
gemacht)

  in   r24, 0x16
  st   Z+,  r24
  reti

Allerdings sind r30/r31 zu wichtig, um sie zu reservieren, man sollte 
eher r26/r27 nehmen

  lds  r26, addr       ;  Rahmenprogramm
  lds  r27, addr+1

-ffixed-24 -ffixed-26 -ffixed-27

ISR(INT2_vect)
{
  in   r24, 0x16
  st   X+,  r24
  reti
}

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

MaWin schrieb:
> Wenn man die Register 24, 30, 31 nun noch reserviert
>
> -ffixed-24 -ffixed-30 -ffixed-31

Damit wirst du kein lauffähiges Programm mehr hinbekommen.

von Detlef (Gast)


Lesenswert?

@PeterII:
Der Versuch erbrachte 53 Zyklen, also 5 Zyklen mehr.

@MaWin
>Ja bist du denn mit dem Klammerbeutel gepudert ?
Nein, aber es gibt immer wieder so Deppen im Netz, stimmts?

In der ISR per Schleife lassen sich die Daten nicht sammeln, da die 
Clock-Takte nicht in zeitlich konstanten Abstand kommen.Mal ist der 
Abstand 3.5µs, mal 4, mal 5.5, ...

D.h. in der Schleife müsste ich den Clk-Pin sampeln und speichern um die 
Flanke zu bekommen. Würde in C oder so ähnlich aussehen
1
ISR(INT2_vect)
2
{
3
  do
4
  {
5
    // Samplen und Speichern des CLK Pins
6
    if(PINB & (1 << PINB2))
7
      prev = 0x01;
8
    else
9
    {
10
      // Falls fallende Flanke
11
      if(
12
          (prev == 0x01)  // Clock-Pin voriges Sample
13
      )
14
      {
15
        r[r_cnt] =  PINB & (1 << PINB2);
16
        r_cnt++;
17
        prev=0;
18
      }
19
    }
20
21
    irq_cnt++;
22
  } while(irq_cnt<1000); // in Schleife bleiben bis sicher mehr als genug Zeit vorbei ist für die 80 bits

Man sieht schon, es werden zu viele Statements um jedes Clk-Signal 
erkennen zu können. Also Danke für den Tipp, war aber leider nichts.

@Johann: Das Entfernen der Maskierung des Bits in C hat einen Zyklus 
gebracht. Allerdings wird C nichts werden (hab einen Test gefahren und 
in der ISR nur einen Pin aktiviert, selbst so vergehen zwischen Flanke 
und Pin hoch >2µs).

D.h. ich werds mit Inline-Assembler versuchen. Seit dem Studium ist 
einige Zeit vorbei, mal schauen wie weit ich komme.

von Detlef (Gast)


Lesenswert?

Korrektur, muss kein Inline sein, da die ganze IR-Routine in ASM 
geschrieben wird.

von Ingo (Gast)


Lesenswert?

Welche Auswirkungen hat der Optimizer?


Ingo

von Peter D. (peda)


Lesenswert?

Detlef schrieb:
> Das Clock-Signal kommt alle paar
> Sekunden und taktet dann 80 Bits, die gesampelt werden sollen.

Hörst Du nicht die lauten Schreie?

Das SPI ruft doch ganz laut: "Hier, nimm mich dafür".
Das sind dann 10 Interrupts und Du hast 8 * 3,3µs Zeit, das Byte bequem 
im Interrupt abzuholen.


Peter

von Detlef (Gast)


Lesenswert?

@Ingo: Falls du den C-Optimizer meinst - hab alle Optionen durch, der 
generierte Assembler-Code ist bei o3 und os gleich und am kleinsten.

@Peter Danegger: SPI ist bereits in Benutzung, sorry.

von Peter D. (peda)


Lesenswert?

Detlef schrieb:
> @Peter Danegger: SPI ist bereits in Benutzung, sorry.

Wirklich als Slave?

SPI-Master geht auch super als Bit-Banging mit beliebigen Pins.
Oder noch bequemer mit einer der UARTs des ATmega324.


Peter

von Verwirrter Anfänger (Gast)


Lesenswert?

Detlef schrieb:
> In der ISR per Schleife lassen sich die Daten nicht sammeln, da die
> Clock-Takte nicht in zeitlich konstanten Abstand kommen.Mal ist der
> Abstand 3.5µs, mal 4, mal 5.5, ...
>
> D.h. in der Schleife müsste ich den Clk-Pin sampeln und speichern um die
> Flanke zu bekommen. Würde in C oder so ähnlich aussehen
> ISR(INT2_vect)
> {
>   do
>   {
>     // Samplen und Speichern des CLK Pins
>     if(PINB & (1 << PINB2))
>       prev = 0x01;
>     else
>     {
>       // Falls fallende Flanke
>       if(
>           (prev == 0x01)  // Clock-Pin voriges Sample
>       )
>       {
>         r[r_cnt] =  PINB & (1 << PINB2);
>         r_cnt++;
>         prev=0;
>       }
>     }
>
>     irq_cnt++;
>   } while(irq_cnt<1000); // in Schleife bleiben bis sicher mehr als genug Zeit 
vorbei ist für die 80 bits
>
> Man sieht schon, es werden zu viele Statements um jedes Clk-Signal
> erkennen zu können. Also Danke für den Tipp, war aber leider nichts.


Grundsätzlich wär mir unwohl den ISR für 400 mS zu blockieren, aber
Was wäre denn mit:
1
ISR(INT2_vect){
2
  uint8_t count = 79;
3
  
4
  *r =  PINB; // erstes bit speichern
5
  r++;
6
  
7
  while(count--){
8
    while(!(PINB & (1 << PINB2))) {
9
      // warten bis clock high wird
10
    }
11
12
    while((PINB & (1 << PINB2))) {
13
      // warten bis clock low wird
14
    }
15
    *r =  PINB; // naechstes bit speichern
16
    r++;
17
  }
18
  
19
}

Grob geschätzt würd ich sagen, dass das schnell genug sein sollte.

von Detlef (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Wirklich als Slave?
>
> SPI-Master geht auch super als Bit-Banging mit beliebigen Pins.
> Oder noch bequemer mit einer der UARTs des ATmega324.
>
>
> Peter

Der µC ist ein SPI Slave, ein ATMEGA32 ist bereits gesetzt. Der eine 
UART ist ebenfalls schon in Benutzung. Es muss also mit INT2 gehen.

Verwirrter Anfänger schrieb:
> Grob geschätzt würd ich sagen, dass das schnell genug sein sollte.
Lieber messen und dann posten. Dein Code ist die langsamste Variante, 
mit 66 Zyklen.

von Ingo (Gast)


Lesenswert?

Was macht dein Code wenn keine 80 Bits ankommen? Aha, n langes 
Gesicht... Da muss mindestens ein Abbruch rein, aber dann wird wieder 
eng. SPI ist Denk ich mal das Beste

von Detlef (Gast)


Lesenswert?

Johann L. schrieb:
> Hier ist nicht einzusehen, warum das Extrahieren des Bits in der ISR
> geschehen muss.  Das kann ebenso nach dem Samplen während der Auswertung
> geschehen.  Damit vereinfacht sich die ISR weiter, denn SREG wird nicht
> verändert:
>    push r24
>   push r30
>   push r31
>
>   lds  r30, addr
>   lds  r31, addr+1
>   in   r24, 0x16
>   st   Z+,  r24
>   sts  r30, addr
>   sts  r31, addr+1
>
>   pop  r31
>   pop  r30
>   pop  r24
>   reti

Johann, dein Vorschlag erscheint mir am sinnvollsten. Aber eine Frage, 
da ich ja nicht so fit bin mit Assembler. Müsste es nicht

>   sts  addr, r30
>   sts  addr+1, r31

heißen?

Gruß
Detlef

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Detlef schrieb:
> Müsste es nicht
>
>>   sts  addr, r30
>>   sts  addr+1, r31
>
> heißen?

Jepp.  Typo.

Ein paar weitere, erbärmliche Ticks lassen sich sparen, indem zB addr in 
einem globalen Register gehalten wird.  Allerdings gibt's ein paar Haken 
und Ösen dann:

1) Kein Modul (auch nicht aus einer Bibliothek) darf die Register
   verwenden.

2) Globale Register sind nicht volatile, was die Synchronisierung
   schwer handhabbar macht.

3) ABI beachten

Man muss also genau wissen, was man tut :o)

von Peter II (Gast)


Lesenswert?

Johann L. schrieb:
> Globale Register sind nicht volatile, was die Synchronisierung
> schwer handhabbar macht.

register sind doch immer volatile

und was sollen nicht globele Register sein?

von Davis (Gast)


Lesenswert?

Peter II schrieb:

> ... und was sollen nicht globele Register sein?

Lokale.

von MaWin (Gast)


Lesenswert?

>   while(!(PINB & (1 << PINB2))) { // warten bis clock high wird
>   while((PINB & (1 << PINB2))) { // warten bis clock low wird

Geile Endlosscheifen.
Schreibt ihr eigentlich immer Programme, die nur bei schönen
Wetter funktionieren und wenn mal 79 statt 80 Takte kommen
quasi abgestürzt sind, und -noch schlimmer- sich nur sehr
schwer wieder synchronisieren (sie warten auf weitere 79
aus Zufall fehlende Bits...)
    }
> Damit wirst du kein lauffähiges Programm mehr hinbekommen.

Drum schrieb ich ja drunter, was man besser machen sollte.
Da hast du wohl schon dein Aufmerksamskeitsdefizit bekommen.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Auch denkbar ist, des zu füllende Objekt (r) an eine bestimmte Adresse 
wie 0x100 zu legen, was die Adressarithmetik vereinfacht: High-Byte ist 
0x1 und Low-Byte der Index (r_cnt).

Peter II schrieb:
> Johann L. schrieb:
>> Globale Register sind nicht volatile, was die Synchronisierung
>> schwer handhabbar macht.
>
> register sind doch immer volatile

Nö, sie sind nie volatile.  Übersetze folgenden Code:
1
register volatile char r2 asm ("2");
2
3
void foo (void)
4
{
5
    extern char volatile v;
6
    r2 = 0;
7
    while (!r2);
8
    v = 1;
9
}
 
und avr-gcc 4.8 macht daraus korrekt:
 
1
foo:
2
.L3:
3
  rjmp .L3

> und was sollen nicht globele Register sein?

Lokale Register-Variablen, wie die globalen eine GNU-Erweiterung.

von Peter II (Gast)


Lesenswert?

Johann L. schrieb:
> Nö, sie sind nie volatile.  Übersetze folgenden Code:

das meinst du.

Ich dachte an Atmel-ASM

ldi R16, 234

und da muss man nichts mit volatile beachten.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

MaWin schrieb:
>Johann L. schrieb:
>> MaWin schrieb:
>>> Wenn man die Register 24, 30, 31 nun noch reserviert
>>>
>>> -ffixed-24 -ffixed-30 -ffixed-31
>>
>> Damit wirst du kein lauffähiges Programm mehr hinbekommen.
>
> Drum schrieb ich ja drunter, was man besser machen sollte.
> Da hast du wohl schon dein Aufmerksamskeitsdefizit bekommen.

MaWin schrieb:

> -ffixed-24 -ffixed-26 -ffixed-27

Ditto in Grün.  Allein die Idee zeigt, daß du besser die Finger davon 
lässt...

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Peter II schrieb:
> Johann L. schrieb:
>> Nö, sie sind nie volatile.
>
> das meinst du.
> Ich dachte an Atmel-ASM
>
> ldi R16, 234
>
> und da muss man nichts mit volatile beachten.

Muss man in Assembler eh nie.  Oder optimiert der Atmel-ASM 
"überflüssige" Instruktionen raus wenn er erkennt, daß die keinen Effekt 
haben?

Oder hat "volatile" in Assembler eine Bedeutung?

von Peter II (Gast)


Lesenswert?

Johann L. schrieb:
> Oder hat "volatile" in Assembler eine Bedeutung?

nein, aus dem Grund hatte mich ja die aussage (inline)asm und volatile 
verwundert.

von Norbert S. (norberts)


Lesenswert?

Hi,

bitte nicht schlagen, dem Meisten was hier diskutiert wird kann ich 
nicht folgen mangels ausreichender ASM oder C Kenntnisse.

Aus Neugier habe ich das mal in Bascom probiert, allerdings ohne Int, 
einfach nur pollen und die Bits in ein Array feuern.
Ich denke das versteht hier jeder was es macht:

Do
If Pind.2 = 0 Then Gosub Int0isr
Loop

Int0isr:

For Datindex = 1 To 80
   Do
   Loop Until Pind.2 = 0
   Datarray(datindex) = Pind.3
   Do
   Loop Until Pind.2 = 1
Next

Return

Die If-Abfrage ist nur drin, weil Bascom den Int nicht simulieren mag, 
der Sprung kostet in Bascom aber dieselben Takte. Das sind viel zu 
viele, weil Bascom alle Register sichert, ob die nun gebraucht werden 
oder nicht.
Kann man ihm aber abgewöhnen, nur mit dem Gosub geht das nicht.

Der Sprung in die ISR bis das erste Bit gespeichert wird dauert 86 
Takte.
Kann man wie gesagt schneller machen.
Dann dauert dieses Pollen komplett 41 Takte, wenn das "Until" jeweils 
sofort erfüllt ist.

Die For Next sieht so aus:
24:       For Datindex = 1 To 80
+00000070:   E081        LDI       R24,0x01       Load immediate
+00000071:   938000B0    STS       0x00B0,R24     Store direct to data 
space
+00000073:   910000B0    LDS       R16,0x00B0     Load direct from data 
space
+00000075:   3500        CPI       R16,0x50       Compare with immediate
24:       For Datindex = 1 To 80
+00000076:   F018        BRCS      PC+0x04        Branch if carry set
+00000077:   F011        BREQ      PC+0x03        Branch if equal
24:       For Datindex = 1 To 80
+00000078:   940C0097    JMP       0x00000097     Jump
26:          Loop Until Pind.2 = 0
+0000007A:   9982        SBIC      0x10,2         Skip if bit in I/O 
register cleared
+0000007B:   940C007A    JMP       0x0000007A     Jump
27:          Datarray(datindex) = Pind.3
+0000007D:   E3A0        LDI       R26,0x30       Load immediate
+0000007E:   E0B0        LDI       R27,0x00       Load immediate
27:          Datarray(datindex) = Pind.3
+0000007F:   918C        LD        R24,X          Load indirect
+00000080:   FB83        BST       R24,3          Bit store from 
register to T
27:          Datarray(datindex) = Pind.3
+00000081:   EBA0        LDI       R26,0xB0       Load immediate
+00000082:   E0B0        LDI       R27,0x00       Load immediate
27:          Datarray(datindex) = Pind.3
+00000083:   90AD        LD        R10,X+         Load indirect and 
postincrement
+00000084:   24BB        CLR       R11            Clear Register
27:          Datarray(datindex) = Pind.3
+00000085:   E5AF        LDI       R26,0x5F       Load immediate
+00000086:   E0B0        LDI       R27,0x00       Load immediate
27:          Datarray(datindex) = Pind.3
+00000087:   0DAA        ADD       R26,R10        Add without carry
+00000088:   1DBB        ADC       R27,R11        Add with carry
27:          Datarray(datindex) = Pind.3
+00000089:   2788        CLR       R24            Clear Register
+0000008A:   F980        BLD       R24,0          Bit load from T to 
register
27:          Datarray(datindex) = Pind.3
+0000008B:   938C        ST        X,R24          Store indirect
29:          Loop Until Pind.2 = 1
+0000008C:   9B82        SBIS      0x10,2         Skip if bit in I/O 
register set
+0000008D:   940C008C    JMP       0x0000008C     Jump
30:       Next
+0000008F:   EBA0        LDI       R26,0xB0       Load immediate
+00000090:   E0B0        LDI       R27,0x00       Load immediate
30:       Next
+00000091:   918C        LD        R24,X          Load indirect
+00000092:   5F8F        SUBI      R24,0xFF       Subtract immediate
30:       Next
+00000093:   938C        ST        X,R24          Store indirect
+00000094:   F410        BRCC      PC+0x03        Branch if carry 
cleared
30:       Next
+00000095:   940C0073    JMP       0x00000073     Jump

Vielleicht inspiriert das die Spezis hier ja irgendwie.
Ich könnte maximal herauslesen, welche Register ich manuell sichern 
müsste um das ganze maximal zu beschleunigen.

Verschwendet natürlich Ram ohne Ende aber so gehts schnell.

Es bleibt das Problem, daß die Sache hängt wenn ein Fehler auftritt, so 
würde ich das auch garantiert nicht umsetzen. Aber mit "Notausstieg" 
wird das sicher zu langsam.

Ach ja, den Mega324 kann man doch fast so in das Design reinwerfen. Was 
der 32 kann, das kann der auch und soweit sind sie Pinkompatibel, oder?

Gruß,
Norbert

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Detlef schrieb:
> Das Entfernen der Maskierung des Bits in C hat einen Zyklus gebracht.

Klar.  Ziel der Übung ist nämlich, daß SREG nicht mehr verändert wird, 
was einen kleineren ISR-Prolog / -Epilog ermöglicht.  Mit einer ISR in C 
kann daraus kein Gewinn gezogen werden [1] – ausser dem Sparen der 
genannten, einen Instruktion.

[1] http://gcc.gnu.org/PR20296

Peter II schrieb:
> Johann L. schrieb:
>> Oder hat "volatile" in Assembler eine Bedeutung?
>
> nein, aus dem Grund hatte mich ja die aussage (inline)asm und volatile
> verwundert.

Es gibt ja nicht nur die ISR, die evtl. in Assembler steht, sondern auch 
noch den "normalen" Code in C.  Alles nach asm umzuschreiben ist i.d.R. 
der Overkill und nicht zielführend.  Daher steht die ISR dann in asm und 
der Rest in C.

Der C-Teil muss bei einen globalen Register, das in der ISR geändert 
wird (egal ob durch asm oder durch C), damit klarkommen, daß globale 
Register nicht volatile sind. Eine Möglichkeit wäre, auch das Register 
per asm zu laden, was in der Richtung:
 
1
register char r2 __asm__ ("2");
2
3
#define R2 (__extension__({__asm__ __volatile__("":"+r"(r2));r2;}))
4
5
char volatile v;
6
7
void foo (void)
8
{
9
    r2 = 0;
10
    while (!R2);
11
    v = 1;
12
}

von Piefke (Gast)


Lesenswert?

Norbert S. schrieb:
> Vielleicht inspiriert das die Spezis hier ja irgendwie.

Insprieren nein, transpirieren ja.
Es ist eine Bestätigung, dass Bascom nichts taugt.

> Ach ja, den Mega324 kann man doch fast so in das Design reinwerfen. Was
> der 32 kann,

Man kann sich auch nen Knopf an die Backe nähen und ein Klavier 
dranhängen.

>  das kann der auch und soweit sind sie Pinkompatibel, oder?

Das weißt du nicht? Schäm!

von Detlef (Gast)


Lesenswert?

@Johann, habe nun die ISR in ASM implementiert. Damit funktioniert das 
Sampling hervorragend. Danke für die Hilfe, ohne Beispiel hätte ich mich 
nimmer getraut das AVR ASM Manual in die Hand zu nehmen. Jetzt wos geht 
ist es ganz einleuchtend.

Danke auch an die anderen.

Gruß,
Detlef

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.