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
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.
Wenn Du kein EEPROM brauchst, kannst Du versuchen, den ATMEGA höherzutakten um so noch etwas zu gewinnen, 20-22Mhz sollten drin sein.
Ok, danke für die antworten. Trotzdem möchte ich das programm noch so schnell machen wies geht.
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 ;-)
> 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).
> ...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.
> 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.
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.
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.
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...
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.