PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : TIMER,Interrupts und Pulse zählen



masterx
23.02.2011, 10:46
Moin zusammen!
Ich möchte mit einem ATMEGA 32/16Mhz an PB0 externe Rechteckimpulse zählen(für ene bestimmte Zeit )also zusätzlich noch einen internen Timer benutzen!(Drehzahl Bestimmung max 16000 impulse pro Minute)
Nun werde ich wohl um das Thema Interrupt routinen und Timer nicht herumkommen!
Ich habe mich nun intensivst mit den beiden Themen beschäftigt aber nach nun knapp einer Woche bin ich leider zu noch überhaupt keinem Ergebnis gekommen!
Ich verstehe das mit den Registern und den Timern ...etc. einfach nicht!
Dabei benötige ich ja eigentlich nur wenige Befehle!
Ich programmiere in C!

Weiterhin möchte ich eine Spannnung an PA7 abfragen und dann einen Schaltzustand an einem Port erwirken!
Wenn PA7 zwischen 1000 und 1024(High) dann soll PC4 HIGH sein
...aber nach nochmaligen betätigen soll PC4 wieder LOW sein!

Die Abfrage der Ports und das Schalten der Zustände ist klar , aber bei meinen Programmierversuchen funktioniert immer nur der Zustand der in der Syntax als erstes steht!Habe es mit Pointern probiert um die AN/AUS Bedingungen gegeneinander zu verriegeln aber geht auch nicht!!!

Ich befürchte das hier eine Interrupt Routine angebracht ist aber auch mit diesem Thema habe ich wahrlich noch keine Fortschritte erzielen können!
Habe nun schon viele Tutorien durch, verstehe aber nichts!

Vielleicht könnt Ihr mir ja etwas auf die Sprünge helfen!

Viele Grüße Stephan

Hubert.G
23.02.2011, 14:08
Vielleicht könntest du mal zeigen womit du nicht zurecht kommst, ist sicher einfacher.

masterx
23.02.2011, 18:17
Ich weiß nicht , wie ich die Funktion :
int zähl_Pulse(void)
mit Pulsen an PB0 für 500 ms zählen...

und einen Interrupt programmieren soll der mir bei der Betätigung eines Tasters (High an PB7) einen anderen Port auf HIGH/LOW schaltet

Grüße Stephan

Hubert.G
23.02.2011, 18:39
Im Timer0 gibt es die Option Ext. Clock Source.
Interrupt Timer Overflow aktivieren, im Interrupt eine Variable hochzählen.
Mit Timer1 eine Zeitbasis generieren mit alle 0,5sec. ein Interrupt.
Mit Start von Timer1 wird auch Timer0 gestartet, nach 0,5sec werden beide Timer gestoppt.
Anzahl der Überläufe von Timer0 *256 + dem Zählerstand von Timer0 ergibt die Anzahl der Takte in 0,5sec.
Tastenerkennung machst du am besten durch pollen, prellen der Taste berücksichtigen.

masterx
23.02.2011, 19:05
Erstmal vielen Dank für die Antwort! Aber ich weiß einfach nicht welche Befehle ich für welchen Timer nehmen muß oder welche Register ich wann auslesen muß und wie die heißen!Ich habe echt null Ahnung von Timern und Interrupts!Auch was Polling ist weiß ich nicht!

Egal was ich darüber lese ich kann aus keinem Tutorial etwas sinnvolles für mich herausfiltern!

Viele Grüße Stephan

masterx
26.02.2011, 14:16
So, ich habe mal was probiert aber es klappt nicht!!!

int drehzahl_MOT(void)
{

int n = 0;


OCR1AH = 0x0c; //Torzeit festlegen 200ms (16Mhz/1024)/5
OCR1AL = 0x35;

TCCR1A = TCCR1A | ~(1<< COM1A1) | ~(1<< COM1A0); //TIMER 1 NORMAL PORT OPERATIONS
TCCR0 = TCCR0 | (1<< CS02) | (1<< CS01) | (1<< CS00); //TIMER0:EXT. Takt an PB0 Steigende Flanke zählen
TCCR1B = TCCR1B | (1<< CS12) | ~(1<< CS11) | (1<< CS10); //TIMER1:PRESCALE 16MHZ/1024 START

while((1<< OCF1A))n = TCNT0; //Abfrage COMPARE Register Timer 1 wenn TORZEIT erreicht ---->dann weiter

TCCR0 = TCCR0 | ~(1<< CS02) | ~(1<< CS01) | ~(1<< CS00); //TIMER0 STOPP
TCCR1B = TCCR1B | ~(1<< CS12) | ~(1<< CS11) | ~(1<< CS10); //Timer1 STOPP
TIFR = TIFR | (1<< OCF1A); //COMPARE-FLAG rücksetzen
TCNT0 = 0x00;
TCNT1H = 0x00;
TCNT1L = 0x00;

return n;

}


ich weiß echt nicht weiter !Ich kappiere das einfach nicht mit den Timern und den Interrupts!!!

WER KANN MIR HELFEN???


Viele Grüße Stephan

Hubert.G
26.02.2011, 14:49
Zum einen verwendest du hier keinen Interrupt.
Bits in einem Register löscht man so:
TCCR1B &= ( ~(1<< CS12) | ~(1<< CS11) | ~(1<< CS10)); //Timer1 STOPP
Schau dir mal diese Tutorial an:
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial

TobiKa
26.02.2011, 14:53
Hier kannst auch mal reingucken (aufmerksam lesen und versuchen es zu verstehen) http://www.rn-wissen.de/index.php/Timer/Counter_(Avr)

masterx
26.02.2011, 15:23
Also ich habe diese tutorials jetzt zum xten mal durch aber mir erschließt sich einfach nicht das Zusammenspiel der Timer und der Interrupts und die Anwendung der dazugehörigen Befehle!!!!

Ich möchte doch nur für eine frei wählbare Zeit Pulse an PB0 zählen!

Tut mir leid ich verstehe das alles nicht!

Vielen Dank für eure Hilfe und viele Grüße Stephan

masterx
26.02.2011, 19:06
So, der letzte Versuch!! Was mache ich falsch der Compiler macht nichts daraus!!!

Vielleicht könnt ihr mir ja mal etwas tatkräftiger unter die Arme greifen anstatt dauernd auf die Tutorials zu verweisen!!!Das hilft mir auch nicht allzusehr weiter!


int n = 0;

TCCR1A = 0x00; //TIMER1 kein PWM ,Normale Portfunktion
TCCR1B = 0xcd; //Fette Modifizierung
OCR1AH = 0x30; //Schreiben Torzeit Kompare-Register High-Byte
OCR1AL = 0xd4; //Schreiben Torzeit Kompare-Register Low-Byte
TCCR0 = 0x07; //Grob:TIMER0 zähler bei steigender Flanke

TIMSK |= (1<<OCIE1A); // Compare Interrupt erlauben
sei(); // Global Interrupts aktivieren

ISR (TIMER1_COMPA_vect)
{
n = TCNT0;
TCNT0 = 0;
TCCR0 = 0x07; //Grob:TIMER0 zähler bei steigender Flanke
}


das habe ich mitten in die Main gepflanzt und habe gehofft das nun n von jeder Stelle im Programm gelesen werden könnte!

Viele Grüße Stephan

TobiKa
26.02.2011, 19:40
das habe ich mitten in die Main gepflanzt und habe gehofft das nun n von jeder Stelle im Programm gelesen werden könnte!
Tja, und das ist absoluter Murks.
Ja du hoffst...


Vielleicht könnt ihr mir ja mal etwas tatkräftiger unter die Arme greifen anstatt dauernd auf die Tutorials zu verweisen!!!Das hilft mir auch nicht allzusehr weiter!
Sag doch einfach das es jemand für dich machen soll.
Es ist in den Tutorials so schön beschrieben... aber da du scheinbar keinerlei Grundkenntnisse hast, kann ich dir nur raten dir diese anzueignen.

Und beklagt dich nicht über mangelnde Hilfe, solange von dir nur irgendein zusammengestückelter Code kommt, den du selbst nicht im Ansatz verstehst.

Man lernt das ganze nicht in 2 Tagen.

Hubert.G
26.02.2011, 19:49
Werde mal probieren ob ich was zusammen bringe

#define Timer_1_wert 0xd800
TIMSK=(1<<OCIE1A)|(1<<TOIE0); /* timer0 für Messtart,timer1 für F-Messung aktiv */
OCR1A=Timer_1_wert;

TCCR1B=0;
TCNT0=0;
TCCR0=(1<<CS01)|(1<<CS02); /*Clock von T0 falling edge*/
TCCR1B=(1<<CS10)|(1<<WGM12);Das #define kommt an den Anfang, der Rest ins main. Dann noch ISR
ISR(TIMER1_COMPA_vect){ /*Takt Sek*/
Timer_1z ++;
}
ISR(TIMER0_OVF_vect){ /*Überlauf timer 0*/
Timer_0z++;und ins main weiter dann die Auswertung
for(;;){
if(Timer_1z>=200){ /*torzeit 1 Sekunde */
TCCR0=0;
TCCR1B=0;
Timer_1z=0;
T0_wert=(Timer_0z*256+TCNT0);/* Anzahl der Überläufe * 256 + Zählerstand */
TCNT0=0;
Timer_0z=0;
TCNT1=0;Die Deklaration der Variblen fehlt noch, Taktfrequenz ist 11,0592MHz

masterx
01.03.2011, 15:46
Moin!

Folgendes habe ich bis jetzt erarbeitet aber es klappt immer noch nicht!
.....


int drehzahl_mot(void)
{
int n = 0;

OCR1AH = 0x30; //TIMER1:COMPARE REGISTER HIGH-BIT
OCR1AL = 0xd4; //TIMER1:COMPARE REGISTER LOW-BIT (COMPARE-WERT 12500 = 0,8 sec. bei 16 Mhz

TCCR1A = 0x00; //TIMER1:CONTROL REGISTER, Output-Pin nicht aktiv, Normaler Timerbetrieb

TCCR1B = 0x05; //TIMER1:Prescale 1024,START
TCCR0 = 0x07; //Counter0, zählen

do
{

n = TCNT0;

}
while(TIFR & (1<<OCF1A)); //!!!Und das funktioniert nicht ich weiß nicht wie ich das Register /speziell das FLAG- Bit abfrage???

TCCR1B = 0x00; //TIMER1:STOP
TCCR0 = 0x00; //TIMER0:STOP

TCNT1H = 0x00;
TCNT1L = 0x00;
TCNT0 = 0x00;

TIFR |= (1<<OCF1A); //Flag löschen


return n;

}

Ich habe mir echt Mühe gegeben und ich hoffe es sieht nicht wieder so gestückelt aus!Habe es auch mit ISR probiert in den verschiedensten Versionen aber ich möchte es erstmal ohne machen damit es nicht zu kompliziert wird!

Nochmal die Funktion:
ATMEGA 32 16PU/16Mhz--> An PortB0 Rechteckimpulse zählen (TIMER0) für ca. 0.8 sec. (TIMER1)

Vielen Dank nochmal für eure Hilfe wollte euch wirklich nicht verärgern!!!

Viele Grüße Stephan

Felix G
01.03.2011, 20:40
while(TIFR & (1<<OCF1A)); //!!!Und das funktioniert nicht ich weiß nicht wie ich das Register /speziell das FLAG- Bit abfrage???Die Abfrage an sich sollte korrekt sein, und falls du das #include <avr/io.h> nicht vergessen hast sollte der Compiler nicht meckern...


Aber was passieren könnte ist, daß er die Schleife komplett wegoptimiert weil er denkt "Da drin passiert nichts, also brauche ich das auch nicht kompilieren". Wenn das der Fall ist, könntest du dein Programm so anpassen:


while(TIFR & (1<<OCF1A))
asm volatile("nop;");

Dann denkt der Compiler es gäbe etwas wahnsinnig Wichtiges in der Schleife von dem er aber nichts weiß.


PS.: Code ist hier im Forum besser lesbar, wenn du Code-Tags benutzt (also so:
Hier kommt dein Code rein)

edit:
wo wir gerade bei besser lesbar sind... das do{ habe ich komplett übersehen.
In diesem Fall könnte es helfen n als volatile zu deklarieren (aber auch so wie der Code jetzt ist dürfte der Compiler eigentlich nichts wegoptimieren)

sternst
01.03.2011, 21:12
do
{
n = TCNT0;
}
while(TIFR & (1<<OCF1A)); //!!!Und das funktioniert nicht ich weiß nicht wie ich das Register /speziell das FLAG- Bit abfrage???Die Schleife soll doch sicher laufen, bis das Flag gesetzt ist, und nicht so lange es gestzt ist, oder? Und warum ist das Lesen des Zählers in der Schleife?
Ich würde es eher so machen:

while(!(TIFR & (1<<OCF1A))); // Warten, bis Zeitfenster abgelaufen
n = TCNT0; // dann Ergebnis auslesen

wkrug
02.03.2011, 08:07
Ich denk mal, Du hast ein grundsätzliches Problem im Verstehen der Interrupts.
Ein Interrupt ist ein Programmteil, der Durch ein Hardware- ( manchmal auch Software- ) Ereignis ausgelöst wird.
Der gerade laufende Programmabschnitt wird verlassen und die zu dem Interrupt gehörige Interruptroutine aufgerufen.
Das verlassene Programm kriegt von dieser Aktion üblicherweise nichts mit!

Ich würde dein Problem wie folgt angehen.

1. Ein Timer wird auf externe Taktquelle umgeschaltet und der Overflowe Interrupt dieses Timers aktiviert.
In diesem Overflow Interrupt wird dann eine Hilfsvariable hochgezählt, damit man auch über den Zählerstand des verwendeten Timers hinauszählen kann.

2. Ein weiterer Timer wird so eingestellt, das alle 500ms ein Interrupt ausgelöst wird.
Das geht am schönsten mit dem Comparematch Interupt und einem Timer der im CTC Modus läuft.
Tritt dann dieser Interrupt nun auf wird der Zählerstand des "Zähler" Timers und der Hilfsvariable Ausgelesen und miteinander verrechnet.
Die Werte werden dann in einer beliebigen Variable gespeichert und ein Flag ( Bit Variable ) als Marker füe die Hauptroutine gesetzt.
Nun wird noch der TCNTx der Zähl Timers und die dazugehörige Hilfsvariable auf 0 gesetzt.

3. In der Hauptschleife wird das Bit Flag abgefragt und wenn es auf 1 steht der Zählerstand aus der Hilfsvariable abgeholt.
Das Bit Flag wird wieder auf 0 gesetzt, damit dieser Programmteil nur bei neuen Werten wieder durchgeführt wird. Voila - Fertig!

Searcher
02.03.2011, 10:15
Hallo,

ich hab meine Impulse folgendermaßen mit Pin Change Interrupt gezählt:

Einfach den PCINT auf dem Port zulassen, an dem der Rechteckimpuls anliegt.
Einen Timer auf Torzeit einstellen, so das er dann zB. alle 500ms einen Overflow Interrupt erzeugt.
In der Interruproutine zum PCINT einfach eine Variable hochzählen (Bei jeden Ansprung eins addieren).
In der Timerinterruptroutine die PCINT Variable zur weiteren Verwendung im Hauptprogramm auslesen und zurücksetzen.

PCINT kann auf bestimmten PINs so konfiguriert werden, daß er nur bei einer Flanke ausgelöst wird. Sonst ausgelesenen Wert durch 2 teilen.

EDIT: Zugegebenermaßen wird der Prozessor durch die vielen PCINTs belastet, aber wenn er sonst nicht viel zu tun hat...

Gruß
Searcher

wkrug
02.03.2011, 13:36
Die Methode von searcher funktioniert auch, allerdings wird dabei bei jedem Taktipuls ein Interrupt ausgelöst.
Man könnte das ebenso mit INT0, INT1 oder auch ICP machen.

Die Methode mit dem Timer belastet den Controller nicht so stark, da ja nur bei jedem 256 bzw 65536 Eingangsimpuls ( je nach verwendetem Timer ) ein Interrupt ausgelöst wird.

Es kommt auf die maximal zu erwartende Pulsanzahl / sek an, welche Methode man verwendet.

masterx
07.03.2011, 20:39
Endlich geschafft!!

Moin zusammen, habe alles gelöscht und einfach den Timer 0 zum zählen aktiviert und nach einer wait(200ms) funktion dann den Timer/Counter ausgelesen und TCNT0 wieder gelöscht!Zum Schluß noch Ausgabe des Zählstandes und ich hatte das was ich brauchte!

Ich habe es einfach nicht geschafft Overflows zu zählen oder mit den Flags die Do While Schleife zu bezwingen, interrupts einzubinden... !Aber ich habe echt viel gelernt und denke das ich darauf aufbauen kann!

Ich glaube ich muß noch ein wenig üben bevor ich TImer und Interrupts in meine Projekte einbeziehe!

Ich möchte mich an dieser Stelle bei euch allen bedanken und hoffe das ich vielleicht auch irgendwann mal Tipps geben kann!

Hier der Code:


int drehzahl_Mot(void)
{
int n = 0;

TCCR0 = 0x07; //Counter0, zählen

wait_ms(300);

n = TCNT0;

TCCR0 = 0x00; //TIMER0:STOP
TCNT0 = 0; //TIMER0: DATA REGISTER = 0

n = ((n * (1000/5) * 1 *2)); //1000/300*60 == 1000/5*1

return n ;
}


Bis dahin alles Gute und viele Grüße Stephan