PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Zeit genau messen



Johannes G.
23.08.2006, 18:55
Hallo,

also, ich habe 2 Taster, T1 und T2. Wenn ich auf T1 drücke soll der AVR anfangen die Zeit zu stoppen bis T2 gedrückt wird (auf 0.01 sek genau wenn es so genau geht ;) ).

Wie programmiere ich sowas am besten?
Da ich noch nicht lage mit AVRs arbeite hab ich da keine Idee wie ich das machen könnte...


Viele Grüße,
Johannes

BlackDevil
23.08.2006, 20:45
ich kann zwar kein C aber überleg dir ma folgendes:
WENN (PORTX=1) THEN
TimerStart
ENDIF

und in den overflow vom timer dann halt wen porty ein high bekommt

denke mal

Johannes G.
24.08.2006, 09:51
Hm, an Timer habe ich auch schon gedacht, aber ich weiß leider nicht, wie die genau funktionieren, bzw. wie ich die mit C starte/stoppe etc...

ogni42
24.08.2006, 10:32
16Bit Timer verwenden.

Dann zwei Interruptroutinen:
Die erste für die Tastendrücke. Wenn die T0 gedrückt wird, werden die Ergebnisregister und das Timerregister zurück gesetzt. Wenn T1 gedrückt wird, wird auf das Ergebnis der aktuelle Timerwert aufaddiert und in eine andere Variable weggeschrieben.

Die zweite Reagiert auf einen Timerüberlauf und zählt das Ergebnissregister hoch.

Bei einem Mega168 kommt man so auf eine Genauigkeit von
ca. 1/20MHz/8 (wegen Verzögerungen in den Interruptroutinen, max. 8 Takte zum Interruptvektor ausführen und Timerregister lesen) und einen Zählbereich von 65536/20Mhz * 32bit = 14073748,8355328s also ca. 3909 Stunden.

Die genaue Funktion der Timer kannst Du in den Datenblättern von Atmel nachlesen. Da gibt es meistens auch Beispiele in C

Johannes G.
24.08.2006, 12:57
Ok, dann fang ich als erstes mit den Interrupts an.
Ich wollte an INT0 und INT1 je einen Taster anschließen... Brauche ich da Pullups/Pulldowns?

Im Datenblatt vom AVR (mega8) hab ich aber kein Beispiel gefunden, wie ich das dann genau programmieren muss, kann mir da bitte jemand mal ein Beispiel zeigen?

Viele Grüße,
Johannes

ogni42
24.08.2006, 14:38
Ja, am einfachsten:

Einen Pullup von 10k und einen Kondensator 47n nach Masse. Den Taster vom Pin ebenfalls nach Masse. So ist die Taste entprellt und Du sparst Dir das Entprellen in Software.

Die Konfiguration ist dann für den INT0 bzw. INT1 "Interrupt on falling edge"

Es gibt Beispiele für die Interruptprogrammierung in den AVR GCC Turtorials hier im wiki oder auf mikrocontroller.net

SprinterSB
24.08.2006, 15:09
Etwas einfacher geht's hier, weil du 2 verschiedene Taster hast auf denen je nur eine Funktion liegt: zu Entprellen brauchst du eigentlich nicht (schon garnicht bei einer Auflösung von 10ms).

Die internen PullUps aktivieren und die Taster vom jeweiligen Port nach Masse.

Falls der Start-Taster kurz prellt, wirst du den Timer zweimal oder öfter kurz hintereinander starten, was aber nicht weiter stören sollte.

Falls du entprellen willst geht das auch einfach dadurch, daß du in INT0 (Start) den INT0 deaktivierst und INT1 (Ende) aktivierst. Und erst in INT1 wird INT0 wieder aktiviert und INT1 deaktiviert.

ogni42
24.08.2006, 15:34
Stimmt, elegante Lösung, das spart ein paar Bauteile.

Johannes G.
24.08.2006, 15:57
Hallo,

ich bekomme das mit den Interrupts nicht ganz hin, hier mal der Code:


#define F_CPU 16000000
#include <avr/io.h>
#include <avr/interrupt.h>
#include <inttypes.h>
#include "usart.c"

ISR(INT0_vect)
{
usart_print("INT0\n");
}

int main()
{
usart_init();
sei();
usart_print("Warte auf Interrupt...\n");
while(1)
;
}



Aber wenn ich INT0 mit VCC verbinde passiert gar nix...


Viele Grüße,
Johannes

RCO
24.08.2006, 16:02
Falls der Start-Taster kurz prellt, wirst du den Timer zweimal oder öfter kurz hintereinander starten, was aber nicht weiter stören sollte.

Es geht aber ohne weiteres auch genauer. Nachdem der Interrupt des ersten Tasters ausgelöst wurde, wird dierser sofort deaktiviert und der Interrupt des zweiten Tasters enabled. Ein Nachprellen auf dem ersten Taster löst somit keine Interruptroutine mehr aus.

Johannes G.
24.08.2006, 16:04
Wie kann ich einzelne Interrupts deaktivieren?
Mit cli(); deaktiviere ich ja alle...

RCO
24.08.2006, 16:14
Ich meine hier:

General Interrupt Control Register – GICR
Seite 49 im Datenblatt

Sorry, ich bin da irgendwie garnicht mehr drin...

ogni42
24.08.2006, 16:26
@johannes:

Schau bitte mal in eines der oben erwähnten Tutorials rein. Da ist alles bestens erklärt.

Kurzer Hinweis: Du musst erst mal Deine Interrupt Register so einstellen, dass der INT0 und INT1 frei geschaltet sind.,

Johannes G.
24.08.2006, 17:37
Hallo,

ok, also hier der aktuelle Code


#define F_CPU 16000000
#include <avr/io.h>
#include <avr/interrupt.h>
#include <inttypes.h>
#include "usart.c"

ISR(INT0_vect)
{
usart_print("INT0\n");
}

ISR(INT1_vect)
{
usart_print("INT1\n");
}

int main()
{
usart_init();

GIMSK |= (1 << INT1) | (1 << INT0);
MCUCR |= (1 << ISC11) | (1 << ISC10);
MCUCR |= (1 << ISC01) | (1 << ISC00);

sei();

usart_print("Warte auf Interrupt\n");
while(1)
;
}


Wenn ich nun INT0 oder INT 1 mit VCC verbinde passiert manchmal gar nix, manchmal kommt Zeichensalat im Terminal an..


Viele Grüße,
Johannes

RCO
24.08.2006, 19:55
Hm, also usart.c habe ich noch nie benutzt.
Setze auf jeden Fall mal die DDR.

ich glaube:
DDRD = 0bxxxx0010;

x := wie es bei dir ist.

Dann solltest du die Ports noch Null setzten (Pulldown):

PORTD &= ~(1<<2);
PORTD &= ~(1<<3);

Johannes G.
25.08.2006, 08:37
Hm, also usart.c habe ich noch nie benutzt. Das ist meine eigene Datei und die funktioniert sonst immer ;)


Setze auf jeden Fall mal die DDR.

ich glaube:
DDRD = 0bxxxx0010;

x := wie es bei dir ist.

Was meinst du damit?

plusminus
25.08.2006, 11:02
Er weiß ja nicht genau, wie die DatenRichtung an deinem PortD aussieht...

Johannes G.
25.08.2006, 12:35
An meinem AVR ist gar nix angeschlossen, nur eben die 2 Taster an INT0/1

Soll ich Port D als eingang oder ausgang definieren?


Viele Grüße,
Johannes

RCO
25.08.2006, 13:03
Er weiß ja nicht genau, wie die DatenRichtung an deinem PortD aussieht...

genau


An meinem AVR ist gar nix angeschlossen, nur eben die 2 Taster an INT0/1
Soll ich Port D als eingang oder ausgang definieren?

Beides alleine geht nicht, da D2 und D3 je Eingänge sein müssen. Du willst aber per serieller Schnittstelle senden, deshalb muss PD2 auf jeden fall Ausgang sein. Falls du noch Empfangen willst, dann muss PD1 ein Eingang sein. Wenn aber sonst ncihts anderes and den AVR angeschlossen ist, probier mal das:

DDRD = 0b11110010;
PORTD &= ~(1<<2);
PORTD &= ~(1<<3);

Wenn du D2 oder D3 jetzt an 5V legst sollte was passieren ;-)
DDRD ist das Data Direction Register des Port D. Eine 1 bedeutet Ausgang, eine 0 Eingang.