Forum: Mikrocontroller und Digitale Elektronik sehr schnelle Signale messen


von Shin (Gast)


Angehängte Dateien:

Lesenswert?

Ich versuche mithilfe des Atmega32 ein Signal zu Analysieren. Ich 
benutze dafür den 16-bit timer und das Input Capture feature. Ich 
triggere nur auf die positiven flanken da mich nur die abstände zwischen 
den Pulsen und nicht die Pulslängen interessieren.

Es funktioniert eigentlich recht alles gut. Nur manchmal gilt es 
schnelle Signale zu messen (ca. 2.7 us) und da stosse ich an die 
grenzen. Ich habe nur noch falsche Messwerte. Nun ich denke in meinem 
programm gibt es noch vieles dass man "besser" bzw schneller 
programmieren kann (ich mache sowas zum ersten mal) und frage mich nun 
wie ich die berechnung mit Overflow und Zählerstände am besten mache um 
keine Pulse zu verpassen.

Der Atmega habe ich mit 16Mhz getaktet und den timer habe ich ohne 
prescaler (0) geschaltet (es muss ziemlich genau sein, und deshalb ist 
das nötig)

Im Anhang findet ihr den Code (nur was damit zu tun hat, den rest habe 
ich weggelöscht)

Ich hoffe ihr könnt mir Tipps geben wie ich "Messung" verschnellern 
kann.

gruss shin

von 6640 (Gast)


Lesenswert?

Viel mehr ist mit einen 16MHz Controller nicht machbar. Das ist eine 
Aufgabe fuer ein CPLD. Eine Capture Einheit besteht aus ein paar 
Zaehlern und ein paar Flipflops. Den Takt kann man auf ueber 100MHz 
haben und dabei die 6 fache Aufloesung eines 16MHz Clocks erreichen.

von Knut B. (Firma: TravelRec.) (travelrec) Benutzerseite


Lesenswert?

Wenn Du kein EEPROM brauchst, kannst Du versuchen, den ATMEGA 
höherzutakten um so noch etwas zu gewinnen, 20-22Mhz sollten drin sein.

von Shin (Gast)


Lesenswert?

Ok, danke für die antworten. Trotzdem möchte ich das programm noch so 
schnell machen wies geht.

von Armada (Gast)


Lesenswert?

Ja, da stösst der Atmega wirklich an seine Grenzen.

von Schwurbl (Gast)


Lesenswert?

Du solltest den Aufruf von Messen() vermeiden, da hierbei der komplette 
Registersatz des AVR gerettet werden muß, was Zeit kostet.

Außerdem ist mir unverständlich, warum in der Interruptbehandlung die 
Interrupts disabled werden, was sie bereits sind. Kostet Zeit und birgt 
Fehler (vor allem das wieder einschalten bei sei)

Warum im else-Zweig auf 1 Prüfen? Es gibt keine andere Möglichkeit.

Warum nach Prüfung auf 1 wieder die Zuweisung der 1? Das kostet nur 
Zeit.

Ich hoffe, die Array-Größe von Messung[] ist 101, nicht 100 ;-)

von Dietmar E (Gast)


Lesenswert?

>  solltest den Aufruf von Messen() vermeiden, da hierbei der komplette
Registersatz des AVR gerettet werden muß

Nicht unbedingt, bei Funktionen, die nur einmal verwendet werden, kann 
man eigentlich davon ausgehen, dass der Compiler die "inline" 
compiliert, ohne Aufruf (zur Sicherheit ein inline-Attribut vor den 
Namen der Funktion).

von Entwickler (Gast)


Lesenswert?

> ...bei Funktionen, die nur einmal verwendet werden, kann
> man eigentlich davon ausgehen, dass der Compiler die "inline"
> compiliert...

Wohl kaum, wenn die Funktion nicht mal als static deklariert ist.

von Unbekannter (Gast)


Lesenswert?

> Wohl kaum, wenn die Funktion nicht mal als static deklariert ist.

Kommt auf die Optimierungs-Flags an.

Wenn der Compiler Code vergrößern darf um Speed zu gewinnnen (z.B. -O3 
beim gcc), dann kann er die Funktion zweimal einbauen. Einmal als inline 
und einmal normal.

Ob der Compiler das macht, und bis welchen Funktionsgrößen, ist 
Einstellungssache.

von Entwickler (Gast)


Lesenswert?

Hm. Ok, dann soll's mir recht sein :-)

Was mich sonst noch beunruhigt ist die Tatsache, dass 'overflow' 
asynchron inkrementiert wird und möglicherweise nicht zusammen gehörende 
Paare aus 'overflow' und ICR1 verarbeitet werden. Da fällt mir nicht mal 
eine Abhilfe ein.

Die optimierte Version könnte derzeit so aussehen:
1
unsigned long Messung[100];
2
unsigned char M_Nr = 0;
3
unsigned long overflow = 0;
4
unsigned int capture1 = 0,capture2;
5
6
SR(TIMER1_CAPT_vect)
7
{
8
    capture2 = ICR1;
9
    Messung[M_Nr++] = overflow + (capture2 - capture1);
10
    if (M_Nr == 100)
11
        M_Nr = 0;
12
    overflow = 0;
13
    capture1 = capture2;
14
}
15
16
ISR(TIMER1_OVF_vect)
17
{
18
    overflow += 65536UL;
19
}

Wohl wissend, dass die erste Flanke so oder so kein gültiges Ergebnis 
liefern könnte. Also weg mit dem 'i'-Flag. Mit der Klammerung wird eine 
16 Bit Operation erzwungen.

von ulrich (Gast)


Lesenswert?

Man könnte Teile in ASM schreiben. Besonders beim overhead der 
Interrrupts kann man da einiges sparen. Zumindestens GCC ist da gerade 
nicht sehr effektiv. Wenn man alles, oder zumindestens den ganzen Teil 
der während der Messung läuft in ASM schreibt, kann man noch etwas mehr 
sparen, denn in ASM kann man seperate Register für das Hauptprogramm und 
die ISR nehmen. Das spart einem dann das retten der meisten Register. 
Außerdem kann man in ASM ggf. mit 24 Bit Integers rechenen.
Die 2,7 us sollten noch machbar sein, auch mit 16 Mhz Takt.

Wegen der Taktrate könnte man eventuell auf einen Mega64x ausweichen, 
der bis 20 MHz geht.

Das Problem mit dem nicht syncronen overflow läßt sich lösen. Der Fall 
der vorkommen kann, ist das die ICP ISR läuft, obwohl erst die overflow 
routine dran ist. Erkennen kann man diese ausnahme daran, das das 
overflow Interruptflag gesetzt ist und der ICP Wert klein (z.B. kleiner 
als 0x800) ist. In diesem Ausnahmefall muss man halt erst den overflow 
abarbeiten und dann den ICP.

von Entwickler (Gast)


Lesenswert?

Verstehe. Ich sehe zwei Fälle und schlage vor:
1
unsigned long Messung[100];
2
unsigned char M_Nr = 0;
3
unsigned long overflow = 0;
4
unsigned int capture1 = 0,capture2;
5
6
SR(TIMER1_CAPT_vect)
7
{
8
    if (TIMER1_OVF)
9
    {
10
        // Timer-Überlauf während Sprung in Capture-Interrupt
11
        overflow += 65536UL;
12
        RESET_TIMER1_OVF();
13
        capture2 = ICR1;
14
    }
15
    else
16
    {
17
        capture2 = ICR1;
18
        if (ICR1 < capture2)
19
        {
20
            // Timer-Überlauf zwischen Prüfung auf
21
            // Capture-Interrupt und Auslesen von ICR1
22
            overflow += 65536UL;
23
            RESET_TIMER1_OVF();
24
        }
25
    }
26
    Messung[M_Nr++] = overflow + (capture2 - capture1);
27
    if (M_Nr == 100)
28
        M_Nr = 0;
29
    overflow = 0;
30
    capture1 = capture2;
31
}
32
33
ISR(TIMER1_OVF_vect)
34
{
35
    overflow += 65536UL;
36
}

Keine Ahnung, wie man die Interrupt-Flags konkret abfragt und rücksetzt, 
aber im Prinzip...

von ulrich (Gast)


Lesenswert?

Es reicht nicht, nur das interruptsflag abzufragen, man muss auch noch 
testen ob der ICP klein ist. Wenn das TimerOverflow flag gesetzt ist, 
kann der ICP Wert eigentliich nur sehr groß oder sehr klein sein. Wenn 
der ICP sehr groß ist, ist der Timer overflow erst nach dem ICP 
passiert, und es muss nichts mehr vorgezogen werden.

Da Testen der Flags geht über das entsprechende Bit im 
TimerInterruptFlag Register. Zum Löschem wird da seltsamerweise eine 1 
reingeschrieben.

von Shinaha (Gast)


Lesenswert?

danke für die vielen hilfreichen antworten, ich denke damit kann ich 
schon vieles verbessern. Leider kann ich erst am Montag wieder daran 
arbeiten.

shin

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.