Forum: Mikrocontroller und Digitale Elektronik externer interrupt


von fixi (Gast)


Lesenswert?

Hallo,

An meinem atmega8 löse ich einen externen interrupt aus. In der ISR wird 
lediglich ein Ausgang auf high gesetzt (1 Zeile Code). Die Reaktionszeit 
beträgt bei 16MHz ca 1,25 Mikrosekunden. Woran liegt das? Gibt es eine 
Möglichkeit das Ganze etwas zu beschleunigen?

von Patrick J. (ho-bit-hun-ter)


Lesenswert?

Hi

0,063µs / Takt -> ~20 Takte

Sprung zur ISR 2/3 Takte
Setzen des IO ebenfalls um den Dreh

Bis auf dem PC-Int reagiert wird, vergehen auch 1/2 Takte im Simulator

Wie viele Takte hattest Du erwartet?
Was spricht der Simulator, wie viele Takte es sein sollten?
Womit hast Du die 1,25µs ermittelt?
Reproduzierbar?

MfG

von fixi (Gast)


Lesenswert?

Ich habe so an die 5-6 Takte erwartet, sprich ca 400ns. Ist das 
realistisch? Mit dem Simulator habe ich nicht getestet. Gemessen habe 
ich mit dem Oszi direkt am Ausgangspin.

von fixi (Gast)


Angehängte Dateien:

Lesenswert?

Hier als Anhang die Oszi-Aufnahme und der Code:
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#define F_CPU 16000000UL
4
#include <util/delay.h>
5
6
int main(void)
7
{
8
9
  DDRB = 0xff;                        
10
  DDRC = 0xff; 
11
  PORTD = 0xff;                      
12
  GICR |= (1 << INT1);               
13
  MCUCR |= (1 << ISC10)|(1<<ISC11);              
14
  sei();  
15
                            
16
  while(1){  
17
  PORTC=0xff;
18
  _delay_ms(1);
19
  PORTC=0x00;
20
  _delay_ms(1);
21
22
}                                                    
23
}
24
25
ISR(INT1_vect)
26
{
27
  PORTB=0xff;
28
  PORTB=0x00;
29
}

Ein Signal des PortC wird dabei zum Test auf den INT1/PD3 gegeben.
Zum Oszibild: blau PortC und gelb PortB

von Noch einer (Gast)


Lesenswert?

14 Takte werden verschwendet? Ins Assemblerlisting schauen, ob der 
Compiler beim Eintritt in die ISR mehrere Register sichert.

von c-hater (Gast)


Lesenswert?

fixi schrieb:

> An meinem atmega8 löse ich einen externen interrupt aus. In der ISR wird
> lediglich ein Ausgang auf high gesetzt (1 Zeile Code). Die Reaktionszeit
> beträgt bei 16MHz ca 1,25 Mikrosekunden. Woran liegt das?

Schlechte Programmierung, falsche Sprache.

> Gibt es eine
> Möglichkeit das Ganze etwas zu beschleunigen?

Klar: Assembler. Damit ist bei Interruptsteuerung ein progagation delay 
für eine einzelne Flanke von 6 Takten möglich, bei einer Endlosschleife 
sind's sogar nur 4.

Sprich, bei 16MHz: 375 bzw. 250ns.

von fixi (Gast)


Lesenswert?

Gibt es eine Möglichkeit Assembler im C-Code zu verwenden?

von Wilhelm M. (wimalopaan)


Lesenswert?

fixi schrieb:
> Gibt es eine Möglichkeit Assembler im C-Code zu verwenden?

Das brauchst Du nicht.

Auf den Prolog und Epilog kannst Du durch das Attribute ISR_NAKED 
verzichten.
Dann werden r0, r1 und SREG (und evtl. andere Register) nicht gesichert.

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

fixi schrieb:
> Ich habe so an die 5-6 Takte erwartet, sprich ca 400ns. Ist das
> realistisch?

Absolut nein.
Der AVR ist ein RISC, da sind 50 Takte nichts.
Schon der nackte Interrupteinsprung + Return kostet 10 Takte. Dann noch 
Prolog, Epilog (Register retten).
Auch muß man damit rechnen, daß ja gerade eine anderer Interrupt in 
Arbeit sein könnte. Assembler ist daher nur selten zielführend.
Kalkuliere nen Interrupthandler mit ~200 Takten, das reicht für die 
meisten kleineren Sachen.

von m.n. (Gast)


Lesenswert?

Peter D. schrieb:
> Kalkuliere nen Interrupthandler mit ~200 Takten, das reicht für die
> meisten kleineren Sachen.

Das ist aber schon Schwarzmalerei. Sofern keine anderen Interrupts aktiv 
sind oder diese passend programmiert werden, ist eher von 30 - 50 Takten 
auszugehen.

von Peter D. (peda)


Lesenswert?

m.n. schrieb:
> Sofern keine anderen Interrupts aktiv
> sind

Genau das ist der Pferdefuß.
Eine Applikation soll ja in der Regel noch viel mehr machen.
Ein Interrupthandler darf nicht so tun, als wäre er ganz allein auf der 
Welt (im Flash). Er muß den anderen Tasks auch CPU-Zeit zugestehen.

von m.n. (Gast)


Lesenswert?

Peter D. schrieb:
> m.n. schrieb:
>> Sofern keine anderen Interrupts aktiv
>> sind

und weiter zitiert: ... oder diese passend programmiert werden.
Wenn man hohe Interruptlast im System hat, muß man sich auch ein paar 
Gedanken über die Prioritäten machen.
Nur sehe ich diese Probleme beim TO überhaupt nicht.

Wilhelm M. schrieb:
> Auf den Prolog und Epilog kannst Du durch das Attribute ISR_NAKED
> verzichten.
> Dann werden r0, r1 und SREG (und evtl. andere Register) nicht gesichert.

Das ist selbst schon bei dieser einfachen ISR tödlich!
Transparente und schnelle ISRs programmiert man in Assembler, wo nur die 
Register gerettet werden, die unbedingt gebraucht werden mit SREG an 
allererster Stelle.

Aber gut, dazu ist schon an anderen Stellen bis zum Abwinken diskutiert 
worden.

von fixi (Gast)


Lesenswert?

Kann jemand meinen Code so umschreiben, dass die Reaktionszeit besser 
wird? Wenn es sein muss, kann ich auch mit Assembler in der ISR leben.

von Arduinoquäler (Gast)


Lesenswert?

Wie so oft:

Vielleicht ist es auch an der Zeit zu erörtern was eigentlich
das Ziel des ganzen Unternehmens sein soll, denn die schnelle
ISR zu schreiben soll ja wohl keinen reinen Selbstzweck
erfüllen.

Sonst werden hier ja nur reihenweise die Pferde scheu gemacht
für nix und wieder nix ....

von Oliver S. (oliverso)


Lesenswert?

c-hater schrieb:
> Sprich, bei 16MHz: 375 bzw. 250ns.

Wetten, daß nicht?

Oliver

von Wilhelm M. (wimalopaan)


Lesenswert?

m.n. schrieb:

> Das ist selbst schon bei dieser einfachen ISR tödlich!

Nö. Aus
1
ISR(TIMER0_COMPA_vect, ISR_NAKED) {
2
    PORTB = 0xff;
3
    reti();
4
}

wird
1
.global __vector_14
2
        .type   __vector_14, @function
3
__vector_14:
4
/* prologue: naked */
5
/* frame size = 0 */
6
/* stack size = 0 */
7
.L__stack_usage = 0
8
        ldi r24,lo8(-1)  ;  tmp43,
9
        out 0x5,r24      ;  MEM[(volatile uint8_t *)37B], tmp43
10
        reti
11
/* epilogue start */
12
/* #NOAPP */
13
        .size   __vector_14, .-__vector_14

von Peter D. (peda)


Lesenswert?

Arduinoquäler schrieb:
> denn die schnelle
> ISR zu schreiben soll ja wohl keinen reinen Selbstzweck
> erfüllen.

Und falls doch, dann ist ein ATmega8 mit 8kB Flash für ein <100Byte 
Progrämmchen leicht oversized.

von Arduinoquäler (Gast)


Lesenswert?

Peter D. schrieb:
> Und falls doch, dann ist ein ATmega8 mit 8kB Flash für ein <100Byte
> Progrämmchen leicht oversized.

Wenn es um den eigentlichen Sinn der Übung geht (es soll ja
keine Übung sein?) wird es sehr schnell sehr ruhig.

Eine oft gemachte Erfahrung hier ....

fixi schrieb:
> Kann jemand meinen Code so umschreiben, dass die Reaktionszeit besser
> wird?

von Wilhelm M. (wimalopaan)


Lesenswert?

fixi schrieb:
> Kann jemand meinen Code so umschreiben, dass die Reaktionszeit besser
> wird? Wenn es sein muss, kann ich auch mit Assembler in der ISR leben.

Hast Du denn schon selbst mal die Vorschläge (ISR_NAKED) ausprobiert?

Bzw. den Assembler-Code untersucht?

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Arduinoquäler schrieb:
> Wie so oft:
>
> Vielleicht ist es auch an der Zeit zu erörtern was eigentlich
> das Ziel des ganzen Unternehmens sein soll, denn die schnelle
> ISR zu schreiben soll ja wohl keinen reinen Selbstzweck
> erfüllen.
>
> Sonst werden hier ja nur reihenweise die Pferde scheu gemacht
> für nix und wieder nix ....

Finde ich nicht! Es ist ein MCVE, das ausreicht. Und ein klare Frage.

von Arduinoquäler (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Es ist ein MCVE, das ausreicht.

Malaysia Commercial Vehicle Expo reicht aus?

Aha ... so so .... sehr interessant .... deine eigene Welt ...

von m.n. (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Nö.

Und Du meinst, man könne R24 mal eben so zerschießen?

von Wilhelm M. (wimalopaan)


Lesenswert?

Arduinoquäler schrieb:
> Wilhelm M. schrieb:
>> Es ist ein MCVE, das ausreicht.
>
> Malaysia Commercial Vehicle Expo reicht aus?
>
> Aha ... so so .... sehr interessant .... deine eigene Welt ...

Wohl kaum! Schon mal gegoogelt: MCVE code -> erster Eintrag.

von Arduinoquäler (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Wohl kaum! Schon mal gegoogelt: MCVE code -> erster Eintrag.

Zu deiner eigenen Welt gehört dazu dass du es dem Suchenden
überlässt wie er sucht.

Also:  MCVE --> Malaysia Commercial Vehicle Expo

Zu deiner eigenen Welt gehört auch dazu dass du ohne Not
irgedwelche Abkürzungen verwendest die kein Schwein kennt.

von Wilhelm M. (wimalopaan)


Lesenswert?

m.n. schrieb:
> Wilhelm M. schrieb:
>> Nö.
>
> Und Du meinst, man könne R24 mal eben so zerschießen?

In diesem, sehr speziellen Fall ist das aber kein Problem! Schau Dir 
hast Listing genau an:
1
  .file  "bm99a.c"
2
__SP_H__ = 0x3e
3
__SP_L__ = 0x3d
4
__SREG__ = 0x3f
5
__tmp_reg__ = 0
6
__zero_reg__ = 1
7
  .section  .text.startup,"ax",@progbits
8
.global  main
9
  .type  main, @function
10
main:
11
/* prologue: function */
12
/* frame size = 0 */
13
/* stack size = 0 */
14
.L__stack_usage = 0
15
  ldi r24,lo8(-1)
16
  out 0x4,r24
17
  out 0x7,r24
18
  out 0xb,r24
19
  in r24,0x35
20
  ori r24,lo8(12)
21
  out 0x35,r24
22
/* #APP */
23
 ;  32 "/home/lmeier/Projekte/wmucpp/doc/bmcpp99/bm99a.c" 1
24
  sei
25
 ;  0 "" 2
26
/* #NOAPP */
27
  ldi r24,lo8(-1)
28
.L2:
29
  out 0x8,r24
30
  ldi r30,lo8(3999)
31
  ldi r31,hi8(3999)
32
1:  sbiw r30,1
33
  brne 1b
34
  rjmp .
35
  nop
36
  out 0x8,__zero_reg__
37
  ldi r30,lo8(3999)
38
  ldi r31,hi8(3999)
39
1:  sbiw r30,1
40
  brne 1b
41
  rjmp .
42
  nop
43
  rjmp .L2
44
  .size  main, .-main
45
  .text
46
.global  __vector_2
47
  .type  __vector_2, @function
48
__vector_2:
49
/* prologue: naked */
50
/* frame size = 0 */
51
/* stack size = 0 */
52
.L__stack_usage = 0
53
  ldi r24,lo8(-1)
54
  out 0x5,r24
55
  out 0x5,__zero_reg__
56
/* epilogue start */
57
  .size  __vector_2, .-__vector_2
58
  .ident  "GCC: (GNU) 8.0.0 20170502 (experimental)"

... also r24 wird mit demselben Wert überschieben ;-) Aber Du hast 
natürlich recht: ich hatte die Schleife in main() nicht beachtet ...

von Wilhelm M. (wimalopaan)


Lesenswert?

Arduinoquäler schrieb:
> Wilhelm M. schrieb:
>> Wohl kaum! Schon mal gegoogelt: MCVE code -> erster Eintrag.
>
> Zu deiner eigenen Welt gehört dazu dass du es dem Suchenden
> überlässt wie er sucht.
>
> Also:  MCVE --> Malaysia Commercial Vehicle Expo
>
> Zu deiner eigenen Welt gehört auch dazu dass du ohne Not
> irgedwelche Abkürzungen verwendest die kein Schwein kennt.

Schon mal was von StackOverflow gehört ... (wer ist das Schwein?)

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Wilhelm M. schrieb:
> Es ist ein MCVE, das ausreicht. Und ein klare Frage.

MCVE nimmt man für Bugreports.
Hier handelt es sich aber um keinen Bug, sondern um falsche Erwartungen 
bezüglich der Ausführungszeit.

Und zur Klarheit fehlen noch sämtliche wichtigen Randbedingungen (wie 
oft, welche anderen Tasks usw.).

Unter speziellen Bedingungen kann man auf eine externe Flanke innerhalb 
2..3 Takten einen Pin setzen:
Timer als Zähler, Auswahl der Flanke, Signal an den Timereingang, Set 
Output-Pin on Compare, Comparewert auf Timer+1.

von Wilhelm M. (wimalopaan)


Lesenswert?

Peter D. schrieb:
> Wilhelm M. schrieb:
>> Es ist ein MCVE, das ausreicht. Und ein klare Frage.
>
> MCVE nimmt man für Bugreports.

Nope, generell überall für jede Art von Frage, die mit einem bestimmten 
Codeverhalten (bug oder auch nicht) zu tun hat:

FYI: https://stackoverflow.com/help/mcve

FYI: for your interest

HTH

HTH: Hope that helps

AFAIK, hat das aber alles nichts mit der Frage zu tun...

http://www.catb.org/jargon/html

von Stefan K. (stefan64)


Lesenswert?

Meiner Meinung nach baut man sich mit ISR_NAKED eine Zeitbombe in den 
Code. Da nicht definiert ist, welche Register der Compiler für die 
folgenden Befehle benutzt, ist eine manuelle Sichtung des Outputs 
eigentlich unumgänglich. Spätestens bei einer Änderung der 
Compilereinstellungen oder einem Compilerupdate können ganz andere 
Register zu sichern sein.

Und ich finde es auch nicht wirklich sinnvoll. Wenn man nicht gerade 
Funktionen im ISR aufruft, die nicht im gleichen C-File implementiert 
sind, dann sichert der gcc die Register sehr sparsam. Meiner Erinnerung 
sind es ein, höchstens 2 Register, die man händisch einsparen könnte.

Falls man tatsächlich einen derart zeitkritischen Code hat, dann sollte 
man auf eingebetteten Asm-Code innerhalb von ISR_NAKED ausweichen.

Gruß, Stefan

von Wilhelm M. (wimalopaan)


Lesenswert?

Stefan K. schrieb:
> Meiner Meinung nach baut man sich mit ISR_NAKED eine Zeitbombe in den
> Code.

Sehe ich genauso. Aber die Frage war nach den Möglichkeiten ...

> Und ich finde es auch nicht wirklich sinnvoll. Wenn man nicht gerade
> Funktionen im ISR aufruft, die nicht im gleichen C-File implementiert
> sind, dann sichert der gcc die Register sehr sparsam.

Oder er macht ein inlining der Funktion. Wenn man Templates (C++) 
verwendet sogar (fast) immer (da es dieselbe TU, ups, 
Übersetzungseinheit ist).

von Oliver S. (oliverso)


Lesenswert?

fixi schrieb:
> Ich habe so an die 5-6 Takte erwartet, sprich ca 400ns. Ist das
> realistisch?

Minimum:
2 Takte Flankenerkennung
4 Takte Interrupt response time
2 Takte rjmp IRQ-Vector
1 Takt Ausgang setzen (per sbi)


In C ohne "naked" kommen da im Idealfall noch 2 Takte zur Rettung des 
Status Registers dazu.

Oliver

von Dorftrottel (Gast)


Lesenswert?

1
ISR(TIMER0_COMPA_vect, ISR_NAKED) {
2
  asm volatile (        
3
    "push r16"      "\n\t"
4
    "ldi r16, 0xFF" "\n\t"
5
    "out %0, r16"   "\n\t"
6
    "pop r16"       "\n\t"
7
    :
8
    : "M" (_SFR_IO_ADDR (PORTB))
9
  );
10
  asm volatile ( "reti" );
11
}

von Dorftrottel (Gast)


Lesenswert?

oh, ich überlas, es ging um den INT1-Interrupt...
dann eben so...
1
ISR(INT1_vect, ISR_NAKED) {
2
  asm volatile (        
3
    "push r16"      "\n\t"
4
    "ldi r16, 0xFF" "\n\t"
5
    "out %0, r16"   "\n\t"
6
    "pop r16"       "\n\t"
7
    :
8
    : "M" (_SFR_IO_ADDR (PORTB))
9
  );
10
  asm volatile ( "reti" );
11
}
Asche über mein Haupt.

von Wilhelm M. (wimalopaan)


Lesenswert?

Dorftrottel schrieb:
> oh, ich überlas, es ging um den INT1-Interrupt...
> ...
> Asche über mein Haupt.

... war wohl ursprünglich mein Fehler ...

von Dorftrottel (Gast)


Lesenswert?

Wilhelm M. schrieb:
> ... war wohl ursprünglich mein Fehler ...

Dem ist wohl so, ärgere mich dennoch über diesen peinlichen Lapsus, so 
entstehen Fehler.

von Amateur (Gast)


Lesenswert?

Der Ablauf ist eigentlich recht einfach:
1. Es dauert ein bisschen, bis eine Unterbrechung erkannt wird
   (1 bis 2 Takte). Üblicherweise sind die ja auch asynchron.
   Siehe Handbuch.
2. Nicht jeder Befehl kann unmittelbar unterbrochen werden, manche
   müssen halt beendet werden (1 bis 2 Takte).
3. Die Rücksprungadresse muss auf dem Stapel abgelegt werden
   (2 Bytes Push).
4. Die neue "Arbeitsadresse" muss in den Programmzähler geholt werden.
5. Arbeitest Du nicht im Assembler, so wird hier praktisch immer noch
   das Statusregister gesichert.

6. MACH DEIN DING. Zusätzlich fast immer 1 X Push und 1 X Pop.

7. Ein Compiler wird hier das Statusregister restaurieren.
8. Interrupt beenden meist mittel IRET. Also mindestens 2 X Pop.

Also: Auch wenn Du in Assembler programmierst, gibt es einige Sachen, 
die unumgänglich sind. Die Meisten sind mit etwas Nachdenken einfach 
nachzuvollziehen.

Natürlich fällt, bei den heutigen Compilern, kein "alle Register 
sichern" und kein "alle Register restaurieren" am Ende der Routine mehr 
an.

Habe bestimmt den einen oder anderen Takt vergessen.

von fixi (Gast)


Lesenswert?

Klar ist der Code etwas primitiv. Es geht aber erstmal nur darum die 
Reaktionszeit zu testen, bevor es ans eigentliche Projekt geht. Dafür 
sollte dieses Beispiel ausreichend sein. Wie es aber scheint, gibt es 
(noch) keine Lösung für ein schnelleres ansprechen :(
Später soll das Programm doch etwas umfangreicher werden.


Dorftrottel schrieb:
> oh, ich überlas, es ging um den INT1-Interrupt...
> dann eben so...ISR(INT1_vect, ISR_NAKED) {
>   asm volatile (
>     "push r16"      "\n\t"
>     "ldi r16, 0xFF" "\n\t"
>     "out %0, r16"   "\n\t"
>     "pop r16"       "\n\t"
>     :
>     : "M" (_SFR_IO_ADDR (PORTB))
>   );
>   asm volatile ( "reti" );
> }
> Asche über mein Haupt.

das funktioniert so leider nicht ganz - PORTB ist dauerhaft auf high.

von Patrick J. (ho-bit-hun-ter)


Lesenswert?

Hi

Wie sieht es aus, die scheinbar nicht auszumerzenden Takte durch 
brachiale Prozessor-Geschwindigkeit anszutricksen?

Wenn bei Deiner aktuellen Geschwindigkeit 40 Takte vergehen, wo Du mit 
maximal 4 Takten leben kannst - Tiger in den Tank und Quarz x10 in den 
Slot - die Wartetakte hast Du zwar immer noch, sind aber viel schneller 
vorbei.

MfG

PS:
fixi schrieb:
> das funktioniert so leider nicht ganz - PORTB ist dauerhaft auf high.

Dann ersetze versuchshalber
"ldi r16, 0xFF" "\n\t"

durch

"ldi r16, 0x00" "\n\t"

: Bearbeitet durch User
von fixi (Gast)


Lesenswert?

1
ISR(INT1_vect, ISR_NAKED) {
2
  PORTB = 0xff;
3
  PORTB = 0x00;
4
  reti();
5
}

Damit beträgt die Reaktionszeit nur noch 600ns. Leider immernoch etwas 
zu langsam...
Aber wie ich den Reaktionen entnehmen kann, sollte ich lieber die Finger 
von diesem Ansatz lassen?

von fixi (Gast)


Lesenswert?

Patrick J. schrieb:
> Dann ersetze versuchshalber
> "ldi r16, 0xFF" "\n\t"
>
> durch
>
> "ldi r16, 0x00" "\n\t"


Nun ist PORTB dauerhaft low

von Patrick J. (ho-bit-hun-ter)


Lesenswert?

Hi

Darauf wollte ich hinaus - wenn im ISR steht, daß der Port bei JEDEM 
Abarbeiten komplett auf HIGH oder LOW gesetzt wird, wird der Port wohl 
so gesetzt.
Sofern das Hauptprogramm da Nichts dran ändert.

Da Das das einzige Register war, was eine Information enthielt und hier 
wohl PortB beschrieben wurde, ging ich davon aus, daß das Port nun 
'anders herum' bleibt.

Wenn Du nur ein Toggel pro ISR möchtest, kannst Du PinB mit 0xFF 
beschreiben - Das sollte Dir alle PortB-Bits 'umdrehen'.
(zumindest beim ATtiny ... vll. reicht der Verwandtschaftsgrad ja bis 
hier hin)

MfG

von Walter S. (avatar)


Lesenswert?

fixi schrieb:
> Nun ist PORTB dauerhaft low

dann setz es doch wieder auf HIGH

von m.n. (Gast)


Lesenswert?

fixi schrieb:
> Wie es aber scheint, gibt es
> (noch) keine Lösung für ein schnelleres ansprechen :(

Doch, gibt es. Ein STM32F4xx schafft es schneller.
Mit der richtigen Hardware geht es noch viel schneller ;-)

> Später soll das Programm doch etwas umfangreicher werden.

Dann sag besser schon jetzt, was später sein soll. Ein ATmega8 ist 
heutzutage ja nicht mehr der absolute Renner - in jeder Beziehung.

von Peter D. (peda)


Lesenswert?

fixi schrieb:
> Später soll das Programm doch etwas umfangreicher werden.

Dann ist Dein Test vollkommen nutzlos.
Du hast nur eine CPU und die muß auch im worst-case alles in der 
geplanten Zeit ausführen können.

Die oben genannten ~200 Zyklen je Interrupthandler sind eine brauchbare 
Praxisabschätzung. Anfänger können auch deutlich mehr brauchen.

von Oliver S. (oliverso)


Lesenswert?

fixi schrieb:
> Damit beträgt die Reaktionszeit nur noch 600ns. Leider immernoch etwas
> zu langsam...

Was wäre denn schnell genug? Denn schneller als 562ns geht's halt mit 
dem Prozessor bei dem Takt nicht. Klingt blöd, ist aber so.

Oliver

von Dorftrottel (Gast)


Lesenswert?

fixi schrieb:
> ISR(INT1_vect, ISR_NAKED) {
>   PORTB = 0xff;
>   PORTB = 0x00;
>   reti();
> }

willst Du sowas?
1
ISR(INT1_vect, ISR_NAKED) {
2
  asm volatile (        
3
    "push r16"      "\n\t"
4
    "ldi r16, 0xFF" "\n\t"
5
    "out %0, r16"   "\n\t"
6
    :
7
    : "M" (_SFR_IO_ADDR (PORTB))
8
  );
9
  asm volatile (        
10
    "ldi r16, 0x00" "\n\t"
11
    "out %0, r16"   "\n\t"
12
    "pop r16"       "\n\t"
13
    :
14
    : "M" (_SFR_IO_ADDR (PORTB))
15
  );
16
  asm volatile ( "reti" );
17
}
wird zu:
1
PUSH      R16            Push register on stack
2
SER       R16            Set Register
3
OUT       0x18,R16       Out to I/O location
4
LDI       R16,0x00       Load immediate
5
OUT       0x18,R16       Out to I/O location
6
POP       R16            Pop register from stack
7
RETI                     Interrupt return

von m.n. (Gast)


Lesenswert?

Dorftrottel schrieb:
> LDI       R16,0x00       Load immediate

Wenn R16 vorher 0xff ist, könnte man es auch durch INC R16 auf 0x00 
bringen ;-)

von Dorftrottel (Gast)


Lesenswert?

m.n. schrieb:
> Wenn R16 vorher 0xff ist, könnte man es auch durch INC R16 auf 0x00
> bringen ;-)

Nö.
Denn laut ATMEL beeinflußt die INCrementierung die Flags Z, N & V im 
Statusregister. Keine gute Idee in nem naked_Interrupt.

;-)

von fixi (Gast)


Lesenswert?

Danke für die zahlreichen Vorschläge. Ich komme mit dem testen noch 
nicht ganz hinterher. Das werde ich aber demnächst ausführlich machen.

Eine Altnernative wäre den Atmega88a einzusetzen. Dieser kann laut 
Datenblatt 20MHz. Gibt es abgesehen von der höheren Frequenz evtl 
weitere Auswirkungen auf die Reaktionszeit?

von Oliver S. (oliverso)


Lesenswert?

Nein. Alles andere bleibt gleich.

Oliver

von m.n. (Gast)


Lesenswert?

Dorftrottel schrieb:
> Keine gute Idee in nem naked_Interrupt.

Und darum sollte man ISRs, deren endgültige Funktion noch nicht 
feststeht, nicht auf Glück programmieren.

m.n. schrieb:
> Transparente und schnelle ISRs programmiert man in Assembler, wo nur die
> Register gerettet werden, die unbedingt gebraucht werden mit SREG an
> allererster Stelle.

von Oliver S. (oliverso)


Lesenswert?

m.n. schrieb:
> m.n. schrieb:
>> Transparente und schnelle ISRs programmiert man in Assembler, wo nur die
>> Register gerettet werden, die unbedingt gebraucht werden mit SREG an
>> allererster Stelle.

Je nun, was anderes macht der Compiler auch nicht...

Oliver

von m.n. (Gast)


Lesenswert?

Oliver S. schrieb:
> Je nun, was anderes macht der Compiler auch nicht...

Stimmt, der macht sogar noch viel mehr ;-)

ISR(INT1_vect)
{
     5f0:  1f 92         push  r1
     5f2:  0f 92         push  r0
     5f4:  0f b6         in  r0, 0x3f  ; 63
     5f6:  0f 92         push  r0
     5f8:  11 24         eor  r1, r1
     5fa:  8f 93         push  r24
  PORTB=0xff;
     5fc:  8f ef         ldi  r24, 0xFF  ; 255
     5fe:  85 b9         out  0x05, r24  ; 5
  PORTB=0x00;
     600:  15 b8         out  0x05, r1  ; 5
}
     602:  8f 91         pop  r24
     604:  0f 90         pop  r0
     606:  0f be         out  0x3f, r0  ; 63
     608:  0f 90         pop  r0
     60a:  1f 90         pop  r1
     60c:  18 95         reti

von fixi (Gast)


Lesenswert?

Wie schnell wäre denn die Reaktion bei aktivem Warten durch polling auf 
einen Spannungspegel am Pin?

von c-hater (Gast)


Lesenswert?

Oliver S. schrieb:

> Je nun, was anderes macht der Compiler auch nicht...

Doch, das tut er durchaus, es sei denn, es ist ein "guter" UND man ist 
sehr mit seinen Eigenheiten vertraut und benutzt sie auch kompetent.

Aber selbst wenn die Kenntnisse da sind und der Compiler zumindest 
potentiell wirklich alle wesentlichen Tricks ermöglicht, die in Asm 
möglich sind, stellt sich am Ende des Tages immer noch die Frage: warum 
zum Teufel muss man das nun unbedingt in C programmieren?

Bilanz:
1.
Der Einsatz von C bringt in einem solchen "engen" Szenario keinerlei 
Vorteile, weil's entweder garnicht geht oder zumindest die 
"Portabilität" als einziger nennenswerter Vorteil von C gegenüber Asm 
praktisch vollständig entfällt.
2.
Es macht mehr Arbeit, weil man neben der allfälligen Kenntnis der 
Zielarchitektur und seiner Assemblersprache auch noch die konkreten 
Features des Compilers perfekt beherrschen muss (weit jenseits der 
C-Sprachstandards).

Fazit:
Wer in solchen Szenarien trotzdem C benutzt, hat einfach irgendwie nicht 
alle Latten am Zaun. Das kann nur ein eingefleischter C-Wanker sein, der 
nix anderes will (und nix anderes kann)...

von Sebastian S. (amateur)


Lesenswert?

Was um alles in der Welt soll:
Out ff
direkt gefolgt von
Out 0
?

Liegt Deine Reaktionszeit nicht in der Größenordnung eines Oszilloskops, 
"siehst" Du nur 00.

Inc r16
verkürzt nur die "Ein-zeit".

von m.n. (Gast)


Lesenswert?

Sebastian S. schrieb:
> Inc r16
> verkürzt nur die "Ein-zeit".

Nein, nicht einmal das. Es sollte nur unbemerkt das SREG zerschiessen.

fixi schrieb:
> Wie schnell wäre denn die Reaktion bei aktivem Warten durch polling auf
> einen Spannungspegel am Pin?

Wie wäre es denn, uns einmal zu sagen, was es werden soll?

von Sebastian S. (amateur)


Lesenswert?

@ m.n.

Es ist natürlich ein sehr seltener Sonderfall, aber die Sequenz:

>PUSH      R16            Push register on stack
>SER       R16            Set Register
>OUT       0x18,R16       Out to I/O location
>LDI       R16,0x00       Load immediate
>OUT       0x18,R16       Out to I/O location
>POP       R16            Pop register from stack
>RETI                     Interrupt return

lässt das Statusregister in Ruhe.

Deshalb gilt:

>Nein, nicht einmal das. Es sollte nur unbemerkt das SREG zerschiessen.

nicht.

von m.n. (Gast)


Lesenswert?

Sebastian S. schrieb:
> Es ist natürlich ein sehr seltener Sonderfall, aber die Sequenz:

Soll ich jetzt noch meine Scherze erklären?

von fixi (Gast)


Lesenswert?

Ist die Reaktion beim aktiven Warten durch polling auf ein High signal 
am Pin schneller als bei einem externen Interrupt?

von m.n. (Gast)


Lesenswert?

Du verschweigst auch auf diverse Nachfragen beharrlich Deine genauen 
Anforderungen. Da hat man doch keine Lust mehr, auf nachgelegte Fragen 
zu antworten.

von fixi (Gast)


Lesenswert?

Die Anforderung ist ganz klar eine möglichst schnelle Reaktion eines 
Ausgangs auf ein Eingangssignal :) Alles andere würde nur zu 
Abschweifungen führen.

von Arduinoquäler (Gast)


Lesenswert?

fixi schrieb:
> Ist die Reaktion beim aktiven Warten durch polling auf ein High signal
> am Pin schneller als bei einem externen Interrupt?

Wie wäre es denn wenn du so einen einfachen Sachverhalt einfach
schnell selbst duch Eigeninitiative in der Praxis klärst. Die
Voraussetzungen dazu hast du ja offensichtlich:

fixi schrieb:
> An meinem atmega8 löse ich einen externen interrupt aus. In der ISR wird
> lediglich ein Ausgang auf high gesetzt (1 Zeile Code). Die Reaktionszeit
> beträgt bei 16MHz ca 1,25 Mikrosekunden.

Andererseit drängst sich bei solchen Fragen und Handlungs-
zusammenhängen deinerseits der Verdacht auf dass hier im Thread
etwas gefaked wird, oder getrollt.

von fixi (Gast)


Lesenswert?

Arduinoquäler schrieb:
> Andererseit drängst sich bei solchen Fragen und Handlungs-
> zusammenhängen deinerseits der Verdacht auf dass hier im Thread
> etwas gefaked wird, oder getrollt.

Ich hab ja sonst nichts zu tun...

Es ist doch ganz einfach. Ich habe kaum Zeit um das auszutesten und will 
einfach nur wissen wie die Erfolgsaussichten sind, bevor ich damit 
anfange. Darum wende ich mich hier an das Forum. Und genau das ist doch 
Sinn und Zweck eines Forums. Es ist doch eine ganz einfach Frage. 
Entweder man weiß die Antwort und hilft, oder man lässt es eben sein. 
Aussagen wie "such doch selbst" oder "teste es doch" sind hier das 
eigentliche rumgetrolle.

von Arduinoquäler (Gast)


Lesenswert?

fixi schrieb:
> Es ist doch ganz einfach. Ich habe kaum Zeit um das auszutesten und will
> einfach nur wissen wie die Erfolgsaussichten sind, bevor ich damit
> anfange.

Verstehe.

Bei solch einem aufwendigen, umfangreichen Test muss man sich
schon gut überlegen ob, wie und wann man diesen Aufwand treibt.

Ich veranschlage mal ca vier Wochen Arbeit a 50 Stunden für das
Schreiben von 10 Zeilen Code, das Hochladen auf den Controller
und das Anschliessen eines Oszilloskops. Betrachten und
Analysieren des Ergebnisses noch nicht einbezogen.

Alternativ dazu drei Wochen Arbeit für das Schreiben von 10
Zeilen Code und das analysieren des compilierten C-Codes auf
Assembler-Ebene.

von Stefan K. (stefan64)


Lesenswert?

fixi schrieb:
> Die Anforderung ist ganz klar eine möglichst schnelle Reaktion eines
> Ausgangs auf ein Eingangssignal

Wenn das alle Anforderungen sind, dann ist Dein Ansatz falsch.
Dieses Verhalten kannst Du mit einem Timer-Eingang und seinem 
OCR-Ausgang lösen. Dazu ist keine CPU-Interaktion erforderlich.

von Amateur (Gast)


Lesenswert?

Grundsätzlich würde ich erst mal auf:

ISR(INT1_vect, ISR_NAKED) {
  PORTB = ~PORTB;
  reti();
}

Umsteigen.

Wie bereits gesagt natürlich nur, wenn Deine Reaktionszeit unter der 
eines Oszilloskops liegt;-)

Wie es mit den minimal möglichen Zeiten aussieht wurde bereits gesagt:

"Unterbrechung erkennen"      Automatisch, abhängig vom Zeitpunkt
                              und gerade aktuellem Befehl    Variabel
PUSH PC                       Automatisch                    Fixzeit
Load Interruptvector -> PC    Automatisch                    Fixzeit
PUSH Work                     Dein Ding PUSH                 Fixzeit
MOV                           Dein Ding Hole Status          Fixzeit
PUSH Status                   Dein Ding PUSH                 Fixzeit
„Hau rein“                    Dein Ding akt. Status holen 1) Fixzeit
                              Dein Ding invertieren und   2) Fixzeit
                              Dein Ding zurückschreiben   3) Fixzeit
POP Status                    Dein Ding POP                  Fixzeit
MOV Status                    Dein Ding                      Fixzeit
POP Work                      Dein Ding POP                  Fixzeit
POP PC                        Dein Ding Indirekt durch IRET  Fixzeit

In diesem Falle muss auch das Statusregister gesichert werden, da der 
Befehl: COM wahrscheinlich den Status ändert.

Besondere Wünsche sehen weder der Compiler noch die Befehlsliste vor.

von Route_66 H. (route_66)


Lesenswert?

fixi schrieb:
> Die Anforderung ist ganz klar eine möglichst schnelle Reaktion eines
> Ausgangs auf ein Eingangssignal :)

Wie wäre es, einen Draht vom Eingang zum Ausgang zu löten. Schneller 
gehts dann nicht mehr.

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.